commit caad8cde58410f8cfbc039522829ad053f943703 Author: Alexey Prokhin Date: Thu Apr 5 11:10:48 2012 +0400 Squashed 'dmd2/' content from commit 10017d5 git-subtree-dir: dmd2 git-subtree-split: 10017d50eaaff4ecdc37a0153b6c37ea0b004c81 diff --git a/access.c b/access.c new file mode 100644 index 00000000..b8b677c6 --- /dev/null +++ b/access.c @@ -0,0 +1,412 @@ + +// Copyright (c) 1999-2012 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#include +#include +#include + +#include "root.h" +#include "rmem.h" + +#include "enum.h" +#include "aggregate.h" +#include "init.h" +#include "attrib.h" +#include "scope.h" +#include "id.h" +#include "mtype.h" +#include "declaration.h" +#include "aggregate.h" +#include "expression.h" +#include "module.h" + +#define LOG 0 + +/* Code to do access checks + */ + +int hasPackageAccess(Scope *sc, Dsymbol *s); + +/**************************************** + * Return PROT access for Dsymbol smember in this declaration. + */ + +enum PROT AggregateDeclaration::getAccess(Dsymbol *smember) +{ + return PROTpublic; +} + +enum PROT StructDeclaration::getAccess(Dsymbol *smember) +{ + enum PROT access_ret = PROTnone; + +#if LOG + printf("+StructDeclaration::getAccess(this = '%s', smember = '%s')\n", + toChars(), smember->toChars()); +#endif + if (smember->toParent() == this) + { + access_ret = smember->prot(); + } + else if (smember->isDeclaration()->isStatic()) + { + access_ret = smember->prot(); + } + return access_ret; +} + +enum PROT ClassDeclaration::getAccess(Dsymbol *smember) +{ + enum PROT access_ret = PROTnone; + +#if LOG + printf("+ClassDeclaration::getAccess(this = '%s', smember = '%s')\n", + toChars(), smember->toChars()); +#endif + if (smember->toParent() == this) + { + access_ret = smember->prot(); + } + else + { + if (smember->isDeclaration()->isStatic()) + { + access_ret = smember->prot(); + } + + for (size_t i = 0; i < baseclasses->dim; i++) + { BaseClass *b = (*baseclasses)[i]; + + enum PROT access = b->base->getAccess(smember); + switch (access) + { + case PROTnone: + break; + + case PROTprivate: + access_ret = PROTnone; // private members of base class not accessible + break; + + case PROTpackage: + case PROTprotected: + case PROTpublic: + case PROTexport: + // If access is to be tightened + if (b->protection < access) + access = b->protection; + + // Pick path with loosest access + if (access > access_ret) + access_ret = access; + break; + + default: + assert(0); + } + } + } +#if LOG + printf("-ClassDeclaration::getAccess(this = '%s', smember = '%s') = %d\n", + toChars(), smember->toChars(), access_ret); +#endif + return access_ret; +} + +/******************************************************** + * Helper function for ClassDeclaration::accessCheck() + * Returns: + * 0 no access + * 1 access + */ + +static int accessCheckX( + Dsymbol *smember, + Dsymbol *sfunc, + AggregateDeclaration *dthis, + AggregateDeclaration *cdscope) +{ + assert(dthis); + +#if 0 + printf("accessCheckX for %s.%s in function %s() in scope %s\n", + dthis->toChars(), smember->toChars(), + sfunc ? sfunc->toChars() : "NULL", + cdscope ? cdscope->toChars() : "NULL"); +#endif + if (dthis->hasPrivateAccess(sfunc) || + dthis->isFriendOf(cdscope)) + { + if (smember->toParent() == dthis) + return 1; + else + { + ClassDeclaration *cdthis = dthis->isClassDeclaration(); + if (cdthis) + { + for (size_t i = 0; i < cdthis->baseclasses->dim; i++) + { BaseClass *b = (*cdthis->baseclasses)[i]; + enum PROT access = b->base->getAccess(smember); + if (access >= PROTprotected || + accessCheckX(smember, sfunc, b->base, cdscope) + ) + return 1; + + } + } + } + } + else + { + if (smember->toParent() != dthis) + { + ClassDeclaration *cdthis = dthis->isClassDeclaration(); + if (cdthis) + { + for (size_t i = 0; i < cdthis->baseclasses->dim; i++) + { BaseClass *b = (*cdthis->baseclasses)[i]; + + if (accessCheckX(smember, sfunc, b->base, cdscope)) + return 1; + } + } + } + } + return 0; +} + +/******************************* + * Do access check for member of this class, this class being the + * type of the 'this' pointer used to access smember. + */ + +void AggregateDeclaration::accessCheck(Loc loc, Scope *sc, Dsymbol *smember) +{ + int result; + + FuncDeclaration *f = sc->func; + AggregateDeclaration *cdscope = sc->getStructClassScope(); + enum PROT access; + +#if LOG + printf("AggregateDeclaration::accessCheck() for %s.%s in function %s() in scope %s\n", + toChars(), smember->toChars(), + f ? f->toChars() : NULL, + cdscope ? cdscope->toChars() : NULL); +#endif + + Dsymbol *smemberparent = smember->toParent(); + if (!smemberparent || !smemberparent->isAggregateDeclaration()) + { +#if LOG + printf("not an aggregate member\n"); +#endif + return; // then it is accessible + } + + // BUG: should enable this check + //assert(smember->parent->isBaseOf(this, NULL)); + + if (smemberparent == this) + { enum PROT access2 = smember->prot(); + + result = access2 >= PROTpublic || + hasPrivateAccess(f) || + isFriendOf(cdscope) || + (access2 == PROTpackage && hasPackageAccess(sc, this)); +#if LOG + printf("result1 = %d\n", result); +#endif + } + else if ((access = this->getAccess(smember)) >= PROTpublic) + { + result = 1; +#if LOG + printf("result2 = %d\n", result); +#endif + } + else if (access == PROTpackage && hasPackageAccess(sc, this)) + { + result = 1; +#if LOG + printf("result3 = %d\n", result); +#endif + } + else + { + result = accessCheckX(smember, f, this, cdscope); +#if LOG + printf("result4 = %d\n", result); +#endif + } + if (!result) + { + error(loc, "member %s is not accessible", smember->toChars()); + } +} + +/**************************************** + * Determine if this is the same or friend of cd. + */ + +int AggregateDeclaration::isFriendOf(AggregateDeclaration *cd) +{ +#if LOG + printf("AggregateDeclaration::isFriendOf(this = '%s', cd = '%s')\n", toChars(), cd ? cd->toChars() : "null"); +#endif + if (this == cd) + return 1; + + // Friends if both are in the same module + //if (toParent() == cd->toParent()) + if (cd && getAccessModule() == cd->getAccessModule()) + { +#if LOG + printf("\tin same module\n"); +#endif + return 1; + } + +#if LOG + printf("\tnot friend\n"); +#endif + return 0; +} + +/**************************************** + * Determine if scope sc has package level access to s. + */ + +int hasPackageAccess(Scope *sc, Dsymbol *s) +{ +#if LOG + printf("hasPackageAccess(s = '%s', sc = '%p')\n", s->toChars(), sc); +#endif + + for (; s; s = s->parent) + { + if (s->isPackage() && !s->isModule()) + break; + } +#if LOG + if (s) + printf("\tthis is in package '%s'\n", s->toChars()); +#endif + + if (s && s == sc->module->parent) + { +#if LOG + printf("\ts is in same package as sc\n"); +#endif + return 1; + } + + +#if LOG + printf("\tno package access\n"); +#endif + return 0; +} + +/********************************** + * Determine if smember has access to private members of this declaration. + */ + +int AggregateDeclaration::hasPrivateAccess(Dsymbol *smember) +{ + if (smember) + { AggregateDeclaration *cd = NULL; + Dsymbol *smemberparent = smember->toParent(); + if (smemberparent) + cd = smemberparent->isAggregateDeclaration(); + +#if LOG + printf("AggregateDeclaration::hasPrivateAccess(class %s, member %s)\n", + toChars(), smember->toChars()); +#endif + + if (this == cd) // smember is a member of this class + { +#if LOG + printf("\tyes 1\n"); +#endif + return 1; // so we get private access + } + + // If both are members of the same module, grant access + while (1) + { Dsymbol *sp = smember->toParent(); + if (sp->isFuncDeclaration() && smember->isFuncDeclaration()) + smember = sp; + else + break; + } + if (!cd && toParent() == smember->toParent()) + { +#if LOG + printf("\tyes 2\n"); +#endif + return 1; + } + if (!cd && getAccessModule() == smember->getAccessModule()) + { +#if LOG + printf("\tyes 3\n"); +#endif + return 1; + } + } +#if LOG + printf("\tno\n"); +#endif + return 0; +} + +/**************************************** + * Check access to d for expression e.d + */ + +void accessCheck(Loc loc, Scope *sc, Expression *e, Declaration *d) +{ +#if LOG + if (e) + { printf("accessCheck(%s . %s)\n", e->toChars(), d->toChars()); + printf("\te->type = %s\n", e->type->toChars()); + } + else + { + printf("accessCheck(%s)\n", d->toPrettyChars()); + } +#endif + if (!e) + { + if (d->prot() == PROTprivate && d->getAccessModule() != sc->module || + d->prot() == PROTpackage && !hasPackageAccess(sc, d)) + { + error(loc, "%s %s is not accessible from module %s", + d->kind(), d->toPrettyChars(), sc->module->toChars()); + } + } + else if (e->type->ty == Tclass) + { // Do access check + ClassDeclaration *cd = (ClassDeclaration *)(((TypeClass *)e->type)->sym); + if (e->op == TOKsuper) + { + ClassDeclaration *cd2 = sc->func->toParent()->isClassDeclaration(); + if (cd2) + cd = cd2; + } + cd->accessCheck(loc, sc, d); + } + else if (e->type->ty == Tstruct) + { // Do access check + StructDeclaration *cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym); + cd->accessCheck(loc, sc, d); + } +} diff --git a/aggregate.h b/aggregate.h new file mode 100644 index 00000000..e11a8061 --- /dev/null +++ b/aggregate.h @@ -0,0 +1,320 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_AGGREGATE_H +#define DMD_AGGREGATE_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "root.h" + +#include "dsymbol.h" + +struct Identifier; +struct Type; +struct TypeFunction; +struct Expression; +struct FuncDeclaration; +struct CtorDeclaration; +struct DtorDeclaration; +struct InvariantDeclaration; +struct NewDeclaration; +struct DeleteDeclaration; +struct InterfaceDeclaration; +struct TypeInfoClassDeclaration; +struct VarDeclaration; +struct dt_t; + + +struct AggregateDeclaration : ScopeDsymbol +{ + Type *type; + StorageClass storage_class; + enum PROT protection; + Type *handle; // 'this' type + unsigned structsize; // size of struct + unsigned alignsize; // size of struct for alignment purposes + unsigned structalign; // struct member alignment in effect + int hasUnions; // set if aggregate has overlapping fields + VarDeclarations fields; // VarDeclaration fields + unsigned sizeok; // set when structsize contains valid data + // 0: no size + // 1: size is correct + // 2: cannot determine size; fwd referenced + Dsymbol *deferred; // any deferred semantic2() or semantic3() symbol + bool isdeprecated; // !=0 if deprecated + +#if DMDV2 + int isnested; // !=0 if is nested + VarDeclaration *vthis; // 'this' parameter if this aggregate is nested +#endif + // Special member functions + InvariantDeclaration *inv; // invariant + NewDeclaration *aggNew; // allocator + DeleteDeclaration *aggDelete; // deallocator + +#if DMDV2 + //CtorDeclaration *ctor; + Dsymbol *ctor; // CtorDeclaration or TemplateDeclaration + CtorDeclaration *defaultCtor; // default constructor + Dsymbol *aliasthis; // forward unresolved lookups to aliasthis + bool noDefaultCtor; // no default construction +#endif + + FuncDeclarations dtors; // Array of destructors + FuncDeclaration *dtor; // aggregate destructor + +#ifdef IN_GCC + Array methods; // flat list of all methods for debug information +#endif + + AggregateDeclaration(Loc loc, Identifier *id); + void semantic2(Scope *sc); + void semantic3(Scope *sc); + void inlineScan(); + unsigned size(Loc loc); + static void alignmember(unsigned salign, unsigned size, unsigned *poffset); + Type *getType(); + void addField(Scope *sc, VarDeclaration *v); + int firstFieldInUnion(int indx); // first field in union that includes indx + int numFieldsInUnion(int firstIndex); // #fields in union starting at index + int isDeprecated(); // is aggregate deprecated? + FuncDeclaration *buildDtor(Scope *sc); + int isNested(); + int isExport(); + + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf); + + // For access checking + virtual PROT getAccess(Dsymbol *smember); // determine access to smember + int isFriendOf(AggregateDeclaration *cd); + int hasPrivateAccess(Dsymbol *smember); // does smember have private access to members of this class? + void accessCheck(Loc loc, Scope *sc, Dsymbol *smember); + + enum PROT prot(); + + // Back end + Symbol *stag; // tag symbol for debug data + Symbol *sinit; + Symbol *toInitializer(); + + AggregateDeclaration *isAggregateDeclaration() { return this; } +}; + +struct AnonymousAggregateDeclaration : AggregateDeclaration +{ + AnonymousAggregateDeclaration() + : AggregateDeclaration(0, NULL) + { + } + + AnonymousAggregateDeclaration *isAnonymousAggregateDeclaration() { return this; } +}; + +struct StructDeclaration : AggregateDeclaration +{ + int zeroInit; // !=0 if initialize with 0 fill +#if DMDV2 + int hasIdentityAssign; // !=0 if has identity opAssign + int hasIdentityEquals; // !=0 if has identity opEquals + FuncDeclaration *cpctor; // generated copy-constructor, if any + FuncDeclarations postblits; // Array of postblit functions + FuncDeclaration *postblit; // aggregate postblit + + FuncDeclaration *xeq; // TypeInfo_Struct.xopEquals + static FuncDeclaration *xerreq; // object.xopEquals +#endif + + StructDeclaration(Loc loc, Identifier *id); + Dsymbol *syntaxCopy(Dsymbol *s); + void semantic(Scope *sc); + Dsymbol *search(Loc, Identifier *ident, int flags); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + char *mangle(); + const char *kind(); + void finalizeSize(); +#if DMDV1 + Expression *cloneMembers(); +#endif +#if DMDV2 + int needOpAssign(); + int needOpEquals(); + FuncDeclaration *buildOpAssign(Scope *sc); + FuncDeclaration *buildOpEquals(Scope *sc); + FuncDeclaration *buildPostBlit(Scope *sc); + FuncDeclaration *buildCpCtor(Scope *sc); + + FuncDeclaration *buildXopEquals(Scope *sc); +#endif + void toDocBuffer(OutBuffer *buf); + + PROT getAccess(Dsymbol *smember); // determine access to smember + + void toObjFile(int multiobj); // compile to .obj file + void toDt(dt_t **pdt); + void toDebug(); // to symbolic debug info + + StructDeclaration *isStructDeclaration() { return this; } +}; + +struct UnionDeclaration : StructDeclaration +{ + UnionDeclaration(Loc loc, Identifier *id); + Dsymbol *syntaxCopy(Dsymbol *s); + const char *kind(); + + UnionDeclaration *isUnionDeclaration() { return this; } +}; + +struct BaseClass +{ + Type *type; // (before semantic processing) + enum PROT protection; // protection for the base interface + + ClassDeclaration *base; + int offset; // 'this' pointer offset + FuncDeclarations vtbl; // for interfaces: Array of FuncDeclaration's + // making up the vtbl[] + + size_t baseInterfaces_dim; + BaseClass *baseInterfaces; // if BaseClass is an interface, these + // are a copy of the InterfaceDeclaration::interfaces + + BaseClass(); + BaseClass(Type *type, enum PROT protection); + + int fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance); + void copyBaseInterfaces(BaseClasses *); +}; + +#if DMDV2 +#define CLASSINFO_SIZE_64 0x98 // value of ClassInfo.size +#define CLASSINFO_SIZE (0x3C+12+4) // value of ClassInfo.size +#else +#define CLASSINFO_SIZE (0x3C+12+4) // value of ClassInfo.size +#endif + +struct ClassDeclaration : AggregateDeclaration +{ + static ClassDeclaration *object; + static ClassDeclaration *classinfo; + static ClassDeclaration *throwable; + static ClassDeclaration *exception; + static ClassDeclaration *errorException; + + ClassDeclaration *baseClass; // NULL only if this is Object +#if DMDV1 + CtorDeclaration *ctor; + CtorDeclaration *defaultCtor; // default constructor +#endif + FuncDeclaration *staticCtor; + FuncDeclaration *staticDtor; + Dsymbols vtbl; // Array of FuncDeclaration's making up the vtbl[] + Dsymbols vtblFinal; // More FuncDeclaration's that aren't in vtbl[] + + BaseClasses *baseclasses; // Array of BaseClass's; first is super, + // rest are Interface's + + size_t interfaces_dim; + BaseClass **interfaces; // interfaces[interfaces_dim] for this class + // (does not include baseClass) + + BaseClasses *vtblInterfaces; // array of base interfaces that have + // their own vtbl[] + + TypeInfoClassDeclaration *vclassinfo; // the ClassInfo object for this ClassDeclaration + int com; // !=0 if this is a COM class (meaning + // it derives from IUnknown) + int isscope; // !=0 if this is an auto class + int isabstract; // !=0 if abstract class +#if DMDV1 + int isnested; // !=0 if is nested + VarDeclaration *vthis; // 'this' parameter if this class is nested +#endif + int inuse; // to prevent recursive attempts + + ClassDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses); + Dsymbol *syntaxCopy(Dsymbol *s); + void semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + int isBaseOf2(ClassDeclaration *cd); + + #define OFFSET_RUNTIME 0x76543210 + virtual int isBaseOf(ClassDeclaration *cd, int *poffset); + + virtual int isBaseInfoComplete(); + Dsymbol *search(Loc, Identifier *ident, int flags); + Dsymbol *searchBase(Loc, Identifier *ident); +#if DMDV2 + int isFuncHidden(FuncDeclaration *fd); +#endif + FuncDeclaration *findFunc(Identifier *ident, TypeFunction *tf); + void interfaceSemantic(Scope *sc); +#if DMDV1 + int isNested(); +#endif + int isCOMclass(); + virtual int isCOMinterface(); +#if DMDV2 + virtual int isCPPinterface(); +#endif + int isAbstract(); + virtual int vtblOffset(); + const char *kind(); + char *mangle(); + void toDocBuffer(OutBuffer *buf); + + PROT getAccess(Dsymbol *smember); // determine access to smember + + void addLocalClass(ClassDeclarations *); + + // Back end + void toObjFile(int multiobj); // compile to .obj file + void toDebug(); + unsigned baseVtblOffset(BaseClass *bc); + Symbol *toSymbol(); + Symbol *toVtblSymbol(); + void toDt(dt_t **pdt); + void toDt2(dt_t **pdt, ClassDeclaration *cd); + + Symbol *vtblsym; + + ClassDeclaration *isClassDeclaration() { return (ClassDeclaration *)this; } +}; + +struct InterfaceDeclaration : ClassDeclaration +{ +#if DMDV2 + int cpp; // !=0 if this is a C++ interface +#endif + InterfaceDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses); + Dsymbol *syntaxCopy(Dsymbol *s); + void semantic(Scope *sc); + int isBaseOf(ClassDeclaration *cd, int *poffset); + int isBaseOf(BaseClass *bc, int *poffset); + const char *kind(); + int isBaseInfoComplete(); + int vtblOffset(); +#if DMDV2 + int isCPPinterface(); +#endif + virtual int isCOMinterface(); + + void toObjFile(int multiobj); // compile to .obj file + Symbol *toSymbol(); + + InterfaceDeclaration *isInterfaceDeclaration() { return this; } +}; + +#endif /* DMD_AGGREGATE_H */ diff --git a/aliasthis.c b/aliasthis.c new file mode 100644 index 00000000..f8a74b48 --- /dev/null +++ b/aliasthis.c @@ -0,0 +1,79 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 2009-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "mars.h" +#include "identifier.h" +#include "aliasthis.h" +#include "scope.h" +#include "aggregate.h" +#include "dsymbol.h" + +#if DMDV2 + + +AliasThis::AliasThis(Loc loc, Identifier *ident) + : Dsymbol(NULL) // it's anonymous (no identifier) +{ + this->loc = loc; + this->ident = ident; +} + +Dsymbol *AliasThis::syntaxCopy(Dsymbol *s) +{ + assert(!s); + /* Since there is no semantic information stored here, + * we don't need to copy it. + */ + return this; +} + +void AliasThis::semantic(Scope *sc) +{ + Dsymbol *parent = sc->parent; + if (parent) + parent = parent->pastMixin(); + AggregateDeclaration *ad = NULL; + if (parent) + ad = parent->isAggregateDeclaration(); + if (ad) + { + assert(ad->members); + Dsymbol *s = ad->search(loc, ident, 0); + if (!s) + { s = sc->search(loc, ident, 0); + if (s) + ::error(loc, "%s is not a member of %s", s->toChars(), ad->toChars()); + else + ::error(loc, "undefined identifier %s", ident->toChars()); + } + else if (ad->aliasthis && s != ad->aliasthis) + error("there can be only one alias this"); + ad->aliasthis = s; + } + else + error("alias this can only appear in struct or class declaration, not %s", parent ? parent->toChars() : "nowhere"); +} + +const char *AliasThis::kind() +{ + return "alias this"; +} + +void AliasThis::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("alias "); + buf->writestring(ident->toChars()); + buf->writestring(" this;\n"); +} + +#endif diff --git a/aliasthis.h b/aliasthis.h new file mode 100644 index 00000000..c804cdb5 --- /dev/null +++ b/aliasthis.h @@ -0,0 +1,41 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 2009-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_ALIASTHIS_H +#define DMD_ALIASTHIS_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "mars.h" +#include "dsymbol.h" + +/**************************************************************/ + +#if DMDV2 + +struct AliasThis : Dsymbol +{ + // alias Identifier this; + Identifier *ident; + + AliasThis(Loc loc, Identifier *ident); + + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + const char *kind(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + AliasThis *isAliasThis() { return this; } +}; + +#endif + +#endif diff --git a/apply.c b/apply.c new file mode 100644 index 00000000..f5a5ede8 --- /dev/null +++ b/apply.c @@ -0,0 +1,165 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "mars.h" +#include "expression.h" + + +/************************************** + * An Expression tree walker that will visit each Expression e in the tree, + * in depth-first evaluation order, and call fp(e,param) on it. + * fp() signals whether the walking continues with its return value: + * Returns: + * 0 continue + * 1 done + * It's a bit slower than using virtual functions, but more encapsulated and less brittle. + * Creating an iterator for this would be much more complex. + */ + +typedef int (*fp_t)(Expression *, void *); + +int Expression::apply(fp_t fp, void *param) +{ + return (*fp)(this, param); +} + +/****************************** + * Perform apply() on an array of Expressions. + */ + +int arrayExpressionApply(Expressions *a, fp_t fp, void *param) +{ + //printf("arrayExpressionApply(%p)\n", a); + if (a) + { + for (size_t i = 0; i < a->dim; i++) + { Expression *e = (*a)[i]; + + if (e) + { + if (e->apply(fp, param)) + return 1; + } + } + } + return 0; +} + +int NewExp::apply(int (*fp)(Expression *, void *), void *param) +{ + //printf("NewExp::apply(): %s\n", toChars()); + + return ((thisexp ? thisexp->apply(fp, param) : 0) || + arrayExpressionApply(newargs, fp, param) || + arrayExpressionApply(arguments, fp, param) || + (*fp)(this, param)); +} + +int NewAnonClassExp::apply(int (*fp)(Expression *, void *), void *param) +{ + //printf("NewAnonClassExp::apply(): %s\n", toChars()); + + return ((thisexp ? thisexp->apply(fp, param) : 0) || + arrayExpressionApply(newargs, fp, param) || + arrayExpressionApply(arguments, fp, param) || + (*fp)(this, param)); +} + +int UnaExp::apply(fp_t fp, void *param) +{ + return e1->apply(fp, param) || + (*fp)(this, param); +} + +int BinExp::apply(fp_t fp, void *param) +{ + return e1->apply(fp, param) || + e2->apply(fp, param) || + (*fp)(this, param); +} + +int AssertExp::apply(fp_t fp, void *param) +{ + //printf("CallExp::apply(fp_t fp, void *param): %s\n", toChars()); + return e1->apply(fp, param) || + (msg ? msg->apply(fp, param) : 0) || + (*fp)(this, param); +} + + +int CallExp::apply(fp_t fp, void *param) +{ + //printf("CallExp::apply(fp_t fp, void *param): %s\n", toChars()); + return e1->apply(fp, param) || + arrayExpressionApply(arguments, fp, param) || + (*fp)(this, param); +} + + +int ArrayExp::apply(fp_t fp, void *param) +{ + //printf("ArrayExp::apply(fp_t fp, void *param): %s\n", toChars()); + return e1->apply(fp, param) || + arrayExpressionApply(arguments, fp, param) || + (*fp)(this, param); +} + + +int SliceExp::apply(fp_t fp, void *param) +{ + return e1->apply(fp, param) || + (lwr ? lwr->apply(fp, param) : 0) || + (upr ? upr->apply(fp, param) : 0) || + (*fp)(this, param); +} + + +int ArrayLiteralExp::apply(fp_t fp, void *param) +{ + return arrayExpressionApply(elements, fp, param) || + (*fp)(this, param); +} + + +int AssocArrayLiteralExp::apply(fp_t fp, void *param) +{ + return arrayExpressionApply(keys, fp, param) || + arrayExpressionApply(values, fp, param) || + (*fp)(this, param); +} + + +int StructLiteralExp::apply(fp_t fp, void *param) +{ + return arrayExpressionApply(elements, fp, param) || + (*fp)(this, param); +} + + +int TupleExp::apply(fp_t fp, void *param) +{ + return arrayExpressionApply(exps, fp, param) || + (*fp)(this, param); +} + + +int CondExp::apply(fp_t fp, void *param) +{ + return econd->apply(fp, param) || + e1->apply(fp, param) || + e2->apply(fp, param) || + (*fp)(this, param); +} + + + diff --git a/argtypes.c b/argtypes.c new file mode 100644 index 00000000..3f1d3620 --- /dev/null +++ b/argtypes.c @@ -0,0 +1,193 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 2010-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// http://www.dsource.org/projects/dmd/browser/branches/dmd-1.x/src/argtypes.c +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "mars.h" +#include "dsymbol.h" +#include "mtype.h" +#include "scope.h" +#include "init.h" +#include "expression.h" +#include "attrib.h" +#include "declaration.h" +#include "template.h" +#include "id.h" +#include "enum.h" +#include "import.h" +#include "aggregate.h" +#include "hdrgen.h" + +/**************************************************** + * This breaks a type down into 'simpler' types that can be passed to a function + * in registers, and returned in registers. + * It's highly platform dependent. + * Returning a tuple of zero length means the type cannot be passed/returned in registers. + */ + + +TypeTuple *Type::toArgTypes() +{ + return NULL; // not valid for a parameter +} + + +TypeTuple *TypeBasic::toArgTypes() +{ Type *t1 = NULL; + Type *t2 = NULL; + switch (ty) + { + case Tvoid: + return NULL; + + case Tbool: + case Tint8: + case Tuns8: + case Tint16: + case Tuns16: + case Tint32: + case Tuns32: + case Tfloat32: + case Tint64: + case Tuns64: + case Tfloat64: + case Tfloat80: + t1 = this; + break; + + case Timaginary32: + t1 = Type::tfloat32; + break; + + case Timaginary64: + t1 = Type::tfloat64; + break; + + case Timaginary80: + t1 = Type::tfloat80; + break; + + case Tcomplex32: + if (global.params.is64bit) + t1 = Type::tfloat64; // weird, eh? + else + { + t1 = Type::tfloat64; + t2 = Type::tfloat64; + } + break; + + case Tcomplex64: + t1 = Type::tfloat64; + t2 = Type::tfloat64; + break; + + case Tcomplex80: + t1 = Type::tfloat80; + t2 = Type::tfloat80; + break; + + case Tascii: + t1 = Type::tuns8; + break; + + case Twchar: + t1 = Type::tuns16; + break; + + case Tdchar: + t1 = Type::tuns32; + break; + + default: assert(0); + } + + TypeTuple *t; + if (t1) + { + if (t2) + t = new TypeTuple(t1, t2); + else + t = new TypeTuple(t1); + } + else + t = new TypeTuple(); + return t; +} + +TypeTuple *TypeVector::toArgTypes() +{ + return new TypeTuple(Type::tfloat64); +} + +TypeTuple *TypeSArray::toArgTypes() +{ +#if DMDV2 + return new TypeTuple(); // pass on the stack for efficiency +#else + return new TypeTuple(Type::tvoidptr); +#endif +} + +TypeTuple *TypeDArray::toArgTypes() +{ + return new TypeTuple(); // pass on the stack for efficiency +} + +TypeTuple *TypeAArray::toArgTypes() +{ + return new TypeTuple(Type::tvoidptr); +} + +TypeTuple *TypePointer::toArgTypes() +{ + return new TypeTuple(this); +} + +TypeTuple *TypeDelegate::toArgTypes() +{ + return new TypeTuple(); // pass on the stack for efficiency +} + +TypeTuple *TypeStruct::toArgTypes() +{ + d_uns64 sz = size(0); + assert(sz < 0xFFFFFFFF); + switch ((unsigned)sz) + { + case 1: + return new TypeTuple(Type::tint8); + case 2: + return new TypeTuple(Type::tint16); + case 4: + return new TypeTuple(Type::tint32); + case 8: + return new TypeTuple(Type::tint64); + } + return new TypeTuple(); // pass on the stack +} + +TypeTuple *TypeEnum::toArgTypes() +{ + return toBasetype()->toArgTypes(); +} + +TypeTuple *TypeTypedef::toArgTypes() +{ + return sym->basetype->toArgTypes(); +} + +TypeTuple *TypeClass::toArgTypes() +{ + return new TypeTuple(Type::tvoidptr); +} + diff --git a/arrayop.c b/arrayop.c new file mode 100644 index 00000000..2eaae398 --- /dev/null +++ b/arrayop.c @@ -0,0 +1,648 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include + +#include "rmem.h" + +#include "aav.h" + +#include "expression.h" +#include "statement.h" +#include "mtype.h" +#include "declaration.h" +#include "scope.h" +#include "id.h" +#include "module.h" +#include "init.h" + +extern int binary(const char *p , const char **tab, int high); + +/************************************** + * Hash table of array op functions already generated or known about. + */ + +AA *arrayfuncs; + +/********************************************** + * Check that there are no uses of arrays without []. + */ +bool isArrayOpValid(Expression *e) +{ + if (e->op == TOKslice) + return true; + Type *tb = e->type->toBasetype(); + + if ( (tb->ty == Tarray) || (tb->ty == Tsarray) ) + { + switch (e->op) + { + case TOKadd: + case TOKmin: + case TOKmul: + case TOKdiv: + case TOKmod: + case TOKxor: + case TOKand: + case TOKor: + case TOKassign: + case TOKaddass: + case TOKminass: + case TOKmulass: + case TOKdivass: + case TOKmodass: + case TOKxorass: + case TOKandass: + case TOKorass: +#if DMDV2 + case TOKpow: + case TOKpowass: +#endif + return isArrayOpValid(((BinExp *)e)->e1) && isArrayOpValid(((BinExp *)e)->e2); + + case TOKcall: + return false; // TODO: Decide if [] is required after arrayop calls. + + case TOKneg: + case TOKtilde: + return isArrayOpValid(((UnaExp *)e)->e1); + + default: + return false; + } + } + return true; +} + +/*********************************** + * Construct the array operation expression. + */ + +Expression *BinExp::arrayOp(Scope *sc) +{ + //printf("BinExp::arrayOp() %s\n", toChars()); + + Type *tb = type->toBasetype(); + assert(tb->ty == Tarray || tb->ty == Tsarray); + if (tb->nextOf()->toBasetype()->ty == Tvoid) + { + error("Cannot perform array operations on void[] arrays"); + return new ErrorExp(); + } + + if (!isArrayOpValid(e2)) + { + e2->error("invalid array operation %s (did you forget a [] ?)", toChars()); + return new ErrorExp(); + } + + Expressions *arguments = new Expressions(); + + /* The expression to generate an array operation for is mangled + * into a name to use as the array operation function name. + * Mangle in the operands and operators in RPN order, and type. + */ + OutBuffer buf; + buf.writestring("_array"); + buildArrayIdent(&buf, arguments); + buf.writeByte('_'); + + /* Append deco of array element type + */ +#if DMDV2 + buf.writestring(type->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco); +#else + buf.writestring(type->toBasetype()->nextOf()->toBasetype()->deco); +#endif + + size_t namelen = buf.offset; + buf.writeByte(0); + char *name = buf.toChars(); + Identifier *ident = Lexer::idPool(name); + + /* Look up name in hash table + */ + FuncDeclaration **pfd = (FuncDeclaration **)_aaGet(&arrayfuncs, ident); + FuncDeclaration *fd = (FuncDeclaration *)*pfd; + if (!fd) + { + /* Some of the array op functions are written as library functions, + * presumably to optimize them with special CPU vector instructions. + * List those library functions here, in alpha order. + */ + static const char *libArrayopFuncs[] = + { + "_arrayExpSliceAddass_a", + "_arrayExpSliceAddass_d", // T[]+=T + "_arrayExpSliceAddass_f", // T[]+=T + "_arrayExpSliceAddass_g", + "_arrayExpSliceAddass_h", + "_arrayExpSliceAddass_i", + "_arrayExpSliceAddass_k", + "_arrayExpSliceAddass_s", + "_arrayExpSliceAddass_t", + "_arrayExpSliceAddass_u", + "_arrayExpSliceAddass_w", + + "_arrayExpSliceDivass_d", // T[]/=T + "_arrayExpSliceDivass_f", // T[]/=T + + "_arrayExpSliceMinSliceAssign_a", + "_arrayExpSliceMinSliceAssign_d", // T[]=T-T[] + "_arrayExpSliceMinSliceAssign_f", // T[]=T-T[] + "_arrayExpSliceMinSliceAssign_g", + "_arrayExpSliceMinSliceAssign_h", + "_arrayExpSliceMinSliceAssign_i", + "_arrayExpSliceMinSliceAssign_k", + "_arrayExpSliceMinSliceAssign_s", + "_arrayExpSliceMinSliceAssign_t", + "_arrayExpSliceMinSliceAssign_u", + "_arrayExpSliceMinSliceAssign_w", + + "_arrayExpSliceMinass_a", + "_arrayExpSliceMinass_d", // T[]-=T + "_arrayExpSliceMinass_f", // T[]-=T + "_arrayExpSliceMinass_g", + "_arrayExpSliceMinass_h", + "_arrayExpSliceMinass_i", + "_arrayExpSliceMinass_k", + "_arrayExpSliceMinass_s", + "_arrayExpSliceMinass_t", + "_arrayExpSliceMinass_u", + "_arrayExpSliceMinass_w", + + "_arrayExpSliceMulass_d", // T[]*=T + "_arrayExpSliceMulass_f", // T[]*=T + "_arrayExpSliceMulass_i", + "_arrayExpSliceMulass_k", + "_arrayExpSliceMulass_s", + "_arrayExpSliceMulass_t", + "_arrayExpSliceMulass_u", + "_arrayExpSliceMulass_w", + + "_arraySliceExpAddSliceAssign_a", + "_arraySliceExpAddSliceAssign_d", // T[]=T[]+T + "_arraySliceExpAddSliceAssign_f", // T[]=T[]+T + "_arraySliceExpAddSliceAssign_g", + "_arraySliceExpAddSliceAssign_h", + "_arraySliceExpAddSliceAssign_i", + "_arraySliceExpAddSliceAssign_k", + "_arraySliceExpAddSliceAssign_s", + "_arraySliceExpAddSliceAssign_t", + "_arraySliceExpAddSliceAssign_u", + "_arraySliceExpAddSliceAssign_w", + + "_arraySliceExpDivSliceAssign_d", // T[]=T[]/T + "_arraySliceExpDivSliceAssign_f", // T[]=T[]/T + + "_arraySliceExpMinSliceAssign_a", + "_arraySliceExpMinSliceAssign_d", // T[]=T[]-T + "_arraySliceExpMinSliceAssign_f", // T[]=T[]-T + "_arraySliceExpMinSliceAssign_g", + "_arraySliceExpMinSliceAssign_h", + "_arraySliceExpMinSliceAssign_i", + "_arraySliceExpMinSliceAssign_k", + "_arraySliceExpMinSliceAssign_s", + "_arraySliceExpMinSliceAssign_t", + "_arraySliceExpMinSliceAssign_u", + "_arraySliceExpMinSliceAssign_w", + + "_arraySliceExpMulSliceAddass_d", // T[] += T[]*T + "_arraySliceExpMulSliceAddass_f", + "_arraySliceExpMulSliceAddass_r", + + "_arraySliceExpMulSliceAssign_d", // T[]=T[]*T + "_arraySliceExpMulSliceAssign_f", // T[]=T[]*T + "_arraySliceExpMulSliceAssign_i", + "_arraySliceExpMulSliceAssign_k", + "_arraySliceExpMulSliceAssign_s", + "_arraySliceExpMulSliceAssign_t", + "_arraySliceExpMulSliceAssign_u", + "_arraySliceExpMulSliceAssign_w", + + "_arraySliceExpMulSliceMinass_d", // T[] -= T[]*T + "_arraySliceExpMulSliceMinass_f", + "_arraySliceExpMulSliceMinass_r", + + "_arraySliceSliceAddSliceAssign_a", + "_arraySliceSliceAddSliceAssign_d", // T[]=T[]+T[] + "_arraySliceSliceAddSliceAssign_f", // T[]=T[]+T[] + "_arraySliceSliceAddSliceAssign_g", + "_arraySliceSliceAddSliceAssign_h", + "_arraySliceSliceAddSliceAssign_i", + "_arraySliceSliceAddSliceAssign_k", + "_arraySliceSliceAddSliceAssign_r", // T[]=T[]+T[] + "_arraySliceSliceAddSliceAssign_s", + "_arraySliceSliceAddSliceAssign_t", + "_arraySliceSliceAddSliceAssign_u", + "_arraySliceSliceAddSliceAssign_w", + + "_arraySliceSliceAddass_a", + "_arraySliceSliceAddass_d", // T[]+=T[] + "_arraySliceSliceAddass_f", // T[]+=T[] + "_arraySliceSliceAddass_g", + "_arraySliceSliceAddass_h", + "_arraySliceSliceAddass_i", + "_arraySliceSliceAddass_k", + "_arraySliceSliceAddass_s", + "_arraySliceSliceAddass_t", + "_arraySliceSliceAddass_u", + "_arraySliceSliceAddass_w", + + "_arraySliceSliceMinSliceAssign_a", + "_arraySliceSliceMinSliceAssign_d", // T[]=T[]-T[] + "_arraySliceSliceMinSliceAssign_f", // T[]=T[]-T[] + "_arraySliceSliceMinSliceAssign_g", + "_arraySliceSliceMinSliceAssign_h", + "_arraySliceSliceMinSliceAssign_i", + "_arraySliceSliceMinSliceAssign_k", + "_arraySliceSliceMinSliceAssign_r", // T[]=T[]-T[] + "_arraySliceSliceMinSliceAssign_s", + "_arraySliceSliceMinSliceAssign_t", + "_arraySliceSliceMinSliceAssign_u", + "_arraySliceSliceMinSliceAssign_w", + + "_arraySliceSliceMinass_a", + "_arraySliceSliceMinass_d", // T[]-=T[] + "_arraySliceSliceMinass_f", // T[]-=T[] + "_arraySliceSliceMinass_g", + "_arraySliceSliceMinass_h", + "_arraySliceSliceMinass_i", + "_arraySliceSliceMinass_k", + "_arraySliceSliceMinass_s", + "_arraySliceSliceMinass_t", + "_arraySliceSliceMinass_u", + "_arraySliceSliceMinass_w", + + "_arraySliceSliceMulSliceAssign_d", // T[]=T[]*T[] + "_arraySliceSliceMulSliceAssign_f", // T[]=T[]*T[] + "_arraySliceSliceMulSliceAssign_i", + "_arraySliceSliceMulSliceAssign_k", + "_arraySliceSliceMulSliceAssign_s", + "_arraySliceSliceMulSliceAssign_t", + "_arraySliceSliceMulSliceAssign_u", + "_arraySliceSliceMulSliceAssign_w", + + "_arraySliceSliceMulass_d", // T[]*=T[] + "_arraySliceSliceMulass_f", // T[]*=T[] + "_arraySliceSliceMulass_i", + "_arraySliceSliceMulass_k", + "_arraySliceSliceMulass_s", + "_arraySliceSliceMulass_t", + "_arraySliceSliceMulass_u", + "_arraySliceSliceMulass_w", + }; + + int i = binary(name, libArrayopFuncs, sizeof(libArrayopFuncs) / sizeof(char *)); + if (i == -1) + { +#ifdef DEBUG // Make sure our array is alphabetized + for (i = 0; i < sizeof(libArrayopFuncs) / sizeof(char *); i++) + { + if (strcmp(name, libArrayopFuncs[i]) == 0) + assert(0); + } +#endif + /* Not in library, so generate it. + * Construct the function body: + * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++) + * loopbody; + * return p; + */ + + Parameters *fparams = new Parameters(); + Expression *loopbody = buildArrayLoop(fparams); + Parameter *p = (*fparams)[0 /*fparams->dim - 1*/]; +#if DMDV1 + // for (size_t i = 0; i < p.length; i++) + Initializer *init = new ExpInitializer(0, new IntegerExp(0, 0, Type::tsize_t)); + Dsymbol *d = new VarDeclaration(0, Type::tsize_t, Id::p, init); + Statement *s1 = new ForStatement(0, + new DeclarationStatement(0, d), + new CmpExp(TOKlt, 0, new IdentifierExp(0, Id::p), new ArrayLengthExp(0, new IdentifierExp(0, p->ident))), + new PostExp(TOKplusplus, 0, new IdentifierExp(0, Id::p)), + new ExpStatement(0, loopbody)); +#else + // foreach (i; 0 .. p.length) + Statement *s1 = new ForeachRangeStatement(0, TOKforeach, + new Parameter(0, NULL, Id::p, NULL), + new IntegerExp(0, 0, Type::tint32), + new ArrayLengthExp(0, new IdentifierExp(0, p->ident)), + new ExpStatement(0, loopbody)); +#endif + Statement *s2 = new ReturnStatement(0, new IdentifierExp(0, p->ident)); + //printf("s2: %s\n", s2->toChars()); + Statement *fbody = new CompoundStatement(0, s1, s2); + + /* Construct the function + */ + TypeFunction *ftype = new TypeFunction(fparams, type, 0, LINKc); + //printf("ftype: %s\n", ftype->toChars()); + fd = new FuncDeclaration(loc, 0, ident, STCundefined, ftype); + fd->fbody = fbody; + fd->protection = PROTpublic; + fd->linkage = LINKc; + fd->isArrayOp = 1; + + sc->module->importedFrom->members->push(fd); + + sc = sc->push(); + sc->parent = sc->module->importedFrom; + sc->stc = 0; + sc->linkage = LINKc; + fd->semantic(sc); + fd->semantic2(sc); + fd->semantic3(sc); + sc->pop(); + } + else + { /* In library, refer to it. + */ + fd = FuncDeclaration::genCfunc(type, ident); + } + *pfd = fd; // cache symbol in hash table + } + + /* Call the function fd(arguments) + */ + Expression *ec = new VarExp(0, fd); + Expression *e = new CallExp(loc, ec, arguments); + e->type = type; + return e; +} + +/****************************************** + * Construct the identifier for the array operation function, + * and build the argument list to pass to it. + */ + +void Expression::buildArrayIdent(OutBuffer *buf, Expressions *arguments) +{ + buf->writestring("Exp"); + arguments->shift(this); +} + +void CastExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) +{ + Type *tb = type->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) + { + e1->buildArrayIdent(buf, arguments); + } + else + Expression::buildArrayIdent(buf, arguments); +} + +void SliceExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) +{ + buf->writestring("Slice"); + arguments->shift(this); +} + +void AssignExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) +{ + /* Evaluate assign expressions right to left + */ + e2->buildArrayIdent(buf, arguments); + e1->buildArrayIdent(buf, arguments); + buf->writestring("Assign"); +} + +#define X(Str) \ +void Str##AssignExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) \ +{ \ + /* Evaluate assign expressions right to left \ + */ \ + e2->buildArrayIdent(buf, arguments); \ + e1->buildArrayIdent(buf, arguments); \ + buf->writestring(#Str); \ + buf->writestring("ass"); \ +} + +X(Add) +X(Min) +X(Mul) +X(Div) +X(Mod) +X(Xor) +X(And) +X(Or) +#if DMDV2 +X(Pow) +#endif + +#undef X + +void NegExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) +{ + e1->buildArrayIdent(buf, arguments); + buf->writestring("Neg"); +} + +void ComExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) +{ + e1->buildArrayIdent(buf, arguments); + buf->writestring("Com"); +} + +#define X(Str) \ +void Str##Exp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) \ +{ \ + /* Evaluate assign expressions left to right \ + */ \ + e1->buildArrayIdent(buf, arguments); \ + e2->buildArrayIdent(buf, arguments); \ + buf->writestring(#Str); \ +} + +X(Add) +X(Min) +X(Mul) +X(Div) +X(Mod) +X(Xor) +X(And) +X(Or) +#if DMDV2 +X(Pow) +#endif + +#undef X + +/****************************************** + * Construct the inner loop for the array operation function, + * and build the parameter list. + */ + +Expression *Expression::buildArrayLoop(Parameters *fparams) +{ + Identifier *id = Identifier::generateId("c", fparams->dim); + Parameter *param = new Parameter(0, type, id, NULL); + fparams->shift(param); + Expression *e = new IdentifierExp(0, id); + return e; +} + +Expression *CastExp::buildArrayLoop(Parameters *fparams) +{ + Type *tb = type->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) + { + return e1->buildArrayLoop(fparams); + } + else + return Expression::buildArrayLoop(fparams); +} + +Expression *SliceExp::buildArrayLoop(Parameters *fparams) +{ + Identifier *id = Identifier::generateId("p", fparams->dim); + Parameter *param = new Parameter(STCconst, type, id, NULL); + fparams->shift(param); + Expression *e = new IdentifierExp(0, id); + Expressions *arguments = new Expressions(); + Expression *index = new IdentifierExp(0, Id::p); + arguments->push(index); + e = new ArrayExp(0, e, arguments); + return e; +} + +Expression *AssignExp::buildArrayLoop(Parameters *fparams) +{ + /* Evaluate assign expressions right to left + */ + Expression *ex2 = e2->buildArrayLoop(fparams); +#if DMDV2 + /* Need the cast because: + * b = c + p[i]; + * where b is a byte fails because (c + p[i]) is an int + * which cannot be implicitly cast to byte. + */ + ex2 = new CastExp(0, ex2, e1->type->nextOf()); +#endif + Expression *ex1 = e1->buildArrayLoop(fparams); + Parameter *param = (*fparams)[0]; + param->storageClass = 0; + Expression *e = new AssignExp(0, ex1, ex2); + return e; +} + +#define X(Str) \ +Expression *Str##AssignExp::buildArrayLoop(Parameters *fparams) \ +{ \ + /* Evaluate assign expressions right to left \ + */ \ + Expression *ex2 = e2->buildArrayLoop(fparams); \ + Expression *ex1 = e1->buildArrayLoop(fparams); \ + Parameter *param = (*fparams)[0]; \ + param->storageClass = 0; \ + Expression *e = new Str##AssignExp(loc, ex1, ex2); \ + return e; \ +} + +X(Add) +X(Min) +X(Mul) +X(Div) +X(Mod) +X(Xor) +X(And) +X(Or) +#if DMDV2 +X(Pow) +#endif + +#undef X + +Expression *NegExp::buildArrayLoop(Parameters *fparams) +{ + Expression *ex1 = e1->buildArrayLoop(fparams); + Expression *e = new NegExp(0, ex1); + return e; +} + +Expression *ComExp::buildArrayLoop(Parameters *fparams) +{ + Expression *ex1 = e1->buildArrayLoop(fparams); + Expression *e = new ComExp(0, ex1); + return e; +} + +#define X(Str) \ +Expression *Str##Exp::buildArrayLoop(Parameters *fparams) \ +{ \ + /* Evaluate assign expressions left to right \ + */ \ + Expression *ex1 = e1->buildArrayLoop(fparams); \ + Expression *ex2 = e2->buildArrayLoop(fparams); \ + Expression *e = new Str##Exp(0, ex1, ex2); \ + return e; \ +} + +X(Add) +X(Min) +X(Mul) +X(Div) +X(Mod) +X(Xor) +X(And) +X(Or) +#if DMDV2 +X(Pow) +#endif + +#undef X + + +/*********************************************** + * Test if operand is a valid array op operand. + */ + +int Expression::isArrayOperand() +{ + //printf("Expression::isArrayOperand() %s\n", toChars()); + if (op == TOKslice) + return 1; + if (type->toBasetype()->ty == Tarray) + { + switch (op) + { + case TOKadd: + case TOKmin: + case TOKmul: + case TOKdiv: + case TOKmod: + case TOKxor: + case TOKand: + case TOKor: + case TOKassign: + case TOKaddass: + case TOKminass: + case TOKmulass: + case TOKdivass: + case TOKmodass: + case TOKxorass: + case TOKandass: + case TOKorass: +#if DMDV2 + case TOKpow: + case TOKpowass: +#endif + case TOKneg: + case TOKtilde: + return 1; + + default: + break; + } + } + return 0; +} diff --git a/arraytypes.h b/arraytypes.h new file mode 100644 index 00000000..be854a62 --- /dev/null +++ b/arraytypes.h @@ -0,0 +1,77 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 2006-2007 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_ARRAYTYPES_H +#define DMD_ARRAYTYPES_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + + +#include "root.h" + +typedef ArrayBase TemplateParameters; + +typedef ArrayBase Expressions; + +typedef ArrayBase Statements; + +typedef ArrayBase BaseClasses; + +typedef ArrayBase ClassDeclarations; + +typedef ArrayBase Dsymbols; + +typedef ArrayBase Objects; + +typedef ArrayBase FuncDeclarations; + +typedef ArrayBase Parameters; + +typedef ArrayBase Identifiers; + +typedef ArrayBase Initializers; + +typedef ArrayBase VarDeclarations; + +typedef ArrayBase Types; + +typedef ArrayBase ScopeDsymbols; + +typedef ArrayBase Catches; + +typedef ArrayBase StaticDtorDeclarations; + +typedef ArrayBase SharedStaticDtorDeclarations; + +typedef ArrayBase AliasDeclarations; + +typedef ArrayBase Modules; + +typedef ArrayBase Files; + +typedef ArrayBase CaseStatements; + +typedef ArrayBase CompoundStatements; + +typedef ArrayBase GotoCaseStatements; + +typedef ArrayBase TemplateInstances; + +//typedef ArrayBase Strings; + +typedef ArrayBase Voids; + +typedef ArrayBase Blocks; + +typedef ArrayBase Symbols; + +#endif diff --git a/artistic.txt b/artistic.txt new file mode 100644 index 00000000..cae432b7 --- /dev/null +++ b/artistic.txt @@ -0,0 +1,117 @@ + + + + + The "Artistic License" + + Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to make +reasonable modifications. + +Definitions: + + "Package" refers to the collection of files distributed by the + Copyright Holder, and derivatives of that collection of files + created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes + of the Copyright Holder as specified below. + + "Copyright Holder" is whoever is named in the copyright or + copyrights for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the + basis of media cost, duplication charges, time of people involved, + and so on. (You will not be required to justify it to the + Copyright Holder, but only to the computing community at large + as a market that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it + under the same conditions they received it. + +1. You may make and give away verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you +duplicate all of the original copyright notices and associated disclaimers. + +2. You may apply bug fixes, portability fixes and other modifications +derived from the Public Domain or from the Copyright Holder. A Package +modified in such a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, provided +that you insert a prominent notice in each changed file stating how and +when you changed that file, and provided that you do at least ONE of the +following: + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or + an equivalent medium, or placing the modifications on a major archive + site such as uunet.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page for each non-standard executable that clearly + documents how it differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where + to get the Standard Version. + + b) accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) give non-standard executables non-standard names, and clearly + document the differences in manual pages (or equivalent), together + with instructions on where to get the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of this +Package. You may charge any fee you choose for support of this +Package. You may not charge a fee for this Package itself. However, +you may distribute this Package in aggregate with other (possibly +commercial) programs as part of a larger (possibly commercial) software +distribution provided that you do not advertise this Package as a +product of your own. You may embed this Package's interpreter within +an executable of yours (by linking); this shall be construed as a mere +form of aggregation, provided that the complete Standard Version of the +interpreter is so embedded. + +6. The source code and object code supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whoever generated +them, and may be sold commercially, and may be aggregated with this +Package. + +7. Aggregation of this Package with a commercial distribution is always +permitted provided that the use of this Package is embedded; that is, +when no overt attempt is made to make this Package's interfaces visible +to the end user of the commercial distribution. Such use shall not be +construed as a distribution of this Package. + +8. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End diff --git a/attrib.c b/attrib.c new file mode 100644 index 00000000..e55dd83b --- /dev/null +++ b/attrib.c @@ -0,0 +1,1524 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include + +#include "rmem.h" + +#include "init.h" +#include "declaration.h" +#include "attrib.h" +#include "cond.h" +#include "scope.h" +#include "id.h" +#include "expression.h" +#include "dsymbol.h" +#include "aggregate.h" +#include "module.h" +#include "parse.h" +#include "template.h" +#if TARGET_NET + #include "frontend.net/pragma.h" +#endif + +extern void obj_includelib(const char *name); +void obj_startaddress(Symbol *s); + + +/********************************* AttribDeclaration ****************************/ + +AttribDeclaration::AttribDeclaration(Dsymbols *decl) + : Dsymbol() +{ + this->decl = decl; +} + +Dsymbols *AttribDeclaration::include(Scope *sc, ScopeDsymbol *sd) +{ + return decl; +} + +int AttribDeclaration::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +{ + int m = 0; + Dsymbols *d = include(sc, sd); + + if (d) + { + for (unsigned i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + //printf("\taddMember %s to %s\n", s->toChars(), sd->toChars()); + m |= s->addMember(sc, sd, m | memnum); + } + } + return m; +} + +void AttribDeclaration::setScopeNewSc(Scope *sc, + StorageClass stc, enum LINK linkage, enum PROT protection, int explicitProtection, + unsigned structalign) +{ + if (decl) + { + Scope *newsc = sc; + if (stc != sc->stc || + linkage != sc->linkage || + protection != sc->protection || + explicitProtection != sc->explicitProtection || + structalign != sc->structalign) + { + // create new one for changes + newsc = new Scope(*sc); + newsc->flags &= ~SCOPEfree; + newsc->stc = stc; + newsc->linkage = linkage; + newsc->protection = protection; + newsc->explicitProtection = explicitProtection; + newsc->structalign = structalign; + } + for (unsigned i = 0; i < decl->dim; i++) + { Dsymbol *s = decl->tdata()[i]; + + s->setScope(newsc); // yes, the only difference from semanticNewSc() + } + if (newsc != sc) + { + sc->offset = newsc->offset; + newsc->pop(); + } + } +} + +void AttribDeclaration::semanticNewSc(Scope *sc, + StorageClass stc, enum LINK linkage, enum PROT protection, int explicitProtection, + unsigned structalign) +{ + if (decl) + { + Scope *newsc = sc; + if (stc != sc->stc || + linkage != sc->linkage || + protection != sc->protection || + explicitProtection != sc->explicitProtection || + structalign != sc->structalign) + { + // create new one for changes + newsc = new Scope(*sc); + newsc->flags &= ~SCOPEfree; + newsc->stc = stc; + newsc->linkage = linkage; + newsc->protection = protection; + newsc->explicitProtection = explicitProtection; + newsc->structalign = structalign; + } + for (unsigned i = 0; i < decl->dim; i++) + { Dsymbol *s = decl->tdata()[i]; + + s->semantic(newsc); + } + if (newsc != sc) + { + sc->offset = newsc->offset; + newsc->pop(); + } + } +} + +void AttribDeclaration::semantic(Scope *sc) +{ + Dsymbols *d = include(sc, NULL); + + //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d); + if (d) + { + for (size_t i = 0; i < d->dim; i++) + { + Dsymbol *s = d->tdata()[i]; + + s->semantic(sc); + } + } +} + +void AttribDeclaration::semantic2(Scope *sc) +{ + Dsymbols *d = include(sc, NULL); + + if (d) + { + for (size_t i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + s->semantic2(sc); + } + } +} + +void AttribDeclaration::semantic3(Scope *sc) +{ + Dsymbols *d = include(sc, NULL); + + if (d) + { + for (size_t i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + s->semantic3(sc); + } + } +} + +void AttribDeclaration::inlineScan() +{ + Dsymbols *d = include(NULL, NULL); + + if (d) + { + for (unsigned i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + //printf("AttribDeclaration::inlineScan %s\n", s->toChars()); + s->inlineScan(); + } + } +} + +void AttribDeclaration::addComment(unsigned char *comment) +{ + //printf("AttribDeclaration::addComment %s\n", comment); + if (comment) + { + Dsymbols *d = include(NULL, NULL); + + if (d) + { + for (unsigned i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + //printf("AttribDeclaration::addComment %s\n", s->toChars()); + s->addComment(comment); + } + } + } +} + +void AttribDeclaration::emitComment(Scope *sc) +{ + //printf("AttribDeclaration::emitComment(sc = %p)\n", sc); + + /* A general problem with this, illustrated by BUGZILLA 2516, + * is that attributes are not transmitted through to the underlying + * member declarations for template bodies, because semantic analysis + * is not done for template declaration bodies + * (only template instantiations). + * Hence, Ddoc omits attributes from template members. + */ + + Dsymbols *d = include(NULL, NULL); + + if (d) + { + for (unsigned i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + //printf("AttribDeclaration::emitComment %s\n", s->toChars()); + s->emitComment(sc); + } + } +} + +void AttribDeclaration::toObjFile(int multiobj) +{ + Dsymbols *d = include(NULL, NULL); + + if (d) + { + for (unsigned i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + s->toObjFile(multiobj); + } + } +} + +int AttribDeclaration::cvMember(unsigned char *p) +{ + int nwritten = 0; + int n; + Dsymbols *d = include(NULL, NULL); + + if (d) + { + for (unsigned i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + n = s->cvMember(p); + if (p) + p += n; + nwritten += n; + } + } + return nwritten; +} + +int AttribDeclaration::hasPointers() +{ + Dsymbols *d = include(NULL, NULL); + + if (d) + { + for (size_t i = 0; i < d->dim; i++) + { + Dsymbol *s = d->tdata()[i]; + if (s->hasPointers()) + return 1; + } + } + return 0; +} + +bool AttribDeclaration::hasStaticCtorOrDtor() +{ + Dsymbols *d = include(NULL, NULL); + + if (d) + { + for (size_t i = 0; i < d->dim; i++) + { + Dsymbol *s = (*d)[i]; + if (s->hasStaticCtorOrDtor()) + return TRUE; + } + } + return FALSE; +} + +const char *AttribDeclaration::kind() +{ + return "attribute"; +} + +int AttribDeclaration::oneMember(Dsymbol **ps, Identifier *ident) +{ + Dsymbols *d = include(NULL, NULL); + + return Dsymbol::oneMembers(d, ps, ident); +} + +void AttribDeclaration::checkCtorConstInit() +{ + Dsymbols *d = include(NULL, NULL); + + if (d) + { + for (unsigned i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + s->checkCtorConstInit(); + } + } +} + +/**************************************** + */ + +void AttribDeclaration::addLocalClass(ClassDeclarations *aclasses) +{ + Dsymbols *d = include(NULL, NULL); + + if (d) + { + for (unsigned i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + s->addLocalClass(aclasses); + } + } +} + + +void AttribDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (decl) + { + if (decl->dim == 0) + buf->writestring("{}"); + else if (decl->dim == 1) + (decl->tdata()[0])->toCBuffer(buf, hgs); + else + { + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + for (unsigned i = 0; i < decl->dim; i++) + { + Dsymbol *s = decl->tdata()[i]; + + buf->writestring(" "); + s->toCBuffer(buf, hgs); + } + buf->writeByte('}'); + } + } + else + buf->writeByte(';'); + buf->writenl(); +} + +/************************* StorageClassDeclaration ****************************/ + +StorageClassDeclaration::StorageClassDeclaration(StorageClass stc, Dsymbols *decl) + : AttribDeclaration(decl) +{ + this->stc = stc; +} + +Dsymbol *StorageClassDeclaration::syntaxCopy(Dsymbol *s) +{ + StorageClassDeclaration *scd; + + assert(!s); + scd = new StorageClassDeclaration(stc, Dsymbol::arraySyntaxCopy(decl)); + return scd; +} + +int StorageClassDeclaration::oneMember(Dsymbol **ps, Identifier *ident) +{ + + int t = Dsymbol::oneMembers(decl, ps, ident); + if (t && *ps) + { + /* This is to deal with the following case: + * struct Tick { + * template to(T) { const T to() { ... } } + * } + * For eponymous function templates, the 'const' needs to get attached to 'to' + * before the semantic analysis of 'to', so that template overloading based on the + * 'this' pointer can be successful. + */ + + FuncDeclaration *fd = (*ps)->isFuncDeclaration(); + if (fd) + { + /* Use storage_class2 instead of storage_class otherwise when we do .di generation + * we'll wind up with 'const const' rather than 'const'. + */ + /* Don't think we need to worry about mutually exclusive storage classes here + */ + fd->storage_class2 |= stc; + } + } + return t; +} + +void StorageClassDeclaration::setScope(Scope *sc) +{ + if (decl) + { + StorageClass scstc = sc->stc; + + /* These sets of storage classes are mutually exclusive, + * so choose the innermost or most recent one. + */ + if (stc & (STCauto | STCscope | STCstatic | STCextern | STCmanifest)) + scstc &= ~(STCauto | STCscope | STCstatic | STCextern | STCmanifest); + if (stc & (STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared)) + scstc &= ~(STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared); + if (stc & (STCconst | STCimmutable | STCmanifest)) + scstc &= ~(STCconst | STCimmutable | STCmanifest); + if (stc & (STCgshared | STCshared | STCtls)) + scstc &= ~(STCgshared | STCshared | STCtls); + if (stc & (STCsafe | STCtrusted | STCsystem)) + scstc &= ~(STCsafe | STCtrusted | STCsystem); + scstc |= stc; + + setScopeNewSc(sc, scstc, sc->linkage, sc->protection, sc->explicitProtection, sc->structalign); + } +} + +void StorageClassDeclaration::semantic(Scope *sc) +{ + if (decl) + { + StorageClass scstc = sc->stc; + + /* These sets of storage classes are mutually exclusive, + * so choose the innermost or most recent one. + */ + if (stc & (STCauto | STCscope | STCstatic | STCextern | STCmanifest)) + scstc &= ~(STCauto | STCscope | STCstatic | STCextern | STCmanifest); + if (stc & (STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared)) + scstc &= ~(STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared); + if (stc & (STCconst | STCimmutable | STCmanifest)) + scstc &= ~(STCconst | STCimmutable | STCmanifest); + if (stc & (STCgshared | STCshared | STCtls)) + scstc &= ~(STCgshared | STCshared | STCtls); + if (stc & (STCsafe | STCtrusted | STCsystem)) + scstc &= ~(STCsafe | STCtrusted | STCsystem); + scstc |= stc; + + semanticNewSc(sc, scstc, sc->linkage, sc->protection, sc->explicitProtection, sc->structalign); + } +} + +void StorageClassDeclaration::stcToCBuffer(OutBuffer *buf, StorageClass stc) +{ + struct SCstring + { + StorageClass stc; + enum TOK tok; + Identifier *id; + }; + + static SCstring table[] = + { + { STCauto, TOKauto }, + { STCscope, TOKscope }, + { STCstatic, TOKstatic }, + { STCextern, TOKextern }, + { STCconst, TOKconst }, + { STCfinal, TOKfinal }, + { STCabstract, TOKabstract }, + { STCsynchronized, TOKsynchronized }, + { STCdeprecated, TOKdeprecated }, + { STCoverride, TOKoverride }, + { STClazy, TOKlazy }, + { STCalias, TOKalias }, + { STCout, TOKout }, + { STCin, TOKin }, +#if DMDV2 + { STCmanifest, TOKenum }, + { STCimmutable, TOKimmutable }, + { STCshared, TOKshared }, + { STCnothrow, TOKnothrow }, + { STCpure, TOKpure }, + { STCref, TOKref }, + { STCtls, TOKtls }, + { STCgshared, TOKgshared }, + { STCproperty, TOKat, Id::property }, + { STCsafe, TOKat, Id::safe }, + { STCtrusted, TOKat, Id::trusted }, + { STCsystem, TOKat, Id::system }, + { STCdisable, TOKat, Id::disable }, +#endif + }; + + for (int i = 0; i < sizeof(table)/sizeof(table[0]); i++) + { + if (stc & table[i].stc) + { + enum TOK tok = table[i].tok; +#if DMDV2 + if (tok == TOKat) + { + buf->writeByte('@'); + buf->writestring(table[i].id->toChars()); + } + else +#endif + buf->writestring(Token::toChars(tok)); + buf->writeByte(' '); + } + } +} + +void StorageClassDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + stcToCBuffer(buf, stc); + AttribDeclaration::toCBuffer(buf, hgs); +} + +/********************************* LinkDeclaration ****************************/ + +LinkDeclaration::LinkDeclaration(enum LINK p, Dsymbols *decl) + : AttribDeclaration(decl) +{ + //printf("LinkDeclaration(linkage = %d, decl = %p)\n", p, decl); + linkage = p; +} + +Dsymbol *LinkDeclaration::syntaxCopy(Dsymbol *s) +{ + LinkDeclaration *ld; + + assert(!s); + ld = new LinkDeclaration(linkage, Dsymbol::arraySyntaxCopy(decl)); + return ld; +} + +void LinkDeclaration::setScope(Scope *sc) +{ + //printf("LinkDeclaration::setScope(linkage = %d, decl = %p)\n", linkage, decl); + if (decl) + { + setScopeNewSc(sc, sc->stc, linkage, sc->protection, sc->explicitProtection, sc->structalign); + } +} + +void LinkDeclaration::semantic(Scope *sc) +{ + //printf("LinkDeclaration::semantic(linkage = %d, decl = %p)\n", linkage, decl); + if (decl) + { + semanticNewSc(sc, sc->stc, linkage, sc->protection, sc->explicitProtection, sc->structalign); + } +} + +void LinkDeclaration::semantic3(Scope *sc) +{ + //printf("LinkDeclaration::semantic3(linkage = %d, decl = %p)\n", linkage, decl); + if (decl) + { enum LINK linkage_save = sc->linkage; + + sc->linkage = linkage; + for (unsigned i = 0; i < decl->dim; i++) + { + Dsymbol *s = decl->tdata()[i]; + + s->semantic3(sc); + } + sc->linkage = linkage_save; + } + else + { + sc->linkage = linkage; + } +} + +void LinkDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ const char *p; + + switch (linkage) + { + case LINKd: p = "D"; break; + case LINKc: p = "C"; break; + case LINKcpp: p = "C++"; break; + case LINKwindows: p = "Windows"; break; + case LINKpascal: p = "Pascal"; break; + default: + assert(0); + break; + } + buf->writestring("extern ("); + buf->writestring(p); + buf->writestring(") "); + AttribDeclaration::toCBuffer(buf, hgs); +} + +char *LinkDeclaration::toChars() +{ + return (char *)"extern ()"; +} + +/********************************* ProtDeclaration ****************************/ + +ProtDeclaration::ProtDeclaration(enum PROT p, Dsymbols *decl) + : AttribDeclaration(decl) +{ + protection = p; + //printf("decl = %p\n", decl); +} + +Dsymbol *ProtDeclaration::syntaxCopy(Dsymbol *s) +{ + ProtDeclaration *pd; + + assert(!s); + pd = new ProtDeclaration(protection, Dsymbol::arraySyntaxCopy(decl)); + return pd; +} + +void ProtDeclaration::setScope(Scope *sc) +{ + if (decl) + { + setScopeNewSc(sc, sc->stc, sc->linkage, protection, 1, sc->structalign); + } +} + +void ProtDeclaration::importAll(Scope *sc) +{ + Scope *newsc = sc; + if (sc->protection != protection || + sc->explicitProtection != 1) + { + // create new one for changes + newsc = new Scope(*sc); + newsc->flags &= ~SCOPEfree; + newsc->protection = protection; + newsc->explicitProtection = 1; + } + + for (size_t i = 0; i < decl->dim; i++) + { + Dsymbol *s = (*decl)[i]; + s->importAll(newsc); + } + + if (newsc != sc) + newsc->pop(); +} + +void ProtDeclaration::semantic(Scope *sc) +{ + if (decl) + { + semanticNewSc(sc, sc->stc, sc->linkage, protection, 1, sc->structalign); + } +} + +void ProtDeclaration::protectionToCBuffer(OutBuffer *buf, enum PROT protection) +{ + const char *p; + + switch (protection) + { + case PROTprivate: p = "private"; break; + case PROTpackage: p = "package"; break; + case PROTprotected: p = "protected"; break; + case PROTpublic: p = "public"; break; + case PROTexport: p = "export"; break; + default: + assert(0); + break; + } + buf->writestring(p); + buf->writeByte(' '); +} + +void ProtDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + protectionToCBuffer(buf, protection); + AttribDeclaration::toCBuffer(buf, hgs); +} + +/********************************* AlignDeclaration ****************************/ + +AlignDeclaration::AlignDeclaration(unsigned sa, Dsymbols *decl) + : AttribDeclaration(decl) +{ + salign = sa; +} + +Dsymbol *AlignDeclaration::syntaxCopy(Dsymbol *s) +{ + AlignDeclaration *ad; + + assert(!s); + ad = new AlignDeclaration(salign, Dsymbol::arraySyntaxCopy(decl)); + return ad; +} + +void AlignDeclaration::setScope(Scope *sc) +{ + //printf("\tAlignDeclaration::setScope '%s'\n",toChars()); + if (decl) + { + setScopeNewSc(sc, sc->stc, sc->linkage, sc->protection, sc->explicitProtection, salign); + } +} + +void AlignDeclaration::semantic(Scope *sc) +{ + //printf("\tAlignDeclaration::semantic '%s'\n",toChars()); + if (decl) + { + semanticNewSc(sc, sc->stc, sc->linkage, sc->protection, sc->explicitProtection, salign); + } +} + + +void AlignDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->printf("align (%d)", salign); + AttribDeclaration::toCBuffer(buf, hgs); +} + +/********************************* AnonDeclaration ****************************/ + +AnonDeclaration::AnonDeclaration(Loc loc, int isunion, Dsymbols *decl) + : AttribDeclaration(decl) +{ + this->loc = loc; + this->isunion = isunion; + this->sem = 0; +} + +Dsymbol *AnonDeclaration::syntaxCopy(Dsymbol *s) +{ + AnonDeclaration *ad; + + assert(!s); + ad = new AnonDeclaration(loc, isunion, Dsymbol::arraySyntaxCopy(decl)); + return ad; +} + +void AnonDeclaration::semantic(Scope *sc) +{ + //printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", this); + + if (sem == 1) + { //printf("already completed\n"); + scope = NULL; + return; // semantic() already completed + } + + Scope *scx = NULL; + if (scope) + { sc = scope; + scx = scope; + scope = NULL; + } + + unsigned dprogress_save = Module::dprogress; + + assert(sc->parent); + + Dsymbol *parent = sc->parent->pastMixin(); + AggregateDeclaration *ad = parent->isAggregateDeclaration(); + + if (!ad || (!ad->isStructDeclaration() && !ad->isClassDeclaration())) + { + error("can only be a part of an aggregate"); + return; + } + + if (decl) + { + AnonymousAggregateDeclaration aad; + int adisunion; + + if (sc->anonAgg) + { ad = sc->anonAgg; + adisunion = sc->inunion; + } + else + adisunion = ad->isUnionDeclaration() != NULL; + +// printf("\tsc->anonAgg = %p\n", sc->anonAgg); +// printf("\tad = %p\n", ad); +// printf("\taad = %p\n", &aad); + + sc = sc->push(); + sc->anonAgg = &aad; + sc->stc &= ~(STCauto | STCscope | STCstatic | STCtls | STCgshared); + sc->inunion = isunion; + sc->offset = 0; + sc->flags = 0; + aad.structalign = sc->structalign; + aad.parent = ad; + + for (unsigned i = 0; i < decl->dim; i++) + { + Dsymbol *s = decl->tdata()[i]; + + s->semantic(sc); + if (isunion) + sc->offset = 0; + if (aad.sizeok == 2) + { + break; + } + } + sc = sc->pop(); + + // If failed due to forward references, unwind and try again later + if (aad.sizeok == 2) + { + ad->sizeok = 2; + //printf("\tsetting ad->sizeok %p to 2\n", ad); + if (!sc->anonAgg) + { + scope = scx ? scx : new Scope(*sc); + scope->setNoFree(); + scope->module->addDeferredSemantic(this); + } + Module::dprogress = dprogress_save; + //printf("\tforward reference %p\n", this); + return; + } + if (sem == 0) + { Module::dprogress++; + sem = 1; + //printf("\tcompleted %p\n", this); + } + else + ;//printf("\talready completed %p\n", this); + + // 0 sized structs are set to 1 byte + if (aad.structsize == 0) + { + aad.structsize = 1; + aad.alignsize = 1; + } + + // Align size of anonymous aggregate +//printf("aad.structalign = %d, aad.alignsize = %d, sc->offset = %d\n", aad.structalign, aad.alignsize, sc->offset); + ad->alignmember(aad.structalign, aad.alignsize, &sc->offset); + //ad->structsize = sc->offset; +//printf("sc->offset = %d\n", sc->offset); + + // Add members of aad to ad + //printf("\tadding members of aad to '%s'\n", ad->toChars()); + for (unsigned i = 0; i < aad.fields.dim; i++) + { + VarDeclaration *v = aad.fields.tdata()[i]; + + v->offset += sc->offset; + ad->fields.push(v); + } + + // Add size of aad to ad + if (adisunion) + { + if (aad.structsize > ad->structsize) + ad->structsize = aad.structsize; + sc->offset = 0; + } + else + { + ad->structsize = sc->offset + aad.structsize; + sc->offset = ad->structsize; + } + + if (ad->alignsize < aad.alignsize) + ad->alignsize = aad.alignsize; + } +} + + +void AnonDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->printf(isunion ? "union" : "struct"); + buf->writestring("\n{\n"); + if (decl) + { + for (unsigned i = 0; i < decl->dim; i++) + { + Dsymbol *s = decl->tdata()[i]; + + //buf->writestring(" "); + s->toCBuffer(buf, hgs); + } + } + buf->writestring("}\n"); +} + +const char *AnonDeclaration::kind() +{ + return (isunion ? "anonymous union" : "anonymous struct"); +} + +/********************************* PragmaDeclaration ****************************/ + +PragmaDeclaration::PragmaDeclaration(Loc loc, Identifier *ident, Expressions *args, Dsymbols *decl) + : AttribDeclaration(decl) +{ + this->loc = loc; + this->ident = ident; + this->args = args; +} + +Dsymbol *PragmaDeclaration::syntaxCopy(Dsymbol *s) +{ + //printf("PragmaDeclaration::syntaxCopy(%s)\n", toChars()); + PragmaDeclaration *pd; + + assert(!s); + pd = new PragmaDeclaration(loc, ident, + Expression::arraySyntaxCopy(args), Dsymbol::arraySyntaxCopy(decl)); + return pd; +} + +void PragmaDeclaration::setScope(Scope *sc) +{ +#if TARGET_NET + if (ident == Lexer::idPool("assembly")) + { + if (!args || args->dim != 1) + { + error("pragma has invalid number of arguments"); + } + else + { + Expression *e = args->tdata()[0]; + e = e->semantic(sc); + e = e->optimize(WANTvalue | WANTinterpret); + args->tdata()[0] = e; + StringExp* se = e->toString(); + if (!se) + { + error("string expected, not '%s'", e->toChars()); + } + PragmaScope* pragma = new PragmaScope(this, sc->parent, se); + + assert(sc); + pragma->setScope(sc); + + //add to module members + assert(sc->module); + assert(sc->module->members); + sc->module->members->push(pragma); + } + } +#endif // TARGET_NET +} + +void PragmaDeclaration::semantic(Scope *sc) +{ // Should be merged with PragmaStatement + + //printf("\tPragmaDeclaration::semantic '%s'\n",toChars()); + if (ident == Id::msg) + { + if (args) + { + for (size_t i = 0; i < args->dim; i++) + { + Expression *e = args->tdata()[i]; + + e = e->semantic(sc); + e = e->optimize(WANTvalue | WANTinterpret); + StringExp *se = e->toString(); + if (se) + { + fprintf(stdmsg, "%.*s", (int)se->len, (char *)se->string); + } + else + fprintf(stdmsg, "%s", e->toChars()); + } + fprintf(stdmsg, "\n"); + } + goto Lnodecl; + } + else if (ident == Id::lib) + { + if (!args || args->dim != 1) + error("string expected for library name"); + else + { + Expression *e = args->tdata()[0]; + + e = e->semantic(sc); + e = e->optimize(WANTvalue | WANTinterpret); + args->tdata()[0] = e; + if (e->op == TOKerror) + goto Lnodecl; + StringExp *se = e->toString(); + if (!se) + error("string expected for library name, not '%s'", e->toChars()); + else if (global.params.verbose) + { + char *name = (char *)mem.malloc(se->len + 1); + memcpy(name, se->string, se->len); + name[se->len] = 0; + printf("library %s\n", name); + mem.free(name); + } + } + goto Lnodecl; + } +#if IN_GCC + else if (ident == Id::GNU_asm) + { + if (! args || args->dim != 2) + error("identifier and string expected for asm name"); + else + { + Expression *e; + Declaration *d = NULL; + StringExp *s = NULL; + + e = args->tdata()[0]; + e = e->semantic(sc); + if (e->op == TOKvar) + { + d = ((VarExp *)e)->var; + if (! d->isFuncDeclaration() && ! d->isVarDeclaration()) + d = NULL; + } + if (!d) + error("first argument of GNU_asm must be a function or variable declaration"); + + e = args->tdata()[1]; + e = e->semantic(sc); + e = e->optimize(WANTvalue | WANTinterpret); + e = e->toString(); + if (e && ((StringExp *)e)->sz == 1) + s = ((StringExp *)e); + else + error("second argument of GNU_asm must be a char string"); + + if (d && s) + d->c_ident = Lexer::idPool((char*) s->string); + } + goto Lnodecl; + } +#endif +#if DMDV2 + else if (ident == Id::startaddress) + { + if (!args || args->dim != 1) + error("function name expected for start address"); + else + { + Expression *e = args->tdata()[0]; + e = e->semantic(sc); + e = e->optimize(WANTvalue | WANTinterpret); + args->tdata()[0] = e; + Dsymbol *sa = getDsymbol(e); + if (!sa || !sa->isFuncDeclaration()) + error("function name expected for start address, not '%s'", e->toChars()); + } + goto Lnodecl; + } +#endif +#if TARGET_NET + else if (ident == Lexer::idPool("assembly")) + { + } +#endif // TARGET_NET + else if (global.params.ignoreUnsupportedPragmas) + { + if (global.params.verbose) + { + /* Print unrecognized pragmas + */ + printf("pragma %s", ident->toChars()); + if (args) + { + for (size_t i = 0; i < args->dim; i++) + { + Expression *e = args->tdata()[i]; + e = e->semantic(sc); + e = e->optimize(WANTvalue | WANTinterpret); + if (i == 0) + printf(" ("); + else + printf(","); + printf("%s", e->toChars()); + } + if (args->dim) + printf(")"); + } + printf("\n"); + } + goto Lnodecl; + } + else + error("unrecognized pragma(%s)", ident->toChars()); + +Ldecl: + if (decl) + { + for (unsigned i = 0; i < decl->dim; i++) + { + Dsymbol *s = decl->tdata()[i]; + + s->semantic(sc); + } + } + return; + +Lnodecl: + if (decl) + { + error("pragma is missing closing ';'"); + goto Ldecl; // do them anyway, to avoid segfaults. + } +} + +int PragmaDeclaration::oneMember(Dsymbol **ps, Identifier *ident) +{ + *ps = NULL; + return TRUE; +} + +const char *PragmaDeclaration::kind() +{ + return "pragma"; +} + +void PragmaDeclaration::toObjFile(int multiobj) +{ + if (ident == Id::lib) + { + assert(args && args->dim == 1); + + Expression *e = args->tdata()[0]; + + assert(e->op == TOKstring); + + StringExp *se = (StringExp *)e; + char *name = (char *)mem.malloc(se->len + 1); + memcpy(name, se->string, se->len); + name[se->len] = 0; +#if OMFOBJ + /* The OMF format allows library names to be inserted + * into the object file. The linker will then automatically + * search that library, too. + */ + obj_includelib(name); +#elif ELFOBJ || MACHOBJ + /* The format does not allow embedded library names, + * so instead append the library name to the list to be passed + * to the linker. + */ + global.params.libfiles->push(name); +#else + error("pragma lib not supported"); +#endif + } +#if DMDV2 + else if (ident == Id::startaddress) + { + assert(args && args->dim == 1); + Expression *e = args->tdata()[0]; + Dsymbol *sa = getDsymbol(e); + FuncDeclaration *f = sa->isFuncDeclaration(); + assert(f); + Symbol *s = f->toSymbol(); + obj_startaddress(s); + } +#endif + AttribDeclaration::toObjFile(multiobj); +} + +void PragmaDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->printf("pragma (%s", ident->toChars()); + if (args && args->dim) + { + buf->writestring(", "); + argsToCBuffer(buf, args, hgs); + } + buf->writeByte(')'); + AttribDeclaration::toCBuffer(buf, hgs); +} + + +/********************************* ConditionalDeclaration ****************************/ + +ConditionalDeclaration::ConditionalDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl) + : AttribDeclaration(decl) +{ + //printf("ConditionalDeclaration::ConditionalDeclaration()\n"); + this->condition = condition; + this->elsedecl = elsedecl; +} + +Dsymbol *ConditionalDeclaration::syntaxCopy(Dsymbol *s) +{ + ConditionalDeclaration *dd; + + assert(!s); + dd = new ConditionalDeclaration(condition->syntaxCopy(), + Dsymbol::arraySyntaxCopy(decl), + Dsymbol::arraySyntaxCopy(elsedecl)); + return dd; +} + + +int ConditionalDeclaration::oneMember(Dsymbol **ps, Identifier *ident) +{ + //printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition->inc); + if (condition->inc) + { + Dsymbols *d = condition->include(NULL, NULL) ? decl : elsedecl; + return Dsymbol::oneMembers(d, ps, ident); + } + *ps = NULL; + return TRUE; +} + +void ConditionalDeclaration::emitComment(Scope *sc) +{ + //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc); + if (condition->inc) + { + AttribDeclaration::emitComment(sc); + } + else if (sc->docbuf) + { + /* If generating doc comment, be careful because if we're inside + * a template, then include(NULL, NULL) will fail. + */ + Dsymbols *d = decl ? decl : elsedecl; + for (unsigned i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + s->emitComment(sc); + } + } +} + +// Decide if 'then' or 'else' code should be included + +Dsymbols *ConditionalDeclaration::include(Scope *sc, ScopeDsymbol *sd) +{ + //printf("ConditionalDeclaration::include()\n"); + assert(condition); + return condition->include(sc, sd) ? decl : elsedecl; +} + +void ConditionalDeclaration::setScope(Scope *sc) +{ + Dsymbols *d = include(sc, NULL); + + //printf("\tConditionalDeclaration::setScope '%s', d = %p\n",toChars(), d); + if (d) + { + for (unsigned i = 0; i < d->dim; i++) + { + Dsymbol *s = d->tdata()[i]; + + s->setScope(sc); + } + } +} + +void ConditionalDeclaration::importAll(Scope *sc) +{ + Dsymbols *d = include(sc, NULL); + + //printf("\tConditionalDeclaration::importAll '%s', d = %p\n",toChars(), d); + if (d) + { + for (unsigned i = 0; i < d->dim; i++) + { + Dsymbol *s = d->tdata()[i]; + + s->importAll(sc); + } + } +} + +void ConditionalDeclaration::addComment(unsigned char *comment) +{ + /* Because addComment is called by the parser, if we called + * include() it would define a version before it was used. + * But it's no problem to drill down to both decl and elsedecl, + * so that's the workaround. + */ + + if (comment) + { + Dsymbols *d = decl; + + for (int j = 0; j < 2; j++) + { + if (d) + { + for (unsigned i = 0; i < d->dim; i++) + { Dsymbol *s; + + s = d->tdata()[i]; + //printf("ConditionalDeclaration::addComment %s\n", s->toChars()); + s->addComment(comment); + } + } + d = elsedecl; + } + } +} + +void ConditionalDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + condition->toCBuffer(buf, hgs); + if (decl || elsedecl) + { + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + if (decl) + { + for (unsigned i = 0; i < decl->dim; i++) + { + Dsymbol *s = decl->tdata()[i]; + + buf->writestring(" "); + s->toCBuffer(buf, hgs); + } + } + buf->writeByte('}'); + if (elsedecl) + { + buf->writenl(); + buf->writestring("else"); + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + for (unsigned i = 0; i < elsedecl->dim; i++) + { + Dsymbol *s = elsedecl->tdata()[i]; + + buf->writestring(" "); + s->toCBuffer(buf, hgs); + } + buf->writeByte('}'); + } + } + else + buf->writeByte(':'); + buf->writenl(); +} + +/***************************** StaticIfDeclaration ****************************/ + +StaticIfDeclaration::StaticIfDeclaration(Condition *condition, + Dsymbols *decl, Dsymbols *elsedecl) + : ConditionalDeclaration(condition, decl, elsedecl) +{ + //printf("StaticIfDeclaration::StaticIfDeclaration()\n"); + sd = NULL; + addisdone = 0; +} + + +Dsymbol *StaticIfDeclaration::syntaxCopy(Dsymbol *s) +{ + StaticIfDeclaration *dd; + + assert(!s); + dd = new StaticIfDeclaration(condition->syntaxCopy(), + Dsymbol::arraySyntaxCopy(decl), + Dsymbol::arraySyntaxCopy(elsedecl)); + return dd; +} + + +int StaticIfDeclaration::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +{ + //printf("StaticIfDeclaration::addMember() '%s'\n",toChars()); + /* This is deferred until semantic(), so that + * expressions in the condition can refer to declarations + * in the same scope, such as: + * + * template Foo(int i) + * { + * const int j = i + 1; + * static if (j == 3) + * const int k; + * } + */ + this->sd = sd; + int m = 0; + + if (memnum == 0) + { m = AttribDeclaration::addMember(sc, sd, memnum); + addisdone = 1; + } + return m; +} + + +void StaticIfDeclaration::importAll(Scope *sc) +{ + // do not evaluate condition before semantic pass +} + +void StaticIfDeclaration::setScope(Scope *sc) +{ + // do not evaluate condition before semantic pass +} + +void StaticIfDeclaration::semantic(Scope *sc) +{ + Dsymbols *d = include(sc, sd); + + //printf("\tStaticIfDeclaration::semantic '%s', d = %p\n",toChars(), d); + if (d) + { + if (!addisdone) + { AttribDeclaration::addMember(sc, sd, 1); + addisdone = 1; + } + + for (unsigned i = 0; i < d->dim; i++) + { + Dsymbol *s = d->tdata()[i]; + + s->semantic(sc); + } + } +} + +const char *StaticIfDeclaration::kind() +{ + return "static if"; +} + + +/***************************** CompileDeclaration *****************************/ + +CompileDeclaration::CompileDeclaration(Loc loc, Expression *exp) + : AttribDeclaration(NULL) +{ + //printf("CompileDeclaration(loc = %d)\n", loc.linnum); + this->loc = loc; + this->exp = exp; + this->sd = NULL; + this->compiled = 0; +} + +Dsymbol *CompileDeclaration::syntaxCopy(Dsymbol *s) +{ + //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars()); + CompileDeclaration *sc = new CompileDeclaration(loc, exp->syntaxCopy()); + return sc; +} + +int CompileDeclaration::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +{ + //printf("CompileDeclaration::addMember(sc = %p, sd = %p, memnum = %d)\n", sc, sd, memnum); + this->sd = sd; + if (memnum == 0) + { /* No members yet, so parse the mixin now + */ + compileIt(sc); + memnum |= AttribDeclaration::addMember(sc, sd, memnum); + compiled = 1; + } + return memnum; +} + +void CompileDeclaration::compileIt(Scope *sc) +{ + //printf("CompileDeclaration::compileIt(loc = %d) %s\n", loc.linnum, exp->toChars()); + exp = exp->semantic(sc); + exp = resolveProperties(sc, exp); + exp = exp->optimize(WANTvalue | WANTinterpret); + StringExp *se = exp->toString(); + if (!se) + { exp->error("argument to mixin must be a string, not (%s)", exp->toChars()); + } + else + { + se = se->toUTF8(sc); + Parser p(sc->module, (unsigned char *)se->string, se->len, 0); + p.loc = loc; + p.nextToken(); + decl = p.parseDeclDefs(0); + if (p.token.value != TOKeof) + exp->error("incomplete mixin declaration (%s)", se->toChars()); + } +} + +void CompileDeclaration::semantic(Scope *sc) +{ + //printf("CompileDeclaration::semantic()\n"); + + if (!compiled) + { + compileIt(sc); + AttribDeclaration::addMember(sc, sd, 0); + compiled = 1; + } + AttribDeclaration::semantic(sc); +} + +void CompileDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("mixin("); + exp->toCBuffer(buf, hgs); + buf->writestring(");"); + buf->writenl(); +} diff --git a/attrib.h b/attrib.h new file mode 100644 index 00000000..ab04c075 --- /dev/null +++ b/attrib.h @@ -0,0 +1,189 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_ATTRIB_H +#define DMD_ATTRIB_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "dsymbol.h" + +struct Expression; +struct Statement; +struct LabelDsymbol; +struct Initializer; +struct Module; +struct Condition; +struct HdrGenState; + +/**************************************************************/ + +struct AttribDeclaration : Dsymbol +{ + Dsymbols *decl; // array of Dsymbol's + + AttribDeclaration(Dsymbols *decl); + virtual Dsymbols *include(Scope *sc, ScopeDsymbol *s); + int addMember(Scope *sc, ScopeDsymbol *s, int memnum); + void setScopeNewSc(Scope *sc, + StorageClass newstc, enum LINK linkage, enum PROT protection, int explictProtection, + unsigned structalign); + void semanticNewSc(Scope *sc, + StorageClass newstc, enum LINK linkage, enum PROT protection, int explictProtection, + unsigned structalign); + void semantic(Scope *sc); + void semantic2(Scope *sc); + void semantic3(Scope *sc); + void inlineScan(); + void addComment(unsigned char *comment); + void emitComment(Scope *sc); + const char *kind(); + int oneMember(Dsymbol **ps, Identifier *ident); + int hasPointers(); + bool hasStaticCtorOrDtor(); + void checkCtorConstInit(); + void addLocalClass(ClassDeclarations *); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toJsonBuffer(OutBuffer *buf); + AttribDeclaration *isAttribDeclaration() { return this; } + + void toObjFile(int multiobj); // compile to .obj file + int cvMember(unsigned char *p); +}; + +struct StorageClassDeclaration: AttribDeclaration +{ + StorageClass stc; + + StorageClassDeclaration(StorageClass stc, Dsymbols *decl); + Dsymbol *syntaxCopy(Dsymbol *s); + void setScope(Scope *sc); + void semantic(Scope *sc); + int oneMember(Dsymbol **ps, Identifier *ident); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + static void stcToCBuffer(OutBuffer *buf, StorageClass stc); +}; + +struct LinkDeclaration : AttribDeclaration +{ + enum LINK linkage; + + LinkDeclaration(enum LINK p, Dsymbols *decl); + Dsymbol *syntaxCopy(Dsymbol *s); + void setScope(Scope *sc); + void semantic(Scope *sc); + void semantic3(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + char *toChars(); +}; + +struct ProtDeclaration : AttribDeclaration +{ + enum PROT protection; + + ProtDeclaration(enum PROT p, Dsymbols *decl); + Dsymbol *syntaxCopy(Dsymbol *s); + void importAll(Scope *sc); + void setScope(Scope *sc); + void semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + static void protectionToCBuffer(OutBuffer *buf, enum PROT protection); +}; + +struct AlignDeclaration : AttribDeclaration +{ + unsigned salign; + + AlignDeclaration(unsigned sa, Dsymbols *decl); + Dsymbol *syntaxCopy(Dsymbol *s); + void setScope(Scope *sc); + void semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct AnonDeclaration : AttribDeclaration +{ + int isunion; + int sem; // 1 if successful semantic() + + AnonDeclaration(Loc loc, int isunion, Dsymbols *decl); + Dsymbol *syntaxCopy(Dsymbol *s); + void semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + const char *kind(); +}; + +struct PragmaDeclaration : AttribDeclaration +{ + Expressions *args; // array of Expression's + + PragmaDeclaration(Loc loc, Identifier *ident, Expressions *args, Dsymbols *decl); + Dsymbol *syntaxCopy(Dsymbol *s); + void semantic(Scope *sc); + void setScope(Scope *sc); + int oneMember(Dsymbol **ps, Identifier *ident); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + const char *kind(); + void toObjFile(int multiobj); // compile to .obj file +}; + +struct ConditionalDeclaration : AttribDeclaration +{ + Condition *condition; + Dsymbols *elsedecl; // array of Dsymbol's for else block + + ConditionalDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl); + Dsymbol *syntaxCopy(Dsymbol *s); + int oneMember(Dsymbol **ps, Identifier *ident); + void emitComment(Scope *sc); + Dsymbols *include(Scope *sc, ScopeDsymbol *s); + void addComment(unsigned char *comment); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toJsonBuffer(OutBuffer *buf); + void importAll(Scope *sc); + void setScope(Scope *sc); +}; + +struct StaticIfDeclaration : ConditionalDeclaration +{ + ScopeDsymbol *sd; + int addisdone; + + StaticIfDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl); + Dsymbol *syntaxCopy(Dsymbol *s); + int addMember(Scope *sc, ScopeDsymbol *s, int memnum); + void semantic(Scope *sc); + void importAll(Scope *sc); + void setScope(Scope *sc); + const char *kind(); +}; + +// Mixin declarations + +struct CompileDeclaration : AttribDeclaration +{ + Expression *exp; + + ScopeDsymbol *sd; + int compiled; + + CompileDeclaration(Loc loc, Expression *exp); + Dsymbol *syntaxCopy(Dsymbol *s); + int addMember(Scope *sc, ScopeDsymbol *sd, int memnum); + void compileIt(Scope *sc); + void semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +#endif /* DMD_ATTRIB_H */ diff --git a/backend/aa.c b/backend/aa.c new file mode 100644 index 00000000..a6d3d38a --- /dev/null +++ b/backend/aa.c @@ -0,0 +1,498 @@ + +// Copyright (c) 2000-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gpl.txt. +// See the included readme.txt for details. + + +#include +#include +#include +#include + +#include "tinfo.h" +#include "aa.h" + +// Implementation of associative array +// Auto-rehash and pre-allocate - Dave Fladebo + +static unsigned long prime_list[] = +{ + 97UL, 389UL, + 1543UL, 6151UL, + 24593UL, 98317UL, + 393241UL, 1572869UL, + 6291469UL, 25165843UL, + 100663319UL, 402653189UL, + 1610612741UL, 4294967291UL +}; + + +/********************************** + * Align to next pointer boundary, so value + * will be aligned. + */ + +size_t aligntsize(size_t tsize) +{ + // Is pointer alignment on the x64 4 bytes or 8? + return (tsize + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1); +} + +/********************************** + * Constructor. + */ + +AArray::AArray(TypeInfo *keyti, size_t valuesize) +{ + this->keyti = keyti; + this->valuesize = valuesize; + this->nodes = 0; + this->buckets = NULL; + this->buckets_length = 0; +} + + +/********************************** + * Destructor. + */ + +void delnodes(aaA* e) +{ aaA* en; + + do + { + if (e->left) + { if (!e->right) + { en = e; + e = e->left; + delete en; + continue; + } + delnodes(e->left); + } + en = e; + e = e->right; + delete en; + } while (e != NULL); +} + +AArray::~AArray() +{ + if (buckets) + { + for (size_t i = 0; i < buckets_length; i++) + { aaA* e = buckets[i]; + + if (e) + delnodes(e); // recursively free all nodes + } + delete [] buckets; + } +} + + +/************************************************* + * Get pointer to value in associative array indexed by key. + * Add entry for key if it is not already there. + */ + +void* AArray::get(void *pkey) +{ + //printf("AArray::get()\n"); + size_t i; + aaA* e; + size_t keysize = aligntsize(keyti->tsize()); + + if (!buckets_length) + { + typedef aaA* aaAp; + buckets_length = prime_list[0]; + buckets = new aaAp[buckets_length]; + memset(buckets, 0, buckets_length * sizeof(buckets[0])); + } + + hash_t key_hash = keyti->getHash(pkey); + i = key_hash % buckets_length; + //printf("key_hash = %x, buckets_length = %d, i = %d\n", key_hash, buckets_length, i); + aaA** pe = &buckets[i]; + while ((e = *pe) != NULL) + { int c; + + c = key_hash - e->hash; + if (c == 0) + { + c = keyti->compare(pkey, e + 1); + if (c == 0) + goto Lret; + } + + if (c < 0) + pe = &e->left; + else + pe = &e->right; + } + + // Not found, create new elem + //printf("create new one\n"); + e = (aaA *) new char[sizeof(aaA) + keysize + valuesize]; + memcpy(e + 1, pkey, keysize); + memset((unsigned char *)(e + 1) + keysize, 0, valuesize); + e->hash = key_hash; + e->left = NULL; + e->right = NULL; + *pe = e; + + ++nodes; + //printf("length = %d, nodes = %d\n", buckets_length, nodes); + if (nodes > buckets_length * 4) + { + //printf("rehash()\n"); + rehash(); + } + +Lret: + return (void *)((char *)(e + 1) + keysize); +} + + +/************************************************* + * Determine if key is in aa. + * Returns: + * NULL not in aa + * !=NULL in aa, return pointer to value + */ + +void* AArray::in(void *pkey) +{ + //printf("_aaIn(), .length = %d, .ptr = %x\n", aa.a.length, cast(uint)aa.a.ptr); + size_t len = buckets_length; + + if (len) + { + hash_t key_hash = keyti->getHash(pkey); + //printf("hash = %d\n", key_hash); + size_t i = key_hash % len; + aaA *e = buckets[i]; + while (e != NULL) + { int c; + + c = key_hash - e->hash; + if (c == 0) + { + c = keyti->compare(pkey, e + 1); + if (c == 0) + return (char *)(e + 1) + aligntsize(keyti->tsize()); + } + + if (c < 0) + e = e->left; + else + e = e->right; + } + } + + // Not found + return NULL; +} + + +/************************************************* + * Delete key entry in aa[]. + * If key is not in aa[], do nothing. + */ + +void AArray::del(void *pkey) +{ + aaA* e; + + if (buckets_length) + { + hash_t key_hash = keyti->getHash(pkey); + //printf("hash = %d\n", key_hash); + size_t i = key_hash % buckets_length; + aaA** pe = &buckets[i]; + while ((e = *pe) != NULL) // NULL means not found + { int c; + + c = key_hash - e->hash; + if (c == 0) + { + c = keyti->compare(pkey, e + 1); + if (c == 0) + { + if (!e->left && !e->right) + { + *pe = NULL; + } + else if (e->left && !e->right) + { + *pe = e->left; + e->left = NULL; + } + else if (!e->left && e->right) + { + *pe = e->right; + e->right = NULL; + } + else + { + *pe = e->left; + e->left = NULL; + do + pe = &(*pe)->right; + while (*pe); + *pe = e->right; + e->right = NULL; + } + + nodes--; + + delete[] e; + break; + } + } + + if (c < 0) + pe = &e->left; + else + pe = &e->right; + } + } +} + + +/******************************************** + * Produce array of keys from aa. + */ + +void *AArray::keys() +{ + void *p = NULL; + + if (nodes) + { + size_t keysize = keyti->tsize(); + + typedef char* charp; + p = (void *)new charp[nodes * keysize]; + void *q = p; + for (size_t i = 0; i < buckets_length; i++) + { aaA* e = buckets[i]; + + if (e) + q = keys_x(e, q, keysize); + } + } + return p; +} + +void *AArray::keys_x(aaA* e, void *p, size_t keysize) +{ + do + { + memcpy(p, e + 1, keysize); + if (e->left) + { if (!e->right) + { e = e->left; + continue; + } + p = keys_x(e->left, p, keysize); + } + e = e->right; + } while (e != NULL); + return p; +} + +/******************************************** + * Produce array of values from aa. + */ + +void *AArray::values() +{ + void *p = NULL; + + if (nodes) + { + p = (void *)new char[nodes * valuesize]; + void *q = p; + for (size_t i = 0; i < buckets_length; i++) + { aaA *e = buckets[i]; + + if (e) + q = values_x(e, q); + } + } + return p; +} + +void *AArray::values_x(aaA *e, void *p) +{ + size_t keysize = keyti->tsize(); + do + { + memcpy(p, + (char *)(e + 1) + keysize, + valuesize); + p = (void *)((char *)p + valuesize); + if (e->left) + { if (!e->right) + { e = e->left; + continue; + } + p = values_x(e->left, p); + } + e = e->right; + } while (e != NULL); + return p; +} + + +/******************************************** + * Rehash an array. + */ + +void AArray::rehash() +{ + //printf("Rehash\n"); + if (buckets_length) + { + size_t len = nodes; + + if (len) + { size_t i; + aaA** newbuckets; + size_t newbuckets_length; + + for (i = 0; i < sizeof(prime_list)/sizeof(prime_list[0]) - 1; i++) + { + if (len <= prime_list[i]) + break; + } + newbuckets_length = prime_list[i]; + typedef aaA* aaAp; + newbuckets = new aaAp[newbuckets_length]; + memset(newbuckets, 0, newbuckets_length * sizeof(newbuckets[0])); + + for (i = 0; i < buckets_length; i++) + { aaA *e = buckets[i]; + + if (e) + rehash_x(e, newbuckets, newbuckets_length); + } + + delete[] buckets; + buckets = newbuckets; + buckets_length = newbuckets_length; + } + } +} + +void AArray::rehash_x(aaA* olde, aaA** newbuckets, size_t newbuckets_length) +{ + while (1) + { + aaA* left = olde->left; + aaA* right = olde->right; + olde->left = NULL; + olde->right = NULL; + + aaA* e; + + //printf("rehash %p\n", olde); + hash_t key_hash = olde->hash; + size_t i = key_hash % newbuckets_length; + aaA** pe = &newbuckets[i]; + while ((e = *pe) != NULL) + { int c; + + //printf("\te = %p, e->left = %p, e->right = %p\n", e, e->left, e->right); + assert(e->left != e); + assert(e->right != e); + c = key_hash - e->hash; + if (c == 0) + c = keyti->compare(olde + 1, e + 1); + if (c < 0) + pe = &e->left; + else if (c > 0) + pe = &e->right; + else + assert(0); + } + *pe = olde; + + if (right) + { + if (!left) + { olde = right; + continue; + } + rehash_x(right, newbuckets, newbuckets_length); + } + if (!left) + break; + olde = left; + } +} + + +/********************************************* + * For each element in the AArray, + * call dg(void *parameter, void *pkey, void *pvalue) + * If dg returns !=0, stop and return that value. + */ + +typedef int (*dg2_t)(void *, void *, void *); + +int AArray::apply(void *parameter, dg2_t dg) +{ int result = 0; + + //printf("_aaApply(aa = %p, keysize = %d, dg = %p)\n", this, keyti->tsize(), dg); + + if (nodes) + { + size_t keysize = aligntsize(keyti->tsize()); + + for (size_t i = 0; i < buckets_length; i++) + { aaA* e = buckets[i]; + + if (e) + { + result = apply_x(e, dg, keysize, parameter); + if (result) + break; + } + } + } + return result; +} + +int AArray::apply_x(aaA* e, dg2_t dg, size_t keysize, void *parameter) +{ int result; + + do + { + //printf("apply_x(e = %p, dg = %p)\n", e, dg); + result = (*dg)(parameter, e + 1, (char *)(e + 1) + keysize); + if (result) + break; + if (e->right) + { if (!e->left) + { + e = e->right; + continue; + } + result = apply_x(e->right, dg, keysize, parameter); + if (result) + break; + } + e = e->left; + } while (e); + + return result; +} + + diff --git a/backend/aa.h b/backend/aa.h new file mode 100644 index 00000000..d628fb9e --- /dev/null +++ b/backend/aa.h @@ -0,0 +1,109 @@ + +// Copyright (c) 2006-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gpl.txt. +// See the included readme.txt for details. + + + +#ifndef AA_H +#define AA_H + +#include + +#include "tinfo.h" + +struct aaA +{ + aaA *left; + aaA *right; + hash_t hash; + /* key */ + /* value */ +}; + +struct AArray +{ + TypeInfo *keyti; + size_t valuesize; + size_t nodes; + + aaA** buckets; + size_t buckets_length; + + AArray(TypeInfo *keyti, size_t valuesize); + + ~AArray(); + + size_t length() + { + return nodes; + } + + /************************************************* + * Get pointer to value in associative array indexed by key. + * Add entry for key if it is not already there. + */ + + void* get(void *pkey); + + void* get(char *string) { return get(&string); } + + /************************************************* + * Determine if key is in aa. + * Returns: + * null not in aa + * !=null in aa, return pointer to value + */ + + void* in(void *pkey); + + void* in(char *string) { return in(&string); } + + /************************************************* + * Delete key entry in aa[]. + * If key is not in aa[], do nothing. + */ + + void del(void *pkey); + + /******************************************** + * Produce array of keys from aa. + */ + + void *keys(); + + /******************************************** + * Produce array of values from aa. + */ + + void *values(); + + /******************************************** + * Rehash an array. + */ + + void rehash(); + + /********************************************* + * For each element in the AArray, + * call dg(void *parameter, void *pkey, void *pvalue) + * If dg returns !=0, stop and return that value. + */ + + typedef int (*dg2_t)(void *, void *, void *); + + int apply(void *parameter, dg2_t dg); + + private: + void *keys_x(aaA* e, void *p, size_t keysize); + void *values_x(aaA *e, void *p); + void rehash_x(aaA* olde, aaA** newbuckets, size_t newbuckets_length); + int apply_x(aaA* e, dg2_t dg, size_t keysize, void *parameter); +}; + +#endif + diff --git a/backend/backend.txt b/backend/backend.txt new file mode 100644 index 00000000..3956a63d --- /dev/null +++ b/backend/backend.txt @@ -0,0 +1,80 @@ +Files in the Back End +===================== + +aa.c simple hash table +aa.h header for simple hash table +bcomplex.c our own complex number implementation because we can't rely on host C compiler +bcomplex.h header for our own complex numbers +blockopt.c manage and simple optimizations on graphs of basic blocks +cc.h main header file for back end +cdef.h configuration +cdeflnx.h configuration for linux +cg.c global variables for code generator +cg87.c x87 FPU code generation +cgcod.c main loop for code generator +cgcs.c compute common subexpressions for non-optimized code generation +cgcv.c CodeView symbol debug info generation +cgcv.h header for cgcv.c +cgelem.c local optimizations of elem trees +cgen.c generate/manage linked list of code instructions +cgobj.c generate OMF object files +cgreg.c register allocator +cgsched.c instruction scheduler +cod1.c code gen +cod2.c code gen +cod3.c code gen +cod4.c code gen +cod5.c code gen +code.c memory management for code instructions +code.h define registers, register masks, and the CPU instruction linked list +cppman.c C++ name mangling +cv4.h CodeView symbolic debug info declarations +debug.c pretty printing for debug builds +dt.c static data for later output to object file +dt.h API for dt.c +dwarf.c generate DWARF symbolic debug info +dwarf.h API for dwarf.c +dwarf2.h Dwarf 3 spec declarations +ee.c handle IDDE debugger expression evaluation +el.c expression trees (intermediate code) +el.h header for el.c +elfobj.c generate ELF object files +evalu8.c constant folding +exh.h exception handling support +gdag.c Directed acyclic graphs and global optimizer common subexpressions +gflow.c global data flow analysis +global.h declarations for back end +glocal.c global optimizations +gloop.c global loop optimizations +go.c global optimizer main loop +go.h global optimizer declarations +gother.c other global optimizations +html.c support for embedding source code in html +html.h header for html.c +iasm.h declarations for inline assembler +mach.h declarations for Mach-O object file format +machobj.c generate Mach-O object files +md5.c implementation of MD5 message digest +md5.h API for md5.c +melf.h declarations for ELF file format +newman.c "new" C++ name mangling scheme +nteh.c Windows structured exception handling support +oper.h operators for expression tree +optabgen.c generate tables for back end +os.c some operating system specific support +out.c write data definitions to object file +outbuf.c resizeable buffer +outbuf.h API for resizeable buffer +ptrntab.c instruction tables for inline assembler +rtlsym.c initialize for compiler 'helper' runtime functions +rtlsym.h compiler 'helper' runtime functions +strtold.c our own version of strtold() because we cannot rely on C's +symbol.c symbols for the back end +tassert.h our own assert macro (to reduce code size) +tinfo.h specialization of hash table aa.c +ti_achar.c specialization of hash tables for array of chars +token.h C/C++ tokens +ty.h type masks +type.c back end type +type.h header for type.c +var.c global variables diff --git a/backend/bcomplex.c b/backend/bcomplex.c new file mode 100644 index 00000000..ec31a93d --- /dev/null +++ b/backend/bcomplex.c @@ -0,0 +1,292 @@ +// public domain + +#include + +#include "bcomplex.h" + +/*********************************************************/ + +Complex_ld Complex_ld::div(Complex_ld &x, Complex_ld &y) +{ + Complex_ld q; + long double r; + long double den; + + if (fabs(y.re) < fabs(y.im)) + { + r = y.re / y.im; + den = y.im + r * y.re; + q.re = (x.re * r + x.im) / den; + q.im = (x.im * r - x.re) / den; + } + else + { + r = y.im / y.re; + den = y.re + r * y.im; + q.re = (x.re + r * x.im) / den; + q.im = (x.im - r * x.re) / den; + } + return q; +} + +Complex_ld Complex_ld::mul(Complex_ld &x, Complex_ld &y) +{ + Complex_ld p; + + p.re = x.re * y.re - x.im * y.im; + p.im = x.im * y.re + x.re * y.im; + return p; +} + +long double Complex_ld::abs(Complex_ld &z) +{ + long double x,y,ans,temp; + + x = fabs(z.re); + y = fabs(z.im); + if (x == 0) + ans = y; + else if (y == 0) + ans = x; + else if (x > y) + { + temp = y / x; + ans = x * sqrt(1 + temp * temp); + } + else + { + temp = x / y; + ans = y * sqrt(1 + temp * temp); + } + return ans; +} + +Complex_ld Complex_ld::sqrtc(Complex_ld &z) +{ + Complex_ld c; + long double x,y,w,r; + + if (z.re == 0 && z.im == 0) + { + c.re = 0; + c.im = 0; + } + else + { + x = fabs(z.re); + y = fabs(z.im); + if (x >= y) + { + r = y / x; + w = sqrt(x) * sqrt(0.5 * (1 + sqrt(1 + r * r))); + } + else + { + r = x / y; + w = sqrt(y) * sqrt(0.5 * (r + sqrt(1 + r * r))); + } + if (z.re >= 0) + { + c.re = w; + c.im = z.im / (w + w); + } + else + { + c.im = (z.im >= 0) ? w : -w; + c.re = z.im / (c.im + c.im); + } + } + return c; +} + +/*********************************************************/ + +Complex_d Complex_d::div(Complex_d &x, Complex_d &y) +{ + Complex_d q; + long double r; + long double den; + + if (fabs(y.re) < fabs(y.im)) + { + r = y.re / y.im; + den = y.im + r * y.re; + q.re = (x.re * r + x.im) / den; + q.im = (x.im * r - x.re) / den; + } + else + { + r = y.im / y.re; + den = y.re + r * y.im; + q.re = (x.re + r * x.im) / den; + q.im = (x.im - r * x.re) / den; + } + return q; +} + +Complex_d Complex_d::mul(Complex_d &x, Complex_d &y) +{ + Complex_d p; + + p.re = x.re * y.re - x.im * y.im; + p.im = x.im * y.re + x.re * y.im; + return p; +} + +long double Complex_d::abs(Complex_d &z) +{ + long double x,y,ans,temp; + + x = fabs(z.re); + y = fabs(z.im); + if (x == 0) + ans = y; + else if (y == 0) + ans = x; + else if (x > y) + { + temp = y / x; + ans = x * sqrt(1 + temp * temp); + } + else + { + temp = x / y; + ans = y * sqrt(1 + temp * temp); + } + return ans; +} + +Complex_d Complex_d::sqrtc(Complex_d &z) +{ + Complex_d c; + long double x,y,w,r; + + if (z.re == 0 && z.im == 0) + { + c.re = 0; + c.im = 0; + } + else + { + x = fabs(z.re); + y = fabs(z.im); + if (x >= y) + { + r = y / x; + w = sqrt(x) * sqrt(0.5 * (1 + sqrt(1 + r * r))); + } + else + { + r = x / y; + w = sqrt(y) * sqrt(0.5 * (r + sqrt(1 + r * r))); + } + if (z.re >= 0) + { + c.re = w; + c.im = z.im / (w + w); + } + else + { + c.im = (z.im >= 0) ? w : -w; + c.re = z.im / (c.im + c.im); + } + } + return c; +} + +/*********************************************************/ + +Complex_f Complex_f::div(Complex_f &x, Complex_f &y) +{ + Complex_f q; + long double r; + long double den; + + if (fabs(y.re) < fabs(y.im)) + { + r = y.re / y.im; + den = y.im + r * y.re; + q.re = (x.re * r + x.im) / den; + q.im = (x.im * r - x.re) / den; + } + else + { + r = y.im / y.re; + den = y.re + r * y.im; + q.re = (x.re + r * x.im) / den; + q.im = (x.im - r * x.re) / den; + } + return q; +} + +Complex_f Complex_f::mul(Complex_f &x, Complex_f &y) +{ + Complex_f p; + + p.re = x.re * y.re - x.im * y.im; + p.im = x.im * y.re + x.re * y.im; + return p; +} + +long double Complex_f::abs(Complex_f &z) +{ + long double x,y,ans,temp; + + x = fabs(z.re); + y = fabs(z.im); + if (x == 0) + ans = y; + else if (y == 0) + ans = x; + else if (x > y) + { + temp = y / x; + ans = x * sqrt(1 + temp * temp); + } + else + { + temp = x / y; + ans = y * sqrt(1 + temp * temp); + } + return ans; +} + +Complex_f Complex_f::sqrtc(Complex_f &z) +{ + Complex_f c; + long double x,y,w,r; + + if (z.re == 0 && z.im == 0) + { + c.re = 0; + c.im = 0; + } + else + { + x = fabs(z.re); + y = fabs(z.im); + if (x >= y) + { + r = y / x; + w = sqrt(x) * sqrt(0.5 * (1 + sqrt(1 + r * r))); + } + else + { + r = x / y; + w = sqrt(y) * sqrt(0.5 * (r + sqrt(1 + r * r))); + } + if (z.re >= 0) + { + c.re = w; + c.im = z.im / (w + w); + } + else + { + c.im = (z.im >= 0) ? w : -w; + c.re = z.im / (c.im + c.im); + } + } + return c; +} + + diff --git a/backend/bcomplex.h b/backend/bcomplex.h new file mode 100644 index 00000000..bdebb6cf --- /dev/null +++ b/backend/bcomplex.h @@ -0,0 +1,36 @@ +// public domain + +#ifndef BCOMPLEX_H +#define BCOMPLEX_H 1 + +// Avoid interfering with system and other +// such; roll our own for reliable bootstrapping + +struct Complex_f +{ float re, im; + + static Complex_f div(Complex_f &x, Complex_f &y); + static Complex_f mul(Complex_f &x, Complex_f &y); + static long double abs(Complex_f &z); + static Complex_f sqrtc(Complex_f &z); +}; + +struct Complex_d +{ double re, im; + + static Complex_d div(Complex_d &x, Complex_d &y); + static Complex_d mul(Complex_d &x, Complex_d &y); + static long double abs(Complex_d &z); + static Complex_d sqrtc(Complex_d &z); +}; + +struct Complex_ld +{ long double re, im; + + static Complex_ld div(Complex_ld &x, Complex_ld &y); + static Complex_ld mul(Complex_ld &x, Complex_ld &y); + static long double abs(Complex_ld &z); + static Complex_ld sqrtc(Complex_ld &z); +}; + +#endif diff --git a/backend/blockopt.c b/backend/blockopt.c new file mode 100644 index 00000000..320b0b26 --- /dev/null +++ b/backend/blockopt.c @@ -0,0 +1,2204 @@ +// Copyright (C) 1986-1997 by Symantec +// Copyright (C) 2000-2010 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +/**************************************************************** + * Handle basic blocks. + */ + +#if !SPP + +#include +#include +#include +#include + +#include "cc.h" +#include "oper.h" +#include "el.h" +#include "type.h" +#include "global.h" +#include "go.h" +#include "code.h" +#if SCPP +#include "parser.h" +#include "iasm.h" +#endif + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +STATIC void bropt(void); +STATIC void brrear(void); +STATIC void search(block *b); +STATIC void elimblks(void); +STATIC int mergeblks(void); +STATIC void blident(void); +STATIC void blreturn(void); +STATIC void brmin(void); +STATIC void bltailmerge(void); +STATIC void block_check(); +STATIC void brtailrecursion(); +STATIC elem * assignparams(elem **pe,int *psi,elem **pe2); +STATIC void emptyloops(); +int el_anyframeptr(elem *e); + +unsigned numblks; // number of basic blocks in current function +block *startblock; /* beginning block of function */ + /* (can have no predecessors) */ + +block **dfo = NULL; /* array of depth first order */ +unsigned dfotop; /* # of items in dfo[] */ + +block *curblock; /* current block being read in */ +block *block_last; // last block read in + +static block * block_freelist; + +//////////////////////////// +// Storage allocator. + +static block blkzero; + +__inline block *block_calloc_i() +{ block *b; + + if (block_freelist) + { b = block_freelist; + block_freelist = b->Bnext; + *b = blkzero; + } + else + b = (block *) mem_fcalloc(sizeof(block)); + return b; +} + +block *block_calloc() +{ + return block_calloc_i(); +} + +////////////////////////////////// +// + +unsigned bc_goal[BCMAX]; + +void block_init() +{ int i; + + for (i = 0; i < BCMAX; i++) + bc_goal[i] = GOALvalue; + bc_goal[BCgoto] = GOALnone; + bc_goal[BCret ] = GOALnone; + bc_goal[BCexit] = GOALnone; +} + +////////////////////////////////// +// + +void block_term() +{ +#if TERMCODE + block *b; + + while (block_freelist) + { b = block_freelist->Bnext; + mem_ffree(block_freelist); + block_freelist = b; + } + +#endif +} + +/************************** + * Finish up this block and start the next one. + */ + +#if MARS +void block_next(Blockx *bctx,enum BC bc,block *bn) +{ + bctx->curblock->BC = bc; + block_last = bctx->curblock; + if (!bn) + bn = block_calloc_i(); + bctx->curblock->Bnext = bn; // next block + bctx->curblock = bctx->curblock->Bnext; // new current block +#if NTEXCEPTIONS + bctx->curblock->Btry = bctx->tryblock; +#endif + bctx->curblock->Bflags |= bctx->flags; +} +#else +void block_next(enum BC bc,block *bn) +{ + curblock->BC = bc; + curblock->Bsymend = globsym.top; + block_last = curblock; + if (!bn) + bn = block_calloc_i(); + curblock->Bnext = bn; // next block + curblock = curblock->Bnext; // new current block + curblock->Bsymstart = globsym.top; + curblock->Btry = pstate.STbtry; +} + +void block_next() +{ + block_next((enum BC)curblock->BC,NULL); +} +#endif + +/************************** + * Finish up this block and start the next one. + */ + +#if MARS + +block *block_goto(Blockx *bx,enum BC bc,block *bn) +{ block *b; + + b = bx->curblock; + block_next(bx,bc,bn); + list_append(&b->Bsucc,bx->curblock); + return bx->curblock; +} + +#endif + +/**************************** + * Goto a block named gotolbl. + * Start a new block that is labelled by newlbl. + */ + +#if SCPP + +void block_goto() +{ + block_goto(block_calloc()); +} + +void block_goto(block *bn) +{ + block_goto(bn,bn); +} + +void block_goto(block *bgoto,block *bnew) +{ + enum BC bc; + + assert(bgoto); + list_append(&(curblock->Bsucc),bgoto); + if (curblock->Bcode) // If there is already code in the block + // then this is an ASM block + bc = BCasm; + else + bc = BCgoto; // fall thru to next block + block_next(bc,bnew); +} + +#endif + +/********************************** + * Replace block numbers with block pointers. + * Also compute numblks and maxblks. + */ + +void block_ptr() +{ block *b; + +/* dbg_printf("block_ptr()\n");*/ + + numblks = 0; + for (b = startblock; b; b = b->Bnext) /* for each block */ + { +#if SCPP + b->Bblknum = numblks; +#endif + numblks++; + } + maxblks = 3 * numblks; /* allow for increase in # of blocks */ +} + +/******************************* + * Build predecessor list (Bpred) for each block. + */ + +void block_pred() +{ block *b; + + //dbg_printf("block_pred()\n"); + for (b = startblock; b; b = b->Bnext) /* for each block */ + list_free(&b->Bpred,FPNULL); + + for (b = startblock; b; b = b->Bnext) /* for each block */ + { register list_t bp; + + //printf("b = %p, BC = ",b); WRBC(b->BC); printf("\n"); + for (bp = b->Bsucc; bp; bp = list_next(bp)) + { /* for each successor to b */ + //printf("\tbs = %p\n",list_block(bp)); + assert(list_block(bp)); + list_prepend(&(list_block(bp)->Bpred),b); + } + } + assert(startblock->Bpred == NULL); /* startblock has no preds */ +} + +/******************************************** + * Clear visit. + */ + +void block_clearvisit() +{ block *b; + + for (b = startblock; b; b = b->Bnext) // for each block + b->Bflags &= ~BFLvisited; // mark as unvisited +} + +/******************************************** + * Visit block and each of its predecessors. + */ + +void block_visit(block *b) +{ list_t l; + + b->Bflags |= BFLvisited; + for (l = b->Bsucc; l; l = list_next(l)) // for each successor + { block *bs = list_block(l); + assert(bs); + if ((bs->Bflags & BFLvisited) == 0) // if not visited + block_visit(bs); + } +} + +/***************************** + * Compute number of parents (Bcount) of each basic block. + */ + +void block_compbcount() +{ + block_clearvisit(); + block_visit(startblock); // visit all reachable blocks + elimblks(); // eliminate unvisited blocks +} + +/******************************* + * Free list of blocks. + */ + +void blocklist_free(block **pb) +{ block *b,*bn; + + for (b = *pb; b; b = bn) + { bn = b->Bnext; + block_free(b); + } + *pb = NULL; +} + +/******************************** + * Free optimizer gathered data. + */ + +void block_optimizer_free(block *b) +{ + vec_free(b->Bdom); + vec_free(b->Binrd); + vec_free(b->Boutrd); + vec_free(b->Binlv); + vec_free(b->Boutlv); + vec_free(b->Bin); + vec_free(b->Bout); + vec_free(b->Bgen); + vec_free(b->Bkill); + vec_free(b->Bout2); + vec_free(b->Bgen2); + vec_free(b->Bkill2); + + memset(&b->_BLU,0,sizeof(b->_BLU)); +} + +/**************************** + * Free a block. + */ + +void block_free(block *b) +{ + assert(b); + if (b->Belem) + el_free(b->Belem); + list_free(&b->Bsucc,FPNULL); + list_free(&b->Bpred,FPNULL); + if (OPTIMIZER) + block_optimizer_free(b); + switch (b->BC) + { case BCswitch: + case BCifthen: + case BCjmptab: +#if MARS + free(b->BS.Bswitch); +#else + MEM_PH_FREE(b->BS.Bswitch); +#endif + break; +#if SCPP + case BCcatch: + type_free(b->Bcatchtype); + break; +#endif + case BCasm: + code_free(b->Bcode); + break; + } + b->Bnext = block_freelist; + block_freelist = b; +} + +/**************************** + * Hydrate/dehydrate a list of blocks. + */ + +#if HYDRATE +void blocklist_hydrate(block **pb) +{ block *b; + + while (isdehydrated(*pb)) + { + /*dbg_printf("blocklist_hydrate(*pb = %p) =",*pb);*/ + b = (block *)ph_hydrate(pb); + /*dbg_printf(" %p\n",b);*/ + el_hydrate(&b->Belem); + list_hydrate(&b->Bsucc,FPNULL); + list_hydrate(&b->Bpred,FPNULL); + (void) ph_hydrate(&b->Btry); +#if SCPP + (void) ph_hydrate(&b->Bendscope); + symbol_hydrate(&b->Binitvar); +#endif + switch (b->BC) + { +#if SCPP + case BCtry: + symbol_hydrate(&b->catchvar); + break; + case BCcatch: + type_hydrate(&b->Bcatchtype); + break; +#endif + case BCswitch: + (void) ph_hydrate(&b->BS.Bswitch); + break; + + case BC_finally: + //(void) ph_hydrate(&b->B_ret); + break; + + case BCasm: + code_hydrate(&b->Bcode); + break; + } +#if TX86 + filename_translate(&b->Bsrcpos); + srcpos_hydrate(&b->Bsrcpos); +#endif + pb = &b->Bnext; + } +} +#endif + +#if DEHYDRATE +void blocklist_dehydrate(block **pb) +{ block *b; + + while ((b = *pb) != NULL && !isdehydrated(b)) + { +#if DEBUG_XSYMGEN + if (xsym_gen && ph_in_head(b)) + return; +#endif + /*dbg_printf("blocklist_dehydrate(*pb = %p) =",b);*/ + ph_dehydrate(pb); + /*dbg_printf(" %p\n",*pb);*/ + el_dehydrate(&b->Belem); + list_dehydrate(&b->Bsucc,FPNULL); + list_dehydrate(&b->Bpred,FPNULL); + ph_dehydrate(&b->Btry); +#if SCPP + ph_dehydrate(&b->Bendscope); + symbol_dehydrate(&b->Binitvar); +#endif + switch (b->BC) + { +#if SCPP + case BCtry: + symbol_dehydrate(&b->catchvar); + break; + case BCcatch: + type_dehydrate(&b->Bcatchtype); + break; +#endif + case BCswitch: + ph_dehydrate(&b->BS.Bswitch); + break; + + case BC_finally: + //ph_dehydrate(&b->B_ret); + break; + + case BCasm: + code_dehydrate(&b->Bcode); + break; + } +#if TX86 + srcpos_dehydrate(&b->Bsrcpos); +#endif + pb = &b->Bnext; + } +} +#endif + +/**************************** + * Append elem to the elems comprising the current block. + * Read in an expression and put it in curblock->Belem. + * If there is one already there, create a tree like: + * , + * / \ + * old e + */ + +void block_appendexp(block *b,elem *e) +{ elem *ec; + elem **pe; + + assert(MARS || PARSER); + if (e) + { + assert(b); + elem_debug(e); + pe = &b->Belem; + ec = *pe; + if (ec != NULL) + { + type *t = e->ET; + + if (t) + type_debug(t); + elem_debug(e); +#if MARS + tym_t ty = e->Ety; + + elem_debug(e); + /* Build tree such that (a,b) => (a,(b,e)) */ + while (ec->Eoper == OPcomma) + { + ec->Ety = ty; + ec->ET = t; + pe = &(ec->E2); + ec = *pe; + } + e = el_bin(OPcomma,ty,ec,e); + e->ET = t; +#else + /* Build tree such that (a,b) => (a,(b,e)) */ + while (ec->Eoper == OPcomma) + { + el_settype(ec,t); + pe = &(ec->E2); + ec = *pe; + } + e = el_bint(OPcomma,t,ec,e); +#endif + } + *pe = e; + } +} + +/************************ + * Mark curblock as initializing symbol s. + */ + +#if SCPP + +#undef block_initvar + +void block_initvar(symbol *s) +{ +#ifdef DEBUG + assert(curblock); +#endif + symbol_debug(s); + curblock->Binitvar = s; +} + +#endif + +/******************* + * Mark end of function. + * flag: + * 0 do a "return" + * 1 do a "return 0" + */ + +void block_endfunc(int flag) +{ +#if SCPP + curblock->Bsymend = globsym.top; + curblock->Bendscope = curblock; +#endif + if (flag) + { elem *e; + + e = el_longt(tsint, 0); + block_appendexp(curblock, e); + curblock->BC = BCretexp; // put a return at the end + } + else + curblock->BC = BCret; // put a return at the end + curblock = NULL; // undefined from now on + block_last = NULL; +} + +/****************************** + * Perform branch optimization on basic blocks. + */ + +void blockopt(int iter) +{ block *b; + int count; + + if (OPTIMIZER) + { + int iterationLimit = 200; + if (iterationLimit < numblks) + iterationLimit = numblks; + count = 0; + do + { + //printf("changes = %d, count = %d, dfotop = %d\n",changes,count,dfotop); +#if MARS + util_progress(); +#else + if (controlc_saw) + util_exit(EXIT_BREAK); +#endif + changes = 0; + bropt(); // branch optimization + brrear(); // branch rearrangement + blident(); // combine identical blocks + blreturn(); // split out return blocks + bltailmerge(); // do tail merging + brtailrecursion(); // do tail recursion + brcombine(); // convert graph to expressions + if (iter >= 2) + brmin(); // minimize branching + do + { + compdfo(); /* compute depth first order (DFO) */ + elimblks(); /* remove blocks not in DFO */ + assert(count < iterationLimit); + count++; + } while (mergeblks()); /* merge together blocks */ + } while (changes); +#ifdef DEBUG + if (debugw) + for (b = startblock; b; b = b->Bnext) + WRblock(b); +#endif + } + else + { + /* canonicalize the trees */ + for (b = startblock; b; b = b->Bnext) + { +#ifdef DEBUG + if (debugb) + WRblock(b); +#endif + if (b->Belem) + { b->Belem = doptelem(b->Belem,bc_goal[b->BC] | GOALstruct); + if (b->Belem) + b->Belem = el_convert(b->Belem); + } +#ifdef DEBUG + if (debugb) + { dbg_printf("after optelem():\n"); + WRblock(b); + } +#endif + } + if (localgot) + { // Initialize with: + // localgot = OPgot; + elem *e = el_long(TYnptr, 0); + e->Eoper = OPgot; + e = el_bin(OPeq, TYnptr, el_var(localgot), e); + startblock->Belem = el_combine(e, startblock->Belem); + } + + bropt(); /* branch optimization */ + brrear(); /* branch rearrangement */ + comsubs(); /* eliminate common subexpressions */ +#ifdef DEBUG + if (debugb) + for (b = startblock; b; b = b->Bnext) + WRblock(b); +#endif + } +} + +/*********************************** + * Try to remove control structure. + * That is, try to resolve if-else, goto and return statements + * into &&, || and ?: combinations. + */ + +void brcombine() +{ block *b; + block *b2,*b3; + int op; + int anychanges; + + cmes("brcombine()\n"); + //for (b = startblock; b; b = b->Bnext) + //WRblock(b); + + if (funcsym_p->Sfunc->Fflags3 & (Fcppeh | Fnteh)) + { // Don't mess up extra EH info by eliminating blocks + return; + } + + do + { + anychanges = 0; + for (b = startblock; b; b = b->Bnext) /* for each block */ + { unsigned char bc; + + /* Look for [e1 IFFALSE L3,L2] L2: [e2 GOTO L3] L3: [e3] */ + /* Replace with [(e1 && e2),e3] */ + bc = b->BC; + if (bc == BCiftrue) + { unsigned char bc2; + + b2 = list_block(b->Bsucc); + b3 = list_block(list_next(b->Bsucc)); + + if (list_next(b2->Bpred)) // if more than one predecessor + continue; + if (b2 == b3) + continue; + if (b2 == startblock) + continue; + if (!PARSER && b2->Belem && EOP(b2->Belem)) + continue; + + bc2 = b2->BC; + if (bc2 == BCgoto && + b3 == list_block(b2->Bsucc)) + { + b->BC = BCgoto; + if (b2->Belem) + { + op = OPandand; + b->Belem = PARSER ? el_bint(op,tsint,b->Belem,b2->Belem) + : el_bin(op,TYint,b->Belem,b2->Belem); + b2->Belem = NULL; + } + list_subtract(&(b->Bsucc),b2); + list_subtract(&(b2->Bpred),b); + cmes("brcombine(): if !e1 then e2 => e1 || e2\n"); + anychanges++; + } + else if (list_next(b3->Bpred) || b3 == startblock) + continue; + else if ((bc2 == BCretexp && b3->BC == BCretexp) + //|| (bc2 == BCret && b3->BC == BCret) + ) + { elem *e; + + if (PARSER) + { + type *t = (bc2 == BCretexp) ? b2->Belem->ET : tsvoid; + e = el_bint(OPcolon2,t,b2->Belem,b3->Belem); + b->Belem = el_bint(OPcond,t,b->Belem,e); + } + else + { + if (EOP(b3->Belem)) + continue; + tym_t ty = (bc2 == BCretexp) ? b2->Belem->Ety : TYvoid; + e = el_bin(OPcolon2,ty,b2->Belem,b3->Belem); + b->Belem = el_bin(OPcond,ty,b->Belem,e); + } + b->BC = bc2; + b->Belem->ET = b2->Belem->ET; + b2->Belem = NULL; + b3->Belem = NULL; + list_free(&b->Bsucc,FPNULL); + list_subtract(&(b2->Bpred),b); + list_subtract(&(b3->Bpred),b); + cmes("brcombine(): if e1 return e2 else return e3 => ret e1?e2:e3\n"); + anychanges++; + } + else if (bc2 == BCgoto && + b3->BC == BCgoto && + list_block(b2->Bsucc) == list_block(b3->Bsucc)) + { elem *e; + block *bsucc; + + bsucc = list_block(b2->Bsucc); + if (b2->Belem) + { + if (PARSER) + { + if (b3->Belem) + { + e = el_bint(OPcolon2,b2->Belem->ET, + b2->Belem,b3->Belem); + e = el_bint(OPcond,e->ET,b->Belem,e); + } + else + { + op = OPandand; + e = el_bint(op,tsint,b->Belem,b2->Belem); + } + } + else + { + if (b3->Belem) + { + if (EOP(b3->Belem)) + continue; + e = el_bin(OPcolon2,b2->Belem->Ety, + b2->Belem,b3->Belem); + e = el_bin(OPcond,e->Ety,b->Belem,e); + e->ET = b2->Belem->ET; + } + else + { + op = OPandand; + e = el_bin(op,TYint,b->Belem,b2->Belem); + } + } + b2->Belem = NULL; + b->Belem = e; + } + else if (b3->Belem) + { + op = OPoror; + b->Belem = PARSER ? el_bint(op,tsint,b->Belem,b3->Belem) + : el_bin(op,TYint,b->Belem,b3->Belem); + } + b->BC = BCgoto; + b3->Belem = NULL; + list_free(&b->Bsucc,FPNULL); + + list_append(&b->Bsucc,bsucc); + list_append(&bsucc->Bpred,b); + + list_free(&(b2->Bpred),FPNULL); + list_free(&(b2->Bsucc),FPNULL); + list_free(&(b3->Bpred),FPNULL); + list_free(&(b3->Bsucc),FPNULL); + b2->BC = BCret; + b3->BC = BCret; + list_subtract(&(bsucc->Bpred),b2); + list_subtract(&(bsucc->Bpred),b3); + cmes("brcombine(): if e1 goto e2 else goto e3 => ret e1?e2:e3\n"); + anychanges++; + } + } + else if (bc == BCgoto && PARSER) + { + b2 = list_block(b->Bsucc); + if (!list_next(b2->Bpred) && b2->BC != BCasm // if b is only parent + && b2 != startblock +#if SCPP + && b2->BC != BCtry +#endif + && b2->BC != BC_try + && b->Btry == b2->Btry + ) + { list_t bl; + + if (b2->Belem) + { + if (PARSER) + { + block_appendexp(b,b2->Belem); + } + else if (b->Belem) + b->Belem = el_bin(OPcomma,b2->Belem->Ety,b->Belem,b2->Belem); + else + b->Belem = b2->Belem; + b2->Belem = NULL; + } + list_subtract(&b->Bsucc,b2); + list_subtract(&b2->Bpred,b); + + /* change predecessor of successors of b2 from b2 to b */ + for (bl = b2->Bsucc; bl; bl = list_next(bl)) + { list_t bp; + + for (bp = list_block(bl)->Bpred; bp; bp = list_next(bp)) + { + if (list_block(bp) == b2) + list_ptr(bp) = (void *)b; + } + } + + b->BC = b2->BC; + b->BS = b2->BS; + b->Bsucc = b2->Bsucc; + b2->Bsucc = NULL; + b2->BC = BCret; /* a harmless one */ + cmes3("brcombine(): %p goto %p eliminated\n",b,b2); + anychanges++; + } + } + } + if (anychanges) + { changes++; + continue; + } + } while (0); +} + +/*********************** + * Branch optimization. + */ + +STATIC void bropt() +{ block *b,*db; + elem *n,**pn; + + cmes("bropt()\n"); + assert(!PARSER); + for (b = startblock; b; b = b->Bnext) /* for each block */ + { + pn = &(b->Belem); + if (OPTIMIZER && *pn) + while ((*pn)->Eoper == OPcomma) + pn = &((*pn)->E2); + + n = *pn; + if (b->BC == BCiftrue) + { + assert(n); + /* Replace IF (!e) GOTO ... with */ + /* IF OPnot (e) GOTO ... */ + if (n->Eoper == OPnot) + { + tym_t tym; + + tym = n->E1->Ety; + *pn = el_selecte1(n); + (*pn)->Ety = tym; + for (n = b->Belem; n->Eoper == OPcomma; n = n->E2) + n->Ety = tym; + b->Bsucc = list_reverse(b->Bsucc); + cmes("CHANGE: if (!e)\n"); + changes++; + } + + /* Take care of IF (constant) */ + if (iftrue(n)) /* if elem is TRUE */ + { + // select first succ + db = list_block(list_next(b->Bsucc)); + goto L1; + } + else if (iffalse(n)) + { + // select second succ + db = list_block(b->Bsucc); + + L1: list_subtract(&(b->Bsucc),db); + list_subtract(&(db->Bpred),b); + b->BC = BCgoto; + /* delete elem if it has no side effects */ + b->Belem = doptelem(b->Belem,GOALnone | GOALagain); + cmes("CHANGE: if (const)\n"); + changes++; + } + + /* Look for both destinations being the same */ + else if (list_block(b->Bsucc) == + list_block(list_next(b->Bsucc))) + { b->BC = BCgoto; + db = list_block(b->Bsucc); + list_subtract(&(b->Bsucc),db); + list_subtract(&(db->Bpred),b); + cmes("CHANGE: if (e) goto L1; else goto L1;\n"); + changes++; + } + } + else if (b->BC == BCswitch) + { /* see we can evaluate this switch now */ + register unsigned i,ncases; + targ_llong *p,value; + register list_t bl; + + while (n->Eoper == OPcomma) + n = n->E2; + if (n->Eoper != OPconst) + continue; + assert(tyintegral(n->Ety)); + value = el_tolong(n); + p = b->BS.Bswitch; /* ptr to switch data */ + assert(p); + ncases = *p++; /* # of cases */ + i = 1; /* first case */ + while (1) + { + if (i > ncases) + { i = 0; /* select default */ + break; + } + if (*p++ == value) + break; /* found it */ + i++; /* next case */ + } + /* the ith entry in Bsucc is the one we want */ + db = list_block(list_nth(b->Bsucc,i)); + /* delete predecessors of successors (!) */ + for (bl = b->Bsucc; bl; bl = list_next(bl)) + if (i--) // if not ith successor + { void *p; + p = list_subtract( + &(list_block(bl)->Bpred),b); + assert(p == b); + } + + /* dump old successor list and create a new one */ + list_free(&b->Bsucc,FPNULL); + list_append(&b->Bsucc,db); + b->BC = BCgoto; + b->Belem = doptelem(b->Belem,GOALnone | GOALagain); + cmes("CHANGE: switch (const)\n"); + changes++; + } + } +} + +/********************************* + * Do branch rearrangement. + */ + +STATIC void brrear() +{ register block *b; + + cmes("brrear()\n"); + for (b = startblock; b; b = b->Bnext) /* for each block */ + { register list_t bl; + + for (bl = b->Bsucc; bl; bl = list_next(bl)) + { /* For each transfer of control block pointer */ + block *bt; + int iter = 0; + + bt = list_block(bl); + + /* If it is a transfer to a block that consists */ + /* of nothing but an unconditional transfer, */ + /* Replace transfer address with that */ + /* transfer address. */ + /* Note: There are certain kinds of infinite */ + /* loops which we avoid by putting a lid on */ + /* the number of iterations. */ + + while (bt->BC == BCgoto && !bt->Belem && +#if SCPP || NTEXCEPTIONS + b->Btry == bt->Btry && +#endif +#if NTEXCEPTIONS + bt->Btry == list_block(bt->Bsucc)->Btry && +#endif + + ++iter < 10) + { + list_ptr(bl) = list_ptr(bt->Bsucc); + if (bt->Bsrcpos.Slinnum && !b->Bsrcpos.Slinnum) + b->Bsrcpos = bt->Bsrcpos; + b->Bflags |= bt->Bflags; + list_append(&(list_block(bl)->Bpred),b); + list_subtract(&(bt->Bpred),b); + cmes("goto->goto\n"); + bt = list_block(bl); + } + + // Bsucc after the first are the targets of + // jumps, calls and loops, and as such to do this right + // we need to traverse the Bcode list and fix up + // the IEV2.Vblock pointers. + if (b->BC == BCasm) + break; + } +#if 0 + /* Replace cases of */ + /* IF (e) GOTO L1 ELSE L2 */ + /* L1: */ + /* with */ + /* IF OPnot (e) GOTO L2 ELSE L1 */ + /* L1: */ + + if (b->BC == BCiftrue || b->BC == BCiffalse) + { register block *bif,*belse; + + bif = list_block(b->Bsucc); + belse = list_block(list_next(b->Bsucc)); + + if (bif == b->Bnext) + { b->BC ^= BCiffalse ^ BCiftrue; /* toggle */ + list_ptr(b->Bsucc) = belse; + list_ptr(list_next(b->Bsucc)) = bif; + b->Bflags |= bif->Bflags & BFLvisited; + cmes("if (e) L1 else L2\n"); + } + } +#endif + } /* for */ +} + +/************************* + * Compute depth first order (DFO). + * Equivalent to Aho & Ullman Fig. 13.8. + * Blocks not in dfo[] are unreachable. + */ + +void compdfo() +{ + register int i; + + cmes("compdfo()\n"); + assert(OPTIMIZER); + block_clearvisit(); +#ifdef DEBUG + if (maxblks == 0 || maxblks < numblks) + dbg_printf("maxblks = %d, numblks = %d\n",maxblks,numblks); +#endif + assert(maxblks && maxblks >= numblks); + debug_assert(!PARSER); + if (!dfo) +#if TX86 + dfo = (block **) util_calloc(sizeof(block *),maxblks); +#else + dfo = (block **) MEM_PARF_CALLOC(sizeof(block *) * maxblks); +#endif + dfotop = numblks; /* assign numbers backwards */ + search(startblock); + assert(dfotop <= numblks); + /* Ripple data to the bottom of the array */ + if (dfotop) /* if not at bottom */ + { for (i = 0; i < numblks - dfotop; i++) + { dfo[i] = dfo[i + dfotop]; + dfo[i]->Bdfoidx = i; + } + } + dfotop = numblks - dfotop; +#if 0 + for (i = 0; i < dfotop; i++) + dbg_printf("dfo[%d] = 0x%x\n",i,dfo[i]); +#endif +} + +/****************************** + * Add block to dfo[], then its successors. + */ + +STATIC void search(block *b) +{ list_t l; + + assert(b); + b->Bflags |= BFLvisited; // executed at least once + for (l = b->Bsucc; l; l = list_next(l)) // for each successor + { block *bs = list_block(l); + + assert(bs); + if ((bs->Bflags & BFLvisited) == 0) // if not visited + search(bs); // search it + } + dfo[--dfotop] = b; // add to dfo[] + b->Bdfoidx = dfotop; // link back +} + +/************************* + * Remove blocks not marked as visited (they aren't in dfo[]). + * A block is not in dfo[] if not visited. + */ + +STATIC void elimblks() +{ block **pb,*b; + list_t s; + block *bf; + +#ifdef DEBUG + if (OPTIMIZER) + { int n; + + n = 0; + for (b = startblock; b; b = b->Bnext) + n++; + //dbg_printf("1 n = %d, numblks = %d, dfotop = %d\n",n,numblks,dfotop); + assert(numblks == n); + } +#endif + + cmes("elimblks()\n"); + bf = NULL; + for (pb = &startblock; (b = *pb) != NULL;) + { + if (((b->Bflags & BFLvisited) == 0) /* if block is not visited */ + && ((b->Bflags & BFLlabel) == 0) /* need label offset */ + ) + { + /* for each marked successor S to b */ + /* remove b from S.Bpred. */ + /* Presumably all predecessors to b are unmarked also. */ + for (s = b->Bsucc; s; s = list_next(s)) + { assert(list_block(s)); + if (list_block(s)->Bflags & BFLvisited) /* if it is marked */ + list_subtract(&(list_block(s)->Bpred),b); + } + if (b->Balign && b->Bnext && b->Balign > b->Bnext->Balign) + b->Bnext->Balign = b->Balign; + *pb = b->Bnext; /* remove from linked list */ + + b->Bnext = bf; + bf = b; /* prepend to deferred list to free */ + cmes2("CHANGE: block %p deleted\n",b); + changes++; + } + else + pb = &((*pb)->Bnext); + } + + // Do deferred free's of the blocks + for ( ; bf; bf = b) + { b = bf->Bnext; + block_free(bf); + numblks--; + } + + cmes("elimblks done\n"); + assert(!OPTIMIZER || numblks == dfotop); +} + +/********************************** + * Merge together blocks where the first block is a goto to the next + * block and the next block has only the first block as a predecessor. + * Example: + * e1; GOTO L2; + * L2: return e2; + * becomes: + * L2: return (e1 , e2); + * Returns: + * # of merged blocks + */ + +STATIC int mergeblks() +{ int merge = 0,i; + + assert(OPTIMIZER); + cmes("mergeblks()\n"); + for (i = 0; i < dfotop; i++) + { block *b; + + b = dfo[i]; + if (b->BC == BCgoto) + { block *bL2 = list_block(b->Bsucc); + + if (b == bL2) + { + Lcontinue: + continue; + } + assert(bL2->Bpred); + if (!list_next(bL2->Bpred) && bL2 != startblock) + { list_t bl; + elem *e; + + if (b == bL2 || bL2->BC == BCasm) + continue; + + if ( +#if SCPP + bL2->BC == BCtry || +#endif + bL2->BC == BC_try || + b->Btry != bL2->Btry) + continue; +#if SCPP + // If any predecessors of b are BCasm, don't merge. + for (bl = b->Bpred; bl; bl = list_next(bl)) + { + if (list_block(bl)->BC == BCasm) + goto Lcontinue; + } +#endif + + /* JOIN the elems */ + e = el_combine(b->Belem,bL2->Belem); + if (b->Belem && bL2->Belem) + e = doptelem(e,bc_goal[bL2->BC] | GOALagain); + bL2->Belem = e; + b->Belem = NULL; + + /* Remove b from predecessors of bL2 */ + list_free(&(bL2->Bpred),FPNULL); + bL2->Bpred = b->Bpred; + b->Bpred = NULL; + /* Remove bL2 from successors of b */ + list_free(&b->Bsucc,FPNULL); + + /* fix up successor list of predecessors */ + for (bl = bL2->Bpred; bl; bl = list_next(bl)) + { register list_t bs; + + for (bs=list_block(bl)->Bsucc; bs; bs=list_next(bs)) + if (list_block(bs) == b) + list_ptr(bs) = (void *)bL2; + } + + merge++; + cmes3("block %p merged with %p\n",b,bL2); + + if (b == startblock) + { /* bL2 is the new startblock */ + block **pb; + + cmes("bL2 is new startblock\n"); + /* Remove bL2 from list of blocks */ + for (pb = &startblock; 1; pb = &(*pb)->Bnext) + { assert(*pb); + if (*pb == bL2) + { *pb = bL2->Bnext; + break; + } + } + + /* And relink bL2 at the start */ + bL2->Bnext = startblock->Bnext; + startblock = bL2; /* new start */ + + block_free(b); + numblks--; + break; /* dfo[] is now invalid */ + } + } + } + } + return merge; +} + +/******************************* + * Combine together blocks that are identical. + */ + +STATIC void blident() +{ block *bn; + block *bnext; + + cmes("blident()\n"); + assert(startblock); + +#if SCPP + // Determine if any asm blocks + int anyasm = 0; + for (bn = startblock; bn; bn = bn->Bnext) + { if (bn->BC == BCasm) + { anyasm = 1; + break; + } + } +#endif + + for (bn = startblock; bn; bn = bnext) + { block *b; + + bnext = bn->Bnext; + if (bn->Bflags & BFLnomerg) + continue; + + for (b = bnext; b; b = b->Bnext) + { + /* Blocks are identical if: */ + /* BC match */ + /* not optimized for time or it's a return */ + /* (otherwise looprotate() is undone) */ + /* successors match */ + /* elems match */ + if (b->BC == bn->BC && + //(!OPTIMIZER || !(mfoptim & MFtime) || !b->Bsucc) && + (!OPTIMIZER || !(b->Bflags & BFLnomerg) || !b->Bsucc) && + list_equal(b->Bsucc,bn->Bsucc) && +#if SCPP || NTEXCEPTIONS + b->Btry == bn->Btry && +#endif + el_match(b->Belem,bn->Belem) + ) + { /* Eliminate block bn */ + list_t bl; + + switch (b->BC) + { + case BCswitch: + if (memcmp(b->BS.Bswitch,bn->BS.Bswitch,list_nitems(bn->Bsucc) * sizeof(*bn->BS.Bswitch))) + continue; + break; + +#if SCPP + case BCtry: + case BCcatch: +#endif +#if MARS + case BCjcatch: +#endif + case BC_try: + case BC_finally: + case BCasm: + Lcontinue: + continue; + } + assert(!b->Bcode); + + for (bl = bn->Bpred; bl; bl = list_next(bl)) + { block *bp; + + bp = list_block(bl); + if (bp->BC == BCasm) + // Can't do this because of jmp's and loop's + goto Lcontinue; + } + +#if 0 && SCPP + // Predecessors must all be at the same btry level. + if (bn->Bpred) + { block *bp; + + bp = list_block(bn->Bpred); + btry = bp->Btry; + if (bp->BC == BCtry) + btry = bp; + } + else + btry = NULL; + + for (bl = b->Bpred; bl; bl = list_next(bl)) + { block *bp; + + bp = list_block(bl); + if (bp->BC != BCtry) + bp = bp->Btry; + if (btry != bp) + goto Lcontinue; + } +#endif + + // if bn is startblock, eliminate b instead of bn + if (bn == startblock) + { + goto Lcontinue; // can't handle predecessors to startblock + bn = b; + b = startblock; /* swap b and bn */ + } + +#if SCPP + // Don't do it if any predecessors are ASM blocks, since + // we'd have to walk the code list to fix up any jmps. + if (anyasm) + { + for (bl = bn->Bpred; bl; bl = list_next(bl)) + { list_t bls; + block *bp; + + bp = list_block(bl); + if (bp->BC == BCasm) + goto Lcontinue; + for (bls=bp->Bsucc; bls; bls=list_next(bls)) + if (list_block(bls) == bn && + list_block(bls)->BC == BCasm) + goto Lcontinue; + } + } +#endif + + /* Change successors to predecessors of bn to point to */ + /* b instead of bn */ + for (bl = bn->Bpred; bl; bl = list_next(bl)) + { list_t bls; + block *bp; + + bp = list_block(bl); + for (bls=bp->Bsucc; bls; bls=list_next(bls)) + if (list_block(bls) == bn) + { list_ptr(bls) = (void *)b; + list_prepend(&b->Bpred,bp); + } + } + + /* Entirely remove predecessor list from bn. */ + /* elimblks() will delete bn entirely. */ + list_free(&(bn->Bpred),FPNULL); + +#ifdef DEBUG + assert(bn->BC != BCcatch); + if (debugc) + dbg_printf("block B%d (%p) removed, it was same as B%d (%p)\n", + bn->Bdfoidx,bn,b->Bdfoidx,b); +#endif + changes++; + break; + } + } + } +} + +/********************************** + * Split out return blocks so the returns can be combined into a + * single block by blident(). + */ + +STATIC void blreturn() +{ + if (!(mfoptim & MFtime)) /* if optimized for space */ + { + int retcount; /* number of return counts */ + block *b; + + retcount = 0; + + /* Find last return block */ + for (b = startblock; b; b = b->Bnext) + { if (b->BC == BCret) + retcount++; + if (b->BC == BCasm) + return; // mucks up blident() + } + + if (retcount < 2) /* quit if nothing to combine */ + return; + + /* Split return blocks */ + for (b = startblock; b; b = b->Bnext) + { if (b->BC != BCret) + continue; +#if SCPP || NTEXCEPTIONS + // If no other blocks with the same Btry, don't split +#if SCPP + if (config.flags3 & CFG3eh) +#endif + { + for (block *b2 = startblock; b2; b2 = b2->Bnext) + { + if (b2->BC == BCret && b != b2 && b->Btry == b2->Btry) + goto L1; + } + continue; + } + L1: ; +#endif + if (b->Belem) + { /* Split b into a goto and a b */ + block *bn; + +#ifdef DEBUG + if (debugc) + dbg_printf("blreturn: splitting block B%d\n",b->Bdfoidx); +#endif + numblks++; + bn = block_calloc(); + bn->BC = BCret; + bn->Bnext = b->Bnext; +#if SCPP || NTEXCEPTIONS + bn->Btry = b->Btry; +#endif + b->BC = BCgoto; + b->Bnext = bn; + list_append(&b->Bsucc,bn); + list_append(&bn->Bpred,b); + + b = bn; + } + } + + blident(); /* combine return blocks */ + } +} + +/***************************************** + * Convert expression into a list. + * Construct the list in reverse, that is, so that the right-most + * expression occurs first in the list. + */ + +STATIC list_t bl_enlist(elem *e) +{ list_t el = NULL; + + if (e) + { + elem_debug(e); + if (e->Eoper == OPcomma) + { list_t el2; + list_t pl; + + el2 = bl_enlist(e->E1); + el = bl_enlist(e->E2); + e->E1 = e->E2 = NULL; + el_free(e); + + /* Append el2 list to el */ + assert(el); + for (pl = el; list_next(pl); pl = list_next(pl)) + ; + list_next(pl) = el2; + } + else + list_prepend(&el,e); + } + return el; +} + +/***************************************** + * Take a list of expressions and convert it back into an expression tree. + */ + +STATIC elem * bl_delist(list_t el) +{ elem *e; + list_t elstart = el; + + for (e = NULL; el; el = list_next(el)) + e = el_combine(list_elem(el),e); + list_free(&elstart,FPNULL); + return e; +} + +/***************************************** + * Do tail merging. + */ + +STATIC void bltailmerge() +{ + cmes("bltailmerge()\n"); + assert(!PARSER && OPTIMIZER); + if (!(mfoptim & MFtime)) /* if optimized for space */ + { + block *b; + block *bn; + list_t bl; + elem *e; + elem *en; + + /* Split each block into a reversed linked list of elems */ + for (b = startblock; b; b = b->Bnext) + b->Blist = bl_enlist(b->Belem); + + /* Search for two blocks that have the same successor list. + If the first expressions both lists are the same, split + off a new block with that expression in it. + */ + for (b = startblock; b; b = b->Bnext) + { + if (!b->Blist) + continue; + e = list_elem(b->Blist); + elem_debug(e); + for (bn = b->Bnext; bn; bn = bn->Bnext) + { + if (b->BC == bn->BC && + list_equal(b->Bsucc,bn->Bsucc) && + bn->Blist && + el_match(e,(en = list_elem(bn->Blist))) +#if SCPP || NTEXCEPTIONS + && b->Btry == bn->Btry +#endif + ) + { + switch (b->BC) + { + case BCswitch: + if (memcmp(b->BS.Bswitch,bn->BS.Bswitch,list_nitems(bn->Bsucc) * sizeof(*bn->BS.Bswitch))) + continue; + break; + +#if SCPP + case BCtry: + case BCcatch: +#endif +#if MARS + case BCjcatch: +#endif + case BC_try: + case BC_finally: + case BCasm: + continue; + } + + /* We've got a match */ + block *bnew; + + /* Create a new block, bnew, which will be the + merged block. Physically locate it just after bn. + */ +#ifdef DEBUG + if (debugc) + dbg_printf("tail merging: %p and %p\n", b, bn); +#endif + numblks++; + bnew = block_calloc(); + bnew->Bnext = bn->Bnext; + bnew->BC = b->BC; +#if SCPP || NTEXCEPTIONS + bnew->Btry = b->Btry; +#endif + if (bnew->BC == BCswitch) + { + bnew->BS.Bswitch = b->BS.Bswitch; + b->BS.Bswitch = NULL; + bn->BS.Bswitch = NULL; + } + bn->Bnext = bnew; + + /* The successor list to bnew is the same as b's was */ + bnew->Bsucc = b->Bsucc; + b->Bsucc = NULL; + list_free(&bn->Bsucc,FPNULL); + + /* Update the predecessor list of the successor list + of bnew, from b to bnew, and removing bn + */ + for (bl = bnew->Bsucc; bl; bl = list_next(bl)) + { + list_subtract(&list_block(bl)->Bpred,b); + list_subtract(&list_block(bl)->Bpred,bn); + list_append(&list_block(bl)->Bpred,bnew); + } + + /* The predecessors to bnew are b and bn */ + list_append(&bnew->Bpred,b); + list_append(&bnew->Bpred,bn); + + /* The successors to b and bn are bnew */ + b->BC = BCgoto; + bn->BC = BCgoto; + list_append(&b->Bsucc,bnew); + list_append(&bn->Bsucc,bnew); + + changes++; + + /* Find all the expressions we can merge */ + do + { + list_append(&bnew->Blist,e); + el_free(en); + list_pop(&b->Blist); + list_pop(&bn->Blist); + if (!b->Blist) + goto nextb; + e = list_elem(b->Blist); + if (!bn->Blist) + break; + en = list_elem(bn->Blist); + } while (el_match(e,en)); + } + } + nextb: ; + } + + /* Recombine elem lists into expression trees */ + for (b = startblock; b; b = b->Bnext) + b->Belem = bl_delist(b->Blist); + } +} + +/********************************** + * Rearrange blocks to minimize jmp's. + */ + +STATIC void brmin() +{ block *b; + block *bnext; + list_t bl,blp; + +#if SCPP + // Dunno how this may mess up generating EH tables. + if (config.flags3 & CFG3eh) // if EH turned on + return; +#endif + + cmes("brmin()\n"); + debug_assert(startblock); + for (b = startblock->Bnext; b; b = b->Bnext) + { + bnext = b->Bnext; + if (!bnext) + break; + for (bl = b->Bsucc; bl; bl = list_next(bl)) + { block *bs; + + bs = list_block(bl); + if (bs == bnext) + goto L1; + } + + // b is a block which does not have bnext as a successor. + // Look for a successor of b for which everyone must jmp to. + + for (bl = b->Bsucc; bl; bl = list_next(bl)) + { block *bs; + block *bn; + + bs = list_block(bl); + for (blp = bs->Bpred; blp; blp = list_next(blp)) + { block *bsp; + + bsp = list_block(blp); + if (bsp->Bnext == bs) + goto L2; + } + + // Move bs so it is the Bnext of b + for (bn = bnext; 1; bn = bn->Bnext) + { + if (!bn) + goto L2; + if (bn->Bnext == bs) + break; + } +#if 1 + bn->Bnext = NULL; + b->Bnext = bs; + for (bn = bs; bn->Bnext; bn = bn->Bnext) + ; + bn->Bnext = bnext; +#else + bn->Bnext = bs->Bnext; + bs->Bnext = bnext; + b->Bnext = bs; +#endif + cmes3("Moving block %p to appear after %p\n",bs,b); + changes++; + break; + + L2: ; + } + + + L1: ; + } +} + +/******************************** + * Check integrity of blocks. + */ + +#if 0 + +STATIC void block_check() +{ block *b; + list_t bl; + + for (b = startblock; b; b = b->Bnext) + { int nsucc; + int npred; + + nsucc = list_nitems(b->Bsucc); + npred = list_nitems(b->Bpred); + switch (b->BC) + { + case BCgoto: + assert(nsucc == 1); + break; + case BCiftrue: + assert(nsucc == 2); + break; + } + + for (bl = b->Bsucc; bl; bl = list_next(bl)) + { block *bs = list_block(bl); + list_t bls; + + for (bls = bs->Bpred; 1; bls = list_next(bls)) + { + assert(bls); + if (list_block(bls) == b) + break; + } + } + } +} + +#endif + +/*************************************** + * Do tail recursion. + */ + +STATIC void brtailrecursion() +{ block *b; + block *bs; + elem **pe; + +#if SCPP +// if (tyvariadic(funcsym_p->Stype)) + return; + return; // haven't dealt with struct params, and ctors/dtors +#endif + if (funcsym_p->Sfunc->Fflags3 & Fnotailrecursion) + return; + if (localgot) + { /* On OSX, tail recursion will result in two OPgot's: + int status5; + struct MyStruct5 { } + void rec5(int i, MyStruct5 s) + { + if( i > 0 ) + { status5++; + rec5(i-1, s); + } + } + */ + + return; + } + for (b = startblock; b; b = b->Bnext) + { + if (b->BC == BC_try) + return; + pe = &b->Belem; + block *bn = NULL; + if (*pe && + (b->BC == BCret || + b->BC == BCretexp || + (b->BC == BCgoto && (bn = list_block(b->Bsucc))->Belem == NULL && + bn->BC == BCret) + ) + ) + { elem *e; + + if (el_anyframeptr(*pe)) + return; + while ((*pe)->Eoper == OPcomma) + pe = &(*pe)->E2; + e = *pe; + if (OTcall(e->Eoper) && + e->E1->Eoper == OPvar && + e->E1->EV.sp.Vsym == funcsym_p) + { +//printf("before:\n"); +//elem_print(*pe); + if (OTunary(e->Eoper)) + { *pe = el_long(TYint,0); + } + else + { int si = 0; + elem *e2 = NULL; + *pe = assignparams(&e->E2,&si,&e2); + *pe = el_combine(*pe,e2); + } + el_free(e); +//printf("after:\n"); +//elem_print(*pe); + + if (b->BC == BCgoto) + { list_subtract(&b->Bsucc,bn); + list_subtract(&bn->Bpred,b); + } + b->BC = BCgoto; + list_append(&b->Bsucc,startblock); + list_append(&startblock->Bpred,b); + + // Create a new startblock, bs, because startblock cannot + // have predecessors. + numblks++; + bs = block_calloc(); + bs->BC = BCgoto; + bs->Bnext = startblock; + list_append(&bs->Bsucc,startblock); + list_append(&startblock->Bpred,bs); + startblock = bs; + + cmes("tail recursion\n"); + changes++; + return; + } + } + } +} + +/***************************************** + * Convert parameter expression to assignment statements. + */ + +STATIC elem * assignparams(elem **pe,int *psi,elem **pe2) +{ + elem *e = *pe; + + if (e->Eoper == OPparam) + { elem *ea = NULL; + elem *eb = NULL; + elem *e2 = assignparams(&e->E2,psi,&eb); + elem *e1 = assignparams(&e->E1,psi,&ea); + e->E1 = NULL; + e->E2 = NULL; + e = el_combine(e1,e2); + *pe2 = el_combine(eb,ea); + } + else + { int si = *psi; + Symbol *sp; + Symbol *s; + int op; + elem *es; + type *t; + + assert(si < globsym.top); + sp = globsym.tab[si]; + s = symbol_genauto(sp->Stype); + s->Sfl = FLauto; + op = OPeq; + if (e->Eoper == OPstrpar) + { + op = OPstreq; + t = e->ET; + elem *ex = e; + e = e->E1; + ex->E1 = NULL; + el_free(ex); + } + es = el_var(s); + es->Ety = e->Ety; + e = el_bin(op,TYvoid,es,e); + if (op == OPstreq) + e->ET = t; + *pe2 = el_bin(op,TYvoid,el_var(sp),el_copytree(es)); + (*pe2)->E1->Ety = es->Ety; + if (op == OPstreq) + (*pe2)->ET = t; + *psi = ++si; + *pe = NULL; + } + return e; +} + +/**************************************************** + * Eliminate empty loops. + */ + +STATIC void emptyloops() +{ + block *b; + + cmes("emptyloops()\n"); + for (b = startblock; b; b = b->Bnext) + { + if (b->BC == BCiftrue && + list_block(b->Bsucc) == b && + list_nitems(b->Bpred) == 2) + { block *bpred; + elem *einit; + elem *erel; + elem *einc; + + // Find predecessor to b + bpred = list_block(b->Bpred); + if (bpred == b) + bpred = list_block(list_next(b->Bpred)); + if (!bpred->Belem) + continue; + + // Find einit + for (einit = bpred->Belem; einit->Eoper == OPcomma; einit = einit->E2) + ; + if (einit->Eoper != OPeq || + einit->E2->Eoper != OPconst || + einit->E1->Eoper != OPvar) + continue; + +#if 1 + // Look for ((i += 1) < limit) + erel = b->Belem; + if (erel->Eoper != OPlt || + erel->E2->Eoper != OPconst || + erel->E1->Eoper != OPaddass) + continue; + + einc = erel->E1; + if (einc->E2->Eoper != OPconst || + einc->E1->Eoper != OPvar || + !el_match(einc->E1,einit->E1)) + continue; + + if (!tyintegral(einit->E1->Ety) || + el_tolong(einc->E2) != 1 || + el_tolong(einit->E2) >= el_tolong(erel->E2) + ) + continue; + + { + erel->Eoper = OPeq; + erel->Ety = erel->E1->Ety; + erel->E1 = el_selecte1(erel->E1); + b->BC = BCgoto; + list_subtract(&b->Bsucc,b); + list_subtract(&b->Bpred,b); +#ifdef DEBUG + if (debugc) + { WReqn(erel); + dbg_printf(" eliminated loop\n"); + } +#endif + changes++; + } +#else + // Find einc and erel + if (b->Belem->Eoper != OPcomma) + continue; + einc = b->Belem->E1; + erel = b->Belem->E2; + if (einc->Eoper != OPaddass || + einc->E1->Eoper != OPvar || + einc->E2->Eoper != OPconst || + erel->Eoper != OPlt || + erel->E1->Eoper != OPvar || + erel->E2->Eoper != OPconst || + !el_match(einit->E1,einc->E1) || + !el_match(einit->E1,erel->E1) + ) + continue; + + if (!tyintegral(einit->E1->Ety) || + el_tolong(einc->E2) != 1 || + el_tolong(einit->E2) >= el_tolong(erel->E2) + ) + continue; + + { + erel->Eoper = OPeq; + erel->Ety = erel->E1->Ety; + b->BC = BCgoto; + list_subtract(&b->Bsucc,b); + list_subtract(&b->Bpred,b); +#ifdef DEBUG + if (debugc) + { WReqn(erel); + dbg_printf(" eliminated loop\n"); + } +#endif + changes++; + } +#endif + } + } +} + +/****************************************** + * Determine if function has any side effects. + * This means, determine if all the function does is return a value; + * no extraneous definitions or effects or exceptions. + * A function with no side effects can be CSE'd. It does not reference + * statics or indirect references. + */ + +STATIC int funcsideeffect_walk(elem *e); + +void funcsideeffects() +{ +#if MARS + block *b; + + //printf("funcsideeffects('%s')\n",funcsym_p->Sident); + for (b = startblock; b; b = b->Bnext) + { + if (b->Belem && funcsideeffect_walk(b->Belem)) + goto Lside; + } + +Lnoside: + funcsym_p->Sfunc->Fflags3 |= Fnosideeff; + //printf(" function '%s' has no side effects\n",funcsym_p->Sident); + //return; + +Lside: + //printf(" function '%s' has side effects\n",funcsym_p->Sident); + ; +#endif +} + +#if MARS + +STATIC int funcsideeffect_walk(elem *e) +{ int op; + Symbol *s; + + assert(e); + elem_debug(e); + if (typemask(e) & mTYvolatile) + goto Lside; + op = e->Eoper; + switch (op) + { + case OPcall: + case OPucall: + if (e->E1->Eoper == OPvar && + tyfunc((s = e->E1->EV.sp.Vsym)->Stype->Tty) && + ((s->Sfunc && s->Sfunc->Fflags3 & Fnosideeff) || s == funcsym_p) + ) + break; + goto Lside; + + case OParray: + case OPfield: + goto Lside; // these can throw exceptions + + // Note: we should allow assignments to local variables as + // not being a 'side effect'. + + default: + assert(op < OPMAX); + return OTsideff(op) || + (OTunary(op) && funcsideeffect_walk(e->E1)) || + (OTbinary(op) && (funcsideeffect_walk(e->E1) || + funcsideeffect_walk(e->E2))); + } + return 0; + + Lside: + return 1; +} + +#endif + +/******************************* + * Determine if there are any OPframeptr's in the tree. + */ + +int el_anyframeptr(elem *e) +{ + while (1) + { + if (OTunary(e->Eoper)) + e = e->E1; + else if (OTbinary(e->Eoper)) + { if (el_anyframeptr(e->E2)) + return 1; + e = e->E1; + } + else if (e->Eoper == OPframeptr) + return 1; + else + break; + } + return 0; +} + + +#endif //!SPP diff --git a/backend/cc.h b/backend/cc.h new file mode 100644 index 00000000..b5278972 --- /dev/null +++ b/backend/cc.h @@ -0,0 +1,1557 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if __SC__ +#pragma once +#endif + +#ifndef CC_H +#define CC_H 1 + +#ifdef SCPP +#define CPP (config.flags3 & CFG3cpp) // if compiling for C++ +#else +#if !SPP +#define SCPP 0 +#endif +#endif + +#ifndef CPP +#define CPP 0 +#endif + +#ifndef SPP +#define SPP 0 +#endif + +#ifndef MARS +#define MARS 0 // not compiling MARS code +#endif + +#ifndef HTOD +#define HTOD 0 +#endif + +#define GENOBJ 1 // generating .obj file + +#define mskl(i) (1L << (i)) /* convert int to mask */ + +#ifndef STATIC +#define STATIC static +#endif + +#ifndef CEXTERN +#define CEXTERN extern +#endif + +// Warnings +enum WM +{ + WM_no_inline = 1, //function '%s' is too complicated to inline + WM_assignment = 2, //possible unintended assignment + WM_nestcomment = 3, //comments do not nest + WM_assignthis = 4, //assignment to 'this' is obsolete, use X::operator new/delete + WM_notagname = 5, //no tag name for struct or enum + WM_valuenotused = 6, //value of expression is not used + WM_extra_semi = 7, //possible extraneous ';' + WM_large_auto = 8, //very large automatic + WM_obsolete_del = 9, //use delete[] rather than delete[expr], expr ignored + WM_obsolete_inc = 10, //using operator++() (or --) instead of missing operator++(int) + WM_init2tmp = 11, //non-const reference initialized to temporary + WM_used_b4_set = 12, //variable '%s' used before set + WM_bad_op = 13, //Illegal type/size of operands for the %s instruction + WM_386_op = 14, //Reference to '%s' caused a 386 instruction to be generated + WM_ret_auto = 15, //returning address of automatic '%s' + WM_ds_ne_dgroup = 16, //DS is not equal to DGROUP + WM_unknown_pragma = 17, //unrecognized pragma + WM_implied_ret = 18, //implied return at closing '}' does not return value + WM_num_args = 19, //%d actual arguments expected for %s, had %d + WM_before_pch = 20, //symbols or macros defined before #include of precompiled header + WM_pch_first = 21, //precompiled header must be first #include when -H is used + WM_pch_config = 22, + WM_divby0 = 23, + WM_badnumber = 24, + WM_ccast = 25, + WM_obsolete = 26, +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + WM_skip_attribute = 27, // skip GNUC attribute specification + WM_warning_message = 28, // preprocessor warning message + WM_bad_vastart = 29, // args for builtin va_start bad + WM_undefined_inline = 30, // static inline not expanded or defined +#endif +}; + +// Language for error messages +enum LANG +{ LANGenglish, + LANGgerman, + LANGfrench, + LANGjapanese, +}; + +#include "cdef.h" // host and target compiler definition + +#define INITIALIZED_STATIC_DEF static + +#if MEMMODELS == 1 +#define LARGEDATA 0 /* don't want 48 bit pointers */ +#define LARGECODE 0 +#endif + +#ifndef __INTSIZE +#define __INTSIZE 4 // host ints are 4 bytes +#endif + +#if SPP || SCPP +#include "msgs2.h" +#endif +#include "ty.h" +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +#include "../tk/mem.h" +#else +#include "mem.h" +#endif +#include "list.h" +#include "vec.h" + +#if SPP +#define COMPILER "Preprocessor" +#define ACTIVITY "preprocessing..." +#elif HTOD +#define COMPILER ".h to D Migration Tool" +#define ACTIVITY "migrating..." +#else +#define COMPILER "C/C++ Compiler" +#define ACTIVITY "compiling..." +#endif + +#ifdef DEBUG +# define debug(a) (a) +# define debugx(a) (a) +# define debug_assert assert +#else +# define debug(a) +# define debugx(a) +# define debug_assert(e) +#endif + + +/*************************** + * Print out debugging information. + */ + +#ifdef DEBUG +#define debugmes(s) (debugw && dbg_printf(s)) +#define cmes(s) (debugc && dbg_printf(s)) +#define cmes2(s,b) (debugc && dbg_printf((s),(b))) +#define cmes3(s,b,c) (debugc && dbg_printf((s),(b),(c))) +#else +#define debugmes(s) +#define cmes(s) +#define cmes2(s,b) +#define cmes3(s,b,c) +#endif + +#define TRUE 1 +#define FALSE 0 + +#ifndef ARG_TRUE +#define ARG_TRUE ,TRUE +#define ARG_FALSE ,FALSE +#endif + +#define arraysize(array) (sizeof(array) / sizeof(array[0])) + +#define IDMAX 900 //467 //254 // identifier max (excluding terminating 0) +#define IDOHD (4+1+sizeof(int)*3) // max amount of overhead to ID added by +#define STRMAX 65000 // max length of string (determined by + // max ph size) + +enum SC; +struct Thunk; +struct token_t; +struct PARAM; +typedef struct PARAM param_t; +struct block; +struct Classsym; +struct Nspacesym; +struct Outbuffer; +struct Aliassym; +struct dt_t; +typedef struct TYPE type; +typedef struct Symbol symbol; +typedef Symbol Funcsym; +//typedef Symbol Classsym; // a Symbol that is an SCclass, SCstruct or SCunion +struct elem; +#if !MARS +typedef struct MACRO macro_t; +typedef struct BLKLST blklst; +#endif +typedef list_t symlist_t; /* list of pointers to Symbols */ +typedef struct SYMTAB_S symtab_t; +struct code; + +extern Config config; + +/////////// Position in source file + +typedef struct Srcpos +{ + unsigned Slinnum; // 0 means no info available +#if SPP || SCPP + struct Sfile **Sfilptr; // file + #define srcpos_sfile(p) (**(p).Sfilptr) + #define srcpos_name(p) (srcpos_sfile(p).SFname) +#endif +#if MARS + const char *Sfilename; + #define srcpos_name(p) ((p)->Sfilename) +#endif +#if M_UNIX + short Sfilnum; // file number +#endif +#if SOURCE_OFFSETS + unsigned long Sfiloff; // byte offset +#endif + + void print(const char *func); +} Srcpos; + +#ifndef TOKEN_H +#include "token.h" +#endif + +/********************************** + * Current 'state' of the compiler. + * Used to gather together most global variables. + * This struct is saved/restored during function body parsing. + */ + +typedef struct Pstate +{ + char STinopeq; // if in n2_createopeq() + char STinarglist; // if !=0, then '>' is the end of a template + // argument list, not an operator + char STinsizeof; // !=0 if in a sizeof expression. Useful to + // prevent being converted to + // . + char STintemplate; // if !=0, then expanding a function template + // (do not expand template Symbols) + char STdeferDefaultArg; // defer parsing of default arg for parameter + char STnoexpand; // if !=0, don't expand template symbols + char STignoretal; // if !=0 ignore template argument list + char STexplicitInstantiation; // if !=0, then template explicit instantiation + char STexplicitSpecialization; // if !=0, then template explicit specialization + char STinconstexp; // if !=0, then parsing a constant expression + char STisaddr; // is this a possible pointer to member expression? + unsigned STinexp; // if !=0, then in an expression +#if NTEXCEPTIONS + char STinfilter; // if !=0 then in exception filter + char STinexcept; // if !=0 then in exception handler + block *STbfilter; // current exception filter +#endif +#if !MARS + int STinitseg; // segment for static constructor function pointer +#endif + Funcsym *STfuncsym_p; // if inside a function, then this is the + // function Symbol. + +# define funcsym_p (pstate.STfuncsym_p) + + unsigned STflags; +# define PFLpreprocessor 1 // in preprocessor +# define preprocessor (pstate.STflags & PFLpreprocessor) +# define PFLmasm 2 // in Microsoft-style inline assembler +# define PFLbasm 4 // in Borland-style inline assembler +# define inline_asm (pstate.STflags & (PFLmasm | PFLbasm)) +# define PFLsemi 8 // ';' means start of comment +//# define PFLautogen 0x10 // automatically generate HX ph file +# define PFLmftemp 0x20 // if expanding member function template +# define PFLextdef 0x40 // we had an external def +# define PFLhxwrote 0x80 // already generated HX ph file +# define PFLhxdone 0x100 // done with HX ph file +#if TX86 +# define PFLhxread 0x200 // have read in an HX ph file +# define PFLhxgen 0x400 // need to generate HX ph file +# define PFLphread 0x800 // read a ph file +# define PFLcomdef 0x1000 // had a common block +# define PFLmacdef 0x2000 // defined a macro in the source code +# define PFLsymdef 0x4000 // declared a global Symbol in the source +# define PFLinclude 0x8000 // read a .h file +# define PFLmfc 0x10000 // something will affect MFC compatibility +#endif +#if !MARS + int STinparamlist; // if != 0, then parser is in + // function parameter list + int STingargs; // in template argument list + list_t STincalias; // list of include aliases + list_t STsysincalias; // list of system include aliases +#endif + +#if TX86 + // should probably be inside #if HYDRATE, but unclear for the dmc source + char SThflag; // FLAG_XXXX: hydration flag +#define FLAG_INPLACE 0 // in place hydration +#define FLAG_HX 1 // HX file hydration +#define FLAG_SYM 2 // .SYM file hydration +#endif + + Classsym *STclasssym; // if in the scope of this class + symlist_t STclasslist; // list of classes that have deferred inline + // functions to parse + Classsym *STstag; // returned by struct_searchmember() and with_search() + SYMIDX STmarksi; // to determine if temporaries are created + char STnoparse; // add to classlist instead of parsing + char STdeferparse; // defer member func parse + enum SC STgclass; // default function storage class + int STdefertemps; // defer allocation of temps + int STdeferaccesscheck; // defer access check for members (BUG: it + // never does get done later) + int STnewtypeid; // parsing new-type-id + int STdefaultargumentexpression; // parsing default argument expression + block *STbtry; // current try block + block *STgotolist; // threaded goto scoping list + long STtdbtimestamp; // timestamp of tdb file + Symbol *STlastfunc; // last function symbol parsed by ext_def() + + // For "point of definition" vs "point of instantiation" template name lookup + unsigned STsequence; // sequence number (Ssequence) of next Symbol + unsigned STmaxsequence; // won't find Symbols with STsequence larger + // than STmaxsequence +} Pstate; + +extern Pstate pstate; + +/**************************** + * Global variables. + */ + +typedef struct Cstate +{ + struct BLKLST *CSfilblk; // current source file we are parsing + Symbol *CSlinkage; // table of forward referenced linkage pragmas + list_t CSlist_freelist; // free list for list package + symtab_t *CSpsymtab; // pointer to current Symbol table +#if MEMORYHX + void **CSphx; // pointer to HX data block +#endif + char *modname; // module unique identifier +} Cstate; + +extern Cstate cstate; + +/* Bits for sytab[] that give characteristics of storage classes */ +#define SCEXP 1 // valid inside expressions +#define SCKEP 2 // Symbol should be kept even when function is done +#define SCSCT 4 // storage class is valid for use in static ctor +#define SCSS 8 // storage class is on the stack +#define SCRD 0x10 // we can do reaching definitions on these + +// Determine if Symbol has a Ssymnum associated with it. +// (That is, it is allocated on the stack or has live variable analysis +// done on it, so it is stack and register variables.) +#define symbol_isintab(s) (sytab[(s)->Sclass] & SCSS) + +#if defined(__SC__) || defined(_MSC_VER) +typedef char enum_SC; +#else +typedef enum SC enum_SC; +#endif + +/****************************************** + * Basic blocks: + * Basic blocks are a linked list of all the basic blocks + * in a function. startblock heads the list. + */ + +#if MARS +struct ClassDeclaration; +struct Declaration; +struct Module; +#endif + +struct Blockx +{ +#if MARS + block *startblock; + block *curblock; + Funcsym *funcsym; + Symbol *context; // eh frame context variable + int scope_index; // current scope index + int next_index; // value for next scope index + unsigned flags; // value to OR into Bflags + block *tryblock; // current enclosing try block + elem *init; // static initializer + ClassDeclaration *classdec; + Declaration *member; // member we're compiling for + Module *module; // module we're in +#endif +}; + +typedef struct block +{ + union + { + elem *Belem; // pointer to elem tree + list_t Blist; // list of expressions + }; + + block *Bnext; // pointer to next block in list + list_t Bsucc; // linked list of pointers to successors + // of this block + list_t Bpred; // and the predecessor list + int Bindex; // into created object stack + int Bendindex; // index at end of block + block *Btry; // BCtry,BC_try: enclosing try block, if any + // BC???: if in try-block, points to BCtry or BC_try + // note that can't have a BCtry and BC_try in + // the same function. + union + { targ_llong *Bswitch; // BCswitch: pointer to switch data + struct { + regm_t usIasmregs; // Registers modified + unsigned char bIasmrefparam; // References parameters? + #define usIasmregs BS.BIASM.usIasmregs + #define bIasmrefparam BS.BIASM.bIasmrefparam + } BIASM; // BCasm + + struct + { Symbol *catchvar; // __throw() fills in this + #define catchvar BS.BITRY.catchvar + } BITRY; // BCtry + +#if SCPP + struct + { type *catchtype; // one type for each catch block + #define Bcatchtype BS.BICATCH.catchtype + } BICATCH; // BCcatch +#endif +#if MARS + struct + { Symbol *catchtype; // one type for each catch block + #define Bcatchtype BS.BIJCATCH.catchtype + } BIJCATCH; // BCjcatch +#endif +#if NTEXCEPTIONS || MARS + struct + { +#if MARS + Symbol *jcatchvar; // __j_throw() fills in this + #define jcatchvar BS.BI_TRY.jcatchvar +#endif + int Bscope_index; // index into scope table + #define Bscope_index BS.BI_TRY.Bscope_index + int Blast_index; // enclosing index into scope table + #define Blast_index BS.BI_TRY.Blast_index + } BI_TRY; // BC_try +#endif + + } BS; + Srcpos Bsrcpos; // line number (0 if not known) + unsigned char BC; // exit condition (enum BC) +// NEW + unsigned char Balign; // alignment + + unsigned short Bflags; // flags (BFLxxxx) + #define BFLvisited 1 // set if block is visited + #define BFLmark 2 // set if block is visited + #define BFLjmpoptdone 4 // set when no more jump optimizations + // are possible for this block + #define BFLnostackopt 8 // set when stack elimination should not + // be done +#if NTEXCEPTIONS + #define BFLehcode 0x10 // set when we need to load exception code + #define BFLunwind 0x1000 // do local_unwind following block +#endif + #define BFLnomerg 0x20 // do not merge with other blocks + #define BFLprolog 0x80 // generate function prolog + #define BFLepilog 0x100 // generate function epilog + #define BFLrefparam 0x200 // referenced parameter + #define BFLreflocal 0x400 // referenced local + #define BFLoutsideprolog 0x800 // outside function prolog/epilog + #define BFLlabel 0x2000 // block preceded by label + #define BFLvolatile 0x4000 // block is volatile + code *Bcode; // code generated for this block + + unsigned Bweight; // relative number of times this block + // is executed (optimizer and codegen) + + unsigned Bdfoidx; // index of this block in dfo[] + union + { + // CPP + struct + { + SYMIDX symstart; // (symstart <= symnum < symend) Symbols + SYMIDX symend; // are declared in this block + block *endscope; // block that forms the end of the + // scope for the declared Symbols + unsigned blknum; // position of block from startblock + Symbol *Binitvar; // !=NULL points to an auto variable with + // an explicit or implicit initializer + block *gotolist; // BCtry, BCcatch: backward list of try scopes + block *gotothread; // BCgoto: threaded list of goto's to + // unknown labels + + #define Bsymstart _BLU._UP.symstart + #define Bsymend _BLU._UP.symend + #define Bendscope _BLU._UP.endscope + #define Bblknum _BLU._UP.blknum + #define Binitvar _BLU._UP.Binitvar + #define Bgotolist _BLU._UP.gotolist + #define Bgotothread _BLU._UP.gotothread + }_UP; + + // OPTIMIZER + struct + { + vec_t Bdom; // mask of dominators for this block + vec_t Binrd; + vec_t Boutrd; // IN and OUT for reaching definitions + vec_t Binlv; + vec_t Boutlv; // IN and OUT for live variables + vec_t Bin; + vec_t Bout; // IN and OUT for other flow analyses + vec_t Bgen; + vec_t Bkill; // pointers to bit vectors used by data + // flow analysis + + // BCiftrue can have different vectors for the 2nd successor: + vec_t Bout2; + vec_t Bgen2; + vec_t Bkill2; + + #define Bdom _BLU._UO.Bdom + #define Binrd _BLU._UO.Binrd + #define Boutrd _BLU._UO.Boutrd + #define Binlv _BLU._UO.Binlv + #define Boutlv _BLU._UO.Boutlv + #define Bin _BLU._UO.Bin + #define Bout _BLU._UO.Bout + #define Bgen _BLU._UO.Bgen + #define Bkill _BLU._UO.Bkill + #define Bout2 _BLU._UO.Bout2 + #define Bgen2 _BLU._UO.Bgen2 + #define Bkill2 _BLU._UO.Bkill2 + }_UO; + + // CODGEN + struct + { + targ_size_t Btablesize; // BCswitch, BCjmptab + targ_size_t Btableoffset; // BCswitch, BCjmptab + targ_size_t Boffset; // code offset of start of this block + targ_size_t Bsize; // code size of this block + con_t Bregcon; // register state at block exit + targ_size_t Btryoff; // BCtry: offset of try block data + + #define Btablesize _BLU._UD.Btablesize + #define Btableoffset _BLU._UD.Btableoffset + #define Boffset _BLU._UD.Boffset + #define Bsize _BLU._UD.Bsize +// #define Bcode _BLU._UD.Bcode + #define Bregcon _BLU._UD.Bregcon + #define Btryoff _BLU._UD.Btryoff + } _UD; + } _BLU; +} block; + +#define list_block(l) ((block *) list_ptr(l)) + +/** Basic block control flow operators. **/ + +enum BC { + BCgoto = 1, // goto Bsucc block + BCiftrue = 2, // if (Belem) goto Bsucc[0] else Bsucc[1] + BCret = 3, // return (no return value) + BCretexp = 4, // return with return value + BCexit = 5, // never reaches end of block (like exit() was called) + BCasm = 6, // inline assembler block (Belem is NULL, Bcode + // contains code generated). + // These blocks have one or more successors in Bsucc, + // never 0 + BCswitch = 7, // switch statement + // Bswitch points to switch data + // Default is Bsucc + // Cases follow in linked list + BCifthen = 8, // a BCswitch is converted to if-then + // statements + BCjmptab = 9, // a BCswitch is converted to a jump + // table (switch value is index into + // the table) + BCtry = 10, // C++ try block + // first block in a try-block. The first block in + // Bsucc is the next one to go to, subsequent + // blocks are the catch blocks + BCcatch = 11, // C++ catch block + BCjump = 12, // Belem specifies (near) address to jump to + BC_try = 13, // SEH: first block of try-except or try-finally + // Jupiter, Mars: try-catch or try-finally + BC_filter = 14, // SEH exception-filter (always exactly one block) + BC_finally = 15, // first block of SEH termination-handler, + // or finally block + BC_ret = 16, // last block of SEH termination-handler or finally block + BC_except = 17, // first block of SEH exception-handler + BCjcatch = 18, // first block of Jupiter or Mars catch-block + BCjplace = 19, // Jupiter: placeholder + BCMAX +}; + +/********************************** + * Functions + */ + +typedef struct SYMTAB_S +{ + SYMIDX top; // 1 past end + SYMIDX symmax; // max # of entries in tab[] possible + Symbol **tab; // local Symbol table +} symtab_t; + +typedef struct FUNC_S +{ + symlist_t Fsymtree; // local Symbol table + block *Fstartblock; // list of blocks comprising function + symtab_t Flocsym; // local Symbol table + Srcpos Fstartline; // starting line # of function + Srcpos Fendline; // line # of closing brace of function + Symbol *F__func__; // symbol for __func__[] string + unsigned Fflags; +# define Fpending 1 // if function has been queued for being written +# define Foutput 2 /* if function has been written out */ +# define Finline 0x10 /* if SCinline, and function really is inline */ +# define Foverload 0x20 /* if function can be overloaded */ +# define Ftypesafe 0x40 /* if function name needs type appended */ +# define Fmustoutput 0x80 /* set for forward ref'd functions that */ + /* must be output */ +# define Finlinenest 0x1000 /* used as a marker to prevent nested */ + /* inlines from expanding */ +# define Flinkage 0x2000 /* linkage is already specified */ +# define Fstatic 0x4000 /* static member function (no this) */ +# define Foperator 4 /* if operator overload */ +# define Fcast 8 /* if cast overload */ +# define Fvirtual 0x100 /* if function is a virtual function */ +# define Fctor 0x200 /* if function is a constructor */ +# define Fdtor 0x400 /* if function is a destructor */ +# define Fnotparent 0x800 /* if function is down Foversym chain */ +# define Fbitcopy 0x8000 /* it's a simple bitcopy (op=() or X(X&)) */ +# define Fpure 0x10000 // pure function +# define Finstance 0x20000 // function is an instance of a template +# define Ffixed 0x40000 // ctor has had cpp_fixconstructor() run on it, + // dtor has had cpp_fixdestructor() +# define Fintro 0x80000 // function doesn't hide a previous virtual function +//# define unused 0x100000 // unused bit +# define Fkeeplink 0x200000 // don't change linkage to default +# define Fnodebug 0x400000 // do not generate debug info for this function +# define Fgen 0x800000 // compiler generated function +# define Finvariant 0x1000000 // __invariant function +# define Fexplicit 0x2000000 // explicit constructor +# define Fsurrogate 0x4000000 // surrogate call function + unsigned Fflags3; + #define Fvtblgen 0x01 // generate vtbl[] when this function is defined + #define Femptyexc 0x02 // empty exception specification (obsolete, use Tflags & TFemptyexc) + #define Fcppeh 0x04 // uses C++ EH + #define Fdeclared 0x10 // already declared function Symbol + #define Fmark 0x20 // has unbalanced OPctor's + #define Fnteh 0x08 // uses NT Structured EH + #define Fdoinline 0x40 // do inline walk + #define Foverridden 0x80 // ignore for overriding purposes +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + #define Fnowrite 0x100 // SCinline should never output definition +#else + #define Fjmonitor 0x100 // Jupiter synchronized function +#endif + #define Fnosideeff 0x200 // function has no side effects + #define F3badoparrow 0x400 // bad operator->() + #define Fmain 0x800 // function is main() or wmain() + #define Fnested 0x1000 // D nested function with 'this' + #define Fmember 0x2000 // D member function with 'this' + #define Fnotailrecursion 0x4000 // no tail recursion optimizations + #define Ffakeeh 0x8000 // allocate space for NT EH context sym anyway + unsigned char Foper; // operator number (OPxxxx) if Foperator + + Symbol *Fparsescope; // use this scope to parse friend functions + // which are defined within a class, so the + // class is in scope, but are not members + // of the class + + Classsym *Fclass; // if member of a class, this is the class + // (I think this is redundant with Sscope) + Funcsym *Foversym; // overloaded function at same scope + symlist_t Fclassfriends; /* Symbol list of classes of which this */ + /* function is a friend */ + block *Fbaseblock; // block where base initializers get attached + block *Fbaseendblock; // block where member destructors get attached + elem *Fbaseinit; /* list of member initializers (meminit_t) */ + /* this field has meaning only for */ + /* functions which are constructors */ + struct token_t *Fbody; /* if deferred parse, this is the list */ + /* of tokens that make up the function */ + /* body */ + // also used if SCfunctempl, SCftexpspec + unsigned Fsequence; // sequence number at point of definition + union + { + Symbol *Ftempl; // if Finstance this is the template that generated it + struct Thunk *Fthunk; // !=NULL if this function is actually a thunk + }; + Funcsym *Falias; // SCfuncalias: function Symbol referenced + // by using-declaration + symlist_t Fthunks; // list of thunks off of this function + param_t *Farglist; // SCfunctempl: the template-parameter-list + param_t *Fptal; // Finstance: this is the template-argument-list + // SCftexpspec: for explicit specialization, this + // is the template-argument-list + list_t Ffwdrefinstances; // SCfunctempl: list of forward referenced instances + list_t Fexcspec; // List of types in the exception-specification + // (NULL if none or empty) + Funcsym *Fexplicitspec; // SCfunctempl, SCftexpspec: threaded list + // of SCftexpspec explicit specializations + Funcsym *Fsurrogatesym; // Fsurrogate: surrogate cast function + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + char *Fredirect; // redirect function name to this name in object +#endif +} func_t; + +#define func_calloc() ((func_t *) mem_fcalloc(sizeof(func_t))) +#define func_free(f) mem_ffree(f) + +/************************** + * Item in list for member initializer. + */ + +typedef struct MEMINIT +{ + list_t MIelemlist; /* arg list for initializer */ + Symbol *MIsym; /* if NULL, then this initializer is */ + /* for the base class. Otherwise, this */ + /* is the member that needs the ctor */ + /* called for it */ +} meminit_t; + + +/************************************ + * Base classes are a list of these. + */ + +typedef struct BASECLASS +{ + Classsym *BCbase; // base class Symbol + struct BASECLASS *BCnext; // next base class + targ_size_t BCoffset; // offset from start of derived class to this +#if VBTABLES + unsigned short BCvbtbloff; // for BCFvirtual, offset from start of + // vbtbl[] to entry for this virtual base. + // Valid in Sbase list +#else + targ_size_t memoffset; /* for BCFvirtual, offset from this to + pointer to virtual base class. + Valid in Sbase list + */ + Symbol *param; /* parameter for this Symbol (in */ + /* Svirtbase list only) */ +#endif + symlist_t BCpublics; // public members of base class (list is freeable) + list_t BCmptrlist; // (in Smptrbase only) this is the vtbl + // (NULL if not different from base class's vtbl + Symbol *BCvtbl; // Symbol for vtbl[] array (in Smptrbase list) + // Symbol for vbtbl[] array (in Svbptrbase list) + unsigned BCflags; // base class flags +#define BCFpublic 1 // base class is public +#define BCFprotected 2 // base class is protected +#define BCFprivate 4 // base class is private +#define BCFpmask (BCFpublic | BCFprotected | BCFprivate) + +#define BCFvirtual 8 // base class is virtual +#define BCFvfirst 0x10 // virtual base class, and this is the + // first virtual appearance of it +#define BCFnewvtbl 0x20 // new vtbl generated for this base class +#define BCFvirtprim 0x40 // Primary base class of a virtual base class +#define BCFdependent 0x80 // base class is a dependent type + Classsym *BCparent; // immediate parent of this base class + // in Smptrbase +#if TX86 + struct BASECLASS *BCpbase; // parent base, NULL if did not come from a parent +#endif +} baseclass_t; + +#define baseclass_malloc() ((baseclass_t *)mem_fmalloc(sizeof(baseclass_t))) +#define baseclass_free(b) ((void)(b)) + +/************************* + * For virtual tables. + */ + +typedef struct MPTR +{ targ_short MPd; + targ_short MPi; + Symbol *MPf; + Symbol *MPparent; // immediate parent of this base class + // in Smptrbase + unsigned char MPflags; +#define MPTRvirtual 1 // it's an offset to a virtual base +#define MPTRcovariant 2 // need covariant return pointer adjustment +} mptr_t; + +#define list_mptr(l) ((mptr_t *) list_ptr(l)) + + +/*********************************** + * Information gathered about externally defined template member functions, + * member data, and member classes. + */ + +struct TMF +{ + Classsym *stag; // if !=NULL, this is the enclosing class + token_t *tbody; // tokens making it up + token_t *to; // pointer within tbody where we left off in + // template_function_decl() + param_t *temp_arglist; // template parameter list + int member_class; // 1: it's a member class + + // These are for member templates + int castoverload; // 1: it's a user defined cast + char *name; // name of template (NULL if castoverload) + int member_template; // 0: regular template + // 1: member template + param_t *temp_arglist2; // if member template, + // then member's template parameter list + + param_t *ptal; // if explicit specialization, this is the + // explicit template-argument-list + Symbol *sclassfriend; // if member function is a friend of class X, + // this is class X + unsigned access_specifier; +}; + +/*********************************** + * Information gathered about primary member template explicit specialization. + */ + +struct TME +{ + /* Given: + * template<> template struct A::B { }; + * temp_arglist2 = + * name = "B" + * ptal = + */ + param_t *ptal; // explicit template-argument-list for enclosing + // template A + symbol *stempl; // template symbol for B +}; + +/*********************************** + * Information gathered about nested explicit specializations. + */ + +struct TMNE +{ + /* For: + * template<> template<> struct A::B { }; + */ + + enum_TK tk; // TKstruct / TKclass / TKunion + char *name; // name of template, i.e. "B" + param_t *ptal; // explicit template-argument-list for enclosing + // template A, i.e. "short" + token_t *tdecl; // the tokens " { }" +}; + +/*********************************** + * Information gathered about nested class friends. + */ + +struct TMNF +{ + /* Given: + * template struct A { struct B { }; }; + * class C { template friend struct A::B; + */ + token_t *tdecl; // the tokens "A::B;" + param_t *temp_arglist; // + Classsym *stag; // the class symbol C + symbol *stempl; // the template symbol A +}; + +/*********************************** + * Special information for class templates. + */ + +typedef struct TEMPLATE +{ + symlist_t TMinstances; // list of Symbols that are instances + param_t *TMptpl; // template-parameter-list + struct token_t *TMbody; // tokens making up class body + unsigned TMsequence; // sequence number at point of definition + list_t TMmemberfuncs; // templates for member functions (list of TMF's) + list_t TMexplicit; // list of TME's: primary member template explicit specializations + list_t TMnestedexplicit; // list of TMNE's: primary member template nested explicit specializations + Symbol *TMnext; // threaded list of template classes headed + // up by template_class_list + enum_TK TMtk; // TKstruct, TKclass or TKunion + int TMflags; // STRxxx flags + + symbol *TMprimary; // primary class template + symbol *TMpartial; // next class template partial specialization + param_t *TMptal; // template-argument-list for partial specialization + // (NULL for primary class template) + list_t TMfriends; // list of Classsym's for which any instantiated + // classes of this template will be friends of + list_t TMnestedfriends; // list of TMNF's + int TMflags2; // !=0 means dummy template created by template_createargtab() +} template_t; + +/*********************************** + * Special information for enums. + */ + +typedef struct ENUM +{ + unsigned SEflags; +#define SENnotagname 1 // no tag name for enum +#define SENforward 2 // forward referenced enum + + Symbol *SEalias; // pointer to identifier E to use if + /* enum was defined as: */ + /* typedef enum { ... } E; */ + symlist_t SEenumlist; // all members of enum +} enum_t; + +/*********************************** + * Special information for structs. + */ + +typedef struct STRUCT +{ + targ_size_t Sstructsize; // size of struct + symlist_t Sfldlst; // all members of struct (list freeable) + Symbol *Sroot; // root of binary tree Symbol table + unsigned Salignsize; // size of struct for alignment purposes + unsigned char Sstructalign; // struct member alignment in effect + unsigned long Sflags; +#define STRanonymous 0x01 // set for unions with no tag names +#define STRglobal 0x02 // defined at file scope +#define STRnotagname 0x04 // struct/class with no tag name +#define STRoutdef 0x08 // we've output the debug definition +#define STRbitfields 0x10 // set if struct contains bit fields +#define STRpredef 0x1000 // a predefined struct +#define STRunion 0x4000 // actually, it's a union + +#define STRabstract 0x20 // abstract class +#define STRbitcopy 0x40 // set if operator=() is merely a bit copy +#define STRanyctor 0x80 // set if any constructors were defined + // by the user +#define STRnoctor 0x100 // no constructors allowed +#define STRgen 0x200 // if struct is an instantiation of a + // template class, and was generated by + // that template +#define STRvtblext 0x400 // generate vtbl[] only when first member function + // definition is encountered (see Fvtblgen) +#define STRexport 0x800 // all member functions are to be _export +#define STRclass 0x8000 // it's a class, not a struct +#if TX86 +#define STRimport 0x40000 // imported class +#define STRstaticmems 0x80000 // class has static members +#endif +#define STR0size 0x100000 // zero sized struct +#define STRinstantiating 0x200000 // if currently being instantiated +#define STRexplicit 0x400000 // if explicit template instantiation +#define STRgenctor0 0x800000 // need to gen X::X() + tym_t ptrtype; // type of pointer to refer to classes by + unsigned short access; // current access privilege, here so + // enum declarations can get at it + targ_size_t Snonvirtsize; // size of struct excluding virtual classes + list_t Svirtual; // freeable list of mptrs + // that go into vtbl[] +#if TX86 + list_t *Spvirtder; // pointer into Svirtual that points to start + // of virtual functions for this (derived) class + symlist_t Sopoverload; // overloaded operator funcs (list freeable) +#endif + symlist_t Scastoverload; // overloaded cast funcs (list freeable) + symlist_t Sclassfriends; // list of classes of which this is a friend + // (list is freeable) + symlist_t Sfriendclass; // classes which are a friend to this class + // (list is freeable) + symlist_t Sfriendfuncs; // functions which are a friend to this class + // (list is freeable) + symlist_t Sinlinefuncs; // list of tokenized functions + baseclass_t *Sbase; // list of direct base classes + baseclass_t *Svirtbase; // list of all virtual base classes + baseclass_t *Smptrbase; // list of all base classes that have + // their own vtbl[] + baseclass_t *Sprimary; // if not NULL, then points to primary + // base class + Funcsym *Svecctor; // constructor for use by vec_new() + Funcsym *Sctor; // constructor function + + Funcsym *Sdtor; // basic destructor +#if VBTABLES + Funcsym *Sprimdtor; // primary destructor + Funcsym *Spriminv; // primary invariant + Funcsym *Sscaldeldtor; // scalar deleting destructor +#endif + + Funcsym *Sinvariant; // basic invariant function + + Symbol *Svptr; // Symbol of vptr + Symbol *Svtbl; // Symbol of vtbl[] +#if VBTABLES + Symbol *Svbptr; // Symbol of pointer to vbtbl[] + Symbol *Svbptr_parent; // base class for which Svbptr is a member. + // NULL if Svbptr is a member of this class + targ_size_t Svbptr_off; // offset of Svbptr member + Symbol *Svbtbl; // virtual base offset table + baseclass_t *Svbptrbase; // list of all base classes in canonical + // order that have their own vbtbl[] +#endif + Funcsym *Sopeq; // X& X::operator =(X&) + Funcsym *Sopeq2; // Sopeq, but no copy of virtual bases + Funcsym *Scpct; // copy constructor + Funcsym *Sveccpct; // vector copy constructor + Symbol *Salias; // pointer to identifier S to use if + // struct was defined as: + // typedef struct { ... } S; + + Symbol *Stempsym; // if this struct is an instantiation + // of a template class, this is the + // template class Symbol + + /* For: + * template struct A { }; + * template struct A { }; + * + * A a; // primary + * Gives: + * Sarglist = + * Spr_arglist = NULL; + * + * A a; // specialization + * Gives: + * Sarglist = + * Spr_arglist = ; + */ + + param_t *Sarglist; // if this struct is an instantiation + // of a template class, this is the + // actual arg list used + param_t *Spr_arglist; // if this struct is an instantiation + // of a specialized template class, this is the + // actual primary arg list used. + // It is NULL for the + // primary template class (since it would be + // identical to Sarglist). +} struct_t; + +#define struct_calloc() ((struct_t *) mem_fcalloc(sizeof(struct_t))) +#define struct_free(st) ((void)(st)) + +/********************************** + * Symbol Table + */ + +#define list_symbol(l) ((Symbol *) list_ptr(l)) +#define list_setsymbol(l,s) list_ptr(l) = (s) +#define list_Classsym(l) ((Classsym *) list_ptr(l)) + +struct Symbol +{ +#ifdef DEBUG + unsigned short id; +#define IDsymbol 0x5678 +#define symbol_debug(s) assert((s)->id == IDsymbol) +#define class_debug(s) assert((s)->id == IDsymbol) +#else +#define symbol_debug(s) +#define class_debug(s) +#endif + + Symbol *Sl,*Sr; // left, right child + Symbol *Snext; // next in threaded list + dt_t *Sdt; // variables: initializer + type *Stype; // type of Symbol + #define ty() Stype->Tty + + union // variants for different Symbol types + { + enum_t *Senum_; // SCenum + #define Senum _SU.Senum_ + struct + { func_t *Sfunc_; // tyfunc + #define Sfunc _SU._SF.Sfunc_ + list_t Spath1_; // SCfuncalias member functions: same as Spath + #define Spath1 _SU._SF.Spath1_ + // and in same position + // SCadl: list of associated functions for ADL lookup + }_SF; + struct // SClabel + { int Slabel_; // TRUE if label was defined + #define Slabel _SU._SL.Slabel_ + block *Slabelblk_; // label block + #define Slabelblk _SU._SL.Slabelblk_ + }_SL; + #define Senumlist Senum->SEenumlist + +#if !MARS + struct // SClinkage + { + long Slinkage_; // tym linkage bits + #define Slinkage _SU._SLK.Slinkage_ + unsigned Smangle_; + #define Smangle _SU._SLK.Smangle_ + }_SLK; +#else + long Slinkage; // SClinkage, tym linkage bits +#endif + + struct + { + char Sbit_; // SCfield: bit position of start of bit field + #define Sbit _SU._SB.Sbit_ + char Swidth_; // SCfield: width in bits of bit field + #define Swidth _SU._SB.Swidth_ + targ_size_t Smemoff_; // SCmember,SCfield: offset from start of struct + #define Smemoff _SU._SB.Smemoff_ + }_SB; + + elem *Svalue_; /* SFLvalue: value of const + SFLdtorexp: for objects with destructor, + conditional expression to precede dtor call + */ + #define Svalue _SU.Svalue_ + + struct_t *Sstruct_; // SCstruct + #define Sstruct _SU.Sstruct_ + template_t *Stemplate_; // SCtemplate + #define Stemplate _SU.Stemplate_ +#if SCPP + struct // SCnamespace + { Symbol *Snameroot_; // the Symbol table for the namespace + #define Snameroot _SU._SN.Snameroot_ + list_t Susing_; // other namespaces from using-directives + #define Susing _SU._SN.Susing_ + }_SN; + struct + { Symbol *Smemalias_; // SCalias: pointer to Symbol to use instead + // (generated by using-declarations and + // namespace-alias-definitions) + // SCmemalias: pointer to member of base class + // to use instead (using-declarations) + #define Smemalias _SU._SA.Smemalias_ + symlist_t Spath_; // SCmemalias: path of classes to get to base + // class of which Salias is a member + #define Spath _SU._SA.Spath_ + }_SA; +#endif +#if !MARS + Symbol *Simport_; // SCextern: if dllimport Symbol, this is the + #define Simport _SU.Simport_ + // Symbol it was imported from +#endif + unsigned char Spreg_; // SCfastpar: register parameter is passed in + #define Spreg _SU.Spreg_ + }_SU; + +#if SCPP || MARS + Symbol *Sscope; // enclosing scope (could be struct tag, + // enclosing inline function for statics, + // or namespace) +#define isclassmember(s) ((s)->Sscope && (s)->Sscope->Sclass == SCstruct) +#endif + +#if SCPP + Symbol *Scover; // if there is a tag name and a regular name + // of the same identifier, Scover is the tag + // Scover can be SCstruct, SCenum, SCtemplate + // or an SCalias to them. +#define isscover(s) ((s)->Sclass == SCstruct || (s)->Sclass == SCenum || (s)->Sclass == SCtemplate) + unsigned Ssequence; // sequence number (used for 2 level lookup) + // also used as 'parameter number' for SCTtemparg +#elif MARS + const char *prettyIdent; // the symbol identifer as the user sees it +#elif AUTONEST + unsigned char Spush; // # of pushes followed by # of + unsigned char Spop; // pops of scope level +#endif + +#if ELFOBJ || MACHOBJ + long obj_si; // Symbol index of coff or elf symbol + unsigned long dwarf_off; // offset into .debug section + targ_size_t code_off; // rel. offset from start of block where var is initialized + targ_size_t last_off; // last offset using var +#endif +#if TARGET_OSX + targ_size_t Slocalgotoffset; +#endif + + enum_SC Sclass; // storage class (SCxxxx) + char Sfl; // flavor (FLxxxx) + SYMFLGS Sflags; // flag bits (SFLxxxx) + #define SFLmark 0x08 // temporary marker + #define SFLvalue 0x01 // Svalue contains const expression + #define SFLimplem 0x02 // if seen implementation of Symbol + // (function body for functions, + // initializer for variables) + #define SFLdouble 0x02 // SCregpar or SCparameter, where float + // is really passed as a double + #define SFLfree 0x04 // if we can symbol_free() a Symbol in + // a Symbol table[] + #define SFLexit 0x10 // tyfunc: function does not return + // (ex: exit,abort,_assert,longjmp) + #define SFLtrue 0x200 // value of Symbol != 0 + #define SFLreplace SFLmark // variable gets replaced in inline expansion + #define SFLskipinit 0x10000 // SCfield, SCmember: initializer is skipped + #define SFLnodebug 0x20000 // don't generate debug info + #define SFLwasstatic 0x800000 // was an uninitialized static + #define SFLweak 0x1000000 // resolve to NULL if not found + + // CPP + #define SFLnodtor 0x10 // set if destructor for Symbol is already called + #define SFLdtorexp 0x80 // Svalue has expression to tack onto dtor + #define SFLmutable 0x100000 // SCmember or SCfield is mutable + #define SFLdyninit 0x200000 // symbol has dynamic initializer + #define SFLtmp 0x400000 // symbol is a generated temporary +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + #define SFLthunk 0x40000 // symbol is temporary for thunk +#endif + + // Possible values for protection bits + #define SFLprivate 0x60 + #define SFLprotected 0x40 + #define SFLpublic 0x20 + #define SFLnone 0x00 + #define SFLpmask 0x60 // mask for the protection bits +#if VEC_VTBL_LIST + #define SFLvtbl 0x2000 // Symbol is a vtable or vbtable +#endif + + // OPTIMIZER and CODGEN + #define GTregcand 0x100 // if Symbol is a register candidate + #define SFLdead 0x800 // this variable is dead + #define GTunregister 0x2000000 // 'unregister' a previous register assignment + + // OPTIMIZER only + #define SFLunambig 0x400 // only accessible by unambiguous reference, + // i.e. cannot be addressed via pointer + // (GTregcand is a subset of this) + // P.S. code generator turns off this + // flag if any reads are done from it. + // This is to eliminate stores to it + // that are never read. + #define SFLlivexit 0x1000 // live on exit from function + #define SFLnotbasiciv 0x4000 // not a basic induction variable + #define SFLnord SFLdouble // SCauto,SCregister,SCtmp: disallow redundant warnings + + // CODGEN only + #define GTtried SFLmark // tried to place in register + #define GTbyte 0x8000 // variable is sometimes accessed as + #define SFLread 0x40000 // variable is actually read from + // (to eliminate dead stores) + #define SFLspill 0x80000 // only in register part of the time + + vec_t Srange; // live range, if any + vec_t Slvreg; // when symbol is in register + targ_size_t Ssize; // tyfunc: size of function + targ_size_t Soffset; // variables: offset of Symbol in its storage class + + // CPP || OPTIMIZER + SYMIDX Ssymnum; // Symbol number (index into globsym.tab[]) + // SCauto,SCparameter,SCtmp,SCregpar,SCregister + // CODGEN + int Sseg; // segment index + int Sweight; // usage count, the higher the number, + // the more worthwhile it is to put in + // a register + union + { + unsigned Sxtrnnum_; // SCcomdef,SCextern,SCcomdat: external symbol # (starting from 1) + #define Sxtrnnum _SXR.Sxtrnnum_ + unsigned long Stypidx_; // SCstruct,SCunion,SCclass,SCenum,SCtypedef: debug info type index + #define Stypidx _SXR.Stypidx_ + struct + { unsigned char Sreglsw_; + #define Sreglsw _SXR._SR.Sreglsw_ + unsigned char Sregmsw_; + #define Sregmsw _SXR._SR.Sregmsw_ + regm_t Sregm_; // mask of registers + #define Sregm _SXR._SR.Sregm_ + }_SR; // SCregister,SCregpar,SCpseudo: register number + }_SXR; + regm_t Sregsaved; // mask of registers not affected by this func + +#if SOURCE_4SYMS + Srcpos Ssrcpos; // file position for definition +#endif + + char Sident[SYM_PREDEF_SZ]; // identifier string (dynamic array) + // (the size is for static Symbols) + + int needThis(); // !=0 if symbol needs a 'this' pointer +}; + +#if __DMC__ +#pragma SC align +#endif + +// Class, struct or union + +struct Classsym : Symbol { }; + +// Namespace Symbol +struct Nspacesym : Symbol { }; + +// Alias for another Symbol +struct Aliassym : Symbol { }; + +// Function symbol +//struct Funcsym : Symbol { }; + +// Determine if this Symbol is stored in a COMDAT +#if MARS +#define symbol_iscomdat(s) ((s)->Sclass == SCcomdat || \ + config.flags2 & CFG2comdat && (s)->Sclass == SCinline || \ + config.flags4 & CFG4allcomdat && ((s)->Sclass == SCglobal)) +#else +#define symbol_iscomdat(s) ((s)->Sclass == SCcomdat || \ + config.flags2 & CFG2comdat && (s)->Sclass == SCinline || \ + config.flags4 & CFG4allcomdat && ((s)->Sclass == SCglobal || (s)->Sclass == SCstatic)) +#endif + +/* Format the identifier for presentation to the user */ +char *cpp_prettyident (Symbol *s); + +inline char *prettyident(Symbol *s) { return CPP ? cpp_prettyident(s) : s->Sident; } + +/********************************** + * Function parameters: + * Pident identifier of parameter + * Ptype type of argument + * Pelem default value for argument + * Psym symbol corresponding to Pident when using the + * parameter list as a symbol table + * For template-parameter-list: + * Pident identifier of parameter + * Ptype if NULL, this is a type-parameter + * else the type for a parameter-declaration value argument + * Pelem default value for value argument + * Pdeftype default value for type-parameter + * Pptpl template-parameter-list for template-template-parameter + * Psym default value for template-template-parameter + * For template-arg-list: (actual arguments) + * Pident NULL + * Ptype type-name + * Pelem expression (either Ptype or Pelem is NULL) + * Psym SCtemplate for template-template-argument + */ + +struct PARAM +{ +#ifdef DEBUG + unsigned short id; +#define IDparam 0x7050 +#define param_debug(s) assert((s)->id == IDparam) +#else +#define param_debug(s) +#endif + + char *Pident; // identifier + type *Ptype; // type of parameter (NULL if not known yet) + elem *Pelem; // default value + token_t *PelemToken; // tokens making up default elem + type *Pdeftype; // Ptype==NULL: default type for type-argument + param_t *Pptpl; // template-parameter-list for template-template-parameter + Symbol *Psym; + PARAM *Pnext; // next in list + unsigned Pflags; + #define PFexplicit 1 // this template argument was explicit, i.e. in < > +#if SOURCE_4PARAMS + Srcpos Psrcpos; // parameter source definition +#endif + + PARAM *createTal(PARAM *); // create template-argument-list blank from + // template-parameter-list + PARAM *search(char *id); // look for Pident matching id + int searchn(char *id); // look for Pident matching id, return index + unsigned length(); // number of parameters in list + void print(); // print this param_t + void print_list(); // print this list of param_t's +}; + +/************************************** + * Element types. + * These should be combined with storage classes. + */ + +enum FL +{ + FLunde, + FLconst, // numerical constant + FLoper, // operator node + FLfunc, // function symbol + FLdata, // ref to data segment variable + FLreg, // ref to register variable + FLpseudo, // pseuodo register variable + FLauto, // ref to automatic variable + FLpara, // ref to function parameter variable + FLextern, // ref to external variable + FLtmp, // ref to a stack temporary, int contains temp number + FLcode, // offset to code + FLblock, // offset to block + FLudata, // ref to udata segment variable + FLcs, // ref to common subexpression number + FLswitch, // ref to offset of switch data block + FLfltreg, // ref to floating reg on stack, int contains offset + FLoffset, // offset (a variation on constant, needed so we + // can add offsets (different meaning for FLconst)) + FLdatseg, // ref to data segment offset + FLctor, // constructed object + FLdtor, // destructed object + FLregsave, // ref to saved register on stack, int contains offset + FLasm, // (code) an ASM code +#if TX86 + FLndp, // saved 8087 register +#endif +#if TARGET_SEGMENTED + FLfardata, // ref to far data segment + FLcsdata, // ref to code segment variable +#endif + FLlocalsize, // replaced with # of locals in the stack frame + FLtlsdata, // thread local storage + FLbprel, // ref to variable at fixed offset from frame pointer + FLframehandler, // ref to C++ frame handler for NT EH + FLblockoff, // address of block + FLallocatmp, // temp for built-in alloca() + FLstack, // offset from ESP rather than EBP + FLdsymbol, // it's a Dsymbol +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + // Change this, update debug.c too + FLgot, // global offset table entry outside this object file + FLgotoff, // global offset table entry inside this object file + //FLoncedata, // link once data + //FLoncecode, // link once code +#endif + FLMAX +}; + +////////// Srcfiles + +#if !MARS +// Collect information about a source file. +typedef struct Sfile +{ +#ifdef DEBUG + unsigned short id; +#define IDsfile (('f' << 8) | 's') +#define sfile_debug(sf) assert((sf)->id == IDsfile) +#else +#define sfile_debug(sf) +#endif + + char *SFname; // name of file + unsigned SFflags; + #define SFonce 0x01 // file is to be #include'd only once + #define SFhx 0x02 // file is in an HX file and has not been loaded + #define SFtop 0x04 // file is a top level source file + #define SFloaded 0x08 // read from PH file + list_t SFfillist; // file pointers of Sfile's that this Sfile is + // dependent on (i.e. they were #include'd). + // Does not include nested #include's + macro_t *SFmacdefs; // threaded list of macros #defined by this file + macro_t **SFpmacdefs; // end of macdefs list + Symbol *SFsymdefs; // threaded list of global symbols declared by this file + symlist_t SFcomdefs; // comdefs defined in PH + symlist_t SFtemp_ft; // template_ftlist + symlist_t SFtemp_class; // template_class_list + Symbol *SFtagsymdefs; // list of tag names (C only) + unsigned SFhashval; // hash of file name +} Sfile; + +// Source files are referred to by a pointer into pfiles[]. This is so that +// when PH files are hydrated, only pfiles[] needs updating. Of course, this +// means that pfiles[] cannot be reallocated to larger numbers, its size is +// fixed at SRCFILES_MAX. + +typedef struct Srcfiles +{ +// Sfile *arr; // array of Sfiles + Sfile **pfiles; // parallel array of pointers into arr[] + #define SRCFILES_MAX (2*512) + unsigned dim; // dimension of array + unsigned idx; // # used in array +} Srcfiles; + +#define sfile(fi) (*srcfiles.pfiles[fi]) +#define srcfiles_name(fi) (sfile(fi).SFname) +#endif + +/************************************************** + * This is to support compiling expressions within the context of a function. + */ + +struct EEcontext +{ + unsigned EElinnum; // line number to insert expression + char *EEexpr; // expression + char *EEtypedef; // typedef identifier + char EEpending; // !=0 means we haven't compiled it yet + char EEimminent; // we've installed it in the source text + char EEcompile; // we're compiling for the EE expression + char EEin; // we are parsing an EE expression + elem *EEelem; // compiled version of EEexpr + Symbol *EEfunc; // function expression is in + code *EEcode; // generated code +}; + +extern EEcontext eecontext; + +#include "rtlsym.h" + +#undef SYMBOL_Z +#define SYMBOL_Z(e,fl,saved,n,flags,ty) RTLSYM_##e, + +enum +{ + RTLSYMS + + RTLSYM_MAX +}; + +extern Symbol *rtlsym[RTLSYM_MAX]; + +// Different goals for el_optimize() +#define GOALnone 0 // evaluate for side effects only +#define GOALvalue 1 // evaluate for value +#define GOALflags 2 // evaluate for flags +#define GOALagain 4 +#define GOALstruct 8 +#define GOALhandle 0x10 // don't replace handle'd objects + +/* Globals returned by declar() */ +struct Declar +{ + Classsym *class_sym; + Nspacesym *namespace_sym; + int oper; + bool constructor; + bool destructor; + bool invariant; + param_t *ptal; + bool explicitSpecialization; + int hasExcSpec; // has exception specification +}; + +extern Declar gdeclar; + +#endif diff --git a/backend/cdef.h b/backend/cdef.h new file mode 100644 index 00000000..5a73153a --- /dev/null +++ b/backend/cdef.h @@ -0,0 +1,1104 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +/* Macros defined by the compiler, not the code: + + Compiler: + __SC__ Symantec compiler + __DMC__ Digital Mars compiler + _MSC_VER Microsoft compiler + __GNUC__ Gnu compiler + __clang__ Clang compiler + __llvm__ Compiler using LLVM as backend (LLVM-GCC/Clang) + + Host operating system: + _WIN32 Microsoft NT, Windows 95, Windows 98, Win32s, Windows 2000 + _WIN64 Windows for AMD64 + linux Linux + __APPLE__ Mac OSX + __FreeBSD__ FreeBSD + __OpenBSD__ OpenBSD + __sun&&__SVR4 Solaris, OpenSolaris (yes, both macros are necessary) + __OS2__ IBM OS/2 + DOS386 32 bit DOS extended executable + DOS16RM Rational Systems 286 DOS extender + + Compiler executable type: + _WINDLL if building a _WIN32 DLL + + Host CPU: + _M_I86 Intel processor + _M_AMD64 AMD 64 processor + + Hosts no longer supported: + __INTSIZE==2 16 bit compilations + __OS2__ IBM OS/2 + DOS386 32 bit DOS extended executable + DOS16RM Rational Systems 286 DOS extender + _MSDOS MSDOS + +One and only one of these macros must be set by the makefile: + + SPP Build C/C++ preprocessor + SCPP Build C/C++ compiler + MARS Build Mars compiler + */ + +/* Windows/DOS/DOSX Version + * ------------------------ + * There are two main issues: hosting the compiler on windows, + * and generating (targetting) windows/dos/dosx executables. + * The "_WIN32" and "__DMC__" macros control hosting issues + * for operating system and compiler dependencies, respectively. + * To target linux executables, use OMFOBJ for things specific to the + * OMF object file format, and TARGET_WINDOS for things specific to + * the windows/dos/dosx memory model. + * If this is all done right, one could generate a win/dos/dosx object file + * even when compiling on linux, and vice versa. + * The compiler source code currently uses these macros very inconsistently + * with these goals, and should be fixed. + */ + +/* Linux Version + * ------------- + * There are two main issues: hosting the compiler on linux, + * and generating (targetting) linux executables. + * The "linux" and "__GNUC__" macros control hosting issues + * for operating system and compiler dependencies, respectively. + * To target linux executables, use ELFOBJ for things specific to the + * ELF object file format, and TARGET_LINUX for things specific to + * the linux memory model. + * If this is all done right, one could generate a linux object file + * even when compiling on win32, and vice versa. + * The compiler source code currently uses these macros very inconsistently + * with these goals, and should be fixed. + */ + +/* OSX Version + * ------------- + * There are two main issues: hosting the compiler on OSX, + * and generating (targetting) OSX executables. + * The "__APPLE__" and "__GNUC__" macros control hosting issues + * for operating system and compiler dependencies, respectively. + * To target OSX executables, use MACHOBJ for things specific to the + * MACH-O object file format, and TARGET_OSX for things specific to + * the OSX memory model. + * If this is all done right, one could generate an OSX object file + * even when compiling on win32, and vice versa. + * The compiler source code currently uses these macros very inconsistently + * with these goals, and should be fixed. + */ + +/* FreeBSD Version + * ------------- + * There are two main issues: hosting the compiler on FreeBSD, + * and generating (targetting) FreeBSD executables. + * The "__FreeBSD__" and "__GNUC__" macros control hosting issues + * for operating system and compiler dependencies, respectively. + * To target FreeBSD executables, use ELFOBJ for things specific to the + * ELF object file format, and TARGET_FREEBSD for things specific to + * the FreeBSD memory model. + * If this is all done right, one could generate a FreeBSD object file + * even when compiling on win32, and vice versa. + * The compiler source code currently uses these macros very inconsistently + * with these goals, and should be fixed. + */ + +/* OpenBSD Version + * ------------- + * There are two main issues: hosting the compiler on OpenBSD, + * and generating (targetting) OpenBSD executables. + * The "__OpenBSD__" and "__GNUC__" macros control hosting issues + * for operating system and compiler dependencies, respectively. + * To target OpenBSD executables, use ELFOBJ for things specific to the + * ELF object file format, and TARGET_FREEBSD for things specific to + * the OpenBSD memory model. + * If this is all done right, one could generate a OpenBSD object file + * even when compiling on win32, and vice versa. + * The compiler source code currently uses these macros very inconsistently + * with these goals, and should be fixed. + */ + +/* Solaris Version + * ------------- + * There are two main issues: hosting the compiler on Solaris, + * and generating (targetting) Solaris executables. + * The "__sun", "__SVR4" and "__GNUC__" macros control hosting issues + * for operating system and compiler dependencies, respectively. + * To target Solaris executables, use ELFOBJ for things specific to the + * ELF object file format, and TARGET_SOLARIS for things specific to + * the Solaris memory model. + * If this is all done right, one could generate a Solaris object file + * even when compiling on win32, and vice versa. + * The compiler source code currently uses these macros very inconsistently + * with these goals, and should be fixed. + */ + +#ifndef CDEF_H +#define CDEF_H 1 + +#define VERSION "8.54.0" // for banner and imbedding in .OBJ file +#define VERSIONHEX "0x854" // for __DMC__ macro +#define VERSIONINT 0x854 // for precompiled headers and DLL version + + +/*********************************** + * Target machine types: + */ + +#define TX86 1 // target is Intel 80X86 processor + +// Set to 1 using the makefile +#ifndef TARGET_LINUX +#define TARGET_LINUX 0 // target is a linux executable +#endif + +// Set to 1 using the makefile +#ifndef TARGET_OSX +#define TARGET_OSX 0 // target is an OSX executable +#endif + +// Set to 1 using the makefile +#ifndef TARGET_FREEBSD +#define TARGET_FREEBSD 0 // target is a FreeBSD executable +#endif + +// Set to 1 using the makefile +#ifndef TARGET_OPENBSD +#define TARGET_OPENBSD 0 // target is an OpenBSD executable +#endif + +// Set to 1 using the makefile +#ifndef TARGET_SOLARIS +#define TARGET_SOLARIS 0 // target is a Solaris executable +#endif + +// This is the default +#ifndef TARGET_WINDOS +#define TARGET_WINDOS (!(TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS)) +#endif + +#if __GNUC__ +#include "cdeflnx.h" +#endif + +#if _WINDLL +#define SUFFIX "nd" +#elif _WIN64 +#define SUFFIX "a" +#elif _WIN32 +#define SUFFIX "n" +#elif DOS386 +#define SUFFIX "x" +#elif DOS16RM +#define SUFFIX "r" +#else +#define SUFFIX "" +#endif + +// C++ Language Features +#define ANGLE_BRACKET_HACK 0 // >> means two template arglist closes + +// C Language Features +#define CPP_COMMENT 1 // allow C++ style comments + +// C/C++ Language Features +#define PSEUDO_REGS 1 +#define INLINE_ASM 1 // support inline assembler +#define HIDDENPARAM_1ST_ARG 1 +#define HEADER_LIST 1 +#define PASCAL_STRINGS 0 +#define KEYWORD_WITH 1 +#define EECONTEXT 1 +#define LOCALE 0 // locale support for Unicode conversion +#define HEXFLOATS 1 // support hex floating point constants +#define OVERLOAD_CV_PARAM 1 // if int foo(int i) and int foo(const int i) + // are different +#define CPP0X 1 // support C++0x features + +// Support generating code for 16 bit memory models +#define SIXTEENBIT (SCPP && TARGET_WINDOS) + +/* Set for supporting the FLAT memory model. + * This is not quite the same as !SIXTEENBIT, as one could + * have near/far with 32 bit code. + */ +#define TARGET_SEGMENTED (!MARS && TARGET_WINDOS) + + +#define STATEMENT_SCOPES CPP + +#if __GNUC__ +#define LONGLONG 1 +#elif __SC__ < 0x700 || _MSC_VER +#define LONGLONG 0 +#else +#define LONGLONG 1 // add in code to support 64 bit integral types +#endif + +#if __GNUC__ +#define LDOUBLE 0 // no support for true long doubles +#else +#define LDOUBLE (config.exe == EX_NT) // support true long doubles +#endif + +// Precompiled header variations +#define MEMORYHX (_WINDLL && _WIN32) // HX and SYM files are cached in memory +#define MMFIO (_WIN32 || linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4) // if memory mapped files +#define LINEARALLOC _WIN32 // if we can reserve address ranges + +// H_STYLE takes on one of these precompiled header methods +#define H_NONE 1 // no hydration/dehydration necessary +#define H_BIT0 2 // bit 0 of the pointer determines if pointer + // is dehydrated, an offset is added to + // hydrate it +#define H_OFFSET 4 // the address range of the pointer determines + // if pointer is dehydrated, and an offset is + // added to hydrate it. No dehydration necessary. +#define H_COMPLEX 8 // dehydrated pointers have bit 0 set, hydrated + // pointers are in non-contiguous buffers + +// Do we need hydration code +#define HYDRATE (H_STYLE & (H_BIT0 | H_OFFSET | H_COMPLEX)) + +// Do we need dehydration code +#define DEHYDRATE (H_STYLE & (H_BIT0 | H_COMPLEX)) + +// Determine hydration style +// NT console: H_NONE +// NT DLL: H_OFFSET +// DOSX: H_COMPLEX +#if MARS +#define H_STYLE H_NONE // precompiled headers only used for C/C++ compiler +#else +#if MMFIO +#if _WINDLL +#define H_STYLE H_OFFSET +#else +#define H_STYLE H_OFFSET //H_NONE +#endif +#elif LINEARALLOC +#define H_STYLE H_BIT0 +#else +#define H_STYLE H_COMPLEX +#endif +#endif + +// NT structured exception handling +// 0: no support +// 1: old style +// 2: new style +#define NTEXCEPTIONS 2 + +// indivisible file I/O +#define INDIVFILEIO 1 + +#define NEWSTATICDTOR 1 // support new style static destructors + +// For Shared Code Base +#define TARGET_INLINEFUNC_NAMES +#define PASCAL pascal +#define HINT int +#define UHINT unsigned int +#if _WINDLL +#define dbg_printf dll_printf +#else +#define dbg_printf printf +#endif + +#ifndef ERRSTREAM +#define ERRSTREAM stdout +#endif +#define err_printf printf +#define err_vprintf vfprintf +#define err_fputc fputc +#define dbg_fputc fputc +#define LF '\n' +#define LF_STR "\n" +#define CR '\r' +#define ANSI config.ansi_c +#define ANSI_STRICT config.ansi_c +#define ANSI_RELAX config.ansi_c +#ifndef TRIGRAPHS +#define TRIGRAPHS ANSI +#endif +#define ARG_TRUE +#define ARG_FALSE +#define T68000(x) +#define T80x86(x) x + +// For Share MEM_ macros - default to mem_xxx package +// PH precompiled header +// PARF parser, life of function +// PARC parser, life of compilation +// BEF back end, function +// BEC back end, compilation + +#define MEM_PH_FREE mem_free +#define MEM_PARF_FREE mem_free +#define MEM_PARC_FREE mem_free +#define MEM_BEF_FREE mem_free +#define MEM_BEC_FREE mem_free + +#define MEM_PH_CALLOC mem_calloc +#define MEM_PARC_CALLOC mem_calloc +#define MEM_PARF_CALLOC mem_calloc +#define MEM_BEF_CALLOC mem_calloc +#define MEM_BEC_CALLOC mem_calloc + +#define MEM_PH_MALLOC mem_malloc +#define MEM_PARC_MALLOC mem_malloc +#define MEM_PARF_MALLOC mem_malloc +#define MEM_BEF_MALLOC mem_malloc +#define MEM_BEC_MALLOC mem_malloc + +#define MEM_PH_STRDUP mem_strdup +#define MEM_PARC_STRDUP mem_strdup +#define MEM_PARF_STRDUP mem_strdup +#define MEM_BEF_STRDUP mem_strdup +#define MEM_BEC_STRDUP mem_strdup + +#define MEM_PH_REALLOC mem_realloc +#define MEM_PARC_REALLOC mem_realloc +#define MEM_PARF_REALLOC mem_realloc +#define MEM_PERM_REALLOC mem_realloc +#define MEM_BEF_REALLOC mem_realloc +#define MEM_BEC_REALLOC mem_realloc + +#define MEM_PH_FREEFP mem_freefp +#define MEM_PARC_FREEFP mem_freefp +#define MEM_PARF_FREEFP mem_freefp +#define MEM_BEF_FREEFP mem_freefp +#define MEM_BEC_FREEFP mem_freefp + + +// If we can use 386 instruction set (possible in 16 bit code) +#define I386 (config.target_cpu >= TARGET_80386) + +// If we are generating 32 bit code +#if MARS +#define I16 0 // no 16 bit code for D +#define I32 (NPTRSIZE == 4) +#define I64 (NPTRSIZE == 8) // 1 if generating 64 bit code +#else +#define I16 (NPTRSIZE == 2) +#define I32 (NPTRSIZE == 4) +#define I64 (NPTRSIZE == 8) // 1 if generating 64 bit code +#endif + +/********************************** + * Limits & machine dependent stuff. + */ + +/* Define stuff that's different between VAX and IBMPC. + * HOSTBYTESWAPPED TRUE if on the host machine the bytes are + * swapped (TRUE for 6809, 68000, FALSE for 8088 + * and VAX). + */ + + +#define EXIT_BREAK 255 // aborted compile with ^C + +/* Take advantage of machines that can store a word, lsb first */ +#if _M_I86 // if Intel processor +#define TOWORD(ptr,val) (*(unsigned short *)(ptr) = (unsigned short)(val)) +#define TOLONG(ptr,val) (*(unsigned *)(ptr) = (unsigned)(val)) +#else +#define TOWORD(ptr,val) (((ptr)[0] = (unsigned char)(val)),\ + ((ptr)[1] = (unsigned char)((val) >> 8))) +#define TOLONG(ptr,val) (((ptr)[0] = (unsigned char)(val)),\ + ((ptr)[1] = (unsigned char)((val) >> 8)),\ + ((ptr)[2] = (unsigned char)((val) >> 16)),\ + ((ptr)[3] = (unsigned char)((val) >> 24))) +#endif + +#define TOOFFSET(a,b) (I32 ? TOLONG(a,b) : TOWORD(a,b)) + +/*************************** + * Target machine data types as they appear on the host. + */ + +typedef signed char targ_char; +typedef unsigned char targ_uchar; +typedef signed char targ_schar; +typedef short targ_short; +typedef unsigned short targ_ushort; +typedef int targ_long; +typedef unsigned targ_ulong; +#if LONGLONG +typedef long long targ_llong; +typedef unsigned long long targ_ullong; +#else +#define targ_llong targ_long +#define targ_ullong targ_ulong +#endif +typedef float targ_float; +typedef double targ_double; +typedef long double targ_ldouble; + +// Extract most significant register from constant +#define MSREG(p) ((REGSIZE == 2) ? (p) >> 16 : ((sizeof(targ_llong) == 8) ? (p) >> 32 : 0)) + +typedef int targ_int; +typedef unsigned targ_uns; + +/* Sizes of base data types in bytes */ + +#define CHARSIZE 1 +#define SHORTSIZE 2 +#define WCHARSIZE 2 // 2 for WIN32, 4 for linux/OSX/FreeBSD/OpenBSD/Solaris +#define LONGSIZE 4 +#define LLONGSIZE 8 +#define FLOATSIZE 4 +#define DOUBLESIZE 8 +#if TARGET_OSX +#define LNGDBLSIZE 16 // 80 bit reals +#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +#define LNGDBLSIZE 12 // 80 bit reals +#else +#define LNGDBLSIZE 10 // 80 bit reals +#endif +#define TMAXSIZE LNGDBLSIZE // largest size a constant can be + +#if 0 +#define NDPSAVESIZE DOUBLESIZE +#else +#define NDPSAVESIZE LNGDBLSIZE +#endif + +#define intsize tysize[TYint] +#define REGSIZE tysize[TYnptr] +#define NPTRSIZE tysize[TYnptr] +#define FPTRSIZE tysize[TYfptr] +#define REGMASK 0xFFFF + +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS || TARGET_OSX +typedef targ_llong targ_ptrdiff_t; /* ptrdiff_t for target machine */ +typedef targ_ullong targ_size_t; /* size_t for the target machine */ +#else +typedef targ_int targ_ptrdiff_t; /* ptrdiff_t for target machine */ +typedef targ_uns targ_size_t; /* size_t for the target machine */ +#endif + +/* Enable/disable various features + (Some features may no longer work the old way when compiled out, + I don't test the old ways once the new way is set.) + */ +#define THUNKS 1 /* use thunks for virtual functions */ +#define SEPNEWDEL 1 // new/delete are not called within the ctor/dtor, + // they are separate +#define VBTABLES 1 // use Microsoft object model +#define UNICODE 1 // support Unicode (wchar_t is unsigned short) +#define DLCMSGS 0 // if 1, have all messages in a file +#define NEWMANGLE TARGET_WINDOS // use MS name mangling scheme +#define NEWTEMPMANGLE (!(config.flags4 & CFG4oldtmangle)) // do new template mangling +#define USEDLLSHELL _WINDLL +#define FARCLASSES 1 // support near/far classes +#define MFUNC (I32) //0 && config.exe == EX_NT) // member functions are TYmfunc +#define AUTONEST 0 // overlap storage of nested auto's +#define CV3 0 // 1 means support CV3 debug format + +/* Object module format + */ +#ifndef OMFOBJ +#define OMFOBJ TARGET_WINDOS +#endif +#ifndef ELFOBJ +#define ELFOBJ (TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) +#endif +#ifndef MACHOBJ +#define MACHOBJ TARGET_OSX +#endif + +#define TOOLKIT_H + +/* Masks so we can easily check size */ +#define CHARMASK (0xFFL) +#define SHORTMASK (0xFFFFL) +#define INTMASK SHORTMASK +#define LONGMASK 0xFFFFFFFF + +/* Common constants often checked for */ +#if LONGLONG +#define LLONGMASK 0xFFFFFFFFFFFFFFFFLL +#define ZEROLL 0LL +#define MINLL 0x8000000000000000LL +#define MAXLL 0x7FFFFFFFFFFFFFFFLL +#else +#define LLONGMASK 0xFFFFFFFFLL +#define ZEROLL 0L +#define MINLL 0x80000000 +#define MAXLL 0x7FFFFFFF +#endif + +#ifndef MEMMODELS +#define LARGEDATA (config.memmodel & 6) +#define LARGECODE (config.memmodel & 5) + +#define Smodel 0 /* 64k code, 64k data */ +#define Mmodel 1 /* large code, 64k data */ +#define Cmodel 2 /* 64k code, large data */ +#define Lmodel 3 /* large code, large data */ +#define Vmodel 4 /* large code, large data, vcm */ +#define MEMMODELS 5 /* number of memory models */ +#endif + +/* Segments */ +#define CODE 1 /* code segment */ +#define DATA 2 /* initialized data */ +#define CDATA 3 /* constant data */ +#define UDATA 4 /* uninitialized data */ +#define UNKNOWN 0x7FFF // unknown segment +#define DGROUPIDX 1 /* group index of DGROUP */ + +#define KEEPBITFIELDS 0 /* 0 means code generator cannot handle bit fields, */ + /* so replace them with shifts and masks */ + +#define REGMAX 29 // registers are numbered 0..10 + +typedef unsigned tym_t; // data type big enough for type masks +typedef int SYMIDX; // symbol table index + +#if 0 +#if defined(__DMC__) && __DMC__ < 0x81e +typedef int bool; +#endif +#define bool int +#endif + +#define _chkstack() (void)0 + +/* For 32 bit compilations, we don't need far keyword */ +#if 1 +#define far +#define _far +#define __far +#define __cs +#define __ss +#define near +#define _near +#define __near +#endif + +// gcc defines this for us, dmc doesn't, so look for it's __I86__ +#if ! (defined(LITTLE_ENDIAN) || defined(BIG_ENDIAN) ) +#if defined(__I86__) || defined(i386) || defined(__x86_64__) +#define LITTLE_ENDIAN 1 +#else +#error unknown platform, so unknown endianness +#endif +#endif + +#if _WINDLL +#define COPYRIGHT "Copyright © 2001 Digital Mars" +#else +#ifdef DEBUG +#define COPYRIGHT "Copyright (C) Digital Mars 2000-2010. All Rights Reserved.\n\ +Written by Walter Bright\n\ +*****BETA TEST VERSION*****" +#else +#if linux +#define COPYRIGHT "Copyright (C) Digital Mars 2000-2010. All Rights Reserved.\n\ +Written by Walter Bright, Linux version by Pat Nelson" +#else +#define COPYRIGHT "Copyright (C) Digital Mars 2000-2010. All Rights Reserved.\n\ +Written by Walter Bright" +#endif +#endif +#endif + +/********************************** + * Configuration + */ + +/* Linkage type */ +typedef enum LINKAGE +{ + LINK_C, /* C style */ + LINK_CPP, /* C++ style */ + LINK_PASCAL, /* Pascal style */ + LINK_FORTRAN, + LINK_SYSCALL, + LINK_STDCALL, + LINK_D, // D code + LINK_MAXDIM /* array dimension */ +} linkage_t; + + +// This part of the configuration is saved in the precompiled header for use +// in comparing to make sure it hasn't changed. + +struct Config +{ + char language; // 'C' = C, 'D' = C++ +//#define CPP (config.language == 'D') + char version[8]; // = VERSION + char exetype[3]; // distinguish exe types so PH + // files are distinct (= SUFFIX) + + char target_cpu; // instruction selection + char target_scheduler; // instruction scheduling (normally same as selection) +#define TARGET_8086 0 +#define TARGET_80286 2 +#define TARGET_80386 3 +#define TARGET_80486 4 +#define TARGET_Pentium 5 +#define TARGET_PentiumMMX 6 +#define TARGET_PentiumPro 7 +#define TARGET_PentiumII 8 +#define TARGET_AMD64 9 (32 or 64 bit mode) + + short versionint; // intermediate file version (= VERSIONINT) + int defstructalign; // struct alignment specified by command line + short hxversion; // HX version number + char fulltypes; +#define CVNONE 0 // No symbolic info +#define CVOLD 1 // Codeview 1 symbolic info +#define CV4 2 // Codeview 4 symbolic info +#define CVSYM 3 // Symantec format +#define CVTDB 4 // Symantec format written to file +#define CVDWARF_C 5 // Dwarf in C format +#define CVDWARF_D 6 // Dwarf in D format +#define CVSTABS 7 // Elf Stabs in C format + + unsigned wflags; // flags for Windows code generation +# define WFwindows 1 // generating code for Windows app or DLL +# define WFdll 2 // generating code for Windows DLL +# define WFincbp 4 // mark far stack frame with inc BP / dec BP +# define WFloadds 8 // assume __loadds for all functions +# define WFexpdef 0x10 // generate export definition records for + // exported functions +# define WFss 0x20 // load DS from SS +# define WFreduced 0x40 // skip DS load for non-exported functions +# define WFdgroup 0x80 // load DS from DGROUP +# define WFexport 0x100 // assume __export for all far functions +# define WFds 0x200 // load DS from DS +# define WFmacros 0x400 // define predefined windows macros +# define WFssneds 0x800 // SS != DS +# define WFthunk 0x1000 // use fixups instead of direct ref to CS +# define WFsaveds 0x2000 // use push/pop DS for far functions +# define WFdsnedgroup 0x4000 // DS != DGROUP +# define WFexe 0x8000 // generating code for Windows EXE + + bool fpxmmregs; // use XMM registers for floating point + char inline8087; /* 0: emulator + 1: IEEE 754 inline 8087 code + 2: fast inline 8087 code + */ + short memmodel; // 0:S,X,N,F, 1:M, 2:C, 3:L, 4:V + unsigned exe; // target operating system +#define EX_DOSX 1 // DOSX 386 program +#define EX_ZPM 2 // ZPM 286 program +#define EX_RATIONAL 4 // RATIONAL 286 program +#define EX_PHARLAP 8 // PHARLAP 386 program +#define EX_COM 0x10 // MSDOS .COM program +//#define EX_WIN16 0x20 // Windows 3.x 16 bit program +#define EX_OS2 0x40 // OS/2 2.0 32 bit program +#define EX_OS1 0x80 // OS/2 1.x 16 bit program +#define EX_NT 0x100 // NT +#define EX_MZ 0x200 // MSDOS real mode program +#define EX_XENIX 0x400 +#define EX_SCOUNIX 0x800 +#define EX_UNIXSVR4 0x1000 +#define EX_LINUX 0x2000 +#define EX_WIN64 0x4000 // AMD64 and Windows (64 bit mode) +#define EX_LINUX64 0x8000 // AMD64 and Linux (64 bit mode) +#define EX_OSX 0x10000 +#define EX_OSX64 0x20000 +#define EX_FREEBSD 0x40000 +#define EX_FREEBSD64 0x80000 +#define EX_SOLARIS 0x100000 +#define EX_SOLARIS64 0x200000 +#define EX_OPENBSD 0x400000 +#define EX_OPENBSD64 0x800000 + +#define EX_flat (EX_OS2 | EX_NT | EX_LINUX | EX_WIN64 | EX_LINUX64 | \ + EX_OSX | EX_OSX64 | EX_FREEBSD | EX_FREEBSD64 | \ + EX_OPENBSD | EX_OPENBSD64 | \ + EX_SOLARIS | EX_SOLARIS64) +#define EX_dos (EX_DOSX | EX_ZPM | EX_RATIONAL | EX_PHARLAP | \ + EX_COM | EX_MZ /*| EX_WIN16*/) + +/* CFGX: flags ignored in precompiled headers + * CFGY: flags copied from precompiled headers into current config + */ + unsigned flags; +#define CFGuchar 1 // chars are unsigned +#define CFGsegs 2 // new code seg for each far func +#define CFGtrace 4 // output trace functions +#define CFGglobal 8 // make all static functions global +#define CFGstack 0x20 // add stack overflow checking +#define CFGalwaysframe 0x40 // always generate stack frame +#define CFGnoebp 0x80 // do not use EBP as general purpose register +#define CFGromable 0x100 // put switch tables in code segment +#define CFGeasyomf 0x200 // generate Pharlap Easy-OMF format +#define CFGfarvtbls 0x800 // store vtables in far segments +#define CFGnoinlines 0x1000 // do not inline functions +#define CFGnowarning 0x8000 // disable warnings +#define CFGX (CFGnowarning) + unsigned flags2; +#define CFG2comdat 1 // use initialized common blocks +#define CFG2nodeflib 2 // no default library imbedded in OBJ file +#define CFG2browse 4 // generate browse records +#define CFG2dyntyping 8 // generate dynamic typing information +#define CFG2fulltypes 0x10 // don't optimize CV4 class info +#define CFG2warniserr 0x20 // treat warnings as errors +#define CFG2phauto 0x40 // automatic precompiled headers +#define CFG2phuse 0x80 // use precompiled headers +#define CFG2phgen 0x100 // generate precompiled header +#define CFG2once 0x200 // only include header files once +#define CFG2hdrdebug 0x400 // generate debug info for header +#define CFG2phautoy 0x800 // fast build precompiled headers +#define CFG2noobj 0x1000 // we are not generating a .OBJ file +#define CFG2noerrmax 0x4000 // no error count maximum +#define CFG2expand 0x8000 // expanded output to list file +#define CFG2seh 0x10000 // use Win32 SEH to support any exception handling +#define CFGX2 (CFG2warniserr | CFG2phuse | CFG2phgen | CFG2phauto | \ + CFG2once | CFG2hdrdebug | CFG2noobj | CFG2noerrmax | \ + CFG2expand | CFG2nodeflib) + unsigned flags3; +#define CFG3ju 1 // char == unsigned char +#define CFG3eh 4 // generate exception handling stuff +#define CFG3strcod 8 // strings are placed in code segment +#define CFG3eseqds 0x10 // ES == DS at all times +#define CFG3ptrchk 0x20 // generate pointer validation code +#define CFG3strictproto 0x40 // strict prototyping +#define CFG3autoproto 0x80 // auto prototyping +#define CFG3rtti 0x100 // add RTTI support +#define CFG3relax 0x200 // relaxed type checking (C only) +#define CFG3cpp 0x400 // C++ compile +#define CFG3igninc 0x800 // ignore standard include directory +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +#define CFG3mars 0x1000 // use mars libs and headers +#define NO_FAR (TRUE) // always ignore __far and __huge keywords +#else +#define CFG3nofar 0x1000 // ignore __far and __huge keywords +#define NO_FAR (config.flags3 & CFG3nofar) +#endif +#define CFG3noline 0x2000 // do not output #line directives +#define CFG3comment 0x4000 // leave comments in preprocessed output +#define CFG3cppcomment 0x8000 // allow C++ style comments +#define CFG3wkfloat 0x10000 // make floating point references weak externs +#define CFG3digraphs 0x20000 // support ANSI C++ digraphs +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +#define CFG3semirelax 0x40000 // moderate relaxed type checking +#endif +#define CFG3pic 0x80000 // position independent code +#define CFGX3 (CFG3strcod | CFG3ptrchk) + + unsigned flags4; +#define CFG4speed 1 // optimized for speed +#define CFG4space 2 // optimized for space +#define CFG4optimized (CFG4speed | CFG4space) +#define CFG4allcomdat 4 // place all functions in COMDATs +#define CFG4fastfloat 8 // fast floating point (-ff) +#define CFG4fdivcall 0x10 // make function call for FDIV opcodes +#define CFG4tempinst 0x20 // instantiate templates for undefined functions +#define CFG4oldstdmangle 0x40 // do stdcall mangling without @ +#define CFG4pascal 0x80 // default to pascal linkage +#define CFG4stdcall 0x100 // default to std calling convention +#define CFG4cacheph 0x200 // cache precompiled headers in memory +#define CFG4alternate 0x400 // if alternate digraph tokens +#define CFG4bool 0x800 // support 'bool' as basic type +#define CFG4wchar_t 0x1000 // support 'wchar_t' as basic type +#define CFG4notempexp 0x2000 // no instantiation of template functions +#define CFG4anew 0x4000 // allow operator new[] and delete[] overloading +#define CFG4oldtmangle 0x8000 // use old template name mangling +#define CFG4dllrtl 0x10000 // link with DLL RTL +#define CFG4noemptybaseopt 0x40000 // turn off empty base class optimization +#define CFG4stackalign CFG4speed // align stack to 8 bytes +#define CFG4nowchar_t 0x80000 // use unsigned short name mangling for wchar_t +#define CFG4forscope 0x100000 // new C++ for scoping rules +#define CFG4warnccast 0x200000 // warn about C style casts +#define CFG4adl 0x400000 // argument dependent lookup +#define CFG4enumoverload 0x800000 // enum overloading +#define CFG4implicitfromvoid 0x1000000 // allow implicit cast from void* to T* +#define CFG4dependent 0x2000000 // dependent / non-dependent lookup +#define CFG4wchar_is_long 0x4000000 // wchar_t is 4 bytes +#define CFG4underscore 0x8000000 // prepend _ for C mangling +#define CFGX4 (CFG4optimized | CFG4fastfloat | CFG4fdivcall | \ + CFG4tempinst | CFG4cacheph | CFG4notempexp | \ + CFG4stackalign | CFG4dependent) +#define CFGY4 (CFG4nowchar_t | CFG4noemptybaseopt | CFG4adl | \ + CFG4enumoverload | CFG4implicitfromvoid | \ + CFG4wchar_is_long | CFG4underscore) + + unsigned flags5; +#define CFG5debug 1 // compile in __debug code +#define CFG5in 2 // compile in __in code +#define CFG5out 4 // compile in __out code +#define CFG5invariant 8 // compile in __invariant code + +#if HTOD + unsigned htodFlags; // configuration for htod +#define HTODFinclude 1 // -hi drill down into #include files +#define HTODFsysinclude 2 // -hs drill down into system #include files +#define HTODFtypedef 4 // -ht drill down into typedefs +#define HTODFcdecl 8 // -hc skip C declarations as comments +#endif + char ansi_c; // strict ANSI C + // 89 for ANSI C89, 99 for ANSI C99 + char asian_char; /* 0: normal, 1: Japanese, 2: Chinese */ + /* and Taiwanese, 3: Korean */ + unsigned threshold; // data larger than threshold is assumed to + // be far (16 bit models only) +#define THRESHMAX 0xFFFF // if threshold == THRESHMAX, all data defaults + // to near + enum LINKAGE linkage; // default function call linkage +}; + +// Configuration that is not saved in precompiled header + +typedef struct Configv +{ + char addlinenumbers; // put line number info in .OBJ file + char verbose; // 0: compile quietly (no messages) + // 1: show progress to DLL (default) + // 2: full verbosity + char *csegname; // code segment name + char *deflibname; // default library name + enum LANG language; // message language + int errmax; // max error count +} Configv; + +struct Classsym; +struct Symbol; +struct LIST; +struct elem; + +typedef unsigned regm_t; // Register mask type +struct immed_t +{ targ_size_t value[REGMAX]; // immediate values in registers + regm_t mval; // Mask of which values in regimmed.value[] are valid +}; + + +struct cse_t +{ elem *value[REGMAX]; // expression values in registers + regm_t mval; // mask of which values in value[] are valid + regm_t mops; // subset of mval that contain common subs that need + // to be stored in csextab[] if they are destroyed +}; + +struct con_t +{ cse_t cse; // CSEs in registers + immed_t immed; // immediate values in registers + regm_t mvar; // mask of register variables + regm_t mpvar; // mask of SCfastpar register variables + regm_t indexregs; // !=0 if more than 1 uncommitted index register + regm_t used; // mask of registers used + regm_t params; // mask of registers which still contain register + // function parameters +}; + +/********************************* + * Bootstrap complex types. + */ + +#include "bcomplex.h" + +/********************************* + * Union of all data types. Storage allocated must be the right + * size of the data on the TARGET, not the host. + */ + +struct Cent +{ + targ_ullong lsw; + targ_ullong msw; +}; + +union eve +{ + targ_char Vchar; + targ_schar Vschar; + targ_uchar Vuchar; + targ_short Vshort; + targ_ushort Vushort; + targ_int Vint; // also used for tmp numbers (FLtmp) + targ_uns Vuns; + targ_long Vlong; + targ_ulong Vulong; + targ_llong Vllong; + targ_ullong Vullong; + Cent Vcent; + targ_float Vfloat; + targ_double Vdouble; + targ_ldouble Vldouble; + Complex_f Vcfloat; // 2x float + Complex_d Vcdouble; // 2x double + Complex_ld Vcldouble; // 2x long double + targ_size_t Vpointer; + targ_ptrdiff_t Vptrdiff; + targ_uchar Vreg; // register number for OPreg elems + struct // 48 bit 386 far pointer + { targ_long Voff; + targ_ushort Vseg; + } Vfp; + struct + { + targ_size_t Voffset;// offset from symbol + Symbol *Vsym; // pointer to symbol table + union + { struct PARAM *Vtal; // template-argument-list for SCfunctempl, + // used only to transmit it to cpp_overload() + LIST *Erd; // OPvar: reaching definitions + } spu; + } sp; + struct + { + targ_size_t Voffset;// member pointer offset + Classsym *Vsym; // struct tag + elem *ethis; // OPrelconst: 'this' for member pointer + } sm; + struct + { + targ_size_t Voffset;// offset from string + char *Vstring; // pointer to string (OPstring or OPasm) + targ_size_t Vstrlen;// length of string + } ss; + struct + { + elem *Eleft; // left child for unary & binary nodes + elem *Eright; // right child for binary nodes + Symbol *Edtor; // OPctor: destructor + } eop; +#if MARS + struct + { + elem *Eleft; // left child for OPddtor + void *Edecl; // VarDeclaration being constructed + } ed; // OPdctor,OPddtor +#endif +}; // variants for each type of elem + +// Symbols + +#ifdef DEBUG +#define IDSYMBOL IDsymbol, +#else +#define IDSYMBOL +#endif + +#if SCPP +#define SYMBOLZERO 0,0,0, +#elif MARS +#define SYMBOLZERO 0,0, +#elif AUTONEST +#define SYMBOLZERO 0,0, +#else +#define SYMBOLZERO +#endif + +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +#define UNIXFIELDS (unsigned)-1,(unsigned)-1,0,0, +#elif TARGET_OSX +#define UNIXFIELDS (unsigned)-1,(unsigned)-1,0,0,0, +#else +#define UNIXFIELDS +#endif + +typedef unsigned SYMFLGS; +#if MARS +#define SYM_PREDEF_SZ 40 +#else +#define SYM_PREDEF_SZ 22 +#endif + +#define SYMBOLY(fl,regsaved,name,flags) \ + {IDSYMBOL \ + (symbol *)0,(symbol *)0,(symbol *)0,(dt_t *)0,(type *)0,{0},\ + SYMBOLZERO\ + UNIXFIELDS\ + SCextern,(fl),(flags),0,0,0,0,0,0,0,{0},(regsaved),{name}} + +/********************************** + * Storage classes + * This macro is used to generate parallel tables and the enum values. + */ + +#define ENUMSCMAC \ + X(unde, SCEXP|SCKEP|SCSCT) /* undefined */ \ + X(auto, SCEXP|SCSS|SCRD ) /* automatic (stack) */ \ + X(static, SCEXP|SCKEP|SCSCT) /* statically allocated */ \ + X(thread, SCEXP|SCKEP ) /* thread local */ \ + X(extern, SCEXP|SCKEP|SCSCT) /* external */ \ + X(register, SCEXP|SCSS|SCRD ) /* registered variable */ \ + X(pseudo, SCEXP ) /* pseudo register variable */ \ + X(global, SCEXP|SCKEP|SCSCT) /* top level global definition */ \ + X(comdat, SCEXP|SCKEP|SCSCT) /* initialized common block */ \ + X(parameter,SCEXP|SCSS ) /* function parameter */ \ + X(regpar, SCEXP|SCSS ) /* function register parameter */ \ + X(fastpar, SCEXP|SCSS ) /* function parameter passed in register */ \ + X(typedef, 0 ) /* type definition */ \ + X(explicit, 0 ) /* explicit */ \ + X(mutable, 0 ) /* mutable */ \ + X(tmp, SCEXP|SCSS|SCRD ) /* compiler generated temporary (just like SCauto \ + but doesn't overlay other SCauto's in scoping) */ \ + X(label, 0 ) /* goto label */ \ + X(struct, SCKEP ) /* struct/class/union tag name */ \ + X(enum, 0 ) /* enum tag name */ \ + X(field, SCEXP|SCKEP ) /* bit field of struct or union */ \ + X(const, SCEXP|SCSCT ) /* constant integer */ \ + X(member, SCEXP|SCKEP|SCSCT) /* member of struct or union */ \ + X(anon, 0 ) /* member of anonymous union */ \ + X(inline, SCEXP|SCKEP ) /* for inline functions */ \ + X(sinline, SCEXP|SCKEP ) /* for static inline functions */ \ + X(einline, SCEXP|SCKEP ) /* for extern inline functions */ \ + X(overload, SCEXP ) /* for overloaded function names */ \ + X(friend, 0 ) /* friend of a class */ \ + X(virtual, 0 ) /* virtual function */ \ + X(locstat, SCEXP|SCSCT ) /* static, but local to a function */ \ + X(template, 0 ) /* class template */ \ + X(functempl,0 ) /* function template */ \ + X(ftexpspec,0 ) /* function template explicit specialization */ \ + X(linkage, 0 ) /* function linkage symbol */ \ + X(public, SCEXP|SCKEP|SCSCT) /* generate a pubdef for this */ \ + X(comdef, SCEXP|SCKEP|SCSCT) /* uninitialized common block */ \ + X(bprel, SCEXP|SCSS ) /* variable at fixed offset from frame pointer */ \ + X(namespace,0 ) /* namespace */ \ + X(alias, 0 ) /* alias to another symbol */ \ + X(funcalias,0 ) /* alias to another function symbol */ \ + X(memalias,0 ) /* alias to base class member */ \ + X(stack, SCEXP|SCSS ) /* offset from stack pointer (not frame pointer) */ \ + X(adl, 0 ) /* list of ADL symbols for overloading */ \ + + //X(union,SCKEP) /* union tag name */ + //X(class,SCKEP) /* class tag name */ + +#define ClassInline(c) ((c) == SCinline || (c) == SCsinline || (c) == SCeinline) +#define SymInline(s) ClassInline((s)->Sclass) + +enum SC { + #define X(a,b) SC##a, + ENUMSCMAC // generate the enum values + SCMAX // array dimension + #undef X +}; + +#define TAG_ARGUMENTS + +/******************************* + * Swap two integers. + */ + +inline void swap(int *a,int *b) +{ int tmp; + + tmp = *a; + *a = *b; + *b = tmp; +} + + +#endif /* CDEF_H */ diff --git a/backend/cdeflnx.h b/backend/cdeflnx.h new file mode 100644 index 00000000..5d1bd12e --- /dev/null +++ b/backend/cdeflnx.h @@ -0,0 +1,85 @@ +// Copyright (C) ?-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#include + +#define M_UNIX 1 +#define IMPLIED_PRAGMA_ONCE 1 +#define MEMMODELS 1 +#if __GNUC__ +#define __SC__ 0 +#define _MSC_VER 0 +#endif + +#define ERRSTREAM stderr + +#define isleadbyte(c) 0 + +#define __cdecl __attribute__ ((__cdecl__)) +#define _cdecl __attribute__ ((__cdecl__)) +#define __stdcall __attribute__ ((__stdcall__)) + +#define __pascal +#define __near +#define _near +#define __far +#define _far +#define __ss +#define __cs + +#if SCPP +// Need to define the token enum before any refs +#define TOKENS_ONLY 1 +#include "token.h" +#undef TOKEN_H +#undef TOKENS_ONLY +#define TOKENS_ONLY 0 +#endif + +// +// Error messages generated by msgsx.c +// + +//#include "msgs2.h" + +char * strupr(char *); + +// +// Attributes +// + +// Types of attributes +#define ATTR_LINKMOD 0x0001 // link modifier +#define ATTR_TYPEMOD 0x0002 // basic type modifier +#define ATTR_FUNCINFO 0x0004 // function information +#define ATTR_DATAINFO 0x0008 // data information +#define ATTR_TRANSU 0x0010 // transparent union +#define ATTR_IGNORED 0x0020 // attribute can be ignored +#define ATTR_WARNING 0x0040 // attribute was ignored +#define ATTR_SEGMENT 0x0080 // segment secified + + +// attribute location in code +#define ALOC_DECSTART 0x001 // start of declaration +#define ALOC_SYMDEF 0x002 // symbol defined +#define ALOC_PARAM 0x004 // follows function parameter +#define ALOC_FUNC 0x008 // follows function declaration + +#define ATTR_LINK_MODIFIERS (mTYconst|mTYvolatile|mTYcdecl|mTYstdcall) +#define ATTR_CAN_IGNORE(a) \ + (((a) & (ATTR_LINKMOD|ATTR_TYPEMOD|ATTR_FUNCINFO|ATTR_DATAINFO|ATTR_TRANSU)) == 0) +#define LNX_CHECK_ATTRIBUTES(a,x) assert(((a) & ~(x|ATTR_IGNORED|ATTR_WARNING)) == 0) + +#if SCPP +#include "optdata.h" +#endif + +#define TRIGRAPHS (ANSI || OPT_IS_SET(OPTtrigraphs)) diff --git a/backend/cg.c b/backend/cg.c new file mode 100644 index 00000000..4fdde086 --- /dev/null +++ b/backend/cg.c @@ -0,0 +1,61 @@ +// Copyright (C) 1984-1995 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#include +#include +#include +#include + +#include "cc.h" +#include "global.h" +#include "code.h" +#include "type.h" +#include "filespec.h" + +///////////////////// GLOBALS ///////////////////// + +#include "fltables.c" + +targ_size_t Poffset; /* size of func parameter variables */ +targ_size_t framehandleroffset; // offset of C++ frame handler +#if TARGET_OSX +targ_size_t localgotoffset; // offset of where localgot refers to +#endif + +int cseg = CODE; // current code segment + // (negative values mean it is the negative + // of the public name index of a COMDAT) + +/* Stack offsets */ +targ_size_t localsize, /* amt subtracted from SP for local vars */ + Toff, /* base for temporaries */ + Poff,Aoff; // comsubexps, params, regs, autos + +/* The following are initialized for the 8088. cod3_set32() or cod3_set64() + * will change them as appropriate. + */ +int BPRM = 6; /* R/M value for [BP] or [EBP] */ +regm_t fregsaved = mBP | mSI | mDI; // mask of registers saved across + // function calls + // (add in mBX for I32) +regm_t FLOATREGS = FLOATREGS_16; +regm_t FLOATREGS2 = FLOATREGS2_16; +regm_t DOUBLEREGS = DOUBLEREGS_16; + +symbol *localgot; // reference to GOT for this function +symbol *tls_get_addr_sym; // function __tls_get_addr + +#if TARGET_OSX +int STACKALIGN = 16; +#else +int STACKALIGN = 0; +#endif diff --git a/backend/cg87.c b/backend/cg87.c new file mode 100644 index 00000000..67c0cbe1 --- /dev/null +++ b/backend/cg87.c @@ -0,0 +1,3638 @@ +// Copyright (C) 1987-1995 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include +#include "cc.h" +#include "el.h" +#include "oper.h" +#include "code.h" +#include "global.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +// Constants that the 8087 supports directly +// BUG: rewrite for 80 bit long doubles +#define PI 3.14159265358979323846 +#define LOG2 0.30102999566398119521 +#define LN2 0.6931471805599453094172321 +#define LOG2T 3.32192809488736234787 +#define LOG2E 1.4426950408889634074 /* 1/LN2 */ + +#define FWAIT 0x9B /* FWAIT opcode */ + +/* Mark variable referenced by e as not a register candidate */ +#define notreg(e) ((e)->EV.sp.Vsym->Sflags &= ~GTregcand) + +/* Generate the appropriate ESC instruction */ +#define ESC(MF,b) (0xD8 + ((MF) << 1) + (b)) +enum MF +{ // Values for MF + MFfloat = 0, + MFlong = 1, + MFdouble = 2, + MFword = 3 +}; + +NDP _8087elems[8]; // 8087 stack +NDP ndp_zero; + +int stackused = 0; /* number of items on the 8087 stack */ + +/********************************* + */ + +struct Dconst +{ + int round; + symbol *roundto0; + symbol *roundtonearest; +}; + +static Dconst oldd; + +#define NDPP 0 // print out debugging info +#define NOSAHF (I64 || config.fpxmmregs) // can't use SAHF instruction + +code *loadComplex(elem *e); +code *opmod_complex87(elem *e,regm_t *pretregs); +code *opass_complex87(elem *e,regm_t *pretregs); +code * genf2(code *c,unsigned op,unsigned rm); + +#define CW_roundto0 0xFBF +#define CW_roundtonearest 0x3BF + +STATIC code *genrnd(code *c, short cw); + +/********************************** + * When we need to temporarilly save 8087 registers, we record information + * about the save into an array of NDP structs: + */ + +NDP *NDP::save = NULL; +int NDP::savemax = 0; /* # of entries in NDP::save[] */ +int NDP::savetop = 0; /* # of entries used in NDP::save[] */ + +#ifdef DEBUG +#define NDPSAVEINC 2 /* flush reallocation bugs */ +#else +#define NDPSAVEINC 8 /* allocation chunk sizes */ +#endif + +/**************************************** + * Store/load to ndp save location i + */ + +code *ndp_fstp(code *c, int i, tym_t ty) +{ unsigned grex = I64 ? (REX_W << 16) : 0; + switch (tybasic(ty)) + { + case TYfloat: + case TYifloat: + case TYcfloat: + c = genc1(c,0xD9,grex | modregrm(2,3,BPRM),FLndp,i); // FSTP m32real i[BP] + break; + + case TYdouble: + case TYdouble_alias: + case TYidouble: + case TYcdouble: + c = genc1(c,0xDD,grex | modregrm(2,3,BPRM),FLndp,i); // FSTP m64real i[BP] + break; + + case TYldouble: + case TYildouble: + case TYcldouble: + c = genc1(c,0xDB,grex | modregrm(2,7,BPRM),FLndp,i); // FSTP m80real i[BP] + break; + + default: + assert(0); + } + return c; +} + +code *ndp_fld(code *c, int i, tym_t ty) +{ unsigned grex = I64 ? (REX_W << 16) : 0; + switch (tybasic(ty)) + { + case TYfloat: + case TYifloat: + case TYcfloat: + c = genc1(c,0xD9,grex | modregrm(2,0,BPRM),FLndp,i); + break; + + case TYdouble: + case TYdouble_alias: + case TYidouble: + case TYcdouble: + c = genc1(c,0xDD,grex | modregrm(2,0,BPRM),FLndp,i); + break; + + case TYldouble: + case TYildouble: + case TYcldouble: + c = genc1(c,0xDB,grex | modregrm(2,5,BPRM),FLndp,i); // FLD m80real i[BP] + break; + + default: + assert(0); + } + return c; +} + +/************************** + * Return index of empty slot in NDP::save[]. + */ + +STATIC int getemptyslot() +{ int i; + + for (i = 0; i < NDP::savemax; i++) + if (NDP::save[i].e == NULL) + goto L1; + /* Out of room, reallocate NDP::save[] */ + NDP::save = (NDP *)mem_realloc(NDP::save, + (NDP::savemax + NDPSAVEINC) * sizeof(*NDP::save)); + /* clear out new portion of NDP::save[] */ + memset(NDP::save + NDP::savemax,0,NDPSAVEINC * sizeof(*NDP::save)); + i = NDP::savemax; + NDP::savemax += NDPSAVEINC; + + L1: if (i >= NDP::savetop) + NDP::savetop = i + 1; + return i; +} + +/********************************* + * Pop 8087 stack. + */ + +#undef pop87 + +void pop87( +#ifdef DEBUG + int line, const char *file +#endif + ) +#ifdef DEBUG +#define pop87() pop87(__LINE__,__FILE__) +#endif +{ + int i; + +#if NDPP + dbg_printf("pop87(%s(%d): stackused=%d)\n", file, line, stackused); +#endif + --stackused; + assert(stackused >= 0); + for (i = 0; i < arraysize(_8087elems) - 1; i++) + _8087elems[i] = _8087elems[i + 1]; + /* end of stack is nothing */ + _8087elems[arraysize(_8087elems) - 1] = ndp_zero; +} + +/******************************* + * Push 8087 stack. Generate and return any code + * necessary to preserve anything that might run off the end of the stack. + */ + +#undef push87 + +#ifdef DEBUG +code *push87(int line, const char *file); +code *push87() { return push87(__LINE__,__FILE__); } +#endif + +code *push87( +#ifdef DEBUG + int line, const char *file +#endif + ) +#ifdef DEBUG +#define push87() push87(__LINE__,__FILE__) +#endif +{ + code *c; + int i; + + c = CNIL; + // if we would lose the top register off of the stack + if (_8087elems[7].e != NULL) + { + i = getemptyslot(); + NDP::save[i] = _8087elems[7]; + c = genf2(c,0xD9,0xF6); // FDECSTP + c = genfwait(c); + c = ndp_fstp(c, i, _8087elems[7].e->Ety); // FSTP i[BP] + assert(stackused == 8); + if (NDPP) dbg_printf("push87() : overflow\n"); + } + else + { +#ifdef DEBUG + if (NDPP) dbg_printf("push87(%s(%d): %d)\n", file, line, stackused); +#endif + stackused++; + assert(stackused <= 8); + } + // Shift the stack up + for (i = 7; i > 0; i--) + _8087elems[i] = _8087elems[i - 1]; + _8087elems[0] = ndp_zero; + return c; +} + +/***************************** + * Note elem e as being in ST(i) as being a value we want to keep. + */ + +#ifdef DEBUG +void note87(elem *e, unsigned offset, int i, int linnum); +void note87(elem *e, unsigned offset, int i) +{ + return note87(e, offset, i, 0); +} +void note87(elem *e, unsigned offset, int i, int linnum) +#define note87(e,offset,i) note87(e,offset,i,__LINE__) +#else +void note87(elem *e, unsigned offset, int i) +#endif +{ +#if NDPP + printf("note87(e = %p.%d, i = %d, stackused = %d, line = %d)\n",e,offset,i,stackused,linnum); +#endif +#if 0 && DEBUG + if (_8087elems[i].e) + printf("_8087elems[%d].e = %p\n",i,_8087elems[i].e); +#endif + //if (i >= stackused) *(char*)0=0; + assert(i < stackused); + _8087elems[i].e = e; + _8087elems[i].offset = offset; +} + +/**************************************************** + * Exchange two entries in 8087 stack. + */ + +void xchg87(int i, int j) +{ + NDP save; + + save = _8087elems[i]; + _8087elems[i] = _8087elems[j]; + _8087elems[j] = save; +} + +/**************************** + * Make sure that elem e is in register ST(i). Reload it if necessary. + * Input: + * i 0..3 8087 register number + * flag 1 don't bother with FXCH + */ + +#ifdef DEBUG +STATIC code * makesure87(elem *e,unsigned offset,int i,unsigned flag,int linnum) +#define makesure87(e,offset,i,flag) makesure87(e,offset,i,flag,__LINE__) +#else +STATIC code * makesure87(elem *e,unsigned offset,int i,unsigned flag) +#endif +{ + code *c; + int j; + +#ifdef DEBUG + if (NDPP) printf("makesure87(e=%p, offset=%d, i=%d, flag=%d, line=%d)\n",e,offset,i,flag,linnum); +#endif + assert(e && i < 4); + c = CNIL; + L1: + if (_8087elems[i].e != e || _8087elems[i].offset != offset) + { +#ifdef DEBUG + if (_8087elems[i].e) + printf("_8087elems[%d].e = %p, .offset = %d\n",i,_8087elems[i].e,_8087elems[i].offset); +#endif + assert(_8087elems[i].e == NULL); + for (j = 0; 1; j++) + { + if (j >= NDP::savetop && e->Eoper == OPcomma) + { + e = e->E2; // try right side + goto L1; + } +#ifdef DEBUG + if (j >= NDP::savetop) + printf("e = %p, NDP::savetop = %d\n",e,NDP::savetop); +#endif + assert(j < NDP::savetop); + //printf("\tNDP::save[%d] = %p, .offset = %d\n", j, NDP::save[j].e, NDP::save[j].offset); + if (e == NDP::save[j].e && offset == NDP::save[j].offset) + break; + } + c = push87(); + c = genfwait(c); + c = ndp_fld(c, j, e->Ety); // FLD j[BP] + if (!(flag & 1)) + { + while (i != 0) + { + genf2(c,0xD9,0xC8 + i); // FXCH ST(i) + i--; + } + } + NDP::save[j] = ndp_zero; // back in 8087 + } + //_8087elems[i].e = NULL; + return c; +} + +/**************************** + * Save in memory any values in the 8087 that we want to keep. + */ + +code *save87() +{ + code *c; + int i; + + c = CNIL; + while (_8087elems[0].e && stackused) + { + /* Save it */ + i = getemptyslot(); + if (NDPP) printf("saving %p in temporary NDP::save[%d]\n",_8087elems[0].e,i); + NDP::save[i] = _8087elems[0]; + + c = genfwait(c); + c = ndp_fstp(c,i,_8087elems[0].e->Ety); // FSTP i[BP] + pop87(); + } + if (c) /* if any stores */ + genfwait(c); /* wait for last one to finish */ + return c; +} + +/****************************************** + * Save any noted values that would be destroyed by n pushes + */ + +code *save87regs(unsigned n) +{ + unsigned j; + unsigned k; + code *c = NULL; + + assert(n <= 7); + j = 8 - n; + if (stackused > j) + { + for (k = 8; k > j; k--) + { + c = genf2(c,0xD9,0xF6); // FDECSTP + c = genfwait(c); + if (k <= stackused) + { int i; + + i = getemptyslot(); + c = ndp_fstp(c, i, _8087elems[k - 1].e->Ety); // FSTP i[BP] + NDP::save[i] = _8087elems[k - 1]; + _8087elems[k - 1] = ndp_zero; + } + } + + for (k = 8; k > j; k--) + { + if (k > stackused) + { c = genf2(c,0xD9,0xF7); // FINCSTP + c = genfwait(c); + } + } + stackused = j; + } + return c; +} + +/***************************************************** + * Save/restore ST0 or ST01 + */ + +void gensaverestore87(regm_t regm, code **csave, code **crestore) +{ + //printf("gensaverestore87(%s)\n", regm_str(regm)); + code *cs1 = *csave; + code *cs2 = *crestore; + assert(regm == mST0 || regm == mST01); + + int i = getemptyslot(); + NDP::save[i].e = el_calloc(); // this blocks slot [i] for the life of this function + cs1 = ndp_fstp(cs1, i, TYldouble); + cs2 = cat(ndp_fld(CNIL, i, TYldouble), cs2); + if (regm == mST01) + { + int j = getemptyslot(); + NDP::save[j].e = el_calloc(); + cs1 = ndp_fstp(cs1, j, TYldouble); + cs2 = cat(ndp_fld(CNIL, j, TYldouble), cs2); + } + *csave = cs1; + *crestore = cs2; +} + +/************************************* + * Find which, if any, slot on stack holds elem e. + */ + +STATIC int cse_get(elem *e, unsigned offset) +{ int i; + + for (i = 0; 1; i++) + { + if (i == stackused) + { + i = -1; + //printf("cse not found\n"); + //elem_print(e); + break; + } + if (_8087elems[i].e == e && + _8087elems[i].offset == offset) + { //printf("cse found %d\n",i); + //elem_print(e); + break; + } + } + return i; +} + +/************************************* + * Reload common subexpression. + */ + +code *comsub87(elem *e,regm_t *pretregs) +{ code *c; + + //printf("comsub87(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); + // Look on 8087 stack + int i = cse_get(e, 0); + + if (tycomplex(e->Ety)) + { + unsigned sz = tysize(e->Ety); + int j = cse_get(e, sz / 2); + if (i >= 0 && j >= 0) + { + c = push87(); + c = cat(c, push87()); + c = genf2(c,0xD9,0xC0 + i); // FLD ST(i) + c = genf2(c,0xD9,0xC0 + j + 1); // FLD ST(j + 1) + c = cat(c,fixresult_complex87(e,mST01,pretregs)); + } + else + // Reload + c = loaddata(e,pretregs); + } + else + { + if (i >= 0) + { + c = push87(); + c = genf2(c,0xD9,0xC0 + i); // FLD ST(i) + if (*pretregs & XMMREGS) + c = cat(c,fixresult87(e,mST0,pretregs)); + else + c = cat(c,fixresult(e,mST0,pretregs)); + } + else + // Reload + c = loaddata(e,pretregs); + } + + freenode(e); + return c; +} + + +/************************** + * Generate code to deal with floatreg. + */ + +code * genfltreg(code *c,unsigned opcode,unsigned reg,targ_size_t offset) +{ + floatreg = TRUE; + reflocal = TRUE; + if ((opcode & ~7) == 0xD8) + c = genfwait(c); + return genc1(c,opcode,modregxrm(2,reg,BPRM),FLfltreg,offset); +} + +/******************************* + * Decide if we need to gen an FWAIT. + */ + +code *genfwait(code *c) +{ + if (ADDFWAIT()) + c = gen1(c,FWAIT); + return c; +} + +/*************************************** + * Generate floating point instruction. + */ + +code * genf2(code *c,unsigned op,unsigned rm) +{ + return gen2(genfwait(c),op,rm); +} + +/*************************** + * Put the 8087 flags into the CPU flags. + */ + +STATIC code * cg87_87topsw(code *c) +{ + /* Note that SAHF is not available on some early I64 processors + * and will cause a seg fault + */ + c = cat(c,getregs(mAX)); + if (config.target_cpu >= TARGET_80286) + c = genf2(c,0xDF,0xE0); // FSTSW AX + else + { c = genfltreg(c,0xD8+5,7,0); /* FSTSW floatreg[BP] */ + genfwait(c); /* FWAIT */ + genfltreg(c,0x8A,4,1); /* MOV AH,floatreg+1[BP] */ + } + gen1(c,0x9E); // SAHF + code_orflag(c,CFpsw); + return c; +} + +/*************************** + * Set the PSW based on the state of ST0. + * Input: + * pop if stack should be popped after test + * Returns: + * start of code appended to c. + */ + +STATIC code * genftst(code *c,elem *e,int pop) +{ + if (NOSAHF) + { + c = cat(c,push87()); + c = gen2(c,0xD9,0xEE); // FLDZ + gen2(c,0xDF,0xE9); // FUCOMIP ST1 + pop87(); + if (pop) + { c = genf2(c,0xDD,modregrm(3,3,0)); // FPOP + pop87(); + } + } + else if (config.flags4 & CFG4fastfloat) // if fast floating point + { + c = genf2(c,0xD9,0xE4); // FTST + c = cg87_87topsw(c); // put 8087 flags in CPU flags + if (pop) + { c = genf2(c,0xDD,modregrm(3,3,0)); // FPOP + pop87(); + } + } + else if (config.target_cpu >= TARGET_80386) + { + // FUCOMP doesn't raise exceptions on QNANs, unlike FTST + c = cat(c,push87()); + c = gen2(c,0xD9,0xEE); // FLDZ + gen2(c,pop ? 0xDA : 0xDD,0xE9); // FUCOMPP / FUCOMP + pop87(); + if (pop) + pop87(); + cg87_87topsw(c); // put 8087 flags in CPU flags + } + else + { + // Call library function which does not raise exceptions + regm_t regm = 0; + + c = cat(c,callclib(e,CLIBftest,®m,0)); + if (pop) + { c = genf2(c,0xDD,modregrm(3,3,0)); // FPOP + pop87(); + } + } + return c; +} + +/************************************* + * Determine if there is a special 8087 instruction to load + * constant e. + * Input: + * im 0 load real part + * 1 load imaginary part + * Returns: + * opcode if found + * 0 if not + */ + +unsigned char loadconst(elem *e, int im) +#if __DMC__ +__in +{ + elem_debug(e); + assert(im == 0 || im == 1); +} +__body +#endif +{ + static float fval[7] = + {0.0,1.0,PI,LOG2T,LOG2E,LOG2,LN2}; + static double dval[7] = + {0.0,1.0,PI,LOG2T,LOG2E,LOG2,LN2}; + static long double ldval[7] = +#if __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#define M_PIl 0x1.921fb54442d1846ap+1L // 3.14159 fldpi +#define M_LOG2T_L 0x1.a934f0979a3715fcp+1L // 3.32193 fldl2t +#define M_LOG2El 0x1.71547652b82fe178p+0L // 1.4427 fldl2e +#define M_LOG2_L 0x1.34413509f79fef32p-2L // 0.30103 fldlg2 +#define M_LN2l 0x1.62e42fefa39ef358p-1L // 0.693147 fldln2 + {0.0,1.0,M_PIl,M_LOG2T_L,M_LOG2El,M_LOG2_L,M_LN2l}; +#elif __GNUC__ + // BUG: should get proper 80 bit values for these + #define M_LOG2T_L LOG2T + #define M_LOG2_L LOG2 + {0.0,1.0,M_PIl,M_LOG2T_L,M_LOG2El,M_LOG2_L,M_LN2l}; +#else + {0.0,1.0,M_PI_L,M_LOG2T_L,M_LOG2E_L,M_LOG2_L,M_LN2_L}; +#endif + static char opcode[7 + 1] = + /* FLDZ,FLD1,FLDPI,FLDL2T,FLDL2E,FLDLG2,FLDLN2,0 */ + {0xEE,0xE8,0xEB,0xE9,0xEA,0xEC,0xED,0}; + int i; + targ_float f; + targ_double d; + targ_ldouble ld; + int sz; + int zero; + void *p; + static char zeros[sizeof(long double)]; + + if (im == 0) + { + switch (tybasic(e->Ety)) + { + case TYfloat: + case TYifloat: + case TYcfloat: + f = e->EV.Vfloat; + sz = 4; + p = &f; + break; + + case TYdouble: + case TYdouble_alias: + case TYidouble: + case TYcdouble: + d = e->EV.Vdouble; + sz = 8; + p = &d; + break; + + case TYldouble: + case TYildouble: + case TYcldouble: + ld = e->EV.Vldouble; + sz = 10; + p = &ld; + break; + + default: + assert(0); + } + } + else + { + switch (tybasic(e->Ety)) + { + case TYcfloat: + f = e->EV.Vcfloat.im; + sz = 4; + p = &f; + break; + + case TYcdouble: + d = e->EV.Vcdouble.im; + sz = 8; + p = &d; + break; + + case TYcldouble: + ld = e->EV.Vcldouble.im; + sz = 10; + p = &ld; + break; + + default: + assert(0); + } + } + + // Note that for this purpose, -0 is not regarded as +0, + // since FLDZ loads a +0 + zero = (memcmp(p, zeros, sz) == 0); + if (zero && config.target_cpu >= TARGET_PentiumPro) + return 0xEE; // FLDZ is the only one with 1 micro-op + + // For some reason, these instructions take more clocks + if (config.flags4 & CFG4speed && config.target_cpu >= TARGET_Pentium) + return 0; + + if (zero) + return 0xEE; + + for (i = 1; i < arraysize(fval); i++) + { + switch (sz) + { + case 4: + if (fval[i] != f) + continue; + break; + case 8: + if (dval[i] != d) + continue; + break; + case 10: + if (ldval[i] != ld) + continue; + break; + default: + assert(0); + } + break; + } + return opcode[i]; +} + +/****************************** + * Given the result of an expression is in retregs, + * generate necessary code to return result in *pretregs. + */ + + +code *fixresult87(elem *e,regm_t retregs,regm_t *pretregs) +{ + regm_t regm; + tym_t tym; + code *c1,*c2; + unsigned sz; + + //printf("fixresult87(e = %p, retregs = x%x, *pretregs = x%x)\n", e,retregs,*pretregs); + //printf("fixresult87(e = %p, retregs = %s, *pretregs = %s)\n", e,regm_str(retregs),regm_str(*pretregs)); + assert(!*pretregs || retregs); + c1 = CNIL; + c2 = CNIL; + tym = tybasic(e->Ety); + sz = tysize[tym]; + //printf("tym = x%x, sz = %d\n", tym, sz); + + if (*pretregs & mST01) + return fixresult_complex87(e, retregs, pretregs); + + /* if retregs needs to be transferred into the 8087 */ + if (*pretregs & mST0 && retregs & (mBP | ALLREGS)) + { + assert(sz <= DOUBLESIZE); + if (!I16) + { + + if (*pretregs & mPSW) + { // Set flags + regm_t r = retregs | mPSW; + c1 = fixresult(e,retregs,&r); + } + c2 = push87(); + if (sz == REGSIZE || (I64 && sz == 4)) + { + unsigned reg = findreg(retregs); + c2 = genfltreg(c2,0x89,reg,0); // MOV fltreg,reg + genfltreg(c2,0xD9,0,0); // FLD float ptr fltreg + } + else + { unsigned msreg,lsreg; + + msreg = findregmsw(retregs); + lsreg = findreglsw(retregs); + c2 = genfltreg(c2,0x89,lsreg,0); // MOV fltreg,lsreg + genfltreg(c2,0x89,msreg,4); // MOV fltreg+4,msreg + genfltreg(c2,0xDD,0,0); // FLD double ptr fltreg + } + } + else + { + regm = (sz == FLOATSIZE) ? FLOATREGS : DOUBLEREGS; + regm |= *pretregs & mPSW; + c1 = fixresult(e,retregs,®m); + regm = 0; // don't worry about result from CLIBxxx + c2 = callclib(e, + ((sz == FLOATSIZE) ? CLIBfltto87 : CLIBdblto87), + ®m,0); + } + } + else if (*pretregs & (mBP | ALLREGS) && retregs & mST0) + { unsigned mf; + unsigned reg; + + assert(sz <= DOUBLESIZE); + mf = (sz == FLOATSIZE) ? MFfloat : MFdouble; + if (*pretregs & mPSW && !(retregs & mPSW)) + c1 = genftst(c1,e,0); + /* FSTP floatreg */ + pop87(); + c1 = genfltreg(c1,ESC(mf,1),3,0); + genfwait(c1); + c2 = allocreg(pretregs,®,(sz == FLOATSIZE) ? TYfloat : TYdouble); + if (sz == FLOATSIZE) + { + if (!I16) + c2 = genfltreg(c2,0x8B,reg,0); + else + { c2 = genfltreg(c2,0x8B,reg,REGSIZE); + genfltreg(c2,0x8B,findreglsw(*pretregs),0); + } + } + else + { assert(sz == DOUBLESIZE); + if (I16) + { c2 = genfltreg(c2,0x8B,AX,6); + genfltreg(c2,0x8B,BX,4); + genfltreg(c2,0x8B,CX,2); + genfltreg(c2,0x8B,DX,0); + } + else if (I32) + { c2 = genfltreg(c2,0x8B,reg,REGSIZE); + genfltreg(c2,0x8B,findreglsw(*pretregs),0); + } + else // I64 + { + c2 = genfltreg(c2,0x8B,reg,0); + code_orrex(c2, REX_W); + } + } + } + else if (*pretregs == 0 && retregs == mST0) + { + c1 = genf2(c1,0xDD,modregrm(3,3,0)); // FPOP + pop87(); + } + else + { if (*pretregs & mPSW) + { if (!(retregs & mPSW)) + { assert(retregs & mST0); + c1 = genftst(c1,e,!(*pretregs & (mST0 | XMMREGS))); // FTST + } + } + if (*pretregs & mST0 && retregs & XMMREGS) + { + assert(sz <= DOUBLESIZE); + unsigned mf = (sz == FLOATSIZE) ? MFfloat : MFdouble; + // MOVD floatreg,XMM? + unsigned reg = findreg(retregs); + c1 = genfltreg(c1,xmmstore(tym),reg - XMM0,0); + c2 = push87(); + c2 = genfltreg(c2,ESC(mf,1),0,0); // FLD float/double ptr fltreg + } + else if (retregs & mST0 && *pretregs & XMMREGS) + { + assert(sz <= DOUBLESIZE); + unsigned mf = (sz == FLOATSIZE) ? MFfloat : MFdouble; + // FSTP floatreg + pop87(); + c1 = genfltreg(c1,ESC(mf,1),3,0); + genfwait(c1); + // MOVD XMM?,floatreg + unsigned reg; + c2 = allocreg(pretregs,®,(sz == FLOATSIZE) ? TYfloat : TYdouble); + c2 = genfltreg(c2,xmmload(tym),reg -XMM0,0); + } + else + assert(!(*pretregs & mST0) || (retregs & mST0)); + } + if (*pretregs & mST0) + note87(e,0,0); + return cat(c1,c2); +} + +/******************************** + * Generate in-line 8087 code for the following operators: + * add + * min + * mul + * div + * cmp + */ + +// Reverse the order that the op is done in +static const char oprev[9] = { -1,0,1,2,3,5,4,7,6 }; + +code *orth87(elem *e,regm_t *pretregs) +{ + unsigned op; + code *c1,*c2,*c3,*c4; + code *cx; + regm_t retregs; + regm_t resregm; + elem *e1; + elem *e2; + int e2oper; + int eoper; + unsigned sz2; + int clib = CLIBMAX; // initialize to invalid value + int reverse = 0; + + //printf("orth87(+e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); +#if 1 // we could be evaluating / for side effects only + assert(*pretregs != 0); +#endif + retregs = mST0; + resregm = mST0; + + e1 = e->E1; + e2 = e->E2; + c3 = CNIL; + c4 = CNIL; + sz2 = tysize(e1->Ety); + if (tycomplex(e1->Ety)) + sz2 /= 2; + + eoper = e->Eoper; + if (eoper == OPmul && e2->Eoper == OPconst && el_toldouble(e->E2) == 2.0L) + { + // Perform "mul 2.0" as fadd ST(0), ST + c1 = codelem(e1,&retregs,FALSE); + c1 = genf2(c1, 0xDC, 0xC0); // fadd ST(0), ST; + c2 = fixresult87(e,mST0,pretregs); // result is in ST(0). + freenode(e2); + return cat(c1,c2); + } + + if (OTrel(eoper)) + eoper = OPeqeq; + #define X(op, ty1, ty2) (((op) << 16) + (ty1) * 256 + (ty2)) + switch (X(eoper, tybasic(e1->Ety), tybasic(e2->Ety))) + { + case X(OPadd, TYfloat, TYfloat): + case X(OPadd, TYdouble, TYdouble): + case X(OPadd, TYdouble_alias, TYdouble_alias): + case X(OPadd, TYldouble, TYldouble): + case X(OPadd, TYldouble, TYdouble): + case X(OPadd, TYdouble, TYldouble): + case X(OPadd, TYifloat, TYifloat): + case X(OPadd, TYidouble, TYidouble): + case X(OPadd, TYildouble, TYildouble): + op = 0; // FADDP + break; + + case X(OPmin, TYfloat, TYfloat): + case X(OPmin, TYdouble, TYdouble): + case X(OPmin, TYdouble_alias, TYdouble_alias): + case X(OPmin, TYldouble, TYldouble): + case X(OPmin, TYldouble, TYdouble): + case X(OPmin, TYdouble, TYldouble): + case X(OPmin, TYifloat, TYifloat): + case X(OPmin, TYidouble, TYidouble): + case X(OPmin, TYildouble, TYildouble): + op = 4; // FSUBP + break; + + case X(OPmul, TYfloat, TYfloat): + case X(OPmul, TYdouble, TYdouble): + case X(OPmul, TYdouble_alias, TYdouble_alias): + case X(OPmul, TYldouble, TYldouble): + case X(OPmul, TYldouble, TYdouble): + case X(OPmul, TYdouble, TYldouble): + case X(OPmul, TYifloat, TYifloat): + case X(OPmul, TYidouble, TYidouble): + case X(OPmul, TYildouble, TYildouble): + case X(OPmul, TYfloat, TYifloat): + case X(OPmul, TYdouble, TYidouble): + case X(OPmul, TYldouble, TYildouble): + case X(OPmul, TYifloat, TYfloat): + case X(OPmul, TYidouble, TYdouble): + case X(OPmul, TYildouble, TYldouble): + op = 1; // FMULP + break; + + case X(OPdiv, TYfloat, TYfloat): + case X(OPdiv, TYdouble, TYdouble): + case X(OPdiv, TYdouble_alias, TYdouble_alias): + case X(OPdiv, TYldouble, TYldouble): + case X(OPdiv, TYldouble, TYdouble): + case X(OPdiv, TYdouble, TYldouble): + case X(OPdiv, TYifloat, TYifloat): + case X(OPdiv, TYidouble, TYidouble): + case X(OPdiv, TYildouble, TYildouble): + op = 6; // FDIVP + break; + + case X(OPmod, TYfloat, TYfloat): + case X(OPmod, TYdouble, TYdouble): + case X(OPmod, TYdouble_alias, TYdouble_alias): + case X(OPmod, TYldouble, TYldouble): + case X(OPmod, TYfloat, TYifloat): + case X(OPmod, TYdouble, TYidouble): + case X(OPmod, TYldouble, TYildouble): + case X(OPmod, TYifloat, TYifloat): + case X(OPmod, TYidouble, TYidouble): + case X(OPmod, TYildouble, TYildouble): + case X(OPmod, TYifloat, TYfloat): + case X(OPmod, TYidouble, TYdouble): + case X(OPmod, TYildouble, TYldouble): + op = (unsigned) -1; + break; + + case X(OPeqeq, TYfloat, TYfloat): + case X(OPeqeq, TYdouble, TYdouble): + case X(OPeqeq, TYdouble_alias, TYdouble_alias): + case X(OPeqeq, TYldouble, TYldouble): + case X(OPeqeq, TYifloat, TYifloat): + case X(OPeqeq, TYidouble, TYidouble): + case X(OPeqeq, TYildouble, TYildouble): + assert(OTrel(e->Eoper)); + assert((*pretregs & mST0) == 0); + c1 = codelem(e1,&retregs,FALSE); + note87(e1,0,0); + resregm = mPSW; + + if (rel_exception(e->Eoper) || config.flags4 & CFG4fastfloat) + { + if (cnst(e2) && !boolres(e2)) + { + if (NOSAHF) + { + c1 = cat(c1,push87()); + c1 = gen2(c1,0xD9,0xEE); // FLDZ + gen2(c1,0xDF,0xF1); // FCOMIP ST1 + pop87(); + } + else + { c1 = genf2(c1,0xD9,0xE4); // FTST + c1 = cg87_87topsw(c1); + } + c2 = genf2(NULL,0xDD,modregrm(3,3,0)); // FPOP + pop87(); + } + else if (NOSAHF) + { + note87(e1,0,0); + c2 = load87(e2,0,&retregs,e1,-1); + c2 = cat(c2,makesure87(e1,0,1,0)); + resregm = 0; + //c2 = genf2(c2,0xD9,0xC8 + 1); // FXCH ST1 + c2 = gen2(c2,0xDF,0xF1); // FCOMIP ST1 + pop87(); + genf2(c2,0xDD,modregrm(3,3,0)); // FPOP + pop87(); + } + else + { + c2 = load87(e2, 0, pretregs, e1, 3); // FCOMPP + } + } + else + { + if (cnst(e2) && !boolres(e2) && + config.target_cpu < TARGET_80386) + { + regm_t regm = 0; + + c2 = callclib(e,CLIBftest0,®m,0); + pop87(); + } + else + { + note87(e1,0,0); + c2 = load87(e2,0,&retregs,e1,-1); + c2 = cat(c2,makesure87(e1,0,1,0)); + resregm = 0; + if (NOSAHF) + { + c3 = gen2(CNIL,0xDF,0xE9); // FUCOMIP ST1 + pop87(); + genf2(c3,0xDD,modregrm(3,3,0)); // FPOP + pop87(); + } + else if (config.target_cpu >= TARGET_80386) + { + c3 = gen2(CNIL,0xDA,0xE9); // FUCOMPP + c3 = cg87_87topsw(c3); + pop87(); + pop87(); + } + else + // Call a function instead so that exceptions + // are not generated. + c3 = callclib(e,CLIBfcompp,&resregm,0); + } + } + + freenode(e2); + return cat4(c1,c2,c3,c4); + + case X(OPadd, TYcfloat, TYcfloat): + case X(OPadd, TYcdouble, TYcdouble): + case X(OPadd, TYcldouble, TYcldouble): + case X(OPadd, TYcfloat, TYfloat): + case X(OPadd, TYcdouble, TYdouble): + case X(OPadd, TYcldouble, TYldouble): + case X(OPadd, TYfloat, TYcfloat): + case X(OPadd, TYdouble, TYcdouble): + case X(OPadd, TYldouble, TYcldouble): + goto Lcomplex; + + case X(OPadd, TYifloat, TYcfloat): + case X(OPadd, TYidouble, TYcdouble): + case X(OPadd, TYildouble, TYcldouble): + goto Lcomplex2; + + case X(OPmin, TYcfloat, TYcfloat): + case X(OPmin, TYcdouble, TYcdouble): + case X(OPmin, TYcldouble, TYcldouble): + case X(OPmin, TYcfloat, TYfloat): + case X(OPmin, TYcdouble, TYdouble): + case X(OPmin, TYcldouble, TYldouble): + case X(OPmin, TYfloat, TYcfloat): + case X(OPmin, TYdouble, TYcdouble): + case X(OPmin, TYldouble, TYcldouble): + goto Lcomplex; + + case X(OPmin, TYifloat, TYcfloat): + case X(OPmin, TYidouble, TYcdouble): + case X(OPmin, TYildouble, TYcldouble): + goto Lcomplex2; + + case X(OPmul, TYcfloat, TYcfloat): + case X(OPmul, TYcdouble, TYcdouble): + case X(OPmul, TYcldouble, TYcldouble): + clib = CLIBcmul; + goto Lcomplex; + + case X(OPdiv, TYcfloat, TYcfloat): + case X(OPdiv, TYcdouble, TYcdouble): + case X(OPdiv, TYcldouble, TYcldouble): + case X(OPdiv, TYfloat, TYcfloat): + case X(OPdiv, TYdouble, TYcdouble): + case X(OPdiv, TYldouble, TYcldouble): + case X(OPdiv, TYifloat, TYcfloat): + case X(OPdiv, TYidouble, TYcdouble): + case X(OPdiv, TYildouble, TYcldouble): + clib = CLIBcdiv; + goto Lcomplex; + + case X(OPdiv, TYifloat, TYfloat): + case X(OPdiv, TYidouble, TYdouble): + case X(OPdiv, TYildouble, TYldouble): + op = 6; // FDIVP + break; + + Lcomplex: + c1 = loadComplex(e1); + c2 = loadComplex(e2); + c3 = makesure87(e1, sz2, 2, 0); + c3 = cat(c3,makesure87(e1, 0, 3, 0)); + retregs = mST01; + if (eoper == OPadd) + { + c4 = genf2(NULL, 0xDE, 0xC0+2); // FADDP ST(2),ST + genf2(c4, 0xDE, 0xC0+2); // FADDP ST(2),ST + pop87(); + pop87(); + } + else if (eoper == OPmin) + { + c4 = genf2(NULL, 0xDE, 0xE8+2); // FSUBP ST(2),ST + genf2(c4, 0xDE, 0xE8+2); // FSUBP ST(2),ST + pop87(); + pop87(); + } + else + c4 = callclib(e, clib, &retregs, 0); + c4 = cat(c4, fixresult_complex87(e, retregs, pretregs)); + return cat4(c1,c2,c3,c4); + + Lcomplex2: + retregs = mST0; + c1 = codelem(e1, &retregs, FALSE); + note87(e1, 0, 0); + c2 = loadComplex(e2); + c3 = makesure87(e1, 0, 2, 0); + retregs = mST01; + if (eoper == OPadd) + { + c4 = genf2(NULL, 0xDE, 0xC0+2); // FADDP ST(2),ST + } + else if (eoper == OPmin) + { + c4 = genf2(NULL, 0xDE, 0xE8+2); // FSUBP ST(2),ST + c4 = genf2(c4, 0xD9, 0xE0); // FCHS + } + else + assert(0); + pop87(); + c4 = genf2(c4, 0xD9, 0xC8 + 1); // FXCH ST(1) + c4 = cat(c4, fixresult_complex87(e, retregs, pretregs)); + return cat4(c1,c2,c3,c4); + + case X(OPeqeq, TYcfloat, TYcfloat): + case X(OPeqeq, TYcdouble, TYcdouble): + case X(OPeqeq, TYcldouble, TYcldouble): + case X(OPeqeq, TYcfloat, TYifloat): + case X(OPeqeq, TYcdouble, TYidouble): + case X(OPeqeq, TYcldouble, TYildouble): + case X(OPeqeq, TYcfloat, TYfloat): + case X(OPeqeq, TYcdouble, TYdouble): + case X(OPeqeq, TYcldouble, TYldouble): + case X(OPeqeq, TYifloat, TYcfloat): + case X(OPeqeq, TYidouble, TYcdouble): + case X(OPeqeq, TYildouble, TYcldouble): + case X(OPeqeq, TYfloat, TYcfloat): + case X(OPeqeq, TYdouble, TYcdouble): + case X(OPeqeq, TYldouble, TYcldouble): + case X(OPeqeq, TYfloat, TYifloat): + case X(OPeqeq, TYdouble, TYidouble): + case X(OPeqeq, TYldouble, TYildouble): + case X(OPeqeq, TYifloat, TYfloat): + case X(OPeqeq, TYidouble, TYdouble): + case X(OPeqeq, TYildouble, TYldouble): + c1 = loadComplex(e1); + c2 = loadComplex(e2); + c3 = makesure87(e1, sz2, 2, 0); + c3 = cat(c3,makesure87(e1, 0, 3, 0)); + retregs = 0; + c4 = callclib(e, CLIBccmp, &retregs, 0); + return cat4(c1,c2,c3,c4); + + + case X(OPadd, TYfloat, TYifloat): + case X(OPadd, TYdouble, TYidouble): + case X(OPadd, TYldouble, TYildouble): + case X(OPadd, TYifloat, TYfloat): + case X(OPadd, TYidouble, TYdouble): + case X(OPadd, TYildouble, TYldouble): + + case X(OPmin, TYfloat, TYifloat): + case X(OPmin, TYdouble, TYidouble): + case X(OPmin, TYldouble, TYildouble): + case X(OPmin, TYifloat, TYfloat): + case X(OPmin, TYidouble, TYdouble): + case X(OPmin, TYildouble, TYldouble): + retregs = mST0; + c1 = codelem(e1, &retregs, FALSE); + note87(e1, 0, 0); + c2 = codelem(e2, &retregs, FALSE); + c3 = makesure87(e1, 0, 1, 0); + if (eoper == OPmin) + c3 = genf2(c3, 0xD9, 0xE0); // FCHS + if (tyimaginary(e1->Ety)) + c3 = genf2(c3, 0xD9, 0xC8 + 1); // FXCH ST(1) + retregs = mST01; + c4 = fixresult_complex87(e, retregs, pretregs); + return cat4(c1,c2,c3,c4); + + case X(OPadd, TYcfloat, TYifloat): + case X(OPadd, TYcdouble, TYidouble): + case X(OPadd, TYcldouble, TYildouble): + op = 0; + goto Lci; + + case X(OPmin, TYcfloat, TYifloat): + case X(OPmin, TYcdouble, TYidouble): + case X(OPmin, TYcldouble, TYildouble): + op = 4; + goto Lci; + + Lci: + c1 = loadComplex(e1); + retregs = mST0; + c2 = load87(e2,sz2,&retregs,e1,op); + freenode(e2); + retregs = mST01; + c3 = makesure87(e1,0,1,0); + c4 = fixresult_complex87(e, retregs, pretregs); + return cat4(c1,c2,c3,c4); + + case X(OPmul, TYcfloat, TYfloat): + case X(OPmul, TYcdouble, TYdouble): + case X(OPmul, TYcldouble, TYldouble): + c1 = loadComplex(e1); + goto Lcm1; + + case X(OPmul, TYcfloat, TYifloat): + case X(OPmul, TYcdouble, TYidouble): + case X(OPmul, TYcldouble, TYildouble): + c1 = loadComplex(e1); + c1 = genf2(c1, 0xD9, 0xE0); // FCHS + genf2(c1,0xD9,0xC8 + 1); // FXCH ST(1) + if (elemisone(e2)) + { + freenode(e2); + c2 = NULL; + c3 = NULL; + goto Lcd4; + } + goto Lcm1; + + Lcm1: + retregs = mST0; + c2 = codelem(e2, &retregs, FALSE); + c3 = makesure87(e1, sz2, 1, 0); + c3 = cat(c3,makesure87(e1, 0, 2, 0)); + goto Lcm2; + + case X(OPmul, TYfloat, TYcfloat): + case X(OPmul, TYdouble, TYcdouble): + case X(OPmul, TYldouble, TYcldouble): + retregs = mST0; + c1 = codelem(e1, &retregs, FALSE); + note87(e1, 0, 0); + c2 = loadComplex(e2); + c3 = makesure87(e1, 0, 2, 0); + c3 = genf2(c3,0xD9,0xC8 + 1); // FXCH ST(1) + genf2(c3,0xD9,0xC8 + 2); // FXCH ST(2) + goto Lcm2; + + case X(OPmul, TYifloat, TYcfloat): + case X(OPmul, TYidouble, TYcdouble): + case X(OPmul, TYildouble, TYcldouble): + retregs = mST0; + c1 = codelem(e1, &retregs, FALSE); + note87(e1, 0, 0); + c2 = loadComplex(e2); + c3 = makesure87(e1, 0, 2, 0); + c3 = genf2(c3, 0xD9, 0xE0); // FCHS + genf2(c3,0xD9,0xC8 + 2); // FXCH ST(2) + goto Lcm2; + + Lcm2: + c3 = genf2(c3,0xDC,0xC8 + 2); // FMUL ST(2), ST + genf2(c3,0xDE,0xC8 + 1); // FMULP ST(1), ST + goto Lcd3; + + case X(OPdiv, TYcfloat, TYfloat): + case X(OPdiv, TYcdouble, TYdouble): + case X(OPdiv, TYcldouble, TYldouble): + c1 = loadComplex(e1); + retregs = mST0; + c2 = codelem(e2, &retregs, FALSE); + c3 = makesure87(e1, sz2, 1, 0); + c3 = cat(c3,makesure87(e1, 0, 2, 0)); + goto Lcd1; + + case X(OPdiv, TYcfloat, TYifloat): + case X(OPdiv, TYcdouble, TYidouble): + case X(OPdiv, TYcldouble, TYildouble): + c1 = loadComplex(e1); + c1 = genf2(c1,0xD9,0xC8 + 1); // FXCH ST(1) + xchg87(0, 1); + genf2(c1, 0xD9, 0xE0); // FCHS + retregs = mST0; + c2 = codelem(e2, &retregs, FALSE); + c3 = makesure87(e1, 0, 1, 0); + c3 = cat(c3,makesure87(e1, sz2, 2, 0)); + Lcd1: + c3 = genf2(c3,0xDC,0xF8 + 2); // FDIV ST(2), ST + genf2(c3,0xDE,0xF8 + 1); // FDIVP ST(1), ST + Lcd3: + pop87(); + Lcd4: + retregs = mST01; + c4 = fixresult_complex87(e, retregs, pretregs); + return cat4(c1, c2, c3, c4); + + case X(OPmod, TYcfloat, TYfloat): + case X(OPmod, TYcdouble, TYdouble): + case X(OPmod, TYcldouble, TYldouble): + case X(OPmod, TYcfloat, TYifloat): + case X(OPmod, TYcdouble, TYidouble): + case X(OPmod, TYcldouble, TYildouble): + /* + fld E1.re + fld E1.im + fld E2 + fxch ST(1) + FM1: fprem + fstsw word ptr sw + fwait + mov AH, byte ptr sw+1 + jp FM1 + fxch ST(2) + FM2: fprem + fstsw word ptr sw + fwait + mov AH, byte ptr sw+1 + jp FM2 + fstp ST(1) + fxch ST(1) + */ + c1 = loadComplex(e1); + retregs = mST0; + c2 = codelem(e2, &retregs, FALSE); + c3 = makesure87(e1, sz2, 1, 0); + c3 = cat(c3,makesure87(e1, 0, 2, 0)); + c3 = genf2(c3, 0xD9, 0xC8 + 1); // FXCH ST(1) + + cx = gen2(NULL, 0xD9, 0xF8); // FPREM + cx = cg87_87topsw(cx); + cx = genjmp(cx, JP, FLcode, (block *)cx); // JP FM1 + cx = genf2(cx, 0xD9, 0xC8 + 2); // FXCH ST(2) + c3 = cat(c3,cx); + + cx = gen2(NULL, 0xD9, 0xF8); // FPREM + cx = cg87_87topsw(cx); + cx = genjmp(cx, JP, FLcode, (block *)cx); // JP FM2 + cx = genf2(cx,0xDD,0xD8 + 1); // FSTP ST(1) + cx = genf2(cx, 0xD9, 0xC8 + 1); // FXCH ST(1) + c3 = cat(c3,cx); + + goto Lcd3; + + default: +#ifdef DEBUG + elem_print(e); +#endif + assert(0); + break; + } + #undef X + + e2oper = e2->Eoper; + + /* Move double-sized operand into the second position if there's a chance + * it will allow combining a load with an operation (DMD Bugzilla 2905) + */ + if ( ((tybasic(e1->Ety) == TYdouble) + && ((e1->Eoper == OPvar) || (e1->Eoper == OPconst)) + && (tybasic(e2->Ety) != TYdouble)) || + (e1->Eoper == OPconst) || + (e1->Eoper == OPvar && + ((e1->Ety & (mTYconst | mTYimmutable) && !OTleaf(e2oper)) || + (e2oper == OPd_f && + (e2->E1->Eoper == OPs32_d || e2->E1->Eoper == OPs64_d || e2->E1->Eoper == OPs16_d) && + e2->E1->E1->Eoper == OPvar + ) || + ((e2oper == OPs32_d || e2oper == OPs64_d || e2oper == OPs16_d) && + e2->E1->Eoper == OPvar + ) + ) + ) + ) + { // Reverse order of evaluation + e1 = e->E2; + e2 = e->E1; + op = oprev[op + 1]; + reverse ^= 1; + } + + c1 = codelem(e1,&retregs,FALSE); + note87(e1,0,0); + + if (config.flags4 & CFG4fdivcall && e->Eoper == OPdiv) + { + regm_t retregs = mST0; + c2 = load87(e2,0,&retregs,e1,-1); + c2 = cat(c2,makesure87(e1,0,1,0)); + if (op == 7) // if reverse divide + c2 = genf2(c2,0xD9,0xC8 + 1); // FXCH ST(1) + c2 = cat(c2,callclib(e,CLIBfdiv87,&retregs,0)); + pop87(); + resregm = mST0; + freenode(e2); + c4 = fixresult87(e,resregm,pretregs); + } + else if (e->Eoper == OPmod) + { + /* + * fld tbyte ptr y + * fld tbyte ptr x // ST = x, ST1 = y + * FM1: // We don't use fprem1 because for some inexplicable + * // reason we get -5 when we do _modulo(15, 10) + * fprem // ST = ST % ST1 + * fstsw word ptr sw + * fwait + * mov AH,byte ptr sw+1 // get msb of status word in AH + * sahf // transfer to flags + * jp FM1 // continue till ST < ST1 + * fstp ST(1) // leave remainder on stack + */ + regm_t retregs = mST0; + c2 = load87(e2,0,&retregs,e1,-1); + c2 = cat(c2,makesure87(e1,0,1,0)); // now have x,y on stack; need y,x + if (!reverse) // if not reverse modulo + c2 = genf2(c2,0xD9,0xC8 + 1); // FXCH ST(1) + + c3 = gen2(NULL, 0xD9, 0xF8); // FM1: FPREM + c3 = cg87_87topsw(c3); + c3 = genjmp(c3, JP, FLcode, (block *)c3); // JP FM1 + c3 = genf2(c3,0xDD,0xD8 + 1); // FSTP ST(1) + + pop87(); + resregm = mST0; + freenode(e2); + c4 = fixresult87(e,resregm,pretregs); + } + else + { c2 = load87(e2,0,pretregs,e1,op); + freenode(e2); + } + if (*pretregs & mST0) + note87(e,0,0); + //printf("orth87(-e = %p, *pretregs = x%x)\n", e, *pretregs); + return cat4(c1,c2,c3,c4); +} + +/***************************** + * Load e into ST01. + */ + +code *loadComplex(elem *e) +{ int sz; + regm_t retregs; + code *c; + + sz = tysize(e->Ety); + switch (tybasic(e->Ety)) + { + case TYfloat: + case TYdouble: + case TYldouble: + retregs = mST0; + c = codelem(e,&retregs,FALSE); + // Convert to complex with a 0 for the imaginary part + c = cat(c, push87()); + c = gen2(c,0xD9,0xEE); // FLDZ + break; + + case TYifloat: + case TYidouble: + case TYildouble: + // Convert to complex with a 0 for the real part + c = push87(); + c = gen2(c,0xD9,0xEE); // FLDZ + retregs = mST0; + c = cat(c, codelem(e,&retregs,FALSE)); + break; + + case TYcfloat: + case TYcdouble: + case TYcldouble: + sz /= 2; + retregs = mST01; + c = codelem(e,&retregs,FALSE); + break; + + default: + assert(0); + } + note87(e, 0, 1); + note87(e, sz, 0); + return c; +} + +/************************* + * If op == -1, load expression e into ST0. + * else compute (eleft op e), eleft is in ST0. + * Must follow same logic as cmporder87(); + */ + +code *load87(elem *e,unsigned eoffset,regm_t *pretregs,elem *eleft,int op) +{ + code *ccomma,*c,*c2,*cpush; + code cs; + regm_t retregs; + unsigned reg,mf,mf1; + int opr; + unsigned char ldop; + tym_t ty; + int i; + +#if NDPP + printf("+load87(e=%p, eoffset=%d, *pretregs=%s, eleft=%p, op=%d, stackused = %d)\n",e,eoffset,regm_str(*pretregs),eleft,op,stackused); +#endif + elem_debug(e); + ccomma = NULL; + cpush = NULL; + if (ADDFWAIT()) + cs.Iflags = CFwait; + else + cs.Iflags = 0; + cs.Irex = 0; + opr = oprev[op + 1]; + ty = tybasic(e->Ety); + if ((ty == TYldouble || ty == TYildouble) && + op != -1 && e->Eoper != OPd_ld) + goto Ldefault; + mf = (ty == TYfloat || ty == TYifloat || ty == TYcfloat) ? MFfloat : MFdouble; + L5: + switch (e->Eoper) + { + case OPcomma: + ccomma = docommas(&e); +// if (op != -1) +// ccomma = cat(ccomma,makesure87(eleft,eoffset,0,0)); + goto L5; + + case OPvar: + notreg(e); + case OPind: + L2: + if (op != -1) + { + if (e->Ecount && e->Ecount != e->Ecomsub && + (i = cse_get(e, 0)) >= 0) + { static unsigned char b2[8] = {0xC0,0xC8,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8}; + + c = genf2(NULL,0xD8,b2[op] + i); // Fop ST(i) + } + else + { + c = getlvalue(&cs,e,0); + if (I64) + cs.Irex &= ~REX_W; // don't use for x87 ops + c = cat(c,makesure87(eleft,eoffset,0,0)); + cs.Iop = ESC(mf,0); + cs.Irm |= modregrm(0,op,0); + c = gen(c,&cs); + } + } + else + { + cpush = push87(); + switch (ty) + { + case TYfloat: + case TYdouble: + case TYifloat: + case TYidouble: + case TYcfloat: + case TYcdouble: + case TYdouble_alias: + c = loadea(e,&cs,ESC(mf,1),0,0,0,0); // FLD var + break; + case TYldouble: + case TYildouble: + case TYcldouble: + c = loadea(e,&cs,0xDB,5,0,0,0); // FLD var + break; + default: + // __debug printf("ty = x%x\n", ty); + assert(0); + break; + } + note87(e,0,0); + } + break; + case OPd_f: + case OPf_d: + case OPd_ld: + mf1 = (tybasic(e->E1->Ety) == TYfloat || tybasic(e->E1->Ety) == TYifloat) + ? MFfloat : MFdouble; + if (op != -1 && stackused) + note87(eleft,eoffset,0); // don't trash this value + if (e->E1->Eoper == OPvar || e->E1->Eoper == OPind) + { +#if 1 + L4: + c = getlvalue(&cs,e->E1,0); + cs.Iop = ESC(mf1,0); + if (ADDFWAIT()) + cs.Iflags |= CFwait; + if (!I16) + cs.Iflags &= ~CFopsize; + if (op != -1) + { cs.Irm |= modregrm(0,op,0); + c = cat(c,makesure87(eleft,eoffset,0,0)); + } + else + { cs.Iop |= 1; + c = cat(c,push87()); + } + c = gen(c,&cs); /* FLD / Fop */ +#else + c = loadea(e->E1,&cs,ESC(mf1,1),0,0,0,0); /* FLD e->E1 */ +#endif + /* Variable cannot be put into a register anymore */ + if (e->E1->Eoper == OPvar) + notreg(e->E1); + freenode(e->E1); + } + else + { + retregs = mST0; + c = codelem(e->E1,&retregs,FALSE); + if (op != -1) + { c = cat(c,makesure87(eleft,eoffset,1,0)); + c = genf2(c,0xDE,modregrm(3,opr,1)); // FopRP + pop87(); + } + } + break; + + case OPs64_d: + if (e->E1->Eoper == OPvar || + (e->E1->Eoper == OPind && e->E1->Ecount == 0)) + { + c = getlvalue(&cs,e->E1,0); + cs.Iop = 0xDF; + if (ADDFWAIT()) + cs.Iflags |= CFwait; + if (!I16) + cs.Iflags &= ~CFopsize; + c = cat(c,push87()); + cs.Irm |= modregrm(0,5,0); + c = gen(c,&cs); // FILD m64 + // Variable cannot be put into a register anymore + if (e->E1->Eoper == OPvar) + notreg(e->E1); + freenode(e->E1); + } + else if (I64) + { + retregs = ALLREGS; + c = codelem(e->E1,&retregs,FALSE); + reg = findreg(retregs); + c = genfltreg(c,0x89,reg,0); // MOV floatreg,reg + code_orrex(c, REX_W); + c = cat(c,push87()); + c = genfltreg(c,0xDF,5,0); // FILD long long ptr floatreg + } + else + { + retregs = ALLREGS; + c = codelem(e->E1,&retregs,FALSE); + reg = findreglsw(retregs); + c = genfltreg(c,0x89,reg,0); // MOV floatreg,reglsw + reg = findregmsw(retregs); + c = genfltreg(c,0x89,reg,4); // MOV floatreg+4,regmsw + c = cat(c,push87()); + c = genfltreg(c,0xDF,5,0); // FILD long long ptr floatreg + } + if (op != -1) + { c = cat(c,makesure87(eleft,eoffset,1,0)); + c = genf2(c,0xDE,modregrm(3,opr,1)); // FopRP + pop87(); + } + break; + + case OPconst: + ldop = loadconst(e, 0); + if (ldop) + { + cpush = push87(); + c = genf2(NULL,0xD9,ldop); // FLDx + if (op != -1) + { genf2(c,0xDE,modregrm(3,opr,1)); // FopRP + pop87(); + } + } + else + { + assert(0); + } + break; + + case OPu16_d: + { + /* This opcode should never be generated */ + /* (probably shouldn't be for 16 bit code too) */ + assert(!I32); + + if (op != -1) + note87(eleft,eoffset,0); // don't trash this value + retregs = ALLREGS & mLSW; + c = codelem(e->E1,&retregs,FALSE); + c = regwithvalue(c,ALLREGS & mMSW,0,®,0); // 0-extend + retregs |= mask[reg]; + mf1 = MFlong; + goto L3; + } + case OPs16_d: mf1 = MFword; goto L6; + case OPs32_d: mf1 = MFlong; goto L6; + L6: + if (op != -1) + note87(eleft,eoffset,0); // don't trash this value + if (e->E1->Eoper == OPvar || + (e->E1->Eoper == OPind && e->E1->Ecount == 0)) + { + goto L4; + } + else + { + retregs = ALLREGS; + c = codelem(e->E1,&retregs,FALSE); + L3: + if (I16 && e->Eoper != OPs16_d) + { + /* MOV floatreg+2,reg */ + reg = findregmsw(retregs); + c = genfltreg(c,0x89,reg,REGSIZE); + retregs &= mLSW; + } + reg = findreg(retregs); + c = genfltreg(c,0x89,reg,0); /* MOV floatreg,reg */ + if (op != -1) + { c = cat(c,makesure87(eleft,eoffset,0,0)); + genfltreg(c,ESC(mf1,0),op,0); /* Fop floatreg */ + } + else + { + /* FLD long ptr floatreg */ + c = cat(c,push87()); + c = genfltreg(c,ESC(mf1,1),0,0); + } + } + break; + default: + Ldefault: + retregs = mST0; +#if 1 /* Do this instead of codelem() to avoid the freenode(e). + We also lose CSE capability */ + if (e->Eoper == OPconst) + { + c = load87(e, 0, &retregs, NULL, -1); + } + else + c = (*cdxxx[e->Eoper])(e,&retregs); +#else + c = codelem(e,&retregs,FALSE); +#endif + if (op != -1) + { + c = cat(c,makesure87(eleft,eoffset,1,(op == 0 || op == 1))); + pop87(); + if (op == 4 || op == 6) // sub or div + { code *cl; + + cl = code_last(c); + if (cl && cl->Iop == 0xD9 && cl->Irm == 0xC9) // FXCH ST(1) + { cl->Iop = NOP; + opr = op; // reverse operands + } + } + c = genf2(c,0xDE,modregrm(3,opr,1)); // FopRP + } + break; + } + if (op == 3) // FCOMP + { pop87(); // extra pop was done + cg87_87topsw(c); + } + c2 = fixresult87(e,((op == 3) ? mPSW : mST0),pretregs); +#if NDPP + printf("-load87(e=%p, eoffset=%d, *pretregs=%s, eleft=%p, op=%d, stackused = %d)\n",e,eoffset,regm_str(*pretregs),eleft,op,stackused); +#endif + return cat4(ccomma,cpush,c,c2); +} + +/******************************** + * Determine if a compare is to be done forwards (return 0) + * or backwards (return 1). + * Must follow same logic as load87(). + */ + +int cmporder87(elem *e) +{ + //printf("cmporder87(%p)\n",e); +L1: + switch (e->Eoper) + { + case OPcomma: + e = e->E2; + goto L1; + + case OPd_f: + case OPf_d: + case OPd_ld: + if (e->E1->Eoper == OPvar || e->E1->Eoper == OPind) + goto ret0; + else + goto ret1; + + case OPconst: + if (loadconst(e, 0) || tybasic(e->Ety) == TYldouble + || tybasic(e->Ety) == TYildouble) +{ +//printf("ret 1, loadconst(e) = %d\n", loadconst(e)); + goto ret1; +} + goto ret0; + + case OPvar: + case OPind: + if (tybasic(e->Ety) == TYldouble || + tybasic(e->Ety) == TYildouble) + goto ret1; + case OPu16_d: + case OPs16_d: + case OPs32_d: + goto ret0; + + case OPs64_d: + goto ret1; + + default: + goto ret1; + } + +ret1: return 1; +ret0: return 0; +} + +/******************************* + * Perform an assignment to a long double/double/float. + */ + +code *eq87(elem *e,regm_t *pretregs) +{ + regm_t retregs; + code *c1,*c2; + code cs; + unsigned op1; + unsigned op2; + tym_t ty1; + + //printf("+eq87(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); + assert(e->Eoper == OPeq); + retregs = mST0 | (*pretregs & mPSW); + c1 = codelem(e->E2,&retregs,FALSE); + ty1 = tybasic(e->E1->Ety); + switch (ty1) + { case TYdouble_alias: + case TYidouble: + case TYdouble: op1 = ESC(MFdouble,1); op2 = 3; break; + case TYifloat: + case TYfloat: op1 = ESC(MFfloat,1); op2 = 3; break; + case TYildouble: + case TYldouble: op1 = 0xDB; op2 = 7; break; + default: + assert(0); + } + if (*pretregs & (mST0 | ALLREGS | mBP | XMMREGS)) // if want result on stack too + { + if (ty1 == TYldouble || ty1 == TYildouble) + { + c1 = cat(c1,push87()); + c1 = genf2(c1,0xD9,0xC0); // FLD ST(0) + pop87(); + } + else + op2 = 2; // FST e->E1 + } + else + { // FSTP e->E1 + pop87(); + } +#if 0 + // Doesn't work if ST(0) gets saved to the stack by getlvalue() + c2 = loadea(e->E1,&cs,op1,op2,0,0,0); +#else + cs.Irex = 0; + cs.Iflags = 0; + cs.Iop = op1; + if (*pretregs & (mST0 | ALLREGS | mBP | XMMREGS)) // if want result on stack too + { // Make sure it's still there + elem *e2 = e->E2; + while (e2->Eoper == OPcomma) + e2 = e2->E2; + note87(e2,0,0); + c2 = getlvalue(&cs, e->E1, 0); + c2 = cat(c2,makesure87(e2,0,0,1)); + } + else + { + c2 = getlvalue(&cs, e->E1, 0); + } + cs.Irm |= modregrm(0,op2,0); // OR in reg field + if (I32) + cs.Iflags &= ~CFopsize; + else if (ADDFWAIT()) + cs.Iflags |= CFwait; + else if (I64) + cs.Irex &= ~REX_W; + c2 = gen(c2, &cs); +#if LNGDBLSIZE == 12 + if (tysize[TYldouble] == 12) + { + /* This deals with the fact that 10 byte reals really + * occupy 12 bytes by zeroing the extra 2 bytes. + */ + if (op1 == 0xDB) + { + cs.Iop = 0xC7; // MOV EA+10,0 + NEWREG(cs.Irm, 0); + cs.IEV1.sp.Voffset += 10; + cs.IFL2 = FLconst; + cs.IEV2.Vint = 0; + cs.Iflags |= CFopsize; + c2 = gen(c2, &cs); + } + } +#endif + if (tysize[TYldouble] == 16) + { + /* This deals with the fact that 10 byte reals really + * occupy 16 bytes by zeroing the extra 6 bytes. + */ + if (op1 == 0xDB) + { + cs.Irex &= ~REX_W; + cs.Iop = 0xC7; // MOV EA+10,0 + NEWREG(cs.Irm, 0); + cs.IEV1.sp.Voffset += 10; + cs.IFL2 = FLconst; + cs.IEV2.Vint = 0; + cs.Iflags |= CFopsize; + c2 = gen(c2, &cs); + + cs.IEV1.sp.Voffset += 2; + cs.Iflags &= ~CFopsize; + c2 = gen(c2, &cs); + } + } +#endif + c2 = genfwait(c2); + freenode(e->E1); + c1 = cat3(c1,c2,fixresult87(e,mST0 | mPSW,pretregs)); + return c1; +} + +/******************************* + * Perform an assignment to a long double/double/float. + */ + +code *complex_eq87(elem *e,regm_t *pretregs) +{ + regm_t retregs; + code *c1,*c2; + code cs; + unsigned op1; + unsigned op2; + unsigned sz; + tym_t ty1; + int fxch = 0; + + //printf("complex_eq87(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); + assert(e->Eoper == OPeq); + cs.Iflags = ADDFWAIT() ? CFwait : 0; + cs.Irex = 0; + retregs = mST01 | (*pretregs & mPSW); + c1 = codelem(e->E2,&retregs,FALSE); + ty1 = tybasic(e->E1->Ety); + switch (ty1) + { + case TYcdouble: op1 = ESC(MFdouble,1); op2 = 3; break; + case TYcfloat: op1 = ESC(MFfloat,1); op2 = 3; break; + case TYcldouble: op1 = 0xDB; op2 = 7; break; + default: + assert(0); + } + if (*pretregs & (mST01 | mXMM0 | mXMM1)) // if want result on stack too + { + if (ty1 == TYcldouble) + { + c1 = cat(c1,push87()); + c1 = cat(c1,push87()); + c1 = genf2(c1,0xD9,0xC0 + 1); // FLD ST(1) + genf2(c1,0xD9,0xC0 + 1); // FLD ST(1) + pop87(); + pop87(); + } + else + { op2 = 2; // FST e->E1 + fxch = 1; + } + } + else + { // FSTP e->E1 + pop87(); + pop87(); + } + sz = tysize(ty1) / 2; + if (*pretregs & (mST01 | mXMM0 | mXMM1)) + { + cs.Iflags = 0; + cs.Irex = 0; + cs.Iop = op1; + c2 = getlvalue(&cs, e->E1, 0); + cs.IEVoffset1 += sz; + cs.Irm |= modregrm(0, op2, 0); + c2 = cat(c2, makesure87(e->E2, sz, 0, 0)); + c2 = gen(c2, &cs); + c2 = genfwait(c2); + c2 = cat(c2, makesure87(e->E2, 0, 1, 0)); + } + else + { + c2 = loadea(e->E1,&cs,op1,op2,sz,0,0); + c2 = genfwait(c2); + } + if (fxch) + c2 = genf2(c2,0xD9,0xC8 + 1); // FXCH ST(1) + cs.IEVoffset1 -= sz; + gen(c2, &cs); + if (fxch) + genf2(c2,0xD9,0xC8 + 1); // FXCH ST(1) + if (tysize[TYldouble] == 12) + { + if (op1 == 0xDB) + { + cs.Iop = 0xC7; // MOV EA+10,0 + NEWREG(cs.Irm, 0); + cs.IEV1.sp.Voffset += 10; + cs.IFL2 = FLconst; + cs.IEV2.Vint = 0; + cs.Iflags |= CFopsize; + c2 = gen(c2, &cs); + cs.IEVoffset1 += 12; + c2 = gen(c2, &cs); // MOV EA+22,0 + } + } + if (tysize[TYldouble] == 16) + { + if (op1 == 0xDB) + { + cs.Iop = 0xC7; // MOV EA+10,0 + NEWREG(cs.Irm, 0); + cs.IEV1.sp.Voffset += 10; + cs.IFL2 = FLconst; + cs.IEV2.Vint = 0; + cs.Iflags |= CFopsize; + c2 = gen(c2, &cs); + + cs.IEV1.sp.Voffset += 2; + cs.Iflags &= ~CFopsize; + c2 = gen(c2, &cs); + + cs.IEV1.sp.Voffset += 14; + cs.Iflags |= CFopsize; + c2 = gen(c2, &cs); + + cs.IEV1.sp.Voffset += 2; + cs.Iflags &= ~CFopsize; + c2 = gen(c2, &cs); + } + } + c2 = genfwait(c2); + freenode(e->E1); + return cat3(c1,c2,fixresult_complex87(e,mST01 | mPSW,pretregs)); +} + +/******************************* + * Perform an assignment while converting to integral type, + * i.e. handle (e1 = (int) e2) + */ + +code *cnvteq87(elem *e,regm_t *pretregs) +{ + regm_t retregs; + code *c1,*c2; + code cs; + unsigned op1; + unsigned op2; + + assert(e->Eoper == OPeq); + assert(!*pretregs); + retregs = mST0; + elem_debug(e->E2); + c1 = codelem(e->E2->E1,&retregs,FALSE); + + switch (e->E2->Eoper) + { case OPd_s16: + op1 = ESC(MFword,1); + op2 = 3; + break; + case OPd_s32: + case OPd_u16: + op1 = ESC(MFlong,1); + op2 = 3; + break; + case OPd_s64: + op1 = 0xDF; + op2 = 7; + break; + default: + assert(0); + } + freenode(e->E2); + + c1 = genfwait(c1); + c1 = genrnd(c1, CW_roundto0); // FLDCW roundto0 + + pop87(); + cs.Iflags = ADDFWAIT() ? CFwait : 0; + if (e->E1->Eoper == OPvar) + notreg(e->E1); // cannot be put in register anymore + c2 = loadea(e->E1,&cs,op1,op2,0,0,0); + + c2 = genfwait(c2); + c2 = genrnd(c2, CW_roundtonearest); // FLDCW roundtonearest + + freenode(e->E1); + return cat(c1,c2); +} + +/********************************** + * Perform +=, -=, *= and /= for doubles. + */ + +code *opass87(elem *e,regm_t *pretregs) +{ + regm_t retregs; + code *cl,*cr,*c; + code cs; + unsigned op; + unsigned opld; + unsigned op1; + unsigned op2; + tym_t ty1; + + ty1 = tybasic(e->E1->Ety); + switch (ty1) + { case TYdouble_alias: + case TYidouble: + case TYdouble: op1 = ESC(MFdouble,1); op2 = 3; break; + case TYifloat: + case TYfloat: op1 = ESC(MFfloat,1); op2 = 3; break; + case TYildouble: + case TYldouble: op1 = 0xDB; op2 = 7; break; + + case TYcfloat: + case TYcdouble: + case TYcldouble: + return (e->Eoper == OPmodass) + ? opmod_complex87(e, pretregs) + : opass_complex87(e, pretregs); + + default: + assert(0); + } + switch (e->Eoper) + { case OPpostinc: + case OPaddass: op = 0 << 3; opld = 0xC1; break; // FADD + case OPpostdec: + case OPminass: op = 5 << 3; opld = 0xE1; /*0xE9;*/ break; // FSUBR + case OPmulass: op = 1 << 3; opld = 0xC9; break; // FMUL + case OPdivass: op = 7 << 3; opld = 0xF1; break; // FDIVR + case OPmodass: break; + default: assert(0); + } + retregs = mST0; + cr = codelem(e->E2,&retregs,FALSE); // evaluate rvalue + note87(e->E2,0,0); + cl = getlvalue(&cs,e->E1,0); + cl = cat(cl,makesure87(e->E2,0,0,0)); + cs.Iflags |= ADDFWAIT() ? CFwait : 0; + if (I32) + cs.Iflags &= ~CFopsize; + if (config.flags4 & CFG4fdivcall && e->Eoper == OPdivass) + { + c = push87(); + cs.Iop = op1; + if (ty1 == TYldouble || ty1 == TYildouble) + cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... + c = gen(c,&cs); + c = genf2(c,0xD9,0xC8 + 1); // FXCH ST(1) + c = cat(c,callclib(e,CLIBfdiv87,&retregs,0)); + pop87(); + } + else if (e->Eoper == OPmodass) + { + /* + * fld tbyte ptr y + * fld tbyte ptr x // ST = x, ST1 = y + * FM1: // We don't use fprem1 because for some inexplicable + * // reason we get -5 when we do _modulo(15, 10) + * fprem // ST = ST % ST1 + * fstsw word ptr sw + * fwait + * mov AH,byte ptr sw+1 // get msb of status word in AH + * sahf // transfer to flags + * jp FM1 // continue till ST < ST1 + * fstp ST(1) // leave remainder on stack + */ + code *c1; + + c = push87(); + cs.Iop = op1; + if (ty1 == TYldouble || ty1 == TYildouble) + cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... + c = gen(c,&cs); // FLD e->E1 + + c1 = gen2(NULL, 0xD9, 0xF8); // FPREM + c1 = cg87_87topsw(c1); + c1 = genjmp(c1, JP, FLcode, (block *)c1); // JP FM1 + c1 = genf2(c1,0xDD,0xD8 + 1); // FSTP ST(1) + c = cat(c,c1); + + pop87(); + } + else if (ty1 == TYldouble || ty1 == TYildouble) + { + c = push87(); + cs.Iop = op1; + cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... + c = gen(c,&cs); // FLD e->E1 + genf2(c,0xDE,opld); // FopP ST(1) + pop87(); + } + else + { cs.Iop = op1 & ~1; + cs.Irm |= op; + c = gen(CNIL,&cs); // Fop e->E1 + } + if (*pretregs & mPSW) + genftst(c,e,0); // FTST ST0 + /* if want result in registers */ + if (*pretregs & (mST0 | ALLREGS | mBP)) + { + if (ty1 == TYldouble || ty1 == TYildouble) + { + c = cat(c,push87()); + c = genf2(c,0xD9,0xC0); // FLD ST(0) + pop87(); + } + else + op2 = 2; // FST e->E1 + } + else + { // FSTP + pop87(); + } + cs.Iop = op1; + NEWREG(cs.Irm,op2); // FSTx e->E1 + freenode(e->E1); + gen(c,&cs); + genfwait(c); + return cat4(cr,cl,c,fixresult87(e,mST0 | mPSW,pretregs)); +} + +/*********************************** + * Perform %= where E1 is complex and E2 is real or imaginary. + */ + +code *opmod_complex87(elem *e,regm_t *pretregs) +{ + regm_t retregs; + code *cl,*cr,*c; + code cs; + tym_t ty1; + unsigned sz2; + + /* fld E2 + fld E1.re + FM1: fprem + fstsw word ptr sw + fwait + mov AH, byte ptr sw+1 + jp FM1 + fxch ST(1) + fld E1.im + FM2: fprem + fstsw word ptr sw + fwait + mov AH, byte ptr sw+1 + jp FM2 + fstp ST(1) + */ + + ty1 = tybasic(e->E1->Ety); + sz2 = tysize[ty1] / 2; + + retregs = mST0; + cr = codelem(e->E2,&retregs,FALSE); // FLD E2 + note87(e->E2,0,0); + cl = getlvalue(&cs,e->E1,0); + cl = cat(cl,makesure87(e->E2,0,0,0)); + cs.Iflags |= ADDFWAIT() ? CFwait : 0; + if (!I16) + cs.Iflags &= ~CFopsize; + + c = push87(); + switch (ty1) + { + case TYcdouble: cs.Iop = ESC(MFdouble,1); break; + case TYcfloat: cs.Iop = ESC(MFfloat,1); break; + case TYcldouble: cs.Iop = 0xDB; cs.Irm |= modregrm(0, 5, 0); break; + default: + assert(0); + } + c = gen(c,&cs); // FLD E1.re + + code *c1; + + c1 = gen2(NULL, 0xD9, 0xF8); // FPREM + c1 = cg87_87topsw(c1); + c1 = genjmp(c1, JP, FLcode, (block *)c1); // JP FM1 + c1 = genf2(c1, 0xD9, 0xC8 + 1); // FXCH ST(1) + c = cat(c,c1); + + c = cat(c, push87()); + cs.IEVoffset1 += sz2; + gen(c, &cs); // FLD E1.im + + c1 = gen2(NULL, 0xD9, 0xF8); // FPREM + c1 = cg87_87topsw(c1); + c1 = genjmp(c1, JP, FLcode, (block *)c1); // JP FM2 + c1 = genf2(c1,0xDD,0xD8 + 1); // FSTP ST(1) + c = cat(c,c1); + + pop87(); + + if (*pretregs & (mST01 | mPSW)) + { + cs.Irm |= modregrm(0, 2, 0); + gen(c, &cs); // FST mreal.im + cs.IEVoffset1 -= sz2; + gen(c, &cs); // FST mreal.re + retregs = mST01; + } + else + { + cs.Irm |= modregrm(0, 3, 0); + gen(c, &cs); // FSTP mreal.im + cs.IEVoffset1 -= sz2; + gen(c, &cs); // FSTP mreal.re + pop87(); + pop87(); + retregs = 0; + } + freenode(e->E1); + genfwait(c); + return cat4(cr,cl,c,fixresult_complex87(e,retregs,pretregs)); +} + +/********************************** + * Perform +=, -=, *= and /= for the lvalue being complex. + */ + +code *opass_complex87(elem *e,regm_t *pretregs) +{ + regm_t retregs; + regm_t idxregs; + code *cl,*cr,*c; + code cs; + unsigned op; + unsigned op2; + tym_t ty1; + unsigned sz2; + + ty1 = tybasic(e->E1->Ety); + sz2 = tysize[ty1] / 2; + switch (e->Eoper) + { case OPpostinc: + case OPaddass: op = 0 << 3; // FADD + op2 = 0xC0; // FADDP ST(i),ST + break; + case OPpostdec: + case OPminass: op = 5 << 3; // FSUBR + op2 = 0xE0; // FSUBRP ST(i),ST + break; + case OPmulass: op = 1 << 3; // FMUL + op2 = 0xC8; // FMULP ST(i),ST + break; + case OPdivass: op = 7 << 3; // FDIVR + op2 = 0xF0; // FDIVRP ST(i),ST + break; + default: assert(0); + } + + if (!tycomplex(e->E2->Ety) && + (e->Eoper == OPmulass || e->Eoper == OPdivass)) + { + retregs = mST0; + cr = codelem(e->E2, &retregs, FALSE); + note87(e->E2, 0, 0); + cl = getlvalue(&cs, e->E1, 0); + cl = cat(cl,makesure87(e->E2,0,0,0)); + cl = cat(cl,push87()); + cl = genf2(cl,0xD9,0xC0); // FLD ST(0) + goto L1; + } + else + { + cr = loadComplex(e->E2); + cl = getlvalue(&cs,e->E1,0); + cl = cat(cl,makesure87(e->E2,sz2,0,0)); + cl = cat(cl,makesure87(e->E2,0,1,0)); + } + cs.Iflags |= ADDFWAIT() ? CFwait : 0; + if (!I16) + cs.Iflags &= ~CFopsize; + + switch (e->Eoper) + { + case OPpostinc: + case OPaddass: + case OPpostdec: + case OPminass: + L1: + if (ty1 == TYcldouble) + { + c = push87(); + c = cat(c, push87()); + cs.Iop = 0xDB; + cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... + c = gen(c,&cs); // FLD e->E1.re + cs.IEVoffset1 += sz2; + gen(c,&cs); // FLD e->E1.im + genf2(c, 0xDE, op2 + 2); // FADDP/FSUBRP ST(2),ST + genf2(c, 0xDE, op2 + 2); // FADDP/FSUBRP ST(2),ST + pop87(); + pop87(); + if (tyimaginary(e->E2->Ety)) + { + if (e->Eoper == OPmulass) + { + genf2(c, 0xD9, 0xE0); // FCHS + genf2(c, 0xD9, 0xC8+1); // FXCH ST(1) + } + else if (e->Eoper == OPdivass) + { + genf2(c, 0xD9, 0xC8+1); // FXCH ST(1) + genf2(c, 0xD9, 0xE0); // FCHS + } + } + L2: + if (*pretregs & (mST01 | mPSW)) + { + c = cat(c,push87()); + c = cat(c,push87()); + c = genf2(c,0xD9,0xC1); // FLD ST(1) + c = genf2(c,0xD9,0xC1); // FLD ST(1) + retregs = mST01; + } + else + retregs = 0; + cs.Iop = 0xDB; + cs.Irm |= modregrm(0,7,0); + gen(c,&cs); // FSTP e->E1.im + cs.IEVoffset1 -= sz2; + gen(c,&cs); // FSTP e->E1.re + pop87(); + pop87(); + + } + else + { unsigned char rmop = cs.Irm | op; + unsigned char rmfst = cs.Irm | modregrm(0,2,0); + unsigned char rmfstp = cs.Irm | modregrm(0,3,0); + unsigned char iopfst = (ty1 == TYcfloat) ? 0xD9 : 0xDD; + unsigned char iop = (ty1 == TYcfloat) ? 0xD8 : 0xDC; + + cs.Iop = iop; + cs.Irm = rmop; + cs.IEVoffset1 += sz2; + c = gen(NULL, &cs); // FSUBR mreal.im + if (tyimaginary(e->E2->Ety) && (e->Eoper == OPmulass || e->Eoper == OPdivass)) + { + if (e->Eoper == OPmulass) + genf2(c, 0xD9, 0xE0); // FCHS + genf2(c,0xD9,0xC8 + 1); // FXCH ST(1) + cs.IEVoffset1 -= sz2; + gen(c, &cs); // FMUL mreal.re + if (e->Eoper == OPdivass) + genf2(c, 0xD9, 0xE0); // FCHS + if (*pretregs & (mST01 | mPSW)) + { + cs.Iop = iopfst; + cs.Irm = rmfst; + cs.IEVoffset1 += sz2; + gen(c, &cs); // FST mreal.im + genf2(c,0xD9,0xC8 + 1); // FXCH ST(1) + cs.IEVoffset1 -= sz2; + gen(c, &cs); // FST mreal.re + genf2(c,0xD9,0xC8 + 1); // FXCH ST(1) + retregs = mST01; + } + else + { + cs.Iop = iopfst; + cs.Irm = rmfstp; + cs.IEVoffset1 += sz2; + gen(c, &cs); // FSTP mreal.im + pop87(); + cs.IEVoffset1 -= sz2; + gen(c, &cs); // FSTP mreal.re + pop87(); + retregs = 0; + } + goto L3; + } + + if (*pretregs & (mST01 | mPSW)) + { + cs.Iop = iopfst; + cs.Irm = rmfst; + gen(c, &cs); // FST mreal.im + genf2(c,0xD9,0xC8 + 1); // FXCH ST(1) + cs.Iop = iop; + cs.Irm = rmop; + cs.IEVoffset1 -= sz2; + gen(c, &cs); // FSUBR mreal.re + cs.Iop = iopfst; + cs.Irm = rmfst; + gen(c, &cs); // FST mreal.re + genf2(c,0xD9,0xC8 + 1); // FXCH ST(1) + retregs = mST01; + } + else + { + cs.Iop = iopfst; + cs.Irm = rmfstp; + gen(c, &cs); // FSTP mreal.im + pop87(); + cs.Iop = iop; + cs.Irm = rmop; + cs.IEVoffset1 -= sz2; + gen(c, &cs); // FSUBR mreal.re + cs.Iop = iopfst; + cs.Irm = rmfstp; + gen(c, &cs); // FSTP mreal.re + pop87(); + retregs = 0; + } + } + L3: + freenode(e->E1); + genfwait(c); + return cat4(cr,cl,c,fixresult_complex87(e,retregs,pretregs)); + + case OPmulass: + c = push87(); + c = cat(c, push87()); + if (ty1 == TYcldouble) + { + cs.Iop = 0xDB; + cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... + c = gen(c,&cs); // FLD e->E1.re + cs.IEVoffset1 += sz2; + gen(c,&cs); // FLD e->E1.im + retregs = mST01; + c = cat(c,callclib(e, CLIBcmul, &retregs, 0)); + goto L2; + } + else + { + cs.Iop = (ty1 == TYcfloat) ? 0xD9 : 0xDD; + cs.Irm |= modregrm(0, 0, 0); // FLD tbyte ptr ... + c = gen(c,&cs); // FLD e->E1.re + cs.IEVoffset1 += sz2; + gen(c,&cs); // FLD e->E1.im + retregs = mST01; + c = cat(c,callclib(e, CLIBcmul, &retregs, 0)); + if (*pretregs & (mST01 | mPSW)) + { + cs.Irm |= modregrm(0, 2, 0); + gen(c, &cs); // FST mreal.im + cs.IEVoffset1 -= sz2; + gen(c, &cs); // FST mreal.re + retregs = mST01; + } + else + { + cs.Irm |= modregrm(0, 3, 0); + gen(c, &cs); // FSTP mreal.im + cs.IEVoffset1 -= sz2; + gen(c, &cs); // FSTP mreal.re + pop87(); + pop87(); + retregs = 0; + } + goto L3; + } + + case OPdivass: + c = push87(); + c = cat(c, push87()); + idxregs = idxregm(&cs); // mask of index regs used + if (ty1 == TYcldouble) + { + cs.Iop = 0xDB; + cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... + c = gen(c,&cs); // FLD e->E1.re + genf2(c,0xD9,0xC8 + 2); // FXCH ST(2) + cs.IEVoffset1 += sz2; + gen(c,&cs); // FLD e->E1.im + genf2(c,0xD9,0xC8 + 2); // FXCH ST(2) + retregs = mST01; + c = cat(c,callclib(e, CLIBcdiv, &retregs, idxregs)); + goto L2; + } + else + { + cs.Iop = (ty1 == TYcfloat) ? 0xD9 : 0xDD; + cs.Irm |= modregrm(0, 0, 0); // FLD tbyte ptr ... + c = gen(c,&cs); // FLD e->E1.re + genf2(c,0xD9,0xC8 + 2); // FXCH ST(2) + cs.IEVoffset1 += sz2; + gen(c,&cs); // FLD e->E1.im + genf2(c,0xD9,0xC8 + 2); // FXCH ST(2) + retregs = mST01; + c = cat(c,callclib(e, CLIBcdiv, &retregs, idxregs)); + if (*pretregs & (mST01 | mPSW)) + { + cs.Irm |= modregrm(0, 2, 0); + gen(c, &cs); // FST mreal.im + cs.IEVoffset1 -= sz2; + gen(c, &cs); // FST mreal.re + retregs = mST01; + } + else + { + cs.Irm |= modregrm(0, 3, 0); + gen(c, &cs); // FSTP mreal.im + cs.IEVoffset1 -= sz2; + gen(c, &cs); // FSTP mreal.re + pop87(); + pop87(); + retregs = 0; + } + goto L3; + } + + default: + assert(0); + } + return NULL; +} + +/************************** + * OPnegass + */ + +code *cdnegass87(elem *e,regm_t *pretregs) +{ regm_t retregs; + tym_t tyml; + unsigned op; + code *cl,*cr,*c,cs; + elem *e1; + int sz; + + //printf("cdnegass87(e = %p, *pretregs = x%x)\n", e, *pretregs); + e1 = e->E1; + tyml = tybasic(e1->Ety); // type of lvalue + sz = tysize[tyml]; + + cl = getlvalue(&cs,e1,0); + cr = modEA(&cs); + cs.Irm |= modregrm(0,6,0); + cs.Iop = 0x80; + cs.Irex = 0; +#if LNGDBLSIZE > 10 + if (tyml == TYldouble || tyml == TYildouble) + cs.IEVoffset1 += 10 - 1; + else if (tyml == TYcldouble) + cs.IEVoffset1 += tysize[TYldouble] + 10 - 1; + else +#endif + cs.IEVoffset1 += sz - 1; + cs.IFL2 = FLconst; + cs.IEV2.Vuns = 0x80; + c = gen(NULL,&cs); // XOR 7[EA],0x80 + if (tycomplex(tyml)) + { + cs.IEVoffset1 -= sz / 2; + gen(c,&cs); // XOR 7[EA],0x80 + } + c = cat3(cl,cr,c); + + if (*pretregs) + { + switch (tyml) + { + case TYifloat: + case TYfloat: cs.Iop = 0xD9; op = 0; break; + case TYidouble: + case TYdouble: + case TYdouble_alias: cs.Iop = 0xDD; op = 0; break; + case TYildouble: + case TYldouble: cs.Iop = 0xDB; op = 5; break; + default: + assert(0); + } + NEWREG(cs.Irm,op); + cs.IEVoffset1 -= sz - 1; + c = cat(c, push87()); + c = gen(c,&cs); // FLD EA + retregs = mST0; + } + else + retregs = 0; + + freenode(e1); + return cat(c,fixresult87(e,retregs,pretregs)); +} + +/************************ + * Take care of OPpostinc and OPpostdec. + */ + +code *post87(elem *e,regm_t *pretregs) +{ + regm_t retregs; + code *cl,*cr,*c; + code cs; + unsigned op; + unsigned op1; + unsigned reg; + tym_t ty1; + + //printf("post87()\n"); + assert(*pretregs); + cl = getlvalue(&cs,e->E1,0); + cs.Iflags |= ADDFWAIT() ? CFwait : 0; + if (!I16) + cs.Iflags &= ~CFopsize; + ty1 = tybasic(e->E1->Ety); + switch (ty1) + { case TYdouble_alias: + case TYidouble: + case TYdouble: + case TYcdouble: op1 = ESC(MFdouble,1); reg = 0; break; + case TYifloat: + case TYfloat: + case TYcfloat: op1 = ESC(MFfloat,1); reg = 0; break; + case TYildouble: + case TYldouble: + case TYcldouble: op1 = 0xDB; reg = 5; break; + default: + assert(0); + } + NEWREG(cs.Irm, reg); + if (reg == 5) + reg = 7; + else + reg = 3; + cs.Iop = op1; + cl = cat(cl,push87()); + cl = gen(cl,&cs); // FLD e->E1 + if (tycomplex(ty1)) + { unsigned sz = tysize[ty1] / 2; + + cl = cat(cl,push87()); + cs.IEVoffset1 += sz; + cl = gen(cl,&cs); // FLD e->E1 + retregs = mST0; // note kludge to only load real part + cr = codelem(e->E2,&retregs,FALSE); // load rvalue + c = genf2(NULL,0xD8, // FADD/FSUBR ST,ST2 + (e->Eoper == OPpostinc) ? 0xC0 + 2 : 0xE8 + 2); + NEWREG(cs.Irm,reg); + pop87(); + cs.IEVoffset1 -= sz; + gen(c,&cs); // FSTP e->E1 + genfwait(c); + freenode(e->E1); + return cat4(cl, cr, c, fixresult_complex87(e, mST01, pretregs)); + } + + if (*pretregs & (mST0 | ALLREGS | mBP)) + { // Want the result in a register + cl = cat(cl,push87()); + genf2(cl,0xD9,0xC0); // FLD ST0 + } + if (*pretregs & mPSW) /* if result in flags */ + genftst(cl,e,0); // FTST ST0 + retregs = mST0; + cr = codelem(e->E2,&retregs,FALSE); /* load rvalue */ + pop87(); + op = (e->Eoper == OPpostinc) ? modregrm(3,0,1) : modregrm(3,5,1); + c = genf2(NULL,0xDE,op); // FADDP/FSUBRP ST1 + NEWREG(cs.Irm,reg); + pop87(); + gen(c,&cs); /* FSTP e->E1 */ + genfwait(c); + freenode(e->E1); + return cat4(cl,cr,c,fixresult87(e,mPSW | mST0,pretregs)); +} + +/************************ + * Do the following opcodes: + * OPd_s16 + * OPd_s32 + * OPd_u16 + * OPd_s64 + */ + +code *cnvt87(elem *e,regm_t *pretregs) +{ + regm_t retregs; + code *c1,*c2; + unsigned mf,rf,reg; + tym_t tym; + int clib; + int sz; + int szoff; + + //printf("cnvt87(e = %p, *pretregs = x%x)\n", e, *pretregs); + assert(*pretregs); + tym = e->Ety; + sz = tysize(tym); + szoff = sz; + unsigned grex = I64 ? REX_W << 16 : 0; + + switch (e->Eoper) + { case OPd_s16: + clib = CLIBdblint87; + mf = ESC(MFword,1); + rf = 3; + break; + + case OPd_u16: + szoff = 4; + case OPd_s32: + clib = CLIBdbllng87; + mf = ESC(MFlong,1); + rf = 3; + break; + + case OPd_s64: + clib = CLIBdblllng; + mf = 0xDF; + rf = 7; + break; + + default: + assert(0); + } + + if (I16) // C may change the default control word + { + if (clib == CLIBdblllng) + { retregs = I32 ? DOUBLEREGS_32 : DOUBLEREGS_16; + c1 = codelem(e->E1,&retregs,FALSE); + c2 = callclib(e,clib,pretregs,0); + } + else + { retregs = mST0; //I32 ? DOUBLEREGS_32 : DOUBLEREGS_16; + c1 = codelem(e->E1,&retregs,FALSE); + c2 = callclib(e,clib,pretregs,0); + pop87(); + } + } + else if (1) + { // Generate: + // sub ESP,12 + // fstcw 8[ESP] + // fldcw roundto0 + // fistp long64 ptr [ESP] + // fldcw 8[ESP] + // pop lsw + // pop msw + // add ESP,4 + + unsigned szpush = szoff + 2; + if (config.flags3 & CFG3pic) + szpush += 2; + szpush = (szpush + REGSIZE - 1) & ~(REGSIZE - 1); + + retregs = mST0; + c1 = codelem(e->E1,&retregs,FALSE); + + if (szpush == REGSIZE) + c1 = gen1(c1,0x50 + AX); // PUSH EAX + else + c1 = genc2(c1,0x81,grex | modregrm(3,5,SP), szpush); // SUB ESP,12 + c1 = genfwait(c1); + genc1(c1,0xD9,modregrm(2,7,4) + 256*modregrm(0,4,SP),FLconst,szoff); // FSTCW szoff[ESP] + + c1 = genfwait(c1); + + if (config.flags3 & CFG3pic) + { + genc(c1,0xC7,modregrm(2,0,4) + 256*modregrm(0,4,SP),FLconst,szoff+2,FLconst,CW_roundto0); // MOV szoff+2[ESP], CW_roundto0 + code_orflag(c1, CFopsize); + genc1(c1,0xD9,modregrm(2,5,4) + 256*modregrm(0,4,SP),FLconst,szoff+2); // FLDCW szoff+2[ESP] + } + else + c1 = genrnd(c1, CW_roundto0); // FLDCW roundto0 + + pop87(); + + c1 = genfwait(c1); + gen2sib(c1,mf,grex | modregrm(0,rf,4),modregrm(0,4,SP)); // FISTP [ESP] + + retregs = *pretregs & (ALLREGS | mBP); + if (!retregs) + retregs = ALLREGS; + c2 = allocreg(&retregs,®,tym); + + c2 = genfwait(c2); // FWAIT + c2 = genc1(c2,0xD9,grex | modregrm(2,5,4) + 256*modregrm(0,4,SP),FLconst,szoff); // FLDCW szoff[ESP] + + if (szoff > REGSIZE) + { szpush -= REGSIZE; + c2 = genpop(c2,findreglsw(retregs)); // POP lsw + } + szpush -= REGSIZE; + c2 = genpop(c2,reg); // POP reg + + if (szpush) + genc2(c2,0x81,grex | modregrm(3,0,SP), szpush); // ADD ESP,4 + c2 = cat(c2,fixresult(e,retregs,pretregs)); + } + else + { + // This is incorrect. For -inf and nan, the 8087 returns the largest + // negative int (0x80000....). For -inf, 0x7FFFF... should be returned, + // and for nan, 0 should be returned. + retregs = mST0; + c1 = codelem(e->E1,&retregs,FALSE); + + c1 = genfwait(c1); + c1 = genrnd(c1, CW_roundto0); // FLDCW roundto0 + + pop87(); + c1 = genfltreg(c1,mf,rf,0); // FISTP floatreg + retregs = *pretregs & (ALLREGS | mBP); + if (!retregs) + retregs = ALLREGS; + c2 = allocreg(&retregs,®,tym); + + c2 = genfwait(c2); + + if (sz > REGSIZE) + { c2 = genfltreg(c2,0x8B,reg,REGSIZE); // MOV reg,floatreg + REGSIZE + // MOV lsreg,floatreg + genfltreg(c2,0x8B,findreglsw(retregs),0); + } + else + c2 = genfltreg(c2,0x8B,reg,0); // MOV reg,floatreg + c2 = genrnd(c2, CW_roundtonearest); // FLDCW roundtonearest + c2 = cat(c2,fixresult(e,retregs,pretregs)); + } + return cat(c1,c2); +} + +/************************ + * Do OPrndtol. + */ + +code *cdrndtol(elem *e,regm_t *pretregs) +{ + regm_t retregs; + code *c1,*c2; + unsigned reg; + tym_t tym; + unsigned sz; + unsigned char op1,op2; + + if (*pretregs == 0) + return codelem(e->E1,pretregs,FALSE); + tym = e->Ety; + retregs = mST0; + c1 = codelem(e->E1,&retregs,FALSE); + + sz = tysize(tym); + switch (sz) + { case 2: + op1 = 0xDF; + op2 = 3; + break; + case 4: + op1 = 0xDB; + op2 = 3; + break; + case 8: + op1 = 0xDF; + op2 = 7; + break; + default: + assert(0); + } + + pop87(); + c1 = genfltreg(c1,op1,op2,0); // FISTP floatreg + retregs = *pretregs & (ALLREGS | mBP); + if (!retregs) + retregs = ALLREGS; + c2 = allocreg(&retregs,®,tym); + c2 = genfwait(c2); // FWAIT + if (tysize(tym) > REGSIZE) + { c2 = genfltreg(c2,0x8B,reg,REGSIZE); // MOV reg,floatreg + REGSIZE + // MOV lsreg,floatreg + genfltreg(c2,0x8B,findreglsw(retregs),0); + } + else + { + c2 = genfltreg(c2,0x8B,reg,0); // MOV reg,floatreg + if (tysize(tym) == 8 && I64) + code_orrex(c2, REX_W); + } + c2 = cat(c2,fixresult(e,retregs,pretregs)); + + return cat(c1,c2); +} + +/************************* + * Do OPscale, OPyl2x, OPyl2xp1. + */ + +code *cdscale(elem *e,regm_t *pretregs) +{ + regm_t retregs; + code *c1,*c2,*c3; + + assert(*pretregs != 0); + + retregs = mST0; + c1 = codelem(e->E1,&retregs,FALSE); + note87(e->E1,0,0); + c2 = codelem(e->E2,&retregs,FALSE); + c2 = cat(c2,makesure87(e->E1,0,1,0)); // now have x,y on stack; need y,x + switch (e->Eoper) + { + case OPscale: + c2 = genf2(c2,0xD9,0xFD); // FSCALE + genf2(c2,0xDD,0xD8 + 1); // FSTP ST(1) + break; + + case OPyl2x: + c2 = genf2(c2,0xD9,0xF1); // FYL2X + break; + + case OPyl2xp1: + c2 = genf2(c2,0xD9,0xF9); // FYL2XP1 + break; + } + pop87(); + c3 = fixresult87(e,mST0,pretregs); + return cat3(c1,c2,c3); +} + + +/********************************** + * Unary -, absolute value, square root, sine, cosine + */ + +code *neg87(elem *e,regm_t *pretregs) +{ + regm_t retregs; + code *c1,*c2; + int op; + + assert(*pretregs); + switch (e->Eoper) + { case OPneg: op = 0xE0; break; + case OPabs: op = 0xE1; break; + case OPsqrt: op = 0xFA; break; + case OPsin: op = 0xFE; break; + case OPcos: op = 0xFF; break; + case OPrint: op = 0xFC; break; // FRNDINT + default: + assert(0); + } + retregs = mST0; + c1 = codelem(e->E1,&retregs,FALSE); + c1 = genf2(c1,0xD9,op); // FCHS/FABS/FSQRT/FSIN/FCOS/FRNDINT + c2 = fixresult87(e,mST0,pretregs); + return cat(c1,c2); +} + +/********************************** + * Unary - for complex operands + */ + +code *neg_complex87(elem *e,regm_t *pretregs) +{ + regm_t retregs; + code *c1,*c2; + + assert(e->Eoper == OPneg); + retregs = mST01; + c1 = codelem(e->E1,&retregs,FALSE); + c1 = genf2(c1,0xD9,0xE0); // FCHS + genf2(c1,0xD9,0xC8 + 1); // FXCH ST(1) + genf2(c1,0xD9,0xE0); // FCHS + genf2(c1,0xD9,0xC8 + 1); // FXCH ST(1) + c2 = fixresult_complex87(e,mST01,pretregs); + return cat(c1,c2); +} + +/********************************* + */ + +code *cdind87(elem *e,regm_t *pretregs) +{ code *c,*ce,cs; + + //printf("cdind87(e = %p, *pretregs = x%x)\n",e,*pretregs); + + c = getlvalue(&cs,e,0); // get addressing mode + if (*pretregs) + { + switch (tybasic(e->Ety)) + { case TYfloat: + case TYifloat: + cs.Iop = 0xD9; + break; + + case TYidouble: + case TYdouble: + case TYdouble_alias: + cs.Iop = 0xDD; + break; + + case TYildouble: + case TYldouble: + cs.Iop = 0xDB; + cs.Irm |= modregrm(0,5,0); + break; + + default: + assert(0); + } + c = cat(c,push87()); + c = gen(c,&cs); // FLD EA + ce = fixresult87(e,mST0,pretregs); + c = cat(c,ce); + } + return c; +} + +/************************************ + * Reset statics for another .obj file. + */ + +void cg87_reset() +{ + memset(&oldd,0,sizeof(oldd)); +} + + +/***************************************** + * Initialize control word constants. + */ + +STATIC code *genrnd(code *c, short cw) +{ + if (config.flags3 & CFG3pic) + { code *c1; + + c1 = genfltreg(NULL, 0xC7, 0, 0); // MOV floatreg, cw + c1->IFL2 = FLconst; + c1->IEV2.Vuns = cw; + + c1 = genfltreg(c1, 0xD9, 5, 0); // FLDCW floatreg + c = cat(c, c1); + } + else + { + if (!oldd.round) // if not initialized + { short cwi; + + oldd.round = 1; + + cwi = CW_roundto0; // round to 0 + oldd.roundto0 = out_readonly_sym(TYshort,&cwi,2); + cwi = CW_roundtonearest; // round to nearest + oldd.roundtonearest = out_readonly_sym(TYshort,&cwi,2); + } + symbol *rnddir = (cw == CW_roundto0) ? oldd.roundto0 : oldd.roundtonearest; + code cs; + cs.Iop = 0xD9; + cs.Iflags = CFoff; + cs.Irex = 0; + cs.IEVsym1 = rnddir; + cs.IFL1 = rnddir->Sfl; + cs.IEVoffset1 = 0; + cs.Irm = modregrm(0,5,BPRM); + c = gen(c,&cs); + } + return c; +} + +/************************* Complex Numbers *********************/ + +/*************************** + * Set the PSW based on the state of ST01. + * Input: + * pop if stack should be popped after test + * Returns: + * start of code appended to c. + */ + +STATIC code * genctst(code *c,elem *e,int pop) +#if __DMC__ +__in +{ + assert(pop == 0 || pop == 1); +} +__body +#endif +{ + // Generate: + // if (pop) + // FLDZ + // FUCOMPP + // FSTSW AX + // SAHF + // FLDZ + // FUCOMPP + // JNE L1 + // JP L1 // if NAN + // FSTSW AX + // SAHF + // L1: + // else + // FLDZ + // FUCOM + // FSTSW AX + // SAHF + // FUCOMP ST(2) + // JNE L1 + // JP L1 // if NAN + // FSTSW AX + // SAHF + // L1: + // FUCOMP doesn't raise exceptions on QNANs, unlike FTST + + code *cnop; + + cnop = gennop(CNIL); + c = cat(c,push87()); + c = gen2(c,0xD9,0xEE); // FLDZ + if (pop) + { + gen2(c,0xDA,0xE9); // FUCOMPP + pop87(); + pop87(); + cg87_87topsw(c); // put 8087 flags in CPU flags + gen2(c,0xD9,0xEE); // FLDZ + gen2(c,0xDA,0xE9); // FUCOMPP + pop87(); + genjmp(c,JNE,FLcode,(block *) cnop); // JNE L1 + genjmp(c,JP, FLcode,(block *) cnop); // JP L1 + cg87_87topsw(c); // put 8087 flags in CPU flags + } + else + { + gen2(c,0xDD,0xE1); // FUCOM + cg87_87topsw(c); // put 8087 flags in CPU flags + gen2(c,0xDD,0xEA); // FUCOMP ST(2) + pop87(); + genjmp(c,JNE,FLcode,(block *) cnop); // JNE L1 + genjmp(c,JP, FLcode,(block *) cnop); // JP L1 + cg87_87topsw(c); // put 8087 flags in CPU flags + } + return cat(c, cnop); +} + +/****************************** + * Given the result of an expression is in retregs, + * generate necessary code to return result in *pretregs. + */ + + +code *fixresult_complex87(elem *e,regm_t retregs,regm_t *pretregs) +{ + tym_t tym; + code *c1,*c2; + unsigned sz; + +#if 0 + printf("fixresult_complex87(e = %p, retregs = %s, *pretregs = %s)\n", + e,regm_str(retregs),regm_str(*pretregs)); +#endif + assert(!*pretregs || retregs); + c1 = CNIL; + c2 = CNIL; + tym = tybasic(e->Ety); + sz = tysize[tym]; + + if (*pretregs == 0 && retregs == mST01) + { + c1 = genf2(c1,0xDD,modregrm(3,3,0)); // FPOP + pop87(); + c1 = genf2(c1,0xDD,modregrm(3,3,0)); // FPOP + pop87(); + } + else if (tym == TYcfloat && *pretregs & (mAX|mDX) && retregs & mST01) + { + if (*pretregs & mPSW && !(retregs & mPSW)) + c1 = genctst(c1,e,0); // FTST + pop87(); + c1 = genfltreg(c1, ESC(MFfloat,1),3,0); // FSTP floatreg + genfwait(c1); + c2 = getregs(mDX|mAX); + c2 = genfltreg(c2, 0x8B, DX, 0); // MOV EDX,floatreg + + pop87(); + c2 = genfltreg(c2, ESC(MFfloat,1),3,0); // FSTP floatreg + genfwait(c2); + c2 = genfltreg(c2, 0x8B, AX, 0); // MOV EAX,floatreg + } + else if (tym == TYcfloat && retregs & (mAX|mDX) && *pretregs & mST01) + { + c1 = push87(); + c1 = genfltreg(c1, 0x89, AX, 0); // MOV floatreg, EAX + genfltreg(c1, 0xD9, 0, 0); // FLD float ptr floatreg + + c2 = push87(); + c2 = genfltreg(c2, 0x89, DX, 0); // MOV floatreg, EDX + genfltreg(c2, 0xD9, 0, 0); // FLD float ptr floatreg + + if (*pretregs & mPSW) + c2 = genctst(c2,e,0); // FTST + } + else if ((tym == TYcfloat || tym == TYcdouble) && + *pretregs & (mXMM0|mXMM1) && retregs & mST01) + { + if (*pretregs & mPSW && !(retregs & mPSW)) + c1 = genctst(c1,e,0); // FTST + pop87(); + c1 = genfltreg(c1, ESC(MFdouble,1),3,0); // FSTP floatreg + genfwait(c1); + c2 = getregs(mXMM0|mXMM1); + c2 = genfltreg(c2, 0xF20F10, XMM1 - XMM0, 0); // MOVD XMM1,floatreg + + pop87(); + c2 = genfltreg(c2, ESC(MFdouble,1),3,0); // FSTP floatreg + genfwait(c2); + c2 = genfltreg(c2, 0xF20F10, XMM0 - XMM0, 0); // MOVD XMM0,floatreg + } + else if ((tym == TYcfloat || tym == TYcdouble) && + retregs & (mXMM0|mXMM1) && *pretregs & mST01) + { + c1 = push87(); + c1 = genfltreg(c1, 0xF20F11, XMM0-XMM0, 0); // MOVD floatreg, XMM0 + genfltreg(c1, 0xDD, 0, 0); // FLD double ptr floatreg + + c2 = push87(); + c2 = genfltreg(c2, 0xF20F11, XMM1-XMM0, 0); // MOV floatreg, XMM1 + genfltreg(c2, 0xDD, 0, 0); // FLD double ptr floatreg + + if (*pretregs & mPSW) + c2 = genctst(c2,e,0); // FTST + } + else + { if (*pretregs & mPSW) + { if (!(retregs & mPSW)) + { assert(retregs & mST01); + c1 = genctst(c1,e,!(*pretregs & mST01)); // FTST + } + } + assert(!(*pretregs & mST01) || (retregs & mST01)); + } + if (*pretregs & mST01) + { note87(e,0,1); + note87(e,sz/2,0); + } + return cat(c1,c2); +} + +/***************************************** + * Operators OPc_r and OPc_i + */ + +code *cdconvt87(elem *e, regm_t *pretregs) +{ + regm_t retregs; + code *c; + + retregs = mST01; + c = codelem(e->E1, &retregs, FALSE); + switch (e->Eoper) + { + case OPc_r: + c = genf2(c,0xDD,0xD8 + 0); // FPOP + pop87(); + break; + + case OPc_i: + c = genf2(c,0xDD,0xD8 + 1); // FSTP ST(1) + pop87(); + break; + + default: + assert(0); + } + retregs = mST0; + c = cat(c, fixresult87(e, retregs, pretregs)); + return c; +} + +/************************************** + * Load complex operand into ST01 or flags or both. + */ + +code *cload87(elem *e, regm_t *pretregs) +#if __DMC__ +__in +{ + assert(I32 && config.inline8087); + elem_debug(e); + assert(*pretregs & (mST01 | mPSW)); + assert(!(*pretregs & ~(mST01 | mPSW))); +} +__out (result) +{ +} +__body +#endif +{ + tym_t ty = tybasic(e->Ety); + code *c = NULL; + code *cpush = NULL; + code cs; + unsigned mf; + unsigned sz; + unsigned char ldop; + regm_t retregs; + int i; + + //printf("cload87(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); + sz = tysize[ty] / 2; + memset(&cs, 0, sizeof(cs)); + if (ADDFWAIT()) + cs.Iflags = CFwait; + switch (ty) + { + case TYcfloat: mf = MFfloat; break; + case TYcdouble: mf = MFdouble; break; + case TYcldouble: break; + default: assert(0); + } + switch (e->Eoper) + { + case OPvar: + notreg(e); // never enregister this variable + case OPind: + cpush = cat(push87(), push87()); + switch (ty) + { + case TYcfloat: + case TYcdouble: + c = loadea(e,&cs,ESC(mf,1),0,0,0,0); // FLD var + cs.IEVoffset1 += sz; + c = gen(c, &cs); + break; + + case TYcldouble: + c = loadea(e,&cs,0xDB,5,0,0,0); // FLD var + cs.IEVoffset1 += sz; + c = gen(c, &cs); + break; + + default: + assert(0); + } + retregs = mST01; + break; + + case OPd_ld: + case OPld_d: + case OPf_d: + case OPd_f: + c = cload87(e->E1, pretregs); + freenode(e->E1); + return c; + + case OPconst: + cpush = cat(push87(), push87()); + for (i = 0; i < 2; i++) + { + ldop = loadconst(e, i); + if (ldop) + { + c = genf2(c,0xD9,ldop); // FLDx + } + else + { + assert(0); + } + } + retregs = mST01; + break; + + default: +#ifdef DEBUG + elem_print(e); +#endif + assert(0); + } + return cat4(cpush,c,fixresult_complex87(e, retregs, pretregs), NULL); +} + +#endif // !SPP diff --git a/backend/cgcod.c b/backend/cgcod.c new file mode 100644 index 00000000..395b4fa2 --- /dev/null +++ b/backend/cgcod.c @@ -0,0 +1,2527 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt. + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include + +#if __sun&&__SVR4 +#include +#endif + +#include "cc.h" +#include "el.h" +#include "oper.h" +#include "code.h" +#include "global.h" +#include "type.h" +#include "exh.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +STATIC void resetEcomsub(elem *e); +STATIC code * loadcse(elem *,unsigned,regm_t); +STATIC void blcodgen(block *); +STATIC void cgcod_eh(); +STATIC code * cse_save(regm_t ms); +STATIC int cse_simple(elem *e,int i); +STATIC code * comsub(elem *,regm_t *); + +bool floatreg; // !=0 if floating register is required + +targ_size_t Aoffset; // offset of automatics and registers +targ_size_t Toffset; // offset of temporaries +targ_size_t EEoffset; // offset of SCstack variables from ESP +int Aalign; // alignment for Aoffset + +REGSAVE regsave; + +CGstate cgstate; // state of code generator + +/************************************ + * # of bytes that SP is beyond BP. + */ + +unsigned stackpush; + +int stackchanged; /* set to !=0 if any use of the stack + other than accessing parameters. Used + to see if we can address parameters + with ESP rather than EBP. + */ +int refparam; // !=0 if we referenced any parameters +int reflocal; // !=0 if we referenced any locals +char anyiasm; // !=0 if any inline assembler +char calledafunc; // !=0 if we called a function +char needframe; // if TRUE, then we will need the frame + // pointer (BP for the 8088) +char usedalloca; // if TRUE, then alloca() was called +char gotref; // !=0 if the GOTsym was referenced +unsigned usednteh; // if !=0, then used NT exception handling + +/* Register contents */ +con_t regcon; + +int pass; // PASSxxxx + +static symbol *retsym; // set to symbol that should be placed in + // register AX + +/**************************** + * Register masks. + */ + +regm_t msavereg; // Mask of registers that we would like to save. + // they are temporaries (set by scodelem()) +regm_t mfuncreg; // Mask of registers preserved by a function +regm_t allregs; // ALLREGS optionally including mBP + +int dfoidx; /* which block we are in */ +struct CSE *csextab = NULL; /* CSE table (allocated for each function) */ +unsigned cstop; /* # of entries in CSE table (csextab[]) */ +unsigned csmax; /* amount of space in csextab[] */ + +targ_size_t funcoffset; // offset of start of function +targ_size_t startoffset; // size of function entry code +targ_size_t retoffset; /* offset from start of func to ret code */ +targ_size_t retsize; /* size of function return */ + +static regm_t lastretregs,last2retregs,last3retregs,last4retregs,last5retregs; + +/********************************* + * Generate code for a function. + * Note at the end of this routine mfuncreg will contain the mask + * of registers not affected by the function. Some minor optimization + * possibilities are here... + */ + +void codgen() +{ block *b,*bn; + bool flag; + int i; + targ_size_t swoffset,coffset; + tym_t functy; + unsigned nretblocks; // number of return blocks + code *cprolog; + regm_t noparams; +#if SCPP + block *btry; +#endif + // Register usage. If a bit is on, the corresponding register is live + // in that basic block. + + //printf("codgen('%s')\n",funcsym_p->Sident); + + cgreg_init(); + csmax = 64; + csextab = (struct CSE *) util_calloc(sizeof(struct CSE),csmax); + functy = tybasic(funcsym_p->ty()); +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + regm_t value = BYTEREGS_INIT; + ALLREGS = ALLREGS_INIT; + BYTEREGS = value; + if (I64) + { ALLREGS = mAX|mBX|mCX|mDX|mSI|mDI| mR8|mR9|mR10|mR11|mR12|mR13|mR14|mR15; + BYTEREGS = ALLREGS; + } +#endif + allregs = ALLREGS; + pass = PASSinit; + +tryagain: + #ifdef DEBUG + if (debugr) + printf("------------------ PASS%s -----------------\n", + (pass == PASSinit) ? "init" : ((pass == PASSreg) ? "reg" : "final")); + #endif + lastretregs = last2retregs = last3retregs = last4retregs = last5retregs = 0; + + // if no parameters, assume we don't need a stack frame + needframe = 0; + usedalloca = 0; + gotref = 0; + stackchanged = 0; + stackpush = 0; + refparam = 0; + anyiasm = 0; + calledafunc = 0; + cgstate.stackclean = 1; + retsym = NULL; + + regsave.reset(); +#if TX86 + memset(_8087elems,0,sizeof(_8087elems)); +#endif + + usednteh = 0; +#if (MARS) && TARGET_WINDOS + if (funcsym_p->Sfunc->Fflags3 & Fjmonitor) + usednteh |= NTEHjmonitor; +#else + if (CPP) + { + if (config.flags2 & CFG2seh && + (funcsym_p->Stype->Tflags & TFemptyexc || funcsym_p->Stype->Texcspec)) + usednteh |= NTEHexcspec; + except_reset(); + } +#endif + + floatreg = FALSE; +#if TX86 + assert(stackused == 0); /* nobody in 8087 stack */ +#endif + cstop = 0; /* no entries in table yet */ + memset(®con,0,sizeof(regcon)); + regcon.cse.mval = regcon.cse.mops = 0; // no common subs yet + msavereg = 0; + nretblocks = 0; + mfuncreg = fregsaved; // so we can see which are used + // (bit is cleared each time + // we use one) + for (b = startblock; b; b = b->Bnext) + { memset(&b->Bregcon,0,sizeof(b->Bregcon)); // Clear out values in registers + if (b->Belem) + resetEcomsub(b->Belem); // reset all the Ecomsubs + if (b->BC == BCasm) + anyiasm = 1; // we have inline assembler + if (b->BC == BCret || b->BC == BCretexp) + nretblocks++; + } + + if (!config.fulltypes || (config.flags4 & CFG4optimized)) + { + noparams = 0; + for (i = 0; i < globsym.top; i++) + { + Symbol *s = globsym.tab[i]; + s->Sflags &= ~SFLread; + switch (s->Sclass) + { case SCfastpar: + regcon.params |= mask[s->Spreg]; + case SCparameter: + if (s->Sfl == FLreg) + noparams |= s->Sregm; + break; + } + } + regcon.params &= ~noparams; + } + + if (config.flags4 & CFG4optimized) + { + if (nretblocks == 0 && // if no return blocks in function + !(funcsym_p->ty() & mTYnaked)) // naked functions may have hidden veys of returning + funcsym_p->Sflags |= SFLexit; // mark function as never returning + + assert(dfo); + + cgreg_reset(); + for (dfoidx = 0; dfoidx < dfotop; dfoidx++) + { regcon.used = msavereg | regcon.cse.mval; // registers already in use + b = dfo[dfoidx]; + blcodgen(b); // gen code in depth-first order + //printf("b->Bregcon.used = x%x\n", b->Bregcon.used); + cgreg_used(dfoidx,b->Bregcon.used); // gather register used information + } + } + else + { pass = PASSfinal; + for (b = startblock; b; b = b->Bnext) + blcodgen(b); // generate the code for each block + } + regcon.immed.mval = 0; + assert(!regcon.cse.mops); // should have all been used + + // See which variables we can put into registers + if (pass != PASSfinal && + !anyiasm) // possible LEA or LES opcodes + { + allregs |= cod3_useBP(); // see if we can use EBP + + // If pic code, but EBX was never needed + if (!(allregs & mBX) && !gotref) + { allregs |= mBX; // EBX can now be used + cgreg_assign(retsym); + pass = PASSreg; + } + else if (cgreg_assign(retsym)) // if we found some registers + pass = PASSreg; + else + pass = PASSfinal; + for (b = startblock; b; b = b->Bnext) + { code_free(b->Bcode); + b->Bcode = NULL; + } + goto tryagain; + } + cgreg_term(); + +#if SCPP + if (CPP) + cgcod_eh(); +#endif + + stackoffsets(1); // compute addresses of stack variables + cod5_prol_epi(); // see where to place prolog/epilog + + // Get rid of unused cse temporaries + while (cstop != 0 && (csextab[cstop - 1].flags & CSEload) == 0) + cstop--; + + if (configv.addlinenumbers) + objlinnum(funcsym_p->Sfunc->Fstartline,Coffset); + + // Otherwise, jmp's to startblock will execute the prolog again + assert(!startblock->Bpred); + + cprolog = prolog(); // gen function start code + if (cprolog) + pinholeopt(cprolog,NULL); // optimize + + funcoffset = Coffset; + coffset = Coffset; + + if (eecontext.EEelem) + genEEcode(); + + for (b = startblock; b; b = b->Bnext) + { + // We couldn't do this before because localsize was unknown + switch (b->BC) + { case BCret: + if (configv.addlinenumbers && b->Bsrcpos.Slinnum && !(funcsym_p->ty() & mTYnaked)) + cgen_linnum(&b->Bcode,b->Bsrcpos); + case BCretexp: + epilog(b); + break; + default: + if (b->Bflags & BFLepilog) + epilog(b); + break; + } + assignaddr(b); // assign addresses + pinholeopt(b->Bcode,b); // do pinhole optimization + if (b->Bflags & BFLprolog) // do function prolog + { + startoffset = coffset + calcblksize(cprolog) - funcoffset; + b->Bcode = cat(cprolog,b->Bcode); + } + cgsched_block(b); + b->Bsize = calcblksize(b->Bcode); // calculate block size + if (b->Balign) + { targ_size_t u = b->Balign - 1; + + coffset = (coffset + u) & ~u; + } + b->Boffset = coffset; /* offset of this block */ + coffset += b->Bsize; /* offset of following block */ + } +#ifdef DEBUG + debugw && printf("code addr complete\n"); +#endif + + // Do jump optimization + do + { flag = FALSE; + for (b = startblock; b; b = b->Bnext) + { if (b->Bflags & BFLjmpoptdone) /* if no more jmp opts for this blk */ + continue; + i = branch(b,0); // see if jmp => jmp short + if (i) /* if any bytes saved */ + { targ_size_t offset; + + b->Bsize -= i; + offset = b->Boffset + b->Bsize; + for (bn = b->Bnext; bn; bn = bn->Bnext) + { + if (bn->Balign) + { targ_size_t u = bn->Balign - 1; + + offset = (offset + u) & ~u; + } + bn->Boffset = offset; + offset += bn->Bsize; + } + coffset = offset; + flag = TRUE; + } + } + if (!I16 && !(config.flags4 & CFG4optimized)) + break; // use the long conditional jmps + } while (flag); // loop till no more bytes saved +#ifdef DEBUG + debugw && printf("code jump optimization complete\n"); +#endif + +#if MARS + if (usednteh & NTEH_try) + { + // Do this before code is emitted because we patch some instructions + nteh_filltables(); + } +#endif + + // Compute starting offset for switch tables +#if ELFOBJ || MACHOBJ + swoffset = (config.flags & CFGromable) ? coffset : CDoffset; +#else + swoffset = (config.flags & CFGromable) ? coffset : Doffset; +#endif + swoffset = align(0,swoffset); + + // Emit the generated code + if (eecontext.EEcompile == 1) + { + codout(eecontext.EEcode); + code_free(eecontext.EEcode); +#if SCPP + el_free(eecontext.EEelem); +#endif + } + else + { + for (b = startblock; b; b = b->Bnext) + { + if (b->BC == BCjmptab || b->BC == BCswitch) + { b->Btableoffset = swoffset; /* offset of sw tab */ + swoffset += b->Btablesize; + } + jmpaddr(b->Bcode); /* assign jump addresses */ +#ifdef DEBUG + if (debugc) + { printf("Boffset = x%lx, Bsize = x%lx, Coffset = x%lx\n", + (long)b->Boffset,(long)b->Bsize,(long)Coffset); + if (b->Bcode) + printf( "First opcode of block is: %0x\n", b->Bcode->Iop ); + } +#endif + if (b->Balign) + { unsigned u = b->Balign; + unsigned nalign = (u - (unsigned)Coffset) & (u - 1); + + while (nalign--) + obj_byte(cseg,Coffset++,0x90); // XCHG AX,AX + } + assert(b->Boffset == Coffset); + +#if SCPP + if (CPP && + !(config.flags2 & CFG2seh)) + { + //printf("b = %p, index = %d\n",b,b->Bindex); + //except_index_set(b->Bindex); + + if (btry != b->Btry) + { + btry = b->Btry; + except_pair_setoffset(b,Coffset - funcoffset); + } + if (b->BC == BCtry) + { + btry = b; + except_pair_setoffset(b,Coffset - funcoffset); + } + } +#endif + codout(b->Bcode); // output code + } + if (coffset != Coffset) + { +#ifdef DEBUG + printf("coffset = %ld, Coffset = %ld\n",(long)coffset,(long)Coffset); +#endif + assert(0); + } + funcsym_p->Ssize = Coffset - funcoffset; // size of function + +#if NTEXCEPTIONS || MARS +#if (SCPP && NTEXCEPTIONS) + if (usednteh & NTEHcpp) +#elif MARS + if (usednteh & NTEH_try) +#endif + { assert(!(config.flags & CFGromable)); + //printf("framehandleroffset = x%x, coffset = x%x\n",framehandleroffset,coffset); + reftocodseg(cseg,framehandleroffset,coffset); + } +#endif + + + // Write out switch tables + flag = FALSE; // TRUE if last active block was a ret + for (b = startblock; b; b = b->Bnext) + { + switch (b->BC) + { case BCjmptab: /* if jump table */ + outjmptab(b); /* write out jump table */ + break; + case BCswitch: + outswitab(b); /* write out switch table */ + break; + case BCret: + case BCretexp: + /* Compute offset to return code from start of function */ + retoffset = b->Boffset + b->Bsize - retsize - funcoffset; +#if MARS + /* Add 3 bytes to retoffset in case we have an exception + * handler. THIS PROBABLY NEEDS TO BE IN ANOTHER SPOT BUT + * IT FIXES THE PROBLEM HERE AS WELL. + */ + if (usednteh & NTEH_try) + retoffset += 3; +#endif + flag = TRUE; + break; + case BCexit: + // Fake it to keep debugger happy + retoffset = b->Boffset + b->Bsize - funcoffset; + break; + } + } + if (flag && configv.addlinenumbers && !(funcsym_p->ty() & mTYnaked)) + /* put line number at end of function on the + start of the last instruction + */ + /* Instead, try offset to cleanup code */ + objlinnum(funcsym_p->Sfunc->Fendline,funcoffset + retoffset); + +#if MARS + if (usednteh & NTEH_try) + { + // Do this before code is emitted because we patch some instructions + nteh_gentables(); + } + if (usednteh & EHtry) + { + except_gentables(); + } +#endif + +#if SCPP +#if NTEXCEPTIONS + // Write out frame handler + if (usednteh & NTEHcpp) + nteh_framehandler(except_gentables()); + else +#endif + { +#if NTEXCEPTIONS + if (usednteh & NTEH_try) + nteh_gentables(); + else +#endif + { + if (CPP) + except_gentables(); + } + ; + } +#endif + for (b = startblock; b; b = b->Bnext) + { + code_free(b->Bcode); + b->Bcode = NULL; + } + + } + + // Mask of regs saved + // BUG: do interrupt functions save BP? + funcsym_p->Sregsaved = (functy == TYifunc) ? mBP : (mfuncreg | fregsaved); + + util_free(csextab); + csextab = NULL; +#if TX86 +#ifdef DEBUG + if (stackused != 0) + printf("stackused = %d\n",stackused); +#endif + assert(stackused == 0); /* nobody in 8087 stack */ + + /* Clean up ndp save array */ + mem_free(NDP::save); + NDP::save = NULL; + NDP::savetop = 0; + NDP::savemax = 0; +#endif +} + + +/****************************** + * Compute offsets for remaining tmp, automatic and register variables + * that did not make it into registers. + */ + +void stackoffsets(int flags) +{ + symbol *s; + targ_size_t Amax,sz; + unsigned alignsize; + int offi; +#if AUTONEST + targ_size_t offstack[20]; + int offi = 0; // index into offstack[] +#endif + vec_t tbl = NULL; + + + //printf("stackoffsets()\n"); + if (config.flags4 & CFG4optimized) + { + tbl = vec_calloc(globsym.top); + } + Aoffset = 0; // automatic & register offset + Toffset = 0; // temporary offset + Poffset = 0; // parameter offset + EEoffset = 0; // for SCstack's + Amax = 0; + Aalign = REGSIZE; + for (int pass = 0; pass < 2; pass++) + { + for (int si = 0; si < globsym.top; si++) + { s = globsym.tab[si]; + if (s->Sflags & SFLdead || + (!anyiasm && !(s->Sflags & SFLread) && s->Sflags & SFLunambig && +#if MARS + /* mTYvolatile was set if s has been reference by a nested function + * meaning we'd better allocate space for it + */ + !(s->Stype->Tty & mTYvolatile) && +#endif + (config.flags4 & CFG4optimized || !config.fulltypes)) + ) + sz = 0; + else + { sz = type_size(s->Stype); + if (sz == 0) + sz++; // can't handle 0 length structs + } + alignsize = type_alignsize(s->Stype); + + //printf("symbol '%s', size = x%lx, align = %d, read = %x\n",s->Sident,(long)sz, (int)type_alignsize(s->Stype), s->Sflags & SFLread); + assert((int)sz >= 0); + + if (pass == 1) + { + if (s->Sclass == SCfastpar) // if parameter s is passed in a register + { + /* Allocate in second pass in order to get these + * right next to the stack frame pointer, EBP. + * Needed so we can call nested contract functions + * frequire and fensure. + */ + if (s->Sfl == FLreg) // if allocated in register + continue; + /* Needed because storing fastpar's on the stack in prolog() + * does the entire register + */ + if (sz < REGSIZE) + sz = REGSIZE; + + Aoffset = align(sz,Aoffset); + s->Soffset = Aoffset; + Aoffset += sz; + if (Aoffset > Amax) + Amax = Aoffset; + //printf("fastpar '%s' sz = %d, auto offset = x%lx\n",s->Sident,sz,(long)s->Soffset); + + // Align doubles to 8 byte boundary + if (!I16 && alignsize > REGSIZE) + Aalign = alignsize; + } + continue; + } + + /* Can't do this for CPP because the inline function expander + adds new symbols on the end. + */ +#if AUTONEST + /*printf("symbol '%s', push = %d, pop = %d\n", + s->Sident,s->Spush,s->Spop);*/ + + /* Can't do this for optimizer if any code motion occurred. + Code motion changes the live range, so variables that + occupy the same space could have live ranges that overlap! + */ + if (config.flags4 & CFG4optimized) + s->Spop = 0; + else + while (s->Spush != 0) + { s->Spush--; + assert(offi < arraysize(offstack)); + /*printf("Pushing offset x%x\n",Aoffset);*/ + offstack[offi++] = Aoffset; + } +#endif + + switch (s->Sclass) + { + case SCfastpar: + break; // ignore on pass 0 + case SCregister: + case SCauto: + if (s->Sfl == FLreg) // if allocated in register + break; + // See if we can share storage with another variable + if (config.flags4 & CFG4optimized && + // Don't share because could stomp on variables + // used in finally blocks + !(usednteh & ~NTEHjmonitor) && + s->Srange && sz && flags && !(s->Sflags & SFLspill)) + { + for (int i = 0; i < si; i++) + { + if (!vec_testbit(i,tbl)) + continue; + symbol *sp = globsym.tab[i]; +//printf("auto s = '%s', sp = '%s', %d, %d, %d\n",s->Sident,sp->Sident,dfotop,vec_numbits(s->Srange),vec_numbits(sp->Srange)); + if (vec_disjoint(s->Srange,sp->Srange) && + sz <= type_size(sp->Stype)) + { + vec_or(sp->Srange,sp->Srange,s->Srange); + //printf("sharing space - '%s' onto '%s'\n",s->Sident,sp->Sident); + s->Soffset = sp->Soffset; + goto L2; + } + } + } + Aoffset = align(sz,Aoffset); + s->Soffset = Aoffset; + //printf("auto '%s' sz = %d, auto offset = x%lx\n",s->Sident,sz,(long)s->Soffset); + Aoffset += sz; + if (Aoffset > Amax) + Amax = Aoffset; + if (s->Srange && sz && !(s->Sflags & SFLspill)) + vec_setbit(si,tbl); + + // Align doubles to 8 byte boundary + if (!I16 && type_alignsize(s->Stype) > REGSIZE) + Aalign = type_alignsize(s->Stype); + L2: + break; + + case SCtmp: + // Allocated separately from SCauto to avoid storage + // overlapping problems. + Toffset = align(sz,Toffset); + s->Soffset = Toffset; + //printf("tmp offset = x%lx\n",(long)s->Soffset); + Toffset += sz; + break; + + case SCstack: + EEoffset = align(sz,EEoffset); + s->Soffset = EEoffset; + //printf("EEoffset = x%lx\n",(long)s->Soffset); + EEoffset += sz; + break; + + case SCparameter: + Poffset = align(REGSIZE,Poffset); /* align on word stack boundary */ + if (I64 && alignsize == 16 && Poffset & 8) + Poffset += 8; + s->Soffset = Poffset; + //printf("%s param offset = x%lx, alignsize = %d\n",s->Sident,(long)s->Soffset, (int)alignsize); + Poffset += (s->Sflags & SFLdouble) + ? type_size(tsdouble) // float passed as double + : type_size(s->Stype); + break; + case SCpseudo: + case SCstatic: + case SCbprel: + break; + default: +#ifdef DEBUG + symbol_print(s); +#endif + assert(0); + } + +#if AUTONEST + while (s->Spop != 0) + { s->Spop--; + assert(offi > 0); + Aoffset = offstack[--offi]; + /*printf("Popping offset x%x\n",Aoffset);*/ + } +#endif + } + } + Aoffset = Amax; + Aoffset = align(0,Aoffset); + if (Aalign > REGSIZE) + Aoffset = (Aoffset + Aalign - 1) & ~(Aalign - 1); + //printf("Aligned Aoffset = x%lx, Toffset = x%lx\n", (long)Aoffset,(long)Toffset); + Toffset = align(0,Toffset); + + if (config.flags4 & CFG4optimized) + { + vec_free(tbl); + } +} + +/**************************** + * Generate code for a block. + */ + +STATIC void blcodgen(block *bl) +{ + code *c; + list_t bpl; + int refparamsave; + regm_t mfuncregsave = mfuncreg; + char *sflsave = NULL; + int anyspill; + + //dbg_printf("blcodgen(%p)\n",bl); + + /* Determine existing immediate values in registers by ANDing + together the values from all the predecessors of b. + */ + assert(bl->Bregcon.immed.mval == 0); + regcon.immed.mval = 0; // assume no previous contents in registers +// regcon.cse.mval = 0; + for (bpl = bl->Bpred; bpl; bpl = list_next(bpl)) + { block *bp = list_block(bpl); + + if (bpl == bl->Bpred) + { regcon.immed = bp->Bregcon.immed; + regcon.params = bp->Bregcon.params; +// regcon.cse = bp->Bregcon.cse; + } + else + { int i; + + regcon.params &= bp->Bregcon.params; + if ((regcon.immed.mval &= bp->Bregcon.immed.mval) != 0) + // Actual values must match, too + for (i = 0; i < REGMAX; i++) + { + if (regcon.immed.value[i] != bp->Bregcon.immed.value[i]) + regcon.immed.mval &= ~mask[i]; + } + } + } + regcon.cse.mops &= regcon.cse.mval; + + // Set regcon.mvar according to what variables are in registers for this block + c = NULL; + regcon.mvar = 0; + regcon.mpvar = 0; + regcon.indexregs = 1; + anyspill = 0; + if (config.flags4 & CFG4optimized) + { SYMIDX i; + code *cload = NULL; + code *cstore = NULL; + + sflsave = (char *) alloca(globsym.top * sizeof(char)); + for (i = 0; i < globsym.top; i++) + { symbol *s = globsym.tab[i]; + + sflsave[i] = s->Sfl; + if (s->Sclass & SCfastpar && + regcon.params & mask[s->Spreg] && + vec_testbit(dfoidx,s->Srange)) + { + regcon.used |= mask[s->Spreg]; + } + + if (s->Sfl == FLreg) + { if (vec_testbit(dfoidx,s->Srange)) + { regcon.mvar |= s->Sregm; + if (s->Sclass == SCfastpar) + regcon.mpvar |= s->Sregm; + } + } + else if (s->Sflags & SFLspill) + { if (vec_testbit(dfoidx,s->Srange)) + { + anyspill = i + 1; + cgreg_spillreg_prolog(bl,s,&cstore,&cload); + if (vec_testbit(dfoidx,s->Slvreg)) + { s->Sfl = FLreg; + regcon.mvar |= s->Sregm; + regcon.cse.mval &= ~s->Sregm; + regcon.immed.mval &= ~s->Sregm; + if (s->Sclass == SCfastpar) + regcon.mpvar |= s->Sregm; + } + } + } + } + if ((regcon.cse.mops & regcon.cse.mval) != regcon.cse.mops) + { code *cx; + + cx = cse_save(regcon.cse.mops & ~regcon.cse.mval); + cstore = cat(cx, cstore); + } + c = cat(cstore,cload); + mfuncreg &= ~regcon.mvar; // use these registers + regcon.used |= regcon.mvar; + + // Determine if we have more than 1 uncommitted index register + regcon.indexregs = IDXREGS & ~regcon.mvar; + regcon.indexregs &= regcon.indexregs - 1; + } + + regsave.idx = 0; + reflocal = 0; + refparamsave = refparam; + refparam = 0; + assert((regcon.cse.mops & regcon.cse.mval) == regcon.cse.mops); + + outblkexitcode(bl, c, anyspill, sflsave, &retsym, mfuncregsave); + + for (int i = 0; i < anyspill; i++) + { symbol *s = globsym.tab[i]; + + s->Sfl = sflsave[i]; // undo block register assignments + } + + if (reflocal) + bl->Bflags |= BFLreflocal; + if (refparam) + bl->Bflags |= BFLrefparam; + refparam |= refparamsave; + bl->Bregcon.immed = regcon.immed; + bl->Bregcon.cse = regcon.cse; + bl->Bregcon.used = regcon.used; + bl->Bregcon.params = regcon.params; +#ifdef DEBUG + debugw && printf("code gen complete\n"); +#endif +} + +/***************************************** + * Add in exception handling code. + */ + +#if SCPP + +STATIC void cgcod_eh() +{ block *btry; + code *c; + code *c1; + list_t stack; + list_t list; + block *b; + int idx; + int lastidx; + int tryidx; + int i; + + if (!(usednteh & (EHtry | EHcleanup))) + return; + + // Compute Bindex for each block + for (b = startblock; b; b = b->Bnext) + { b->Bindex = -1; + b->Bflags &= ~BFLvisited; /* mark as unvisited */ + } + btry = NULL; + lastidx = 0; + startblock->Bindex = 0; + for (b = startblock; b; b = b->Bnext) + { + if (btry == b->Btry && b->BC == BCcatch) // if don't need to pop try block + { block *br; + + br = list_block(b->Bpred); // find corresponding try block + assert(br->BC == BCtry); + b->Bindex = br->Bindex; + } + else if (btry != b->Btry && b->BC != BCcatch || + !(b->Bflags & BFLvisited)) + b->Bindex = lastidx; + b->Bflags |= BFLvisited; +#ifdef DEBUG + if (debuge) + { + WRBC(b->BC); + dbg_printf(" block (%p) Btry=%p Bindex=%d\n",b,b->Btry,b->Bindex); + } +#endif + except_index_set(b->Bindex); + if (btry != b->Btry) // exited previous try block + { + except_pop(b,NULL,btry); + btry = b->Btry; + } + if (b->BC == BCtry) + { + except_push(b,NULL,b); + btry = b; + tryidx = except_index_get(); + b->Bcode = cat(nteh_gensindex(tryidx - 1),b->Bcode); + } + + stack = NULL; + for (c = b->Bcode; c; c = code_next(c)) + { + if ((c->Iop & 0xFF) == ESCAPE) + { + c1 = NULL; + switch (c->Iop & 0xFFFF00) + { + case ESCctor: +//printf("ESCctor\n"); + except_push(c,c->IEV1.Vtor,NULL); + goto L1; + + case ESCdtor: +//printf("ESCdtor\n"); + except_pop(c,c->IEV1.Vtor,NULL); + L1: if (config.flags2 & CFG2seh) + { + c1 = nteh_gensindex(except_index_get() - 1); + code_next(c1) = code_next(c); + code_next(c) = c1; + } + break; + case ESCmark: +//printf("ESCmark\n"); + idx = except_index_get(); + list_prependdata(&stack,idx); + except_mark(); + break; + case ESCrelease: +//printf("ESCrelease\n"); + idx = list_data(stack); + list_pop(&stack); + if (idx != except_index_get()) + { + if (config.flags2 & CFG2seh) + { c1 = nteh_gensindex(idx - 1); + code_next(c1) = code_next(c); + code_next(c) = c1; + } + else + { except_pair_append(c,idx - 1); + c->Iop = ESCAPE | ESCoffset; + } + } + except_release(); + break; + case ESCmark2: +//printf("ESCmark2\n"); + except_mark(); + break; + case ESCrelease2: +//printf("ESCrelease2\n"); + except_release(); + break; + } + } + } + assert(stack == NULL); + b->Bendindex = except_index_get(); + + if (b->BC != BCret && b->BC != BCretexp) + lastidx = b->Bendindex; + + // Set starting index for each of the successors + i = 0; + for (list = b->Bsucc; list; list = list_next(list)) + { block *bs = list_block(list); + + if (b->BC == BCtry) + { switch (i) + { case 0: // block after catches + bs->Bindex = b->Bendindex; + break; + case 1: // 1st catch block + bs->Bindex = tryidx; + break; + default: // subsequent catch blocks + bs->Bindex = b->Bindex; + break; + } +#ifdef DEBUG + if (debuge) + { + dbg_printf(" 1setting %p to %d\n",bs,bs->Bindex); + } +#endif + } + else if (!(bs->Bflags & BFLvisited)) + { + bs->Bindex = b->Bendindex; +#ifdef DEBUG + if (debuge) + { + dbg_printf(" 2setting %p to %d\n",bs,bs->Bindex); + } +#endif + } + bs->Bflags |= BFLvisited; + i++; + } + } + + if (config.flags2 & CFG2seh) + for (b = startblock; b; b = b->Bnext) + { + if (/*!b->Bcount ||*/ b->BC == BCtry) + continue; + for (list = b->Bpred; list; list = list_next(list)) + { int pi; + + pi = list_block(list)->Bendindex; + if (b->Bindex != pi) + { + b->Bcode = cat(nteh_gensindex(b->Bindex - 1),b->Bcode); + break; + } + } + } +} + +#endif + +/****************************** + * Count the number of bits set in a register mask. + */ + +int numbitsset(regm_t regm) +{ int n; + + n = 0; + if (regm) + do + n++; + while ((regm &= regm - 1) != 0); + return n; +} + +/****************************** + * Given a register mask, find and return the number + * of the first register that fits. + */ + +#undef findreg + +unsigned findreg(regm_t regm +#ifdef DEBUG + ,int line,const char *file +#endif + ) +#ifdef DEBUG +#define findreg(regm) findreg((regm),__LINE__,__FILE__) +#endif +{ +#ifdef DEBUG + regm_t regmsave = regm; +#endif + int i = 0; + while (1) + { + if (!(regm & 0xF)) + { + regm >>= 4; + i += 4; + if (!regm) + break; + } + if (regm & 1) + return i; + regm >>= 1; + i++; + } +#ifdef DEBUG + printf("findreg(x%x, line=%d, file='%s')\n",regmsave,line,file); + fflush(stdout); +#endif +//*(char*)0=0; + assert(0); + return 0; +} + +/*************** + * Free element (but not it's leaves! (assume they are already freed)) + * Don't decrement Ecount! This is so we can detect if the common subexp + * has already been evaluated. + * If common subexpression is not required anymore, eliminate + * references to it. + */ + +void freenode(elem *e) +{ unsigned i; + + elem_debug(e); + //dbg_printf("freenode(%p) : comsub = %d, count = %d\n",e,e->Ecomsub,e->Ecount); + if (e->Ecomsub--) return; /* usage count */ + if (e->Ecount) /* if it was a CSE */ + { for (i = 0; i < arraysize(regcon.cse.value); i++) + { if (regcon.cse.value[i] == e) /* if a register is holding it */ + { regcon.cse.mval &= ~mask[i]; + regcon.cse.mops &= ~mask[i]; /* free masks */ + } + } + for (i = 0; i < cstop; i++) + { if (csextab[i].e == e) + csextab[i].e = NULL; + } + } +} + +/********************************* + * Reset Ecomsub for all elem nodes, i.e. reverse the effects of freenode(). + */ + +STATIC void resetEcomsub(elem *e) +{ unsigned op; + + while (1) + { + elem_debug(e); + e->Ecomsub = e->Ecount; + op = e->Eoper; + if (!OTleaf(op)) + { if (OTbinary(op)) + resetEcomsub(e->E2); + e = e->E1; + } + else + break; + } +} + +/********************************* + * Determine if elem e is a register variable. + * If so: + * *pregm = mask of registers that make up the variable + * *preg = the least significant register + * returns TRUE + * Else + * returns FALSE + */ + +int isregvar(elem *e,regm_t *pregm,unsigned *preg) +{ symbol *s; + unsigned u; + regm_t m; + regm_t regm; + unsigned reg; + + elem_debug(e); + if (e->Eoper == OPvar || e->Eoper == OPrelconst) + { + s = e->EV.sp.Vsym; + switch (s->Sfl) + { case FLreg: + if (s->Sclass == SCparameter) + { refparam = TRUE; + reflocal = TRUE; + } + reg = s->Sreglsw; + regm = s->Sregm; + //assert(tyreg(s->ty())); +#if 0 + // Let's just see if there is a CSE in a reg we can use + // instead. This helps avoid AGI's. + if (e->Ecount && e->Ecount != e->Ecomsub) + { int i; + + for (i = 0; i < arraysize(regcon.cse.value); i++) + { + if (regcon.cse.value[i] == e) + { reg = i; + break; + } + } + } +#endif + assert(regm & regcon.mvar && !(regm & ~regcon.mvar)); + goto Lreg; + + case FLpseudo: +#if MARS + assert(0); +#else + u = s->Sreglsw; + m = pseudomask[u]; + if (m & ALLREGS && (u & ~3) != 4) // if not BP,SP,EBP,ESP,or ?H + { reg = pseudoreg[u] & 7; + regm = m; + goto Lreg; + } +#endif + break; + } + } + return FALSE; + +Lreg: + if (preg) + *preg = reg; + if (pregm) + *pregm = regm; + return TRUE; +} + +/********************************* + * Allocate some registers. + * Input: + * pretregs Pointer to mask of registers to make selection from. + * tym Mask of type we will store in registers. + * Output: + * *pretregs Mask of allocated registers. + * *preg Register number of first allocated register. + * msavereg,mfuncreg retregs bits are cleared. + * regcon.cse.mval,regcon.cse.mops updated + * Returns: + * pointer to code generated if necessary to save any regcon.cse.mops on the + * stack. + */ + +#undef allocreg + +code *allocreg(regm_t *pretregs,unsigned *preg,tym_t tym +#ifdef DEBUG + ,int line,const char *file +#endif + ) +#ifdef DEBUG +#define allocreg(a,b,c) allocreg((a),(b),(c),__LINE__,__FILE__) +#endif +{ regm_t r; + regm_t retregs; + unsigned reg; + unsigned msreg,lsreg; + int count; + unsigned size; + +#if 0 + if (pass == PASSfinal) + { dbg_printf("allocreg %s,%d: regcon.mvar %s regcon.cse.mval %s msavereg %s *pretregs %s tym ", + file,line,regm_str(regcon.mvar),regm_str(regcon.cse.mval), + regm_str(msavereg),regm_str(*pretregs)); + WRTYxx(tym); + dbg_printf("\n"); + } +#endif + tym = tybasic(tym); + size = tysize[tym]; + *pretregs &= mES | allregs | XMMREGS; + retregs = *pretregs; + if ((retregs & regcon.mvar) == retregs) // if exactly in reg vars + { + if (size <= REGSIZE || (retregs & XMMREGS)) + { *preg = findreg(retregs); + assert(retregs == mask[*preg]); /* no more bits are set */ + } + else if (size <= 2 * REGSIZE) + { *preg = findregmsw(retregs); + assert(retregs & mLSW); + } + else + assert(0); + return getregs(retregs); + } + count = 0; +L1: + //printf("L1: allregs = x%x, *pretregs = x%x\n", allregs, *pretregs); + assert(++count < 20); /* fail instead of hanging if blocked */ + assert(retregs); + msreg = lsreg = (unsigned)-1; /* no value assigned yet */ +L3: + //printf("L2: allregs = x%x, *pretregs = x%x\n", allregs, *pretregs); + r = retregs & ~(msavereg | regcon.cse.mval | regcon.params); + if (!r) + { + r = retregs & ~(msavereg | regcon.cse.mval); + if (!r) + { + r = retregs & ~(msavereg | regcon.cse.mops); + if (!r) + { r = retregs & ~msavereg; + if (!r) + r = retregs; + } + } + } + + if (size <= REGSIZE || retregs & XMMREGS) + { + if (r & ~mBP) + r &= ~mBP; + + // If only one index register, prefer to not use LSW registers + if (!regcon.indexregs && r & ~mLSW) + r &= ~mLSW; + + if (pass == PASSfinal && r & ~lastretregs && !I16) + { // Try not to always allocate the same register, + // to schedule better + + r &= ~lastretregs; + if (r & ~last2retregs) + { r &= ~last2retregs; + if (r & ~last3retregs) + { r &= ~last3retregs; + if (r & ~last4retregs) + { r &= ~last4retregs; +// if (r & ~last5retregs) +// r &= ~last5retregs; + } + } + } + if (r & ~mfuncreg) + r &= ~mfuncreg; + } + reg = findreg(r); + retregs = mask[reg]; + } + else if (size <= 2 * REGSIZE) + { + /* Select pair with both regs free. Failing */ + /* that, select pair with one reg free. */ + + if (r & mBP) + { retregs &= ~mBP; + goto L3; + } + + if (r & mMSW) + { + if (r & mDX) + msreg = DX; /* prefer to use DX over CX */ + else + msreg = findregmsw(r); + r &= mLSW; /* see if there's an LSW also */ + if (r) + lsreg = findreg(r); + else if (lsreg == -1) /* if don't have LSW yet */ + { retregs &= mLSW; + goto L3; + } + } + else + { + if (I64 && !(r & mLSW)) + { retregs = *pretregs & (mMSW | mLSW); + assert(retregs); + goto L1; + } + lsreg = findreglsw(r); + if (msreg == -1) + { retregs &= mMSW; + assert(retregs); + goto L3; + } + } + reg = (msreg == ES) ? lsreg : msreg; + retregs = mask[msreg] | mask[lsreg]; + } + else if (I16 && (tym == TYdouble || tym == TYdouble_alias)) + { +#ifdef DEBUG + if (retregs != DOUBLEREGS) + printf("retregs = x%x, *pretregs = x%x\n",retregs,*pretregs); +#endif + assert(retregs == DOUBLEREGS); + reg = AX; + } + else + { +#ifdef DEBUG + WRTYxx(tym); + printf("\nallocreg: fil %s lin %d, regcon.mvar x%x msavereg x%x *pretregs x%x, reg %d, tym x%x\n", + file,line,regcon.mvar,msavereg,*pretregs,*preg,tym); +#endif + assert(0); + } + if (retregs & regcon.mvar) // if conflict with reg vars + { + if (!(size > REGSIZE && *pretregs == (mAX | mDX))) + { + retregs = (*pretregs &= ~(retregs & regcon.mvar)); + goto L1; // try other registers + } + } + *preg = reg; + *pretregs = retregs; + + //printf("Allocating %s\n",regm_str(retregs)); + last5retregs = last4retregs; + last4retregs = last3retregs; + last3retregs = last2retregs; + last2retregs = lastretregs; + lastretregs = retregs; + return getregs(retregs); +} + +/************************* + * Mark registers as used. + */ + +void useregs(regm_t regm) +{ + //printf("useregs(x%x) %s\n", regm, regm_str(regm)); + mfuncreg &= ~regm; + regcon.used |= regm; // registers used in this block + regcon.params &= ~regm; + if (regm & regcon.mpvar) // if modified a fastpar register variable + regcon.params = 0; // toss them all out +} + +/************************* + * We are going to use the registers in mask r. + * Generate any code necessary to save any regs. + */ + +code *getregs(regm_t r) +{ regm_t ms; + + //printf("getregs(x%x)\n",r); + ms = r & regcon.cse.mops; // mask of common subs we must save + useregs(r); + regcon.cse.mval &= ~r; + msavereg &= ~r; // regs that are destroyed + regcon.immed.mval &= ~r; + return ms ? cse_save(ms) : NULL; +} + +/***************************************** + * Copy registers in cse.mops into memory. + */ + +STATIC code * cse_save(regm_t ms) +{ unsigned reg,i,op; + code *c = NULL; + regm_t regm; + + assert((ms & regcon.cse.mops) == ms); + regcon.cse.mops &= ~ms; + + /* Skip CSEs that are already saved */ + for (regm = 1; regm <= mES; regm <<= 1) + { + if (regm & ms) + { elem *e; + + e = regcon.cse.value[findreg(regm)]; + for (i = 0; i < csmax; i++) + { + if (csextab[i].e == e) + { + tym_t tym; + unsigned sz; + + tym = e->Ety; + sz = tysize(tym); + if (sz <= REGSIZE || + sz <= 2 * REGSIZE && + (regm & mMSW && csextab[i].regm & mMSW || + regm & mLSW && csextab[i].regm & mLSW) || + sz == 4 * REGSIZE && regm == csextab[i].regm + ) + { + ms &= ~regm; + if (!ms) + goto Lret; + break; + } + } + } + } + } + + for (i = cstop; ms; i++) + { + if (i >= csmax) /* array overflow */ + { unsigned cseinc; + +#ifdef DEBUG + cseinc = 8; /* flush out reallocation bugs */ +#else + cseinc = csmax + 32; +#endif + csextab = (struct CSE *) util_realloc(csextab, + (csmax + cseinc), sizeof(csextab[0])); + memset(&csextab[csmax],0,cseinc * sizeof(csextab[0])); + csmax += cseinc; + goto L1; + } + if (i >= cstop) + { + memset(&csextab[cstop],0,sizeof(csextab[0])); + goto L1; + } + if (csextab[i].e == NULL || i >= cstop) + { + L1: + reg = findreg(ms); /* the register to save */ + csextab[i].e = regcon.cse.value[reg]; + csextab[i].regm = mask[reg]; + csextab[i].flags &= CSEload; + if (i >= cstop) + cstop = i + 1; + + ms &= ~mask[reg]; /* turn off reg bit in ms */ + + // If we can simply reload the CSE, we don't need to save it + if (!cse_simple(csextab[i].e,i)) + { + c = cat(c, gensavereg(reg, i)); + reflocal = TRUE; + } + } + } +Lret: + return c; +} + +/****************************************** + * Getregs without marking immediate register values as gone. + */ + +code *getregs_imm(regm_t r) +{ code *c; + regm_t save; + + save = regcon.immed.mval; + c = getregs(r); + regcon.immed.mval = save; + return c; +} + +/****************************************** + * Flush all CSE's out of registers and into memory. + * Input: + * do87 !=0 means save 87 registers too + */ + +code *cse_flush(int do87) +{ code *c; + + //dbg_printf("cse_flush()\n"); + c = cse_save(regcon.cse.mops); // save any CSEs to memory + if (do87) + c = cat(c,save87()); // save any 8087 temporaries + return c; +} + +/************************************************* + */ + +STATIC int cse_simple(elem *e,int i) +{ regm_t regm; + unsigned reg; + code *c; + int sz; + + sz = tysize[tybasic(e->Ety)]; + if (!I16 && // don't bother with 16 bit code + e->Eoper == OPadd && + sz == REGSIZE && + e->E2->Eoper == OPconst && + e->E1->Eoper == OPvar && + isregvar(e->E1,®m,®) && + sz <= REGSIZE && + !(e->E1->EV.sp.Vsym->Sflags & SFLspill) + ) + { + c = &csextab[i].csimple; + memset(c,0,sizeof(*c)); + + // Make this an LEA instruction + c->Iop = 0x8D; // LEA + buildEA(c,reg,-1,1,e->E2->EV.Vuns); + if (I64) + { if (sz == 8) + c->Irex |= REX_W; + else if (sz == 1 && reg >= 4) + c->Irex |= REX; + } + + csextab[i].flags |= CSEsimple; + return 1; + } + else if (e->Eoper == OPind && + sz <= REGSIZE && + e->E1->Eoper == OPvar && + isregvar(e->E1,®m,®) && + (I32 || I64 || regm & IDXREGS) && + !(e->E1->EV.sp.Vsym->Sflags & SFLspill) + ) + { + c = &csextab[i].csimple; + memset(c,0,sizeof(*c)); + + // Make this a MOV instruction + c->Iop = (sz == 1) ? 0x8A : 0x8B; // MOV reg,EA + buildEA(c,reg,-1,1,0); + if (sz == 2 && I32) + c->Iflags |= CFopsize; + else if (I64) + { if (sz == 8) + c->Irex |= REX_W; + else if (sz == 1 && reg >= 4) + c->Irex |= REX; + } + + csextab[i].flags |= CSEsimple; + return 1; + } + return 0; +} + +/************************* + * Common subexpressions exist in registers. Note this in regcon.cse.mval. + * Input: + * e the subexpression + * regm mask of registers holding it + * opsflag if != 0 then regcon.cse.mops gets set too + */ + +void cssave(elem *e,regm_t regm,unsigned opsflag) +{ unsigned i; + + /*if (e->Ecount && e->Ecount == e->Ecomsub)*/ + if (e->Ecount && e->Ecomsub) + { + //printf("cssave(e = %p, regm = x%x, opsflag = %d)\n", e, regm, opsflag); + if (!opsflag && pass != PASSfinal && (I32 || I64)) + return; + + //printf("cssave(e = %p, regm = x%x, opsflag = x%x)\n", e, regm, opsflag); + regm &= mBP | ALLREGS | mES; /* just to be sure */ + +#if 0 + /* Do not register CSEs if they are register variables and */ + /* are not operator nodes. This forces the register allocation */ + /* to go through allocreg(), which will prevent using register */ + /* variables for scratch. */ + if (opsflag || !(regm & regcon.mvar)) +#endif + for (i = 0; regm; i++) + { regm_t mi; + + mi = mask[i]; + if (regm & mi) + { + regm &= ~mi; + + // If we don't need this CSE, and the register already + // holds a CSE that we do need, don't mark the new one + if (regcon.cse.mval & mi && regcon.cse.value[i] != e && + !opsflag && regcon.cse.mops & mi) + continue; + + regcon.cse.mval |= mi; + if (opsflag) + regcon.cse.mops |= mi; + //printf("cssave set: regcon.cse.value[%s] = %p\n",regstring[i],e); + regcon.cse.value[i] = e; + } + } + } +} + +/************************************* + * Determine if a computation should be done into a register. + */ + +bool evalinregister(elem *e) +{ regm_t emask; + unsigned i; + unsigned sz; + + if (e->Ecount == 0) /* elem is not a CSE, therefore */ + /* we don't need to evaluate it */ + /* in a register */ + return FALSE; + if (EOP(e)) /* operators are always in register */ + return TRUE; + + // Need to rethink this code if float or double can be CSE'd + sz = tysize(e->Ety); + if (e->Ecount == e->Ecomsub) /* elem is a CSE that needs */ + /* to be generated */ + { + if ((I32 || I64) && pass == PASSfinal && sz <= REGSIZE) + { + // Do it only if at least 2 registers are available + regm_t m; + + m = allregs & ~regcon.mvar; + if (sz == 1) + m &= BYTEREGS; + if (m & (m - 1)) // if more than one register + { // Need to be at least 3 registers available, as + // addressing modes can use up 2. + while (!(m & 1)) + m >>= 1; + m >>= 1; + if (m & (m - 1)) + return TRUE; + } + } + return FALSE; + } + + /* Elem is now a CSE that might have been generated. If so, and */ + /* it's in a register already, the computation should be done */ + /* using that register. */ + emask = 0; + for (i = 0; i < arraysize(regcon.cse.value); i++) + if (regcon.cse.value[i] == e) + emask |= mask[i]; + emask &= regcon.cse.mval; // mask of available CSEs + if (sz <= REGSIZE) + return emask != 0; /* the CSE is in a register */ + else if (sz <= 2 * REGSIZE) + return (emask & mMSW) && (emask & mLSW); + return TRUE; /* cop-out for now */ +} + +/******************************************************* + * Return mask of scratch registers. + */ + +regm_t getscratch() +{ regm_t scratch; + + scratch = 0; + if (pass == PASSfinal) + { + scratch = allregs & ~(regcon.mvar | regcon.mpvar | regcon.cse.mval | + regcon.immed.mval | regcon.params | mfuncreg); + } + return scratch; +} + +/****************************** + * Evaluate an elem that is a common subexp that has been encountered + * before. + * Look first to see if it is already in a register. + */ + +STATIC code * comsub(elem *e,regm_t *pretregs) +{ tym_t tym; + regm_t regm,emask,csemask; + unsigned reg,i,byte,sz; + code *c; + + //printf("comsub(e = %p, *pretregs = %s)\n",e,regm_str(*pretregs)); + elem_debug(e); +#ifdef DEBUG + if (e->Ecomsub > e->Ecount) + elem_print(e); +#endif + assert(e->Ecomsub <= e->Ecount); + + c = CNIL; + if (*pretregs == 0) goto done; /* no possible side effects anyway */ + + if (tyfloating(e->Ety) && config.inline8087) + return comsub87(e,pretregs); + + /* First construct a mask, emask, of all the registers that */ + /* have the right contents. */ + + emask = 0; + for (i = 0; i < arraysize(regcon.cse.value); i++) + { + //dbg_printf("regcon.cse.value[%d] = %p\n",i,regcon.cse.value[i]); + if (regcon.cse.value[i] == e) /* if contents are right */ + emask |= mask[i]; /* turn on bit for reg */ + } + emask &= regcon.cse.mval; /* make sure all bits are valid */ + + /* create mask of what's in csextab[] */ + csemask = 0; + for (i = 0; i < cstop; i++) + { if (csextab[i].e) + elem_debug(csextab[i].e); + if (csextab[i].e == e) + csemask |= csextab[i].regm; + } + csemask &= ~emask; /* stuff already in registers */ + +#ifdef DEBUG +if (debugw) +{ +printf("comsub(e=%p): *pretregs=%x, emask=%x, csemask=%x, regcon.cse.mval=%x, regcon.mvar=%x\n", + e,*pretregs,emask,csemask,regcon.cse.mval,regcon.mvar); +if (regcon.cse.mval & 1) elem_print(regcon.cse.value[i]); +} +#endif + + tym = tybasic(e->Ety); + sz = tysize[tym]; + byte = sz == 1; + + if (sz <= REGSIZE) // if data will fit in one register + { + /* First see if it is already in a correct register */ + + regm = emask & *pretregs; + if (regm == 0) + regm = emask; /* try any other register */ + if (regm) /* if it's in a register */ + { + if (EOP(e) || !(regm & regcon.mvar) || (*pretregs & regcon.mvar) == *pretregs) + { + regm = mask[findreg(regm)]; + goto fix; + } + } + + if (!EOP(e)) /* if not op or func */ + goto reload; /* reload data */ + for (i = cstop; i--;) /* look through saved comsubs */ + if (csextab[i].e == e) /* found it */ + { regm_t retregs; + + if (csextab[i].flags & CSEsimple) + { code *cr; + + retregs = *pretregs; + if (byte && !(retregs & BYTEREGS)) + retregs = BYTEREGS; + else if (!(retregs & allregs)) + retregs = allregs; + c = allocreg(&retregs,®,tym); + cr = &csextab[i].csimple; + cr->setReg(reg); + c = gen(c,cr); + goto L10; + } + else + { + reflocal = TRUE; + csextab[i].flags |= CSEload; + if (*pretregs == mPSW) /* if result in CCs only */ + { // CMP cs[BP],0 + c = genc(NULL,0x81 ^ byte,modregrm(2,7,BPRM), + FLcs,i, FLconst,(targ_uns) 0); + if (I32 && sz == 2) + c->Iflags |= CFopsize; + } + else + { + retregs = *pretregs; + if (byte && !(retregs & BYTEREGS)) + retregs = BYTEREGS; + c = allocreg(&retregs,®,tym); + // MOV reg,cs[BP] + c = genc1(c,0x8B,modregxrm(2,reg,BPRM),FLcs,(targ_uns) i); + if (I64) + code_orrex(c, REX_W); + L10: + regcon.cse.mval |= mask[reg]; // cs is in a reg + regcon.cse.value[reg] = e; + c = cat(c,fixresult(e,retregs,pretregs)); + } + } + freenode(e); + return c; + } +#ifdef DEBUG + printf("couldn't find cse e = %p, pass = %d\n",e,pass); + elem_print(e); +#endif + assert(0); /* should have found it */ + } + else /* reg pair is req'd */ + if (sz <= 2 * REGSIZE) + { unsigned msreg,lsreg; + + /* see if we have both */ + if (!((emask | csemask) & mMSW && (emask | csemask) & (mLSW | mBP))) + { /* we don't have both */ +#if DEBUG + if (EOP(e)) + { + printf("e = %p, op = x%x, emask = x%x, csemask = x%x\n", + e,e->Eoper,emask,csemask); + //printf("mMSW = x%x, mLSW = x%x\n", mMSW, mLSW); + elem_print(e); + } +#endif + assert(!EOP(e)); /* must have both for operators */ + goto reload; + } + + /* Look for right vals in any regs */ + + regm = *pretregs & mMSW; + if (emask & regm) + msreg = findreg(emask & regm); + else if (emask & mMSW) + msreg = findregmsw(emask); + else /* reload from cse array */ + { + if (!regm) + regm = mMSW & ALLREGS; + c = allocreg(®m,&msreg,TYint); + c = cat(c,loadcse(e,msreg,mMSW)); + } + + regm = *pretregs & (mLSW | mBP); + if (emask & regm) + lsreg = findreg(emask & regm); + else if (emask & (mLSW | mBP)) + lsreg = findreglsw(emask); + else + { + if (!regm) + regm = mLSW; + c = cat(c,allocreg(®m,&lsreg,TYint)); + c = cat(c,loadcse(e,lsreg,mLSW | mBP)); + } + + regm = mask[msreg] | mask[lsreg]; /* mask of result */ + goto fix; + } + else if (tym == TYdouble || tym == TYdouble_alias) // double + { + assert(I16); + if (((csemask | emask) & DOUBLEREGS_16) == DOUBLEREGS_16) + { + for (reg = AX; reg != -1; reg = dblreg[reg]) + { assert((int) reg >= 0 && reg <= 7); + if (mask[reg] & csemask) + c = cat(c,loadcse(e,reg,mask[reg])); + } + regm = DOUBLEREGS_16; + goto fix; + } + if (!EOP(e)) goto reload; +#if DEBUG + printf("e = %p, csemask = x%x, emask = x%x\n",e,csemask,emask); +#endif + assert(0); + } + else + { +#if DEBUG + printf("e = %p, tym = x%x\n",e,tym); +#endif + assert(0); + } + +reload: /* reload result from memory */ + switch (e->Eoper) + { + case OPrelconst: + c = cdrelconst(e,pretregs); + break; +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + case OPgot: + c = cdgot(e,pretregs); + break; +#endif + default: + c = loaddata(e,pretregs); + break; + } + cssave(e,*pretregs,FALSE); + freenode(e); + return c; + +fix: /* we got result in regm, fix */ + c = cat(c,fixresult(e,regm,pretregs)); +done: + freenode(e); + return c; +} + + +/***************************** + * Load reg from cse stack. + * Returns: + * pointer to the MOV instruction + */ + +STATIC code * loadcse(elem *e,unsigned reg,regm_t regm) +{ unsigned i,op; + code *c; + + for (i = cstop; i--;) + { + //printf("csextab[%d] = %p, regm = x%x\n", i, csextab[i].e, csextab[i].regm); + if (csextab[i].e == e && csextab[i].regm & regm) + { + reflocal = TRUE; + csextab[i].flags |= CSEload; /* it was loaded */ + c = getregs(mask[reg]); + regcon.cse.value[reg] = e; + regcon.cse.mval |= mask[reg]; + op = 0x8B; + if (reg == ES) + { op = 0x8E; + reg = 0; + } + c = genc1(c,op,modregxrm(2,reg,BPRM),FLcs,(targ_uns) i); + if (I64) + code_orrex(c, REX_W); + return c; + } + } +#if DEBUG + printf("loadcse(e = %p, reg = %d, regm = x%x)\n",e,reg,regm); +elem_print(e); +#endif + assert(0); + /* NOTREACHED */ + return 0; +} + +/*************************** + * Generate code sequence for an elem. + * Input: + * pretregs mask of possible registers to return result in + * Note: longs are in AX,BX or CX,DX or SI,DI + * doubles are AX,BX,CX,DX only + * constflag TRUE if user of result will not modify the + * registers returned in *pretregs. + * Output: + * *pretregs mask of registers result is returned in + * Returns: + * pointer to code sequence generated + */ + +#include "cdxxx.c" /* jump table */ + +code *codelem(elem *e,regm_t *pretregs,bool constflag) +{ code *c; + Symbol *s; + unsigned op; + +#ifdef DEBUG + if (debugw) + { printf("+codelem(e=%p,*pretregs=%s) ",e,regm_str(*pretregs)); + WROP(e->Eoper); + printf("msavereg=x%x regcon.cse.mval=x%x regcon.cse.mops=x%x\n", + msavereg,regcon.cse.mval,regcon.cse.mops); + printf("Ecount = %d, Ecomsub = %d\n", e->Ecount, e->Ecomsub); + } +#endif + assert(e); + elem_debug(e); + if ((regcon.cse.mops & regcon.cse.mval) != regcon.cse.mops) + { +#ifdef DEBUG + printf("+codelem(e=%p,*pretregs=x%x) ",e,*pretregs); + elem_print(e); + printf("msavereg=x%x regcon.cse.mval=x%x regcon.cse.mops=x%x\n", + msavereg,regcon.cse.mval,regcon.cse.mops); + printf("Ecount = %d, Ecomsub = %d\n", e->Ecount, e->Ecomsub); +#endif + assert(0); + } + + if (!constflag && *pretregs & (mES | ALLREGS | mBP | XMMREGS) & ~regcon.mvar) + *pretregs &= ~regcon.mvar; /* can't use register vars */ + op = e->Eoper; + if (e->Ecount && e->Ecount != e->Ecomsub) /* if common subexp */ + { c = comsub(e,pretregs); + goto L1; + } + + switch (op) + { + default: + if (e->Ecount) /* if common subexp */ + { + /* if no return value */ + if ((*pretregs & (mSTACK | mES | ALLREGS | mBP)) == 0) + { if (tysize(e->Ety) == 1) + *pretregs |= BYTEREGS; + else if (tybasic(e->Ety) == TYdouble || tybasic(e->Ety) == TYdouble_alias) + *pretregs |= DOUBLEREGS; + else + *pretregs |= ALLREGS; /* make one */ + } + + /* BUG: For CSEs, make sure we have both an MSW */ + /* and an LSW specified in *pretregs */ + } + assert(op <= OPMAX); + c = (*cdxxx[op])(e,pretregs); + break; + case OPrelconst: + c = cdrelconst(e,pretregs); + break; + case OPvar: + if (constflag && (s = e->EV.sp.Vsym)->Sfl == FLreg && + (s->Sregm & *pretregs) == s->Sregm) + { + if (tysize(e->Ety) <= REGSIZE && tysize(s->Stype->Tty) == 2 * REGSIZE) + *pretregs &= mPSW | (s->Sregm & mLSW); + else + *pretregs &= mPSW | s->Sregm; + } + case OPconst: + if (*pretregs == 0 && (e->Ecount >= 3 || e->Ety & mTYvolatile)) + { + switch (tybasic(e->Ety)) + { + case TYbool: + case TYchar: + case TYschar: + case TYuchar: + *pretregs |= BYTEREGS; + break; +#if JHANDLE + case TYjhandle: +#endif + case TYnptr: +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: +#endif + *pretregs |= IDXREGS; + break; + case TYshort: + case TYushort: + case TYint: + case TYuint: + case TYlong: + case TYulong: + case TYllong: + case TYullong: + case TYcent: + case TYucent: +#if TARGET_SEGMENTED + case TYfptr: + case TYhptr: + case TYvptr: +#endif + *pretregs |= ALLREGS; + break; + } + } + c = loaddata(e,pretregs); + break; + } + cssave(e,*pretregs,!OTleaf(op)); + freenode(e); +L1: +#ifdef DEBUG + if (debugw) + { printf("-codelem(e=%p,*pretregs=x%x) ",e,*pretregs); + WROP(op); + printf("msavereg=x%x regcon.cse.mval=x%x regcon.cse.mops=x%x\n", + msavereg,regcon.cse.mval,regcon.cse.mops); + } +#endif + if (configv.addlinenumbers && e->Esrcpos.Slinnum) + cgen_prelinnum(&c,e->Esrcpos); + return c; +} + +/******************************* + * Same as codelem(), but do not destroy the registers in keepmsk. + * Use scratch registers as much as possible, then use stack. + * Input: + * constflag TRUE if user of result will not modify the + * registers returned in *pretregs. + */ + +code *scodelem(elem *e,regm_t *pretregs,regm_t keepmsk,bool constflag) +{ code *c,*cs1,*cs2,*cs3; + unsigned i,j; + regm_t oldmfuncreg,oldregcon,oldregimmed,overlap,tosave,touse; + int adjesp; + unsigned stackpushsave; + char calledafuncsave; + +#ifdef DEBUG + if (debugw) + printf("+scodelem(e=%p *pretregs=%s keepmsk=%s constflag=%d\n", + e,regm_str(*pretregs),regm_str(keepmsk),constflag); +#endif + elem_debug(e); + if (constflag) + { regm_t regm; + unsigned reg; + + if (isregvar(e,®m,®) && // if e is a register variable + (regm & *pretregs) == regm && // in one of the right regs + e->EV.sp.Voffset == 0 + ) + { + unsigned sz1 = tysize(e->Ety); + unsigned sz2 = tysize(e->EV.sp.Vsym->Stype->Tty); + if (sz1 <= REGSIZE && sz2 > REGSIZE) + regm &= mLSW | XMMREGS; + c = fixresult(e,regm,pretregs); + cssave(e,regm,0); + freenode(e); +#ifdef DEBUG + if (debugw) + printf("-scodelem(e=%p *pretregs=x%x keepmsk=x%x constflag=%d\n", + e,*pretregs,keepmsk,constflag); +#endif + return c; + } + } + overlap = msavereg & keepmsk; + msavereg |= keepmsk; /* add to mask of regs to save */ + oldregcon = regcon.cse.mval; + oldregimmed = regcon.immed.mval; + oldmfuncreg = mfuncreg; /* remember old one */ + mfuncreg = (mBP | mES | ALLREGS) & ~regcon.mvar; + stackpushsave = stackpush; + calledafuncsave = calledafunc; + calledafunc = 0; + c = codelem(e,pretregs,constflag); /* generate code for the elem */ + + tosave = keepmsk & ~msavereg; /* registers to save */ + if (tosave) + { cgstate.stackclean++; + c = genstackclean(c,stackpush - stackpushsave,*pretregs | msavereg); + cgstate.stackclean--; + } + + /* Assert that no new CSEs are generated that are not reflected */ + /* in mfuncreg. */ +#ifdef DEBUG + if ((mfuncreg & (regcon.cse.mval & ~oldregcon)) != 0) + printf("mfuncreg x%x, regcon.cse.mval x%x, oldregcon x%x, regcon.mvar x%x\n", + mfuncreg,regcon.cse.mval,oldregcon,regcon.mvar); +#endif + assert((mfuncreg & (regcon.cse.mval & ~oldregcon)) == 0); + + /* bugzilla 3521 + * The problem is: + * reg op (reg = exp) + * where reg must be preserved (in keepregs) while the expression to be evaluated + * must change it. + * The only solution is to make this variable not a register. + */ + if (regcon.mvar & tosave) + { + //elem_print(e); + //printf("test1: regcon.mvar x%x tosave x%x\n", regcon.mvar, tosave); + cgreg_unregister(regcon.mvar & tosave); + } + + /* which registers can we use to save other registers in? */ + if (config.flags4 & CFG4space || // if optimize for space + config.target_cpu >= TARGET_80486) // PUSH/POP ops are 1 cycle + touse = 0; // PUSH/POP pairs are always shorter + else + { touse = mfuncreg & allregs & ~(msavereg | oldregcon | regcon.cse.mval); + /* Don't use registers we'll have to save/restore */ + touse &= ~(fregsaved & oldmfuncreg); + /* Don't use registers that have constant values in them, since + the code generated might have used the value. + */ + touse &= ~oldregimmed; + } + + cs1 = cs2 = cs3 = NULL; + adjesp = 0; + + for (i = 0; tosave; i++) + { regm_t mi = mask[i]; + + assert(i < REGMAX); + if (mi & tosave) /* i = register to save */ + { + if (touse) /* if any scratch registers */ + { for (j = 0; j < 8; j++) + { regm_t mj = mask[j]; + + if (touse & mj) + { cs1 = genmovreg(cs1,j,i); + cs2 = cat(genmovreg(CNIL,i,j),cs2); + touse &= ~mj; + mfuncreg &= ~mj; + regcon.used |= mj; + break; + } + } + assert(j < 8); + } + else /* else use stack */ + { + stackchanged = 1; + adjesp += REGSIZE; + gensaverestore2(mask[i], &cs1, &cs2); + } + cs3 = cat(getregs(mi),cs3); + tosave &= ~mi; + } + } + if (adjesp) + { + // If this is done an odd number of times, it + // will throw off the 8 byte stack alignment. + // We should *only* worry about this if a function + // was called in the code generation by codelem(). + int sz; + if (STACKALIGN == 16) + sz = -(adjesp & (STACKALIGN - 1)) & (STACKALIGN - 1); + else + sz = -(adjesp & 7) & 7; + if (calledafunc && !I16 && sz && (STACKALIGN == 16 || config.flags4 & CFG4stackalign)) + { + unsigned grex = I64 ? REX_W << 16 : 0; + regm_t mval_save = regcon.immed.mval; + regcon.immed.mval = 0; // prevent reghasvalue() optimizations + // because c hasn't been executed yet + cs1 = genc2(cs1,0x81,grex | modregrm(3,5,SP),sz); // SUB ESP,sz + if (I64) + code_orrex(cs1, REX_W); + regcon.immed.mval = mval_save; + cs1 = genadjesp(cs1, sz); + + code *cx = genc2(CNIL,0x81,grex | modregrm(3,0,SP),sz); // ADD ESP,sz + if (I64) + code_orrex(cx, REX_W); + cx = genadjesp(cx, -sz); + cs2 = cat(cx, cs2); + } + + cs1 = genadjesp(cs1,adjesp); + cs2 = genadjesp(cs2,-adjesp); + } + + calledafunc |= calledafuncsave; + msavereg &= ~keepmsk | overlap; /* remove from mask of regs to save */ + mfuncreg &= oldmfuncreg; /* update original */ +#ifdef DEBUG + if (debugw) + printf("-scodelem(e=%p *pretregs=x%x keepmsk=x%x constflag=%d\n", + e,*pretregs,keepmsk,constflag); +#endif + return cat4(cs1,c,cs3,cs2); +} + +/********************************************* + * Turn register mask into a string suitable for printing. + */ + +#ifdef DEBUG + +const char *regm_str(regm_t rm) +{ + #define NUM 4 + #define SMAX 128 + static char str[NUM][SMAX + 1]; + static int i; + + if (rm == 0) + return "0"; + if (rm == ALLREGS) + return "ALLREGS"; + if (rm == BYTEREGS) + return "BYTEREGS"; + if (rm == allregs) + return "allregs"; + if (rm == XMMREGS) + return "XMMREGS"; + char *p = str[i]; + if (++i == NUM) + i = 0; + *p = 0; + for (size_t j = 0; j < 32; j++) + { + if (mask[j] & rm) + { + strcat(p,regstring[j]); + rm &= ~mask[j]; + if (rm) + strcat(p,"|"); + } + } + if (rm) + { char *s = p + strlen(p); + sprintf(s,"x%02x",rm); + } + assert(strlen(p) <= SMAX); + return strdup(p); +} + +#endif + +/********************************* + * Scan down comma-expressions. + * Output: + * *pe = first elem down right side that is not an OPcomma + * Returns: + * code generated for left branches of comma-expressions + */ + +code *docommas(elem **pe) +{ elem *e; + code *cc; + unsigned stackpushsave; + int stackcleansave; + + stackpushsave = stackpush; + stackcleansave = cgstate.stackclean; + cgstate.stackclean = 0; + cc = CNIL; + e = *pe; + while (1) + { elem *eold; + regm_t retregs; + + if (configv.addlinenumbers && e->Esrcpos.Slinnum) + { cc = genlinnum(cc,e->Esrcpos); + //e->Esrcpos.Slinnum = 0; // don't do it twice + } + if (e->Eoper != OPcomma) + break; + retregs = 0; + cc = cat(cc,codelem(e->E1,&retregs,TRUE)); + eold = e; + e = e->E2; + freenode(eold); + } + *pe = e; + assert(cgstate.stackclean == 0); + cgstate.stackclean = stackcleansave; + cc = genstackclean(cc,stackpush - stackpushsave,0); + return cc; +} + +/************************** + * For elems in regcon that don't match regconsave, + * clear the corresponding bit in regcon.cse.mval. + * Do same for regcon.immed. + */ + +void andregcon(con_t *pregconsave) +{ + regm_t m = ~1; + for (int i = 0; i < REGMAX; i++) + { if (pregconsave->cse.value[i] != regcon.cse.value[i]) + regcon.cse.mval &= m; + if (pregconsave->immed.value[i] != regcon.immed.value[i]) + regcon.immed.mval &= m; + m <<= 1; + m |= 1; + } + //printf("regcon.cse.mval = x%x, regconsave->mval = x%x ",regcon.cse.mval,pregconsave->cse.mval); + regcon.used |= pregconsave->used; + regcon.cse.mval &= pregconsave->cse.mval; + regcon.immed.mval &= pregconsave->immed.mval; + regcon.params &= pregconsave->params; + //printf("regcon.cse.mval®con.cse.mops = x%x, regcon.cse.mops = x%x\n",regcon.cse.mval & regcon.cse.mops,regcon.cse.mops); + regcon.cse.mops &= regcon.cse.mval; +} + +#endif // !SPP diff --git a/backend/cgcs.c b/backend/cgcs.c new file mode 100644 index 00000000..9b981f04 --- /dev/null +++ b/backend/cgcs.c @@ -0,0 +1,683 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2012 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include + +#include "cc.h" +#include "oper.h" +#include "global.h" +#include "code.h" +#include "type.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +/********************************* + * Struct for each elem: + * Helem pointer to elem + * Hhash hash value for the elem + */ + +typedef struct HCS + { elem *Helem; + unsigned Hhash; + } hcs; + +static hcs *hcstab = NULL; /* array of hcs's */ +static unsigned hcsmax = 0; /* max index into hcstab[] */ +static unsigned hcstop; /* # of entries in hcstab[] */ +static unsigned touchstari; +static unsigned touchfunci[2]; + +// Use a bit vector for quick check if expression is possibly in hcstab[]. +// This results in much faster compiles when hcstab[] gets big. +static vec_t csvec; // vector of used entries +#define CSVECDIM 16001 //8009 //3001 // dimension of csvec (should be prime) + +STATIC void ecom(elem **); +STATIC unsigned cs_comphash(elem *); +STATIC void addhcstab(elem *,int); +STATIC void touchlvalue(elem *); +STATIC void touchfunc(int); +STATIC void touchstar(void); +STATIC void touchaccess(elem *); + +/******************************* + * Eliminate common subexpressions across extended basic blocks. + * String together as many blocks as we can. + */ + +void comsubs() +{ register block *bl,*blc,*bln; + register int n; /* # of blocks to treat as one */ + +//static int xx; +//printf("comsubs() %d\n", ++xx); +//debugx = (xx == 37); + +#ifdef DEBUG + if (debugx) dbg_printf("comsubs(%p)\n",startblock); +#endif + + // No longer do we just compute Bcount. We now eliminate unreachable + // blocks. + block_compbcount(); // eliminate unreachable blocks +#if SCPP + if (errcnt) + return; +#endif + + if (!csvec) + { + csvec = vec_calloc(CSVECDIM); + } + + for (bl = startblock; bl; bl = bln) + { + bln = bl->Bnext; + if (!bl->Belem) + continue; /* if no expression or no parents */ + + // Count up n, the number of blocks in this extended basic block (EBB) + n = 1; // always at least one block in EBB + blc = bl; + while (bln && list_nitems(bln->Bpred) == 1 && + ((blc->BC == BCiftrue && + list_block(list_next(blc->Bsucc)) == bln) || + (blc->BC == BCgoto && list_block(blc->Bsucc) == bln) + ) && + bln->BC != BCasm // no CSE's extending across ASM blocks + ) + { + n++; // add block to EBB + blc = bln; + bln = blc->Bnext; + } + vec_clear(csvec); + hcstop = 0; + touchstari = 0; + touchfunci[0] = 0; + touchfunci[1] = 0; + bln = bl; + while (n--) // while more blocks in EBB + { +#ifdef DEBUG + if (debugx) + dbg_printf("cses for block %p\n",bln); +#endif + if (bln->Belem) + ecom(&bln->Belem); // do the tree + bln = bln->Bnext; + } + } + +#ifdef DEBUG + if (debugx) + dbg_printf("done with comsubs()\n"); +#endif +} + +/******************************* + */ + +void cgcs_term() +{ + vec_free(csvec); + csvec = NULL; +#ifdef DEBUG + debugw && dbg_printf("freeing hcstab\n"); +#endif +#if TX86 + util_free(hcstab); +#else + MEM_PARF_FREE(hcstab); +#endif + hcstab = NULL; + hcsmax = 0; +} + +/************************* + * Eliminate common subexpressions for an element. + */ + +STATIC void ecom(elem **pe) +{ int i,op,hcstopsave; + unsigned hash; + elem *e,*ehash; + tym_t tym; + + e = *pe; + assert(e); + elem_debug(e); +#ifdef DEBUG + assert(e->Ecount == 0); + //assert(e->Ecomsub == 0); +#endif + tym = tybasic(e->Ety); + op = e->Eoper; + switch (op) + { + case OPconst: + case OPvar: + case OPrelconst: + break; + case OPstreq: + case OPpostinc: + case OPpostdec: + case OPeq: + case OPaddass: + case OPminass: + case OPmulass: + case OPdivass: + case OPmodass: + case OPshrass: + case OPashrass: + case OPshlass: + case OPandass: + case OPxorass: + case OPorass: +#if TX86 + /* Reverse order of evaluation for double op=. This is so that */ + /* the pushing of the address of the second operand is easier. */ + /* However, with the 8087 we don't need the kludge. */ + if (op != OPeq && tym == TYdouble && !config.inline8087) + { if (EOP(e->E1)) + ecom(&e->E1->E1); + ecom(&e->E2); + } + else +#endif + { + /* Don't mark the increment of an i++ or i-- as a CSE, if it */ + /* can be done with an INC or DEC instruction. */ + if (!(OTpost(op) && elemisone(e->E2))) + ecom(&e->E2); /* evaluate 2nd operand first */ + case OPnegass: + if (EOP(e->E1)) /* if lvalue is an operator */ + { +#ifdef DEBUG + if (e->E1->Eoper != OPind) + elem_print(e); +#endif + assert(e->E1->Eoper == OPind); + ecom(&(e->E1->E1)); + } + } + touchlvalue(e->E1); + if (!OTpost(op)) /* lvalue of i++ or i-- is not a cse*/ + { + hash = cs_comphash(e->E1); + vec_setbit(hash % CSVECDIM,csvec); + addhcstab(e->E1,hash); // add lvalue to hcstab[] + } + return; + + case OPbtc: + case OPbts: + case OPbtr: + ecom(&e->E1); + ecom(&e->E2); + touchfunc(0); // indirect assignment + return; + + case OPandand: + case OPoror: + ecom(&e->E1); + hcstopsave = hcstop; + ecom(&e->E2); + hcstop = hcstopsave; /* no common subs by E2 */ + return; /* if comsub then logexp() will */ + /* break */ + case OPcond: + ecom(&e->E1); + hcstopsave = hcstop; + ecom(&e->E2->E1); /* left condition */ + hcstop = hcstopsave; /* no common subs by E2 */ + ecom(&e->E2->E2); /* right condition */ + hcstop = hcstopsave; /* no common subs by E2 */ + return; /* can't be a common sub */ + case OPcall: + case OPcallns: + ecom(&e->E2); /* eval right first */ + /* FALL-THROUGH */ + case OPucall: + case OPucallns: + ecom(&e->E1); + touchfunc(1); + return; + case OPstrpar: /* so we don't break logexp() */ +#if TX86 + case OPinp: /* never CSE the I/O instruction itself */ +#endif + ecom(&e->E1); + /* FALL-THROUGH */ + case OPasm: + case OPstrthis: // don't CSE these + case OPframeptr: + case OPgot: + case OPctor: + case OPdtor: + case OPdctor: + case OPmark: + return; + + case OPddtor: + return; + + case OPparam: +#if TX86 + case OPoutp: +#endif + ecom(&e->E1); + case OPinfo: + ecom(&e->E2); + return; + case OPcomma: + case OPremquo: + ecom(&e->E1); + ecom(&e->E2); + break; +#if TARGET_SEGMENTED + case OPvp_fp: + case OPcvp_fp: + ecom(&e->E1); + touchaccess(e); + break; +#endif + case OPind: + ecom(&e->E1); + /* Generally, CSEing a *(double *) results in worse code */ + if (tyfloating(tym)) + return; + break; +#if TX86 + case OPstrcpy: + case OPstrcat: + case OPmemcpy: + case OPmemset: + ecom(&e->E2); + case OPsetjmp: + ecom(&e->E1); + touchfunc(0); + return; +#endif + default: /* other operators */ +#if TX86 +#ifdef DEBUG + if (!EBIN(e)) WROP(e->Eoper); +#endif + assert(EBIN(e)); + case OPadd: + case OPmin: + case OPmul: + case OPdiv: + case OPor: + case OPxor: + case OPand: + case OPeqeq: + case OPne: + case OPscale: + case OPyl2x: + case OPyl2xp1: + ecom(&e->E1); + ecom(&e->E2); + break; +#else +#ifdef DEBUG + if (!EOP(e)) WROP(e->Eoper); +#endif + assert(EOP(e)); + ecom(&e->E1); + if (EBIN(e)) + ecom(&e->E2); /* eval left first */ + break; +#endif + case OPstring: + case OPaddr: + case OPbit: +#ifdef DEBUG + WROP(e->Eoper); + elem_print(e); +#endif + assert(0); /* optelem() should have removed these */ + /* NOTREACHED */ + + // Explicitly list all the unary ops for speed + case OPnot: case OPcom: case OPneg: case OPuadd: + case OPabs: case OPsqrt: case OPrndtol: case OPsin: case OPcos: case OPrint: + case OPpreinc: case OPpredec: + case OPbool: case OPstrlen: case OPs16_32: case OPu16_32: + case OPd_s32: case OPd_u32: + case OPs32_d: case OPu32_d: case OPd_s16: case OPs16_d: case OP32_16: + case OPd_f: case OPf_d: + case OPd_ld: case OPld_d: + case OPc_r: case OPc_i: + case OPu8_16: case OPs8_16: case OP16_8: + case OPu32_64: case OPs32_64: case OP64_32: case OPmsw: + case OPu64_128: case OPs64_128: case OP128_64: + case OPd_s64: case OPs64_d: case OPd_u64: case OPu64_d: + case OPstrctor: case OPu16_d: case OPd_u16: + case OParrow: + case OPvoid: case OPnullcheck: + case OPbsf: case OPbsr: case OPbswap: case OPvector: + case OPld_u64: +#if TARGET_SEGMENTED + case OPoffset: case OPnp_fp: case OPnp_f16p: case OPf16p_np: +#endif + ecom(&e->E1); + break; + case OPhalt: + return; + } + + /* don't CSE structures or unions or volatile stuff */ + if (tym == TYstruct || + tym == TYvoid || + e->Ety & mTYvolatile +#if TX86 + || tyxmmreg(tym) + // don't CSE doubles if inline 8087 code (code generator can't handle it) + || (tyfloating(tym) && config.inline8087) +#endif + ) + return; + + hash = cs_comphash(e); /* must be AFTER leaves are done */ + + /* Search for a match in hcstab[]. + * Search backwards, as most likely matches will be towards the end + * of the list. + */ + +#ifdef DEBUG + if (debugx) dbg_printf("elem: %p hash: %6d\n",e,hash); +#endif + int csveci = hash % CSVECDIM; + if (vec_testbit(csveci,csvec)) + { + for (i = hcstop; i--;) + { +#ifdef DEBUG + if (debugx) + dbg_printf("i: %2d Hhash: %6d Helem: %p\n", + i,hcstab[i].Hhash,hcstab[i].Helem); +#endif + if (hash == hcstab[i].Hhash && (ehash = hcstab[i].Helem) != NULL) + { + /* if elems are the same and we still have room for more */ + if (el_match(e,ehash) && ehash->Ecount < 0xFF) + { + /* Make sure leaves are also common subexpressions + * to avoid false matches. + */ + if (!OTleaf(op)) + { + if (!e->E1->Ecount) + continue; + if (OTbinary(op) && !e->E2->Ecount) + continue; + } + ehash->Ecount++; + *pe = ehash; +#ifdef DEBUG + if (debugx) + dbg_printf("**MATCH** %p with %p\n",e,*pe); +#endif + el_free(e); + return; + } + } + } + } + else + vec_setbit(csveci,csvec); + addhcstab(e,hash); // add this elem to hcstab[] +} + +/************************** + * Compute hash function for elem e. + */ + +STATIC unsigned cs_comphash(elem *e) +{ register int hash; + unsigned op; + + elem_debug(e); + op = e->Eoper; +#if TX86 + hash = (e->Ety & (mTYbasic | mTYconst | mTYvolatile)) + (op << 8); +#else + hash = e->Ety + op; +#endif + if (!OTleaf(op)) + { hash += (size_t) e->E1; + if (OTbinary(op)) + hash += (size_t) e->E2; + } + else + { hash += e->EV.Vint; + if (op == OPvar || op == OPrelconst) + hash += (size_t) e->EV.sp.Vsym; + } + return hash; +} + +/**************************** + * Add an elem to the common subexpression table. + * Recompute hash if it is 0. + */ + +STATIC void addhcstab(elem *e,int hash) +{ unsigned h = hcstop; + + if (h >= hcsmax) /* need to reallocate table */ + { + assert(h == hcsmax); + // With 32 bit compiles, we've got memory to burn + hcsmax += (__INTSIZE == 4) ? (hcsmax + 128) : 100; + assert(h < hcsmax); +#if TX86 + hcstab = (hcs *) util_realloc(hcstab,hcsmax,sizeof(hcs)); +#else + hcstab = (hcs *) MEM_PARF_REALLOC(hcstab,hcsmax*sizeof(hcs)); +#endif + //printf("hcstab = %p; hcstop = %d, hcsmax = %d\n",hcstab,hcstop,hcsmax); + } + hcstab[h].Helem = e; + hcstab[h].Hhash = hash; + hcstop++; +} + +/*************************** + * "touch" the elem. + * If it is a pointer, "touch" all the suspects + * who could be pointed to. + * Eliminate common subs that are indirect loads. + */ + +STATIC void touchlvalue(elem *e) +{ register int i; + + if (e->Eoper == OPind) /* if indirect store */ + { + /* NOTE: Some types of array assignments do not need + * to touch all variables. (Like a[5], where a is an + * array instead of a pointer.) + */ + + touchfunc(0); + return; + } + + for (i = hcstop; --i >= 0;) + { if (hcstab[i].Helem && + hcstab[i].Helem->EV.sp.Vsym == e->EV.sp.Vsym) + hcstab[i].Helem = NULL; + } + + assert(e->Eoper == OPvar || e->Eoper == OPrelconst); + switch (e->EV.sp.Vsym->Sclass) + { + case SCregpar: + case SCregister: + case SCtmp: + case SCpseudo: + break; + case SCauto: + case SCparameter: + case SCfastpar: + case SCbprel: + if (e->EV.sp.Vsym->Sflags & SFLunambig) + break; + /* FALL-THROUGH */ + case SCstatic: + case SCextern: + case SCglobal: + case SClocstat: + case SCcomdat: + case SCinline: + case SCsinline: + case SCeinline: + case SCcomdef: + touchstar(); + break; + default: +#ifdef DEBUG + elem_print(e); + symbol_print(e->EV.sp.Vsym); +#endif + assert(0); + } +} + +/************************** + * "touch" variables that could be changed by a function call or + * an indirect assignment. + * Eliminate any subexpressions that are "starred" (they need to + * be recomputed). + * Input: + * flag If !=0, then this is a function call. + * If 0, then this is an indirect assignment. + */ + +STATIC void touchfunc(int flag) +{ register hcs *pe,*petop; + register elem *he; + + //printf("touchfunc(%d)\n", flag); + petop = &hcstab[hcstop]; + //pe = &hcstab[0]; printf("pe = %p, petop = %p\n",pe,petop); + for (pe = &hcstab[0]; pe < petop; pe++) + //for (pe = &hcstab[touchfunci[flag]]; pe < petop; pe++) + { he = pe->Helem; + if (!he) + continue; + switch (he->Eoper) + { + case OPvar: + switch (he->EV.sp.Vsym->Sclass) + { + case SCregpar: + case SCregister: + case SCtmp: + break; + case SCauto: + case SCparameter: + case SCfastpar: + case SCbprel: + //printf("he = '%s'\n", he->EV.sp.Vsym->Sident); + if (he->EV.sp.Vsym->Sflags & SFLunambig) + break; + /* FALL-THROUGH */ + case SCstatic: + case SCextern: + case SCcomdef: + case SCglobal: + case SClocstat: + case SCcomdat: + case SCpseudo: + case SCinline: + case SCsinline: + case SCeinline: + if (!(he->EV.sp.Vsym->ty() & mTYconst)) + goto L1; + break; + default: + debug(WRclass((enum SC)he->EV.sp.Vsym->Sclass)); + assert(0); + } + break; + case OPind: +#if TX86 + case OPstrlen: + case OPstrcmp: + case OPmemcmp: + case OPbt: +#endif + goto L1; +#if TARGET_SEGMENTED + case OPvp_fp: + case OPcvp_fp: + if (flag == 0) /* function calls destroy vptrfptr's, */ + break; /* not indirect assignments */ +#endif + L1: + pe->Helem = NULL; + break; + } + } + touchfunci[flag] = hcstop; +} + + +/******************************* + * Eliminate all common subexpressions that + * do any indirection ("starred" elems). + */ + +STATIC void touchstar() +{ register int i; + register elem *e; + + for (i = touchstari; i < hcstop; i++) + { e = hcstab[i].Helem; + if (e && (e->Eoper == OPind || e->Eoper == OPbt) /*&& !(e->Ety & mTYconst)*/) + hcstab[i].Helem = NULL; + } + touchstari = hcstop; +} + +#if TARGET_SEGMENTED +/***************************************** + * Eliminate any common subexpressions that could be modified + * if a handle pointer access occurs. + */ + +STATIC void touchaccess(elem *ev) +{ register int i; + register elem *e; + + ev = ev->E1; + for (i = 0; i < hcstop; i++) + { e = hcstab[i].Helem; + /* Invalidate any previous handle pointer accesses that */ + /* are not accesses of ev. */ + if (e && (e->Eoper == OPvp_fp || e->Eoper == OPcvp_fp) && e->E1 != ev) + hcstab[i].Helem = NULL; + } +} +#endif + +#endif // !SPP diff --git a/backend/cgcv.c b/backend/cgcv.c new file mode 100644 index 00000000..96645a25 --- /dev/null +++ b/backend/cgcv.c @@ -0,0 +1,2705 @@ +// Copyright (C) 1984-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +#if (SCPP || MARS) && !HTOD + +#include +#include +#include +#include + +#if _WIN32 || linux +#include +#endif + +#if __sun&&__SVR4 +#include +#endif + +#include "cc.h" +#include "type.h" +#include "code.h" +#include "cgcv.h" +#include "cv4.h" +#include "global.h" +#if SCPP +#include "parser.h" +#include "cpp.h" +#endif + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +// Convert from SFL protections to CV4 protections +#define SFLtoATTR(sfl) (4 - (((sfl) & SFLpmask) >> 5)) + +extern targ_size_t startoffset; // size of function entry code +extern targ_size_t retoffset; // offset from start of func to ret code + +/* Dynamic array of debtyp_t's */ +static debtyp_t **debtyp; +static unsigned debtyptop; // # of used entries in debtyp[] +static unsigned debtypmax; // current size of debtyp[] +static vec_t debtypvec; // vector of used entries +#define DEBTYPVECDIM 16001 //8009 //3001 // dimension of debtypvec (should be prime) + +#define DEBTYPHASHDIM 1009 +static unsigned debtyphash[DEBTYPHASHDIM]; + +#if MARS +// char *ftdbname; // in ztc/var.c +#define TDB 0 +#else +#define TDB 1 +#endif + +#define DEB_NULL cgcv.deb_offset // index of null debug type record + +/* This limitation is because of 4K page sizes + * in optlink/cv/cvhashes.asm + */ +#define CVIDMAX (0xFF0-20) // the -20 is picked by trial and error + +#if 0 +#define DBG(a) a +#else +#define DBG(a) +#endif + +#define LOCATsegrel 0xC000 + +/* Unfortunately, the fixup stuff is different for EASY OMF and Microsoft */ +#define EASY_LCFDoffset (LOCATsegrel | 0x1404) +#define EASY_LCFDpointer (LOCATsegrel | 0x1800) + +#define LCFD32offset (LOCATsegrel | 0x2404) +#define LCFD32pointer (LOCATsegrel | 0x2C00) +#define LCFD16pointer (LOCATsegrel | 0x0C00) + +Cgcv cgcv; + +STATIC void cv3_symdes ( unsigned char *p , unsigned next ); +STATIC unsigned cv3_paramlist ( type *t , unsigned nparam ); +STATIC unsigned cv3_struct ( symbol *s ); +STATIC char * cv4_prettyident(symbol *s); +STATIC unsigned cv4_symtypidx ( symbol *s ); +STATIC void cv4_outsym(symbol *s); +STATIC void cv4_func(Funcsym *s); + +/****************************************** + * Return number of bytes consumed in OBJ file by a name. + */ + +#if SCPP +inline +#endif +int cv_stringbytes(const char *name) +{ size_t len; + + len = strlen(name); + if (len > CVIDMAX) + len = CVIDMAX; + return len + ((len > 255) ? 4 : 1); +} + +/****************************************** + * Stuff a namestring into p. + * Returns: + * number of bytes consumed + */ + +int cv_namestring(unsigned char *p,const char *name) +{ unsigned len; + + len = strlen(name); + if (len > 255) + { p[0] = 0xFF; + p[1] = 0; + if (len > CVIDMAX) + len = CVIDMAX; + TOWORD(p + 2,len); + memcpy(p + 4,name,len); + len += 4; + } + else + { p[0] = len; + memcpy(p + 1,name,len); + len++; + } + return len; +} + +/*********************************** + * Compute debug register number for symbol s. + * Returns: + * 0..7 byte registers + * 8..15 word registers + * 16..23 dword registers + */ + +STATIC int cv_regnum(symbol *s) +{ unsigned reg; + + reg = s->Sreglsw; +#if SCPP + if (s->Sclass == SCpseudo) + { + reg = pseudoreg[reg]; + } + else +#endif + { + assert(reg < 8); + assert(s->Sfl == FLreg); + switch (type_size(s->Stype)) + { + case LONGSIZE: + case 3: reg += 8; + case SHORTSIZE: reg += 8; + case CHARSIZE: break; + + case LLONGSIZE: + reg += (s->Sregmsw << 8) + (16 << 8) + 16; + if (config.fulltypes == CV4) + reg += (1 << 8); + break; + + default: +#if 0 + symbol_print(s); + type_print(s->Stype); + printf("size = %d\n",type_size(s->Stype)); +#endif + assert(0); + } + } + if (config.fulltypes == CV4) + reg++; + return reg; +} + +/*********************************** + * Allocate a debtyp_t. + */ + +debtyp_t * debtyp_alloc(unsigned length) +{ + debtyp_t *d; + + //printf("len = %u, x%x\n", length, length); +#ifdef DEBUG + unsigned len = sizeof(debtyp_t) - sizeof(d->data) + length; + assert(len < 4 * 4096 - 100); + d = (debtyp_t *) mem_malloc(len /*+ 1*/); + memset(d, 0xAA, len); +// ((char*)d)[len] = 0x2E; +#else + assert(length < 0x10000); + d = (debtyp_t *) malloc(sizeof(debtyp_t) - sizeof(d->data) + length); +#endif + d->length = length; + //printf("debtyp_alloc(%d) = %p\n", length, d); + return d; +} + +/*********************************** + * Free a debtyp_t. + */ + +STATIC void debtyp_free(debtyp_t *d) +{ + //printf("debtyp_free(length = %d, %p)\n", d->length, d); + //fflush(stdout); +#ifdef DEBUG + unsigned len = sizeof(debtyp_t) - sizeof(d->data) + d->length; + assert(len < 4 * 4096 - 100); +// assert(((char*)d)[len] == 0x2E); + memset(d, 0x55, len); + mem_free(d); +#else + free(d); +#endif +} + +#if 0 +void debtyp_check(debtyp_t *d,int linnum) +{ int i; + static volatile char c; + + //printf("linnum = %d\n",linnum); + //printf(" length = %d\n",d->length); + for (i = 0; i < d->length; i++) + c = d->data[i]; +} + +#define debtyp_check(d) debtyp_check(d,__LINE__); +#else +#define debtyp_check(d) +#endif + +/*********************************** + * Search for debtyp_t in debtyp[]. If it is there, return the index + * of it, and free d. Otherwise, add it. + * Returns: + * index in debtyp[] + */ + +idx_t cv_debtyp(debtyp_t *d) +{ unsigned u; + unsigned short length; + unsigned hashi; + + assert(d); + length = d->length; + //printf("length = %3d\n",length); +#if OMFOBJ && TDB + if (config.fulltypes == CVTDB) + { + idx_t result; + +#if 1 + assert(length); + debtyp_check(d); + result = tdb_typidx(&d->length); +#else + unsigned char *buf; + + // Allocate buffer + buf = malloc(6 + length); + if (!buf) + err_nomem(); // out of memory + + // Fill the buffer + TOLONG(buf,cgcv.signature); + memcpy(buf + 4,(char *)d + sizeof(unsigned),2 + length); + +#if 0 +{int i; + for (i=0;i= sizeof(unsigned)) + { + // Hash consists of the sum of the first 4 bytes with the last 4 bytes + union { unsigned char* cp; unsigned* up; } u; + u.cp = d->data; + hash += *u.up; + u.cp += length - sizeof(unsigned); + hash += *u.up; + } + hashi = hash % DEBTYPHASHDIM; + hash %= DEBTYPVECDIM; + + if (vec_testbit(hash,debtypvec)) + { +//printf(" test"); +#if 1 + // Threaded list is much faster + for (u = debtyphash[hashi]; u; u = debtyp[u]->prev) +#else + for (u = debtyptop; u--; ) +#endif + { + if (length == debtyp[u]->length && + memcmp(d->data,debtyp[u]->data,length) == 0) + { debtyp_free(d); +//printf(" match %d\n",u); + return u + cgcv.deb_offset; + } + } + } + else + vec_setbit(hash,debtypvec); + } + else + hashi = 1; +//printf(" add %d\n",debtyptop); + d->prev = debtyphash[hashi]; + debtyphash[hashi] = debtyptop; + + /* It's not already in the array, so add it */ +L1: + if (debtyptop == debtypmax) + { + //printf("reallocate debtyp[] %p\n", debtyp); +#ifdef DEBUG + debtypmax += 10; +#else + debtypmax += debtypmax + 16; +#if __INTSIZE == 4 + if (debtypmax > 0xE000) + debtypmax = 0xE000; +#if SCPP + if (debtyptop >= debtypmax) + err_fatal(EM_2manytypes,debtypmax); // too many types +#endif +#endif +#endif + // Don't use MEM here because we can allocate pretty big + // arrays with this, and we don't want to overflow the PH + // page size. + debtyp = (debtyp_t **) util_realloc(debtyp,sizeof(*debtyp),debtypmax); + } + debtyp[debtyptop] = d; + return debtyptop++ + cgcv.deb_offset; +} + +/**************************** + * Store a null record at DEB_NULL. + */ + +void cv_init() +{ debtyp_t *d; + + //printf("cv_init()\n"); + + // Initialize statics + debtyp = NULL; + debtyptop = 0; + debtypmax = 0; + if (!ftdbname) + ftdbname = (char *)"symc.tdb"; + + memset(&cgcv,0,sizeof(cgcv)); + cgcv.sz_idx = 2; + cgcv.LCFDoffset = LCFD32offset; + cgcv.LCFDpointer = LCFD16pointer; + + debtypvec = vec_calloc(DEBTYPVECDIM); + memset(debtyphash,0,sizeof(debtyphash)); + + /* Reset for different OBJ file formats */ + if (I32) + { + // Adjust values in old CV tables for 32 bit ints + dttab[TYenum] = dttab[TYlong]; + dttab[TYint] = dttab[TYlong]; + dttab[TYuint] = dttab[TYulong]; + + // Adjust Codeview 4 values for 32 bit ints and 32 bit pointer offsets + dttab4[TYenum] = 0x74; + dttab4[TYint] = 0x74; + dttab4[TYuint] = 0x75; + dttab4[TYptr] = 0x400; + dttab4[TYnptr] = 0x400; + dttab4[TYjhandle] = 0x400; +#if TARGET_SEGMENTED + dttab4[TYsptr] = 0x400; + dttab4[TYcptr] = 0x400; + dttab4[TYfptr] = 0x500; +#endif + + if (config.flags & CFGeasyomf) + { cgcv.LCFDoffset = EASY_LCFDoffset; + cgcv.LCFDpointer = EASY_LCFDpointer; + assert(config.fulltypes == CVOLD); + } + else + cgcv.LCFDpointer = LCFD32pointer; + + if (config.exe & EX_flat) + cgcv.FD_code = 0x10; + } + + if (config.fulltypes >= CV4) + { int flags; + static unsigned short memmodel[5] = {0,0x100,0x20,0x120,0x120}; + char version[1 + sizeof(VERSION)]; + unsigned char debsym[8 + sizeof(version)]; + const char *x = "1MYS"; + + // Put out signature indicating CV4 format + cgcv.signature = (config.fulltypes == CV4) ? 1 : *(int *) x; + + cgcv.deb_offset = 0x1000; + + if (config.fulltypes >= CVSYM) + { cgcv.sz_idx = 4; + if (!(config.flags2 & CFG2phgen)) + cgcv.deb_offset = 0x80000000; + } + + obj_write_bytes(SegData[DEBSYM],4,&cgcv.signature); + + // Allocate an LF_ARGLIST with no arguments + if (config.fulltypes == CV4) + { d = debtyp_alloc(4); + TOWORD(d->data,LF_ARGLIST); + TOWORD(d->data + 2,0); + } + else + { d = debtyp_alloc(6); + TOWORD(d->data,LF_ARGLIST); + TOLONG(d->data + 2,0); + } + + // Put out S_COMPILE record + TOWORD(debsym + 2,S_COMPILE); + switch (config.target_cpu) + { case TARGET_8086: debsym[4] = 0; break; + case TARGET_80286: debsym[4] = 2; break; + case TARGET_80386: debsym[4] = 3; break; + case TARGET_80486: debsym[4] = 4; break; + case TARGET_Pentium: + case TARGET_PentiumMMX: + debsym[4] = 5; break; + case TARGET_PentiumPro: + case TARGET_PentiumII: + debsym[4] = 6; break; + default: assert(0); + } + debsym[5] = (CPP != 0); // 0==C, 1==C++ + flags = (config.inline8087) ? (0<<3) : (1<<3); + if (I32) + flags |= 0x80; // 32 bit addresses + flags |= memmodel[config.memmodel]; + TOWORD(debsym + 6,flags); + version[0] = 'Z'; + strcpy(version + 1,VERSION); + cv_namestring(debsym + 8,version); + TOWORD(debsym,6 + sizeof(version)); + obj_write_bytes(SegData[DEBSYM],8 + sizeof(version),debsym); + +#if OMFOBJ && TDB + // Put out S_TDBNAME record + if (config.fulltypes == CVTDB) + { + unsigned char *ds; + size_t len; + + pstate.STtdbtimestamp = tdb_gettimestamp(); + len = cv_stringbytes(ftdbname); + ds = (unsigned char *) alloca(8 + len); + TOWORD(ds,6 + len); + TOWORD(ds + 2,S_TDBNAME); + TOLONG(ds + 4,pstate.STtdbtimestamp); + cv_namestring(ds + 8,ftdbname); + obj_write_bytes(SegData[DEBSYM],8 + len,ds); + } +#endif + } + else + { + assert(0); + } +#if TDB + if (config.fulltypes == CVTDB) + cgcv.deb_offset = cv_debtyp(d); + else +#endif + cv_debtyp(d); +} + +/////////////////////////// CodeView 4 /////////////////////////////// + +/*********************************** + * Return number of bytes required to store a numeric leaf. + */ + +inline unsigned cv4_numericbytes(targ_size_t value) +{ unsigned u; + + if (value < 0x8000) + u = 2; + else if (value < 0x10000) + u = 4; + else + u = 6; + return u; +} + +/******************************** + * Store numeric leaf. + * Must use exact same number of bytes as cv4_numericbytes(). + */ + +void cv4_storenumeric(unsigned char *p,targ_size_t value) +{ + if (value < 0x8000) + TOWORD(p,value); + else if (value < 0x10000) + { TOWORD(p,LF_USHORT); + p += 2; + TOWORD(p,value); + } + else + { TOWORD(p,LF_ULONG); + *(targ_ulong *)(p + 2) = (unsigned long) value; + } +} + +/********************************* + * Generate a type index for a parameter list. + */ + +idx_t cv4_arglist(type *t,unsigned *pnparam) +{ unsigned u; + unsigned nparam; + idx_t paramidx; + debtyp_t *d; + param_t *p; + + // Compute nparam, number of parameters + nparam = 0; + for (p = t->Tparamtypes; p; p = p->Pnext) + nparam++; + *pnparam = nparam; + + // Construct an LF_ARGLIST of those parameters + if (nparam == 0) + paramidx = DEB_NULL; + else + { + if (config.fulltypes == CV4) + { d = debtyp_alloc(2 + 2 + nparam * 2); + TOWORD(d->data,LF_ARGLIST); + TOWORD(d->data + 2,nparam); + + p = t->Tparamtypes; + for (u = 0; u < nparam; u++) + { TOWORD(d->data + 4 + u * 2,cv4_typidx(p->Ptype)); + p = p->Pnext; + } + } + else + { d = debtyp_alloc(2 + 4 + nparam * 4); + TOWORD(d->data,LF_ARGLIST); + TOLONG(d->data + 2,nparam); + + p = t->Tparamtypes; + for (u = 0; u < nparam; u++) + { TOLONG(d->data + 6 + u * 4,cv4_typidx(p->Ptype)); + p = p->Pnext; + } + } + paramidx = cv_debtyp(d); + } + return paramidx; +} + +/***************************** + * Build LF_METHODLIST for overloaded member function. + * Output: + * *pcount # of entries in method list + * Returns: + * type index of method list + * 0 don't do this one + */ + +#if SCPP + +STATIC int cv4_methodlist(symbol *sf,int *pcount) +{ int count; + int mlen; + symbol *s; + debtyp_t *d; + unsigned char *p; + unsigned short attribute; + + symbol_debug(sf); + + // First, compute how big the method list is + count = 0; + mlen = 2; + for (s = sf; s; s = s->Sfunc->Foversym) + { + if (s->Sclass == SCtypedef || s->Sclass == SCfunctempl) + continue; + if (s->Sfunc->Fflags & Fnodebug) + continue; + if (s->Sfunc->Fflags & Fintro) + mlen += 4; + mlen += cgcv.sz_idx * 2; + count++; + } + + if (!count) + return 0; + + // Allocate and fill it in + d = debtyp_alloc(mlen); + p = d->data; + TOWORD(p,LF_METHODLIST); + p += 2; + for (s = sf; s; s = s->Sfunc->Foversym) + { + if (s->Sclass == SCtypedef || s->Sclass == SCfunctempl) + continue; + if (s->Sfunc->Fflags & Fnodebug) + continue; + attribute = SFLtoATTR(s->Sflags); + // Make sure no overlapping bits + assert((Fvirtual | Fpure | Fintro | Fstatic) == (Fvirtual ^ Fpure ^ Fintro ^ Fstatic)); + switch ((s->Sfunc->Fflags & (Fvirtual | Fstatic)) | + (s->Sfunc->Fflags & (Fpure | Fintro))) + { + // BUG: should we have 0x0C, friend functions? + case Fstatic: attribute |= 0x08; break; + case Fvirtual: attribute |= 0x04; break; + case Fvirtual | Fintro: attribute |= 0x10; break; + case Fvirtual | Fpure: attribute |= 0x14; break; + case Fvirtual | Fintro | Fpure: attribute |= 0x18; break; + case 0: + break; + default: +#ifdef DEBUG + symbol_print(s); +#endif + assert(0); + } + TOIDX(p,attribute); + p += cgcv.sz_idx; + TOIDX(p,cv4_symtypidx(s)); + p += cgcv.sz_idx; + if (s->Sfunc->Fflags & Fintro) + { TOLONG(p,cpp_vtbloffset((Classsym *)s->Sscope,s)); + p += 4; + } + } + assert(p - d->data == mlen); + + *pcount = count; + return cv_debtyp(d); +} + +#endif + +/********************************** + * Pretty-print indentifier for CV4 types. + */ + +#if SCPP + +STATIC char * cv4_prettyident(symbol *s) +{ symbol *stmp; + char *p; + + stmp = s->Sscope; + s->Sscope = NULL; // trick cpp_prettyident into leaving off :: + p = cpp_prettyident(s); + s->Sscope = (Classsym *)stmp; + return p; +} + +#endif + +/**************************** + * Return type index of struct. + * Input: + * s struct tag symbol + * flags + * 0 generate a reference to s + * 1 just saw the definition of s + * 2 saw key function for class s + * 3 no longer have a key function for class s + */ + +idx_t cv4_struct(Classsym *s,int flags) +{ targ_size_t size; + debtyp_t *d,*dt; + unsigned len; + unsigned nfields,fnamelen; + idx_t typidx; + type *t; + symlist_t sl; + struct_t *st; + char *id; +#if SCPP + baseclass_t *b; +#endif + unsigned numidx; + unsigned leaf; + unsigned property; + unsigned attribute; + unsigned char *p; + int refonly; + int i; + int count; // COUNT field in LF_CLASS + + _chkstack(); + symbol_debug(s); + assert(config.fulltypes >= CV4); + st = s->Sstruct; + if (st->Sflags & STRanonymous) // if anonymous class/union + return 0; + + //dbg_printf("cv4_struct(%s,%d)\n",s->Sident,flags); + t = s->Stype; + //printf("t = %p, Tflags = x%x\n", t, t->Tflags); + type_debug(t); + + // Determine if we should do a reference or a definition + refonly = 1; // assume reference only + if (MARS || t->Tflags & TFsizeunknown || st->Sflags & STRoutdef) + { + //printf("ref only\n"); + } + else + { + // We have a definition that we have not put out yet + switch (flags) + { case 0: // reference to s +#if SCPP + if (!CPP || + config.flags2 & (CFG2fulltypes | CFG2hdrdebug) || + !(st->Sflags & STRvtblext)) + refonly = 0; +#else + refonly = 0; +#endif + break; + case 1: // saw def of s + if (!s->Stypidx) // if not forward referenced + return 0; +#if SCPP + if (!CPP || + config.flags2 & CFG2fulltypes || + !(st->Sflags & STRvtblext)) + refonly = 0; +#endif + break; +#if SCPP + case 2: // saw key func for s + if (config.flags2 & CFG2fulltypes) + return 0; + refonly = 0; + break; + case 3: // no longer have key func for s + if (!s->Stypidx || config.flags2 & CFG2fulltypes) + return 0; + refonly = 0; + break; +#endif + default: + assert(0); + } + } + + if (MARS || refonly) + { + if (s->Stypidx) // if reference already generated + { //assert(s->Stypidx - cgcv.deb_offset < debtyptop); + return s->Stypidx; // use already existing reference + } + size = 0; + property = 0x80; // class is forward referenced + } + else + { size = type_size(t); + st->Sflags |= STRoutdef; + property = 0; + } + +#if SCPP + if (CPP) + { + if (s->Sscope) // if class is nested + property |= 8; + if (st->Sctor || st->Sdtor) + property |= 2; // class has ctors and/or dtors + if (st->Sopoverload) + property |= 4; // class has overloaded operators + if (st->Scastoverload) + property |= 0x40; // class has casting methods + if (st->Sopeq && !(st->Sopeq->Sfunc->Fflags & Fnodebug)) + property |= 0x20; // class has overloaded assignment + } +#endif + id = prettyident(s); + if (config.fulltypes == CV4) + { numidx = (st->Sflags & STRunion) ? 8 : 12; + len = numidx + cv4_numericbytes(size); + d = debtyp_alloc(len + cv_stringbytes(id)); + cv4_storenumeric(d->data + numidx,size); + } + else + { numidx = (st->Sflags & STRunion) ? 10 : 18; + len = numidx + 4; + d = debtyp_alloc(len + cv_stringbytes(id)); + TOLONG(d->data + numidx,size); + } + len += cv_namestring(d->data + len,id); + switch (s->Sclass) + { case SCstruct: + leaf = LF_STRUCTURE; + if (st->Sflags & STRunion) + { leaf = LF_UNION; + break; + } + if (st->Sflags & STRclass) + leaf = LF_CLASS; + goto L1; + L1: + if (config.fulltypes == CV4) + TOWORD(d->data + 8,0); // dList + else + TOLONG(d->data + 10,0); // dList +#if SCPP + if (CPP) + { debtyp_t *vshape; + unsigned n; + unsigned char descriptor; + list_t vl; + + vl = st->Svirtual; + n = list_nitems(vl); + if (n == 0) // if no virtual functions + { + if (config.fulltypes == CV4) + TOWORD(d->data + 10,0); // vshape is 0 + else + TOLONG(d->data + 14,0); // vshape is 0 + } + else + { + vshape = debtyp_alloc(4 + (n + 1) / 2); + TOWORD(vshape->data,LF_VTSHAPE); + TOWORD(vshape->data + 2,1); + + n = 0; + descriptor = 0; + for (; vl; vl = list_next(vl)) + { mptr_t *m; + tym_t ty; + + m = list_mptr(vl); + symbol_debug(m->MPf); + ty = tybasic(m->MPf->ty()); + assert(tyfunc(ty)); + if (intsize == 4) + descriptor |= 5; + if (tyfarfunc(ty)) + descriptor++; + vshape->data[4 + n / 2] = descriptor; + descriptor <<= 4; + n++; + } + if (config.fulltypes == CV4) + TOWORD(d->data + 10,cv_debtyp(vshape)); // vshape + else + TOLONG(d->data + 14,cv_debtyp(vshape)); // vshape + } + } + else +#endif + { + if (config.fulltypes == CV4) + TOWORD(d->data + 10,0); // vshape + else + TOLONG(d->data + 14,0); // vshape + } + break; + default: +#if DEBUG && SCPP + symbol_print(s); +#endif + assert(0); + } + TOWORD(d->data,leaf); + + // Assign a number to prevent infinite recursion if a struct member + // references the same struct. +#if OMFOBJ && TDB + if (config.fulltypes == CVTDB) + { + TOWORD(d->data + 2,0); // number of fields + TOLONG(d->data + 6,0); // field list is 0 + TOWORD(d->data + 4,property | 0x80); // set fwd ref bit +#if 0 +printf("fwd struct ref\n"); +{int i; + printf("len = %d, length = %d\n",len,d->length); + for (i=0;ilength;i++) + printf("%02x ",d->data[i]); + printf("\n"); +} +#endif + debtyp_check(d); + s->Stypidx = tdb_typidx(&d->length); // forward reference it + } + else +#endif + { + d->length = 0; // so cv_debtyp() will allocate new + s->Stypidx = cv_debtyp(d); + d->length = len; // restore length + } + + if (refonly) // if reference only + { + //printf("refonly\n"); + TOWORD(d->data + 2,0); // count: number of fields is 0 + if (config.fulltypes == CV4) + { TOWORD(d->data + 4,0); // field list is 0 + TOWORD(d->data + 6,property); + } + else + { TOLONG(d->data + 6,0); // field list is 0 + TOWORD(d->data + 4,property); + } + return s->Stypidx; + } + +#if MARS + util_progress(); +#else + file_progress(); +#endif + + // Compute the number of fields, and the length of the fieldlist record + nfields = 0; + fnamelen = 2; +#if SCPP + if (CPP) + { + // Base classes come first + for (b = st->Sbase; b; b = b->BCnext) + { + if (b->BCflags & BCFvirtual) // skip virtual base classes + continue; + nfields++; + fnamelen += ((config.fulltypes == CV4) ? 6 : 8) + + cv4_numericbytes(b->BCoffset); + } + + // Now virtual base classes (direct and indirect) + for (b = st->Svirtbase; b; b = b->BCnext) + { + nfields++; + fnamelen += ((config.fulltypes == CV4) ? 8 : 12) + + cv4_numericbytes(st->Svbptr_off) + + cv4_numericbytes(b->BCvbtbloff / intsize); + } + + // Now friend classes + i = list_nitems(st->Sfriendclass); + nfields += i; + fnamelen += i * ((config.fulltypes == CV4) ? 4 : 8); + + // Now friend functions + for (sl = st->Sfriendfuncs; sl; sl = list_next(sl)) + { symbol *sf = list_symbol(sl); + + symbol_debug(sf); + if (sf->Sclass == SCfunctempl) + continue; + nfields++; + fnamelen += ((config.fulltypes == CV4) ? 4 : 6) + + cv_stringbytes(cpp_unmangleident(sf->Sident)); + } + } +#endif + count = nfields; + for (sl = st->Sfldlst; sl; sl = list_next(sl)) + { symbol *sf = list_symbol(sl); + targ_size_t offset; + char *id; + unsigned len; + + symbol_debug(sf); + id = sf->Sident; + switch (sf->Sclass) + { case SCmember: + case SCfield: +#if SCPP + if (CPP && sf == s->Sstruct->Svptr) + fnamelen += ((config.fulltypes == CV4) ? 4 : 8); + else +#endif + { offset = sf->Smemoff; + fnamelen += ((config.fulltypes == CV4) ? 6 : 8) + + cv4_numericbytes(offset) + cv_stringbytes(id); + } + break; +#if SCPP + case SCstruct: + if (sf->Sstruct->Sflags & STRanonymous) + continue; + if (sf->Sstruct->Sflags & STRnotagname) + id = cpp_name_none; + property |= 0x10; // class contains nested classes + goto Lnest2; + + case SCenum: + if (sf->Senum->SEflags & SENnotagname) + id = cpp_name_none; + goto Lnest2; + + case SCtypedef: + Lnest2: + fnamelen += ((config.fulltypes == CV4) ? 4 : 8) + + cv_stringbytes(id); + break; + + case SCextern: + case SCcomdef: + case SCglobal: + case SCstatic: + case SCinline: + case SCsinline: + case SCeinline: + case SCcomdat: + if (tyfunc(sf->ty())) + { symbol *so; + int nfuncs; + + nfuncs = 0; + for (so = sf; so; so = so->Sfunc->Foversym) + { + if (so->Sclass == SCtypedef || + so->Sclass == SCfunctempl || + so->Sfunc->Fflags & Fnodebug) // if compiler generated + continue; // skip it + nfuncs++; + } + if (nfuncs == 0) + continue; + + if (nfuncs > 1) + count += nfuncs - 1; + + id = cv4_prettyident(sf); + } + fnamelen += ((config.fulltypes == CV4) ? 6 : 8) + + cv_stringbytes(id); + break; +#endif + default: + continue; + } + nfields++; + count++; + } + + TOWORD(d->data + 2,count); + if (config.fulltypes == CV4) + TOWORD(d->data + 6,property); + else + TOWORD(d->data + 4,property); + + // Generate fieldlist type record + dt = debtyp_alloc(fnamelen); + p = dt->data; + TOWORD(p,LF_FIELDLIST); + + // And fill it in + p += 2; +#if SCPP + if (CPP) + { + // Put out real base classes + for (b = st->Sbase; b; b = b->BCnext) + { targ_size_t offset; + + if (b->BCflags & BCFvirtual) // skip virtual base classes + continue; + offset = b->BCoffset; + typidx = cv4_symtypidx(b->BCbase); + + attribute = (b->BCflags & BCFpmask); + if (attribute & 4) + attribute = 1; + else + attribute = 4 - attribute; + + TOWORD(p,LF_BCLASS); + if (config.fulltypes == CV4) + { TOWORD(p + 2,typidx); + TOWORD(p + 4,attribute); + p += 6; + } + else + { TOLONG(p + 4,typidx); + TOWORD(p + 2,attribute); + p += 8; + } + + cv4_storenumeric(p,offset); + p += cv4_numericbytes(offset); + } + + // Now direct followed by indirect virtual base classes + i = LF_VBCLASS; + do + { + for (b = st->Svirtbase; b; b = b->BCnext) + { targ_size_t vbpoff,vboff; + type *vbptype; // type of virtual base pointer + idx_t vbpidx; + + if (baseclass_find(st->Sbase,b->BCbase)) // if direct vbase + { if (i == LF_IVBCLASS) + continue; + } + else + { if (i == LF_VBCLASS) + continue; + } + + typidx = cv4_symtypidx(b->BCbase); + + vbptype = type_allocn(TYarray,tsint); + vbptype->Tflags |= TFsizeunknown; + vbptype = newpointer(vbptype); + vbptype->Tcount++; + vbpidx = cv4_typidx(vbptype); + type_free(vbptype); + + attribute = (b->BCflags & BCFpmask); + if (attribute & 4) + attribute = 1; + else + attribute = 4 - attribute; + + vbpoff = st->Svbptr_off; + vboff = b->BCvbtbloff / intsize; + + if (config.fulltypes == CV4) + { TOWORD(p,i); + TOWORD(p + 2,typidx); + TOWORD(p + 4,vbpidx); + TOWORD(p + 6,attribute); + p += 8; + } + else + { TOWORD(p,i); + TOLONG(p + 4,typidx); // btype + TOLONG(p + 8,vbpidx); // vbtype + TOWORD(p + 2,attribute); + p += 12; + } + + cv4_storenumeric(p,vbpoff); + p += cv4_numericbytes(vbpoff); + cv4_storenumeric(p,vboff); + p += cv4_numericbytes(vboff); + } + i ^= LF_VBCLASS ^ LF_IVBCLASS; // toggle between them + } while (i != LF_VBCLASS); + + // Now friend classes + for (sl = s->Sstruct->Sfriendclass; sl; sl = list_next(sl)) + { symbol *sf = list_symbol(sl); + + symbol_debug(sf); + typidx = cv4_symtypidx(sf); + if (config.fulltypes == CV4) + { TOWORD(p,LF_FRIENDCLS); + TOWORD(p + 2,typidx); + p += 4; + } + else + { TOLONG(p,LF_FRIENDCLS); + TOLONG(p + 4,typidx); + p += 8; + } + } + + // Now friend functions + for (sl = s->Sstruct->Sfriendfuncs; sl; sl = list_next(sl)) + { symbol *sf = list_symbol(sl); + + symbol_debug(sf); + if (sf->Sclass == SCfunctempl) + continue; + typidx = cv4_symtypidx(sf); + TOWORD(p,LF_FRIENDFCN); + if (config.fulltypes == CV4) + { TOWORD(p + 2,typidx); + p += 4; + } + else + { TOLONG(p + 2,typidx); + p += 6; + } + p += cv_namestring(p,cpp_unmangleident(sf->Sident)); + } + } +#endif + for (sl = s->Sstruct->Sfldlst; sl; sl = list_next(sl)) + { symbol *sf = list_symbol(sl); + targ_size_t offset; + char *id; + + symbol_debug(sf); + id = sf->Sident; + switch (sf->Sclass) + { case SCfield: + { debtyp_t *db; + + if (config.fulltypes == CV4) + { db = debtyp_alloc(6); + TOWORD(db->data,LF_BITFIELD); + db->data[2] = sf->Swidth; + db->data[3] = sf->Sbit; + TOWORD(db->data + 4,cv4_symtypidx(sf)); + } + else + { db = debtyp_alloc(8); + TOWORD(db->data,LF_BITFIELD); + db->data[6] = sf->Swidth; + db->data[7] = sf->Sbit; + TOLONG(db->data + 2,cv4_symtypidx(sf)); + } + typidx = cv_debtyp(db); + goto L3; + } + case SCmember: + typidx = cv4_symtypidx(sf); + L3: +#if SCPP + if (CPP && sf == s->Sstruct->Svptr) + { + if (config.fulltypes == CV4) + { TOWORD(p,LF_VFUNCTAB); + TOWORD(p + 2,typidx); + p += 4; + } + else + { TOLONG(p,LF_VFUNCTAB); // 0 fill 2 bytes + TOLONG(p + 4,typidx); + p += 8; + } + break; + } +#endif + offset = sf->Smemoff; + TOWORD(p,LF_MEMBER); +#if SCPP + attribute = CPP ? SFLtoATTR(sf->Sflags) : 0; + assert((attribute & ~3) == 0); +#else + attribute = 0; +#endif + if (config.fulltypes == CV4) + { TOWORD(p + 2,typidx); + TOWORD(p + 4,attribute); + p += 6; + } + else + { TOLONG(p + 4,typidx); + TOWORD(p + 2,attribute); + p += 8; + } + cv4_storenumeric(p,offset); + p += cv4_numericbytes(offset); + p += cv_namestring(p,id); + break; +#if SCPP + case SCstruct: + if (sf->Sstruct->Sflags & STRanonymous) + continue; + if (sf->Sstruct->Sflags & STRnotagname) + id = cpp_name_none; + goto Lnest; + + case SCenum: + if (sf->Senum->SEflags & SENnotagname) + id = cpp_name_none; + goto Lnest; + + case SCtypedef: + Lnest: + TOWORD(p,LF_NESTTYPE); + typidx = cv4_symtypidx(sf); + if (config.fulltypes == CV4) + { TOWORD(p + 2,typidx); + p += 4; + } + else + { TOLONG(p + 4,typidx); + p += 8; + } + L2: + p += cv_namestring(p,id); + break; + + case SCextern: + case SCcomdef: + case SCglobal: + case SCstatic: + case SCinline: + case SCsinline: + case SCeinline: + case SCcomdat: + if (tyfunc(sf->ty())) + { int count; + + typidx = cv4_methodlist(sf,&count); + if (!typidx) + break; + id = cv4_prettyident(sf); + TOWORD(p,LF_METHOD); + TOWORD(p + 2,count); + p += 4; + TOIDX(p,typidx); + p += cgcv.sz_idx; + goto L2; + } + else + { + TOWORD(p,LF_STMEMBER); + typidx = cv4_symtypidx(sf); + attribute = SFLtoATTR(sf->Sflags); + if (config.fulltypes == CV4) + { TOWORD(p + 2,typidx); + TOWORD(p + 4,attribute); + p += 6; + } + else + { TOLONG(p + 4,typidx); + TOWORD(p + 2,attribute); + p += 8; + } + goto L2; + } + break; +#endif + default: + continue; + } + } + //dbg_printf("fnamelen = %d, p-dt->data = %d\n",fnamelen,p-dt->data); + assert(p - dt->data == fnamelen); + if (config.fulltypes == CV4) + TOWORD(d->data + 4,cv_debtyp(dt)); + else + TOLONG(d->data + 6,cv_debtyp(dt)); + +#if TDB + if (config.fulltypes == CVTDB) + s->Stypidx = cv_debtyp(d); +#endif +#if SCPP + if (CPP) + { + symbol_debug(s); + if (st->Sflags & STRglobal) + list_prepend(&cgcv.list,s); + else + cv4_outsym(s); + } +#endif + return s->Stypidx; +} + +/**************************** + * Return type index of enum. + */ + +#if SCPP + +STATIC unsigned cv4_enum(symbol *s) +{ + debtyp_t *d,*dt; + unsigned nfields,fnamelen; + unsigned len; + type *t; + type *tbase; + symlist_t sl; + unsigned property; + unsigned attribute; + int i; + char *id; + + _chkstack(); + symbol_debug(s); + if (s->Stypidx) // if already converted + { //assert(s->Stypidx - cgcv.deb_offset < debtyptop); + return s->Stypidx; + } + + //dbg_printf("cv4_enum(%s)\n",s->Sident); + t = s->Stype; + type_debug(t); + tbase = t->Tnext; + property = 0; + if (s->Senum->SEflags & SENforward) + property |= 0x80; // enum is forward referenced + + id = s->Sident; + if (s->Senum->SEflags & SENnotagname) + id = cpp_name_none; + if (config.fulltypes == CV4) + { len = 10; + d = debtyp_alloc(len + cv_stringbytes(id)); + TOWORD(d->data,LF_ENUM); + TOWORD(d->data + 4,cv4_typidx(tbase)); + TOWORD(d->data + 8,property); + } + else + { len = 14; + d = debtyp_alloc(len + cv_stringbytes(id)); + TOWORD(d->data,LF_ENUM); + TOLONG(d->data + 6,cv4_typidx(tbase)); + TOWORD(d->data + 4,property); + } + len += cv_namestring(d->data + len,id); + + // Assign a number to prevent infinite recursion if an enum member + // references the same enum. +#if OMFOBJ && TDB + if (config.fulltypes == CVTDB) + { debtyp_t *df; + + TOWORD(d->data + 2,0); + TOWORD(d->data + 6,0); + debtyp_check(d); + s->Stypidx = tdb_typidx(&d->length); // forward reference it + } + else +#endif + { + d->length = 0; // so cv_debtyp() will allocate new + s->Stypidx = cv_debtyp(d); + d->length = len; // restore length + } + + // Compute the number of fields, and the length of the fieldlist record + nfields = 0; + fnamelen = 2; + for (sl = s->Senumlist; sl; sl = list_next(sl)) + { symbol *sf = list_symbol(sl); + unsigned long value; + + symbol_debug(sf); + value = el_tolongt(sf->Svalue); + nfields++; + fnamelen += 4 + cv4_numericbytes(value) + cv_stringbytes(sf->Sident); + } + + TOWORD(d->data + 2,nfields); + + // If forward reference, then field list is 0 + if (s->Senum->SEflags & SENforward) + { + TOWORD(d->data + 6,0); + return s->Stypidx; + } + + // Generate fieldlist type record + dt = debtyp_alloc(fnamelen); + TOWORD(dt->data,LF_FIELDLIST); + + // And fill it in + i = 2; + for (sl = s->Senumlist; sl; sl = list_next(sl)) + { symbol *sf = list_symbol(sl); + unsigned long value; + + symbol_debug(sf); + value = el_tolongt(sf->Svalue); + TOWORD(dt->data + i,LF_ENUMERATE); + attribute = SFLtoATTR(sf->Sflags); + TOWORD(dt->data + i + 2,attribute); + cv4_storenumeric(dt->data + i + 4,value); + i += 4 + cv4_numericbytes(value); + i += cv_namestring(dt->data + i,sf->Sident); + + // If enum is not a member of a class, output enum members as constants + if (!isclassmember(s)) + { symbol_debug(sf); + cv4_outsym(sf); + } + } + assert(i == fnamelen); + if (config.fulltypes == CV4) + TOWORD(d->data + 6,cv_debtyp(dt)); + else + TOLONG(d->data + 10,cv_debtyp(dt)); + + symbol_debug(s); + if (CPP) + cv4_outsym(s); + return s->Stypidx; +} + +#endif + +/************************************************ + * Return 'calling convention' type of function. + */ + +unsigned char cv4_callconv(type *t) +{ unsigned char call; + + switch (tybasic(t->Tty)) + { +#if TARGET_SEGMENTED + case TYffunc: call = 1; break; + case TYfpfunc: call = 3; break; + case TYf16func: call = 3; break; + case TYfsfunc: call = 8; break; + case TYnsysfunc: call = 9; break; + case TYfsysfunc: call = 10; break; +#endif + case TYnfunc: call = 0; break; + case TYnpfunc: call = 2; break; + case TYnsfunc: call = 7; break; + case TYifunc: call = 1; break; + case TYjfunc: call = 2; break; + case TYmfunc: call = 11; break; // this call + default: + assert(0); + } + return call; +} + +/********************************************** + * Return type index for the type of a symbol. + */ + +#if MARS + +STATIC unsigned cv4_symtypidx(symbol *s) +{ + return cv4_typidx(s->Stype); +} + +#endif + +#if SCPP + +STATIC unsigned cv4_symtypidx(symbol *s) +{ type *t; + debtyp_t *d; + unsigned char *p; + + if (!CPP) + return cv4_typidx(s->Stype); + symbol_debug(s); + if (isclassmember(s)) + { t = s->Stype; + if (tyfunc(t->Tty)) + { param_t *pa; + unsigned nparam; + idx_t paramidx; + idx_t thisidx; + unsigned u; + func_t *f; + unsigned char call; + + // It's a member function, which gets a special type record + + f = s->Sfunc; + if (f->Fflags & Fstatic) + thisidx = dttab4[TYvoid]; + else + { type *tthis = cpp_thistype(s->Stype,(Classsym *)s->Sscope); + + thisidx = cv4_typidx(tthis); + type_free(tthis); + } + + paramidx = cv4_arglist(t,&nparam); + call = cv4_callconv(t); + + if (config.fulltypes == CV4) + { + d = debtyp_alloc(18); + p = d->data; + TOWORD(p,LF_MFUNCTION); + TOWORD(p + 2,cv4_typidx(t->Tnext)); + TOWORD(p + 4,cv4_symtypidx(s->Sscope)); + TOWORD(p + 6,thisidx); + p[8] = call; + p[9] = 0; // reserved + TOWORD(p + 10,nparam); + TOWORD(p + 12,paramidx); + TOLONG(p + 14,0); // thisadjust + } + else + { + d = debtyp_alloc(26); + p = d->data; + TOWORD(p,LF_MFUNCTION); + TOLONG(p + 2,cv4_typidx(t->Tnext)); + TOLONG(p + 6,cv4_symtypidx(s->Sscope)); + TOLONG(p + 10,thisidx); + p[14] = call; + p[15] = 0; // reserved + TOWORD(p + 16,nparam); + TOLONG(p + 18,paramidx); + TOLONG(p + 22,0); // thisadjust + } + return cv_debtyp(d); + } + } + return cv4_typidx(s->Stype); +} + +#endif + +/*********************************** + * Return CV4 type index for a type. + */ + +unsigned cv4_typidx(type *t) +{ unsigned typidx; + unsigned u; + unsigned next; + unsigned key; + debtyp_t *d; + targ_size_t size; + tym_t tym; + tym_t tycv; + tym_t tymnext; + type *tv; + unsigned dt; + unsigned attribute; + unsigned char call; + + //dbg_printf("cv4_typidx(%p)\n",t); + if (!t) + return dttab4[TYint]; // assume int + type_debug(t); + next = cv4_typidx(t->Tnext); + tycv = t->Tty; + tym = tybasic(tycv); + tycv &= mTYconst | mTYvolatile | mTYimmutable; + attribute = 0; +L1: + dt = dttab4[tym]; + switch (tym) + { + case TYllong: + if (t->Tnext) + goto Ldelegate; + assert(dt); + typidx = dt; + break; + + case TYullong: + if (t->Tnext) + goto Ldarray; + assert(dt); + typidx = dt; + break; + + case TYvoid: + case TYchar: + case TYschar: + case TYuchar: + case TYchar16: + case TYshort: + case TYushort: + case TYint: + case TYuint: + case TYulong: + case TYlong: + case TYfloat: + case TYdouble: + case TYdouble_alias: + case TYldouble: + case TYifloat: + case TYidouble: + case TYildouble: + case TYcfloat: + case TYcdouble: + case TYcldouble: + case TYbool: + case TYwchar_t: + case TYdchar: + assert(dt); + typidx = dt; + break; + +#if JHANDLE + case TYjhandle: +#if TDB + if (config.fulltypes == CVTDB) { + attribute |= 20; + goto L2; + } +#endif + goto Lptr; +#endif + case TYnptr: +#if MARS + if (t->Tkey) + goto Laarray; +#endif +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: +#endif + Lptr: + attribute |= I32 ? 10 : 0; goto L2; +#if TARGET_SEGMENTED + case TYfptr: + case TYvptr: attribute |= I32 ? 11 : 1; goto L2; + case TYhptr: attribute |= 2; goto L2; +#endif + + L2: +#if 1 + // This is a hack to duplicate bugs in VC, so that the VC + // debugger will work. + tymnext = t->Tnext->Tty; + if (tymnext & (mTYconst | mTYimmutable | mTYvolatile) && + !tycv && + tyarithmetic(tymnext) && + !(attribute & 0xE0) + ) + { + typidx = dt | dttab4[tybasic(tymnext)]; + break; + } +#endif + if ((next & 0xFF00) == 0 && !(attribute & 0xE0)) + typidx = next | dt; + else + { + if (tycv & (mTYconst | mTYimmutable)) + attribute |= 0x400; + if (tycv & mTYvolatile) + attribute |= 0x200; + tycv = 0; + if (config.fulltypes == CV4) + { d = debtyp_alloc(6); + TOWORD(d->data,LF_POINTER); + TOWORD(d->data + 2,attribute); + TOWORD(d->data + 4,next); + } + else + { d = debtyp_alloc(10); + TOWORD(d->data,LF_POINTER); + TOLONG(d->data + 2,attribute); + TOLONG(d->data + 6,next); + } + typidx = cv_debtyp(d); + } + break; + + Ldarray: + assert(config.fulltypes == CV4); +#if 1 + d = debtyp_alloc(12); + TOWORD(d->data, LF_OEM); + TOWORD(d->data + 2, OEM); + TOWORD(d->data + 4, 1); // 1 = dynamic array + TOWORD(d->data + 6, 2); // count of type indices to follow + TOWORD(d->data + 8, 0x12); // index type, T_LONG + TOWORD(d->data + 10, next); // element type +#else + d = debtyp_alloc(6); + TOWORD(d->data,LF_DYN_ARRAY); + TOWORD(d->data + 2, 0x12); // T_LONG + TOWORD(d->data + 4, next); +#endif + typidx = cv_debtyp(d); + break; + + Laarray: + assert(config.fulltypes == CV4); +#if MARS + key = cv4_typidx(t->Tkey); +#if 1 + d = debtyp_alloc(12); + TOWORD(d->data, LF_OEM); + TOWORD(d->data + 2, OEM); + TOWORD(d->data + 4, 2); // 2 = associative array + TOWORD(d->data + 6, 2); // count of type indices to follow + TOWORD(d->data + 8, key); // key type + TOWORD(d->data + 10, next); // element type +#else + d = debtyp_alloc(6); + TOWORD(d->data,LF_ASSOC_ARRAY); + TOWORD(d->data + 2, key); // key type + TOWORD(d->data + 4, next); // element type +#endif + typidx = cv_debtyp(d); +#endif + break; + + Ldelegate: + assert(config.fulltypes == CV4); + tv = type_fake(TYnptr); + tv->Tcount++; + key = cv4_typidx(tv); + type_free(tv); +#if 1 + d = debtyp_alloc(12); + TOWORD(d->data, LF_OEM); + TOWORD(d->data + 2, OEM); + TOWORD(d->data + 4, 3); // 3 = delegate + TOWORD(d->data + 6, 2); // count of type indices to follow + TOWORD(d->data + 8, key); // type of 'this', which is void* + TOWORD(d->data + 10, next); // function type +#else + d = debtyp_alloc(6); + TOWORD(d->data,LF_DELEGATE); + TOWORD(d->data + 2, key); // type of 'this', which is void* + TOWORD(d->data + 4, next); // function type +#endif + typidx = cv_debtyp(d); + break; + + case TYarray: + if (t->Tflags & TFsizeunknown) + size = 0; // don't complain if don't know size + else + size = type_size(t); + u = cv4_numericbytes(size); + if (config.fulltypes == CV4) + { + d = debtyp_alloc(6 + u + 1); + TOWORD(d->data,LF_ARRAY); + TOWORD(d->data + 2,next); + TOWORD(d->data + 4,I32 ? 0x12 : 0x11); // T_LONG : T_SHORT + d->data[6 + u] = 0; // no name + cv4_storenumeric(d->data + 6,size); + } + else + { + d = debtyp_alloc(10 + u + 1); + TOWORD(d->data,LF_ARRAY); + TOLONG(d->data + 2,next); + TOLONG(d->data + 6,I32 ? 0x12 : 0x11); // T_LONG : T_SHORT + d->data[10 + u] = 0; // no name + cv4_storenumeric(d->data + 10,size); + } + typidx = cv_debtyp(d); + break; + +#if TARGET_SEGMENTED + case TYffunc: + case TYfpfunc: + case TYf16func: + case TYfsfunc: + case TYnsysfunc: + case TYfsysfunc: +#endif + case TYnfunc: + case TYnpfunc: + case TYnsfunc: + case TYmfunc: + case TYjfunc: + case TYifunc: + { param_t *p; + unsigned nparam; + idx_t paramidx; + unsigned u; + + call = cv4_callconv(t); + paramidx = cv4_arglist(t,&nparam); + + // Construct an LF_PROCEDURE + if (config.fulltypes == CV4) + { d = debtyp_alloc(2 + 2 + 1 + 1 + 2 + 2); + TOWORD(d->data,LF_PROCEDURE); + TOWORD(d->data + 2,next); // return type + d->data[4] = call; + d->data[5] = 0; // reserved + TOWORD(d->data + 6,nparam); + TOWORD(d->data + 8,paramidx); + } + else + { d = debtyp_alloc(2 + 4 + 1 + 1 + 2 + 4); + TOWORD(d->data,LF_PROCEDURE); + TOLONG(d->data + 2,next); // return type + d->data[6] = call; + d->data[7] = 0; // reserved + TOWORD(d->data + 8,nparam); + TOLONG(d->data + 10,paramidx); + } + + typidx = cv_debtyp(d); + break; + } + + case TYstruct: + { int foo = t->Ttag->Stypidx; + typidx = cv4_struct(t->Ttag,0); + //printf("struct '%s' %x %x\n", t->Ttag->Sident, foo, typidx); + break; + } + + case TYenum: +#if SCPP + if (CPP) + typidx = cv4_enum(t->Ttag); + else +#endif + typidx = dttab4[t->Tnext->Tty]; + break; + +#if SCPP + case TYvtshape: + { unsigned count; + unsigned char *p; + unsigned char descriptor; + + count = 1 + list_nitems(t->Ttag->Sstruct->Svirtual); + d = debtyp_alloc(4 + ((count + 1) >> 1)); + p = d->data; + TOWORD(p,LF_VTSHAPE); + TOWORD(p + 2,count); + descriptor = I32 ? 0x55 : (LARGECODE ? 0x11 : 0); + memset(p + 4,descriptor,(count + 1) >> 1); + + typidx = cv_debtyp(d); + break; + } + + case TYref: + case TYnref: + case TYfref: + attribute |= 0x20; // indicate reference pointer + case TYmemptr: + tym = tybasic(tym_conv(t)); // convert to C data type + goto L1; // and try again +#endif +#if MARS + case TYref: + attribute |= 0x20; // indicate reference pointer + tym = TYnptr; // convert to C data type + goto L1; // and try again +#endif + case TYnullptr: + tym = TYnptr; + next = cv4_typidx(tsvoid); // rewrite as void* + t = tspvoid; + goto L1; + + default: +#ifdef DEBUG + WRTYxx(tym); +#endif + assert(0); + } + + // Add in const and/or volatile modifiers + if (tycv & (mTYconst | mTYimmutable | mTYvolatile)) + { unsigned modifier; + + modifier = (tycv & (mTYconst | mTYimmutable)) ? 1 : 0; + modifier |= (tycv & mTYvolatile) ? 2 : 0; + if (config.fulltypes == CV4) + { + d = debtyp_alloc(6); + TOWORD(d->data,LF_MODIFIER); + TOWORD(d->data + 2,modifier); + TOWORD(d->data + 4,typidx); + } + else + { + d = debtyp_alloc(10); + TOWORD(d->data,LF_MODIFIER); + TOLONG(d->data + 2,modifier); + TOLONG(d->data + 6,typidx); + } + typidx = cv_debtyp(d); + } + + assert(typidx); + return typidx; +} + +/****************************************** + * Write out symbol s. + */ + +STATIC void cv4_outsym(symbol *s) +{ + unsigned len; + type *t; + unsigned length; + unsigned u; + tym_t tym; + const char *id; + unsigned char __ss *debsym; + + //dbg_printf("cv4_outsym(%s)\n",s->Sident); + symbol_debug(s); +#if MARS + if (s->Sflags & SFLnodebug) + return; +#endif + t = s->Stype; + type_debug(t); + tym = tybasic(t->Tty); + if (tyfunc(tym) && s->Sclass != SCtypedef) + { int framedatum,targetdatum,fd; + char idfree; + idx_t typidx; + + if (s != funcsym_p) + return; +#if SCPP + if (CPP && isclassmember(s)) // if method + { Outbuffer buf; + + param_tostring(&buf,s->Stype); + buf.prependBytes(cpp_prettyident(s)); + id = alloca_strdup(buf.toString()); + } + else + { + id = prettyident(s); + } +#else + id = s->prettyIdent ? s->prettyIdent : s->Sident; +#endif + len = cv_stringbytes(id); + + // Length of record + length = 2 + 2 + 4 * 3 + intsize * 4 + 2 + cgcv.sz_idx + 1; + debsym = (unsigned char __ss *) alloca(length + len); + memset(debsym,0,length + len); + + // Symbol type + u = (s->Sclass == SCstatic) ? S_LPROC16 : S_GPROC16; + if (I32) + u += S_GPROC32 - S_GPROC16; + TOWORD(debsym + 2,u); + + if (config.fulltypes == CV4) + { + // Offsets + if (I32) + { TOLONG(debsym + 16,s->Ssize); // proc length + TOLONG(debsym + 20,startoffset); // debug start + TOLONG(debsym + 24,retoffset); // debug end + u = 28; // offset to fixup + } + else + { TOWORD(debsym + 16,s->Ssize); // proc length + TOWORD(debsym + 18,startoffset); // debug start + TOWORD(debsym + 20,retoffset); // debug end + u = 22; // offset to fixup + } + length += cv_namestring(debsym + u + intsize + 2 + cgcv.sz_idx + 1,id); + typidx = cv4_symtypidx(s); + TOIDX(debsym + u + intsize + 2,typidx); // proc type + debsym[u + intsize + 2 + cgcv.sz_idx] = tyfarfunc(tym) ? 4 : 0; + TOWORD(debsym,length - 2); + } + else + { + // Offsets + if (I32) + { TOLONG(debsym + 16 + cgcv.sz_idx,s->Ssize); // proc length + TOLONG(debsym + 20 + cgcv.sz_idx,startoffset); // debug start + TOLONG(debsym + 24 + cgcv.sz_idx,retoffset); // debug end + u = 28; // offset to fixup + } + else + { TOWORD(debsym + 16 + cgcv.sz_idx,s->Ssize); // proc length + TOWORD(debsym + 18 + cgcv.sz_idx,startoffset); // debug start + TOWORD(debsym + 20 + cgcv.sz_idx,retoffset); // debug end + u = 22; // offset to fixup + } + u += cgcv.sz_idx; + length += cv_namestring(debsym + u + intsize + 2 + 1,id); + typidx = cv4_symtypidx(s); + TOIDX(debsym + 16,typidx); // proc type + debsym[u + intsize + 2] = tyfarfunc(tym) ? 4 : 0; + TOWORD(debsym,length - 2); + } + + unsigned soffset = Offset(DEBSYM); + obj_write_bytes(SegData[DEBSYM],length,debsym); + + // Put out fixup for function start offset + reftoident(DEBSYM,soffset + u,s,0,CFseg | CFoff); + } + else + { targ_size_t base; + int reg; + unsigned fd; + unsigned idx1,idx2; + unsigned long value; + unsigned fixoff; + idx_t typidx; + + typidx = cv4_typidx(t); +#if MARS + id = s->prettyIdent ? s->prettyIdent : prettyident(s); +#else + id = prettyident(s); +#endif + len = strlen(id); + debsym = (unsigned char __ss *) alloca(39 + IDOHD + len); + switch (s->Sclass) + { + case SCparameter: + case SCregpar: + if (s->Sfl == FLreg) + { + s->Sfl = FLpara; + cv4_outsym(s); + s->Sfl = FLreg; + goto case_register; + } + base = Poff - BPoff; // cancel out add of BPoff + goto L1; + case SCauto: + if (s->Sfl == FLreg) + goto case_register; + case_auto: + base = Aoff; + L1: + TOWORD(debsym + 2,I32 ? S_BPREL32 : S_BPREL16); + if (config.fulltypes == CV4) + { TOOFFSET(debsym + 4,s->Soffset + base + BPoff); + TOIDX(debsym + 4 + intsize,typidx); + } + else + { TOOFFSET(debsym + 4 + cgcv.sz_idx,s->Soffset + base + BPoff); + TOIDX(debsym + 4,typidx); + } + length = 2 + 2 + intsize + cgcv.sz_idx; + length += cv_namestring(debsym + length,id); + TOWORD(debsym,length - 2); + break; + case SCbprel: + base = -BPoff; + goto L1; + case SCfastpar: + case SCregister: + if (s->Sfl != FLreg) + goto case_auto; + case SCpseudo: + case_register: + TOWORD(debsym + 2,S_REGISTER); + reg = cv_regnum(s); + TOIDX(debsym + 4,typidx); + TOWORD(debsym + 4 + cgcv.sz_idx,reg); + length = 2 * 3 + cgcv.sz_idx; + length += 1 + cv_namestring(debsym + length,id); + TOWORD(debsym,length - 2); + break; + + case SCextern: + case SCcomdef: + // Common blocks have a non-zero Sxtrnnum and an UNKNOWN seg + if (!(s->Sxtrnnum && s->Sseg == UNKNOWN)) // if it's not really a common block + { + return; + } + /* FALL-THROUGH */ + case SCglobal: + case SCcomdat: + u = S_GDATA16; + goto L2; + case SCstatic: + case SClocstat: + u = S_LDATA16; + L2: + if (I32) + u += S_GDATA32 - S_GDATA16; + TOWORD(debsym + 2,u); + if (config.fulltypes == CV4) + { + fixoff = 4; + length = 2 + 2 + intsize + 2; + TOOFFSET(debsym + fixoff,s->Soffset); + TOWORD(debsym + fixoff + intsize,0); + TOIDX(debsym + length,typidx); + } + else + { + fixoff = 8; + length = 2 + 2 + intsize + 2; + TOOFFSET(debsym + fixoff,s->Soffset); + TOWORD(debsym + fixoff + intsize,0); // segment + TOIDX(debsym + 4,typidx); + } + length += cgcv.sz_idx; + length += cv_namestring(debsym + length,id); + TOWORD(debsym,length - 2); + assert(length <= 40 + len); + + if (s->Sseg == UNKNOWN || s->Sclass == SCcomdat) // if common block + { + if (config.exe & EX_flat) + { + fd = 0x16; + idx1 = DGROUPIDX; + idx2 = s->Sxtrnnum; + } + else + { + fd = 0x26; + idx1 = idx2 = s->Sxtrnnum; + } + } +#if TARGET_SEGMENTED + else if (s->ty() & (mTYfar | mTYcs)) + { fd = 0x04; + idx1 = idx2 = SegData[s->Sseg]->segidx; + } +#endif + else + { fd = 0x14; + idx1 = DGROUPIDX; + idx2 = SegData[s->Sseg]->segidx; + } + /* Because of the linker limitations, the length cannot + * exceed 0x1000. + * See optlink\cv\cvhashes.asm + */ + assert(length <= 0x1000); + if (idx2 != 0) + { unsigned offset = Offset(DEBSYM); + obj_write_bytes(SegData[DEBSYM],length,debsym); + obj_long(DEBSYM,offset + fixoff,s->Soffset, + cgcv.LCFDpointer + fd,idx1,idx2); + } + return; + +#if 1 + case SCtypedef: + s->Stypidx = typidx; + goto L4; + + case SCstruct: + if (s->Sstruct->Sflags & STRnotagname) + return; + goto L4; + + case SCenum: +#if SCPP + if (CPP && s->Senum->SEflags & SENnotagname) + return; +#endif + L4: + // Output a 'user-defined type' for the tag name + TOWORD(debsym + 2,S_UDT); + TOIDX(debsym + 4,typidx); + length = 2 + 2 + cgcv.sz_idx; + length += cv_namestring(debsym + length,id); + TOWORD(debsym,length - 2); + list_subtract(&cgcv.list,s); + break; + + case SCconst: + // The only constants are enum members + value = el_tolongt(s->Svalue); + TOWORD(debsym + 2,S_CONST); + TOIDX(debsym + 4,typidx); + length = 4 + cgcv.sz_idx; + cv4_storenumeric(debsym + length,value); + length += cv4_numericbytes(value); + length += cv_namestring(debsym + length,id); + TOWORD(debsym,length - 2); + break; +#endif + default: + return; + } + assert(length <= 40 + len); + obj_write_bytes(SegData[DEBSYM],length,debsym); + } +} + +/****************************************** + * Write out any deferred symbols. + */ + +STATIC void cv_outlist() +{ + while (cgcv.list) + cv_outsym((Symbol *) list_pop(&cgcv.list)); +} + +/****************************************** + * Write out symbol table for current function. + */ + +STATIC void cv4_func(Funcsym *s) +{ + SYMIDX si; + int endarg; + + cv4_outsym(s); // put out function symbol + + // Put out local symbols + endarg = 0; + for (si = 0; si < globsym.top; si++) + { //printf("globsym.tab[%d] = %p\n",si,globsym.tab[si]); + symbol *sa = globsym.tab[si]; +#if MARS + if (endarg == 0 && sa->Sclass != SCparameter && sa->Sclass != SCfastpar) + { static unsigned short endargs[] = { 2,S_ENDARG }; + + obj_write_bytes(SegData[DEBSYM],sizeof(endargs),endargs); + endarg = 1; + } +#endif + cv4_outsym(sa); + } + + // Put out function return record + if (1) + { unsigned char sreturn[2+2+2+1+1+4]; + unsigned short flags; + unsigned char style; + tym_t ty; + tym_t tyret; + unsigned u; + + u = 2+2+1; + ty = tybasic(s->ty()); + + flags = tyrevfunc(ty) ? 0 : 1; + flags |= typfunc(ty) ? 0 : 2; + TOWORD(sreturn + 4,flags); + + tyret = tybasic(s->Stype->Tnext->Tty); + switch (tyret) + { + case TYvoid: + default: + style = 0; + break; + case TYbool: + case TYchar: + case TYschar: + case TYuchar: + sreturn[7] = 1; + sreturn[8] = 1; // AL + goto L1; + + case TYwchar_t: + case TYchar16: + case TYshort: + case TYushort: + goto case_ax; + + case TYint: + case TYuint: +#if JHANDLE + case TYjhandle: +#endif +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: +#endif + case TYnullptr: + case TYnptr: + if (I32) + goto case_eax; + else + goto case_ax; + + case TYfloat: + case TYifloat: + if (config.exe & EX_flat) + goto case_st0; + case TYlong: + case TYulong: + case TYdchar: + if (I32) + goto case_eax; + else + goto case_dxax; + +#if TARGET_SEGMENTED + case TYfptr: + case TYhptr: + if (I32) + goto case_edxeax; + else + goto case_dxax; + + case TYvptr: + if (I32) + goto case_edxebx; + else + goto case_dxbx; +#endif + + case TYdouble: + case TYidouble: + case TYdouble_alias: + if (config.exe & EX_flat) + goto case_st0; + if (I32) + goto case_edxeax; + else + goto case_axbxcxdx; + + case TYllong: + case TYullong: + assert(I32); + goto case_edxeax; + + case TYldouble: + case TYildouble: + goto case_st0; + + case TYcfloat: + case TYcdouble: + case TYcldouble: + goto case_st01; + + case_ax: + sreturn[7] = 1; + sreturn[8] = 9; // AX + goto L1; + + case_eax: + sreturn[7] = 1; + sreturn[8] = 17; // EAX + goto L1; + + + case_dxax: + sreturn[7] = 2; + sreturn[8] = 11; // DX + sreturn[9] = 9; // AX + goto L1; + + case_dxbx: + sreturn[7] = 2; + sreturn[8] = 11; // DX + sreturn[9] = 12; // BX + goto L1; + + case_axbxcxdx: + sreturn[7] = 4; + sreturn[8] = 9; // AX + sreturn[9] = 12; // BX + sreturn[10] = 10; // CX + sreturn[11] = 11; // DX + goto L1; + + case_edxeax: + sreturn[7] = 2; + sreturn[8] = 19; // EDX + sreturn[9] = 17; // EAX + goto L1; + + case_edxebx: + sreturn[7] = 2; + sreturn[8] = 19; // EDX + sreturn[9] = 20; // EBX + goto L1; + + case_st0: + sreturn[7] = 1; + sreturn[8] = 128; // ST0 + goto L1; + + case_st01: + sreturn[7] = 2; + sreturn[8] = 128; // ST0 (imaginary) + sreturn[9] = 129; // ST1 (real) + goto L1; + + L1: + style = 1; + u += sreturn[7] + 1; + break; + } + sreturn[6] = style; + + TOWORD(sreturn,u); + TOWORD(sreturn + 2,S_RETURN); + obj_write_bytes(SegData[DEBSYM],u + 2,sreturn); + } + + // Put out end scope + { static unsigned short endproc[] = { 2,S_END }; + + obj_write_bytes(SegData[DEBSYM],sizeof(endproc),endproc); + } + + cv_outlist(); +} + +////////////////////////////////////////////////////////// + +/****************************************** + * Write out data to .OBJ file. + */ + +void cv_term() +{ unsigned u; + + //printf("cv_term(): debtyptop = %d\n",debtyptop); + cv_outlist(); + switch (config.fulltypes) + { + case CV4: + case CVSYM: + obj_write_bytes(SegData[DEBTYP],4,&cgcv.signature); + if (debtyptop != 1) + { + for (u = 0; u < debtyptop; u++) + { debtyp_t *d = debtyp[u]; + + obj_write_bytes(SegData[DEBTYP],2 + d->length,(char *)d + sizeof(unsigned)); +#if TERMCODE || _WIN32 || MARS + debtyp_free(d); +#endif + } + } + else if (debtyptop) + { +#if TERMCODE || _WIN32 || MARS + debtyp_free(debtyp[0]); +#endif + } + break; + +#if _WIN32 && TDB + case CVTDB: +#if 1 + tdb_term(); +#else + { unsigned char *buf; + unsigned char *p; + size_t len; + + // Calculate size of buffer + len = 4; + for (u = 0; u < debtyptop; u++) + { debtyp_t *d = debtyp[u]; + + len += 2 + d->length; + } + + // Allocate buffer + buf = malloc(len); + if (!buf) + err_nomem(); // out of memory + + // Fill the buffer + TOLONG(buf,cgcv.signature); + p = buf + 4; + for (u = 0; u < debtyptop; u++) + { debtyp_t *d = debtyp[u]; + + len = 2 + d->length; + memcpy(p,(char *)d + sizeof(unsigned),len); + p += len; + } + + tdb_write(buf,len,debtyptop); + } +#endif + break; +#endif + default: + assert(0); + } +#if TERMCODE || _WIN32 || MARS + util_free(debtyp); + debtyp = NULL; + vec_free(debtypvec); + debtypvec = NULL; +#endif +} + +/****************************************** + * Write out symbol table for current function. + */ + +#if TARGET_WINDOS +void cv_func(Funcsym *s) +{ +#if SCPP + if (errcnt) // if we had any errors + return; // don't bother putting stuff in .OBJ file +#endif + + //dbg_printf("cv_func('%s')\n",s->Sident); +#if MARS + if (s->Sflags & SFLnodebug) + return; +#else + if (CPP && s->Sfunc->Fflags & Fnodebug) // if don't generate debug info + return; +#endif + switch (config.fulltypes) + { + case CV4: + case CVSYM: + case CVTDB: + cv4_func(s); + break; + default: + assert(0); + } +} +#endif + +/****************************************** + * Write out symbol table for current function. + */ + +#if TARGET_WINDOS +void cv_outsym(symbol *s) +{ + //dbg_printf("cv_outsym('%s')\n",s->Sident); + symbol_debug(s); +#if MARS + if (s->Sflags & SFLnodebug) + return; +#endif + switch (config.fulltypes) + { + case CV4: + case CVSYM: + case CVTDB: + cv4_outsym(s); + break; + default: + assert(0); + } +} +#endif + +/****************************************** + * Return cv type index for a type. + */ + +unsigned cv_typidx(type *t) +{ unsigned ti; + + //dbg_printf("cv_typidx(%p)\n",t); + switch (config.fulltypes) + { + case CV4: + case CVTDB: + case CVSYM: + ti = cv4_typidx(t); + break; + default: +#ifdef DEBUG + printf("fulltypes = %d\n",config.fulltypes); +#endif + assert(0); + } + return ti; +} + +#endif // !SPP + + diff --git a/backend/cgcv.h b/backend/cgcv.h new file mode 100644 index 00000000..9af3727c --- /dev/null +++ b/backend/cgcv.h @@ -0,0 +1,76 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +/* Header for cgcv.c */ + +#ifndef CGCV_H +#define CGCV_H +//#pragma once + +extern char *ftdbname; + +void cv_init ( void ); +unsigned cv_typidx ( type *t ); +void cv_outsym ( Symbol *s ); +void cv_func ( Symbol *s ); +void cv_term ( void ); +unsigned long cv4_struct(Classsym *,int); + + +/* =================== Added for MARS compiler ========================= */ + +typedef unsigned long idx_t; // type of type index + +/* Data structure for a type record */ + +#pragma pack(1) + +typedef struct DEBTYP_T +{ + unsigned prev; // previous debtyp_t with same hash + unsigned short length; // length of following array + unsigned char data[2]; // variable size array +} debtyp_t; + +#pragma pack() + +struct Cgcv +{ + long signature; + symlist_t list; // deferred list of symbols to output + idx_t deb_offset; // offset added to type index + unsigned sz_idx; // size of stored type index + int LCFDoffset; + int LCFDpointer; + int FD_code; // frame for references to code +}; + +extern Cgcv cgcv; + +debtyp_t * debtyp_alloc(unsigned length); +int cv_stringbytes(const char *name); +inline unsigned cv4_numericbytes(targ_size_t value); +void cv4_storenumeric(unsigned char *p,targ_size_t value); +idx_t cv_debtyp ( debtyp_t *d ); +int cv_namestring ( unsigned char *p , const char *name ); +unsigned cv4_typidx(type *t); +idx_t cv4_arglist(type *t,unsigned *pnparam); +unsigned char cv4_callconv(type *t); + +#define TOIDX(a,b) ((cgcv.sz_idx == 4) ? TOLONG(a,b) : TOWORD(a,b)) + +#define DEBSYM 5 /* segment of symbol info */ +#define DEBTYP 6 /* segment of type info */ + + +#endif + diff --git a/backend/cgelem.c b/backend/cgelem.c new file mode 100644 index 00000000..b72a8f89 --- /dev/null +++ b/backend/cgelem.c @@ -0,0 +1,4522 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include "cc.h" +#include "oper.h" +#include "global.h" +#include "el.h" +#include "dt.h" +#include "code.h" +#include "type.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +extern void error(const char *filename, unsigned linnum, const char *format, ...); + +STATIC elem * optelem(elem *,HINT); +STATIC elem * elarray(elem *e); +STATIC elem * eldiv(elem *); + +CEXTERN elem * evalu8(elem *); + +static int expgoal; +static int again; +static int cgelem_goal; + +/***************************** + */ + +STATIC elem * cgel_lvalue(elem *e) +{ elem *e1; + elem *e11; + int op; + + //printf("cgel_lvalue()\n"); elem_print(e); + e1 = e->E1; + op = e->Eoper; + if (e1->Eoper == OPbit) + { + e11 = e1->E1; + + if (e11->Eoper == OPcomma) + { + // Replace (((e,v) bit x) op e2) with (e,((v bit x) op e2)) + e1->E1 = e11->E2; + e11->E2 = e; + e11->Ety = e->Ety; + e11->ET = e->ET; + e = e11; + goto L1; + } + else if (OTassign(e11->Eoper)) + { + // Replace (((e op= v) bit x) op e2) with ((e op= v) , ((e bit x) op e2)) + e1->E1 = el_copytree(e11->E1); + e = el_bin(OPcomma,e->Ety,e11,e); + goto L1; + } + } + else if (e1->Eoper == OPcomma) + { + // Replace ((e,v) op e2) with (e,(v op e2)) + e->Eoper = OPcomma; + e1->Eoper = op; + e1->Ety = e->Ety; + e1->ET = e->ET; + e->E1 = e1->E1; + e1->E1 = e1->E2; + e1->E2 = e->E2; + e->E2 = e1; + goto L1; + } + else if (OTassign(e1->Eoper)) + { + // Replace ((e op= v) op e2) with ((e op= v) , (e op e2)) + e->E1 = el_copytree(e1->E1); + e = el_bin(OPcomma,e->Ety,e1,e); + L1: + e = optelem(e,TRUE); + } + return e; +} + + +/****************************** + * Scan down commas. + */ + +STATIC elem * elscancommas(elem *e) +{ + while (e->Eoper == OPcomma +#if SCPP + || e->Eoper == OPinfo +#endif + ) + e = e->E2; + return e; +} + +/************************* + * Return TRUE if elem is the constant 1. + */ + +int elemisone(elem *e) +{ + if (e->Eoper == OPconst) + { switch (tybasic(e->Ety)) + { + case TYchar: + case TYuchar: + case TYschar: + case TYchar16: + case TYshort: + case TYushort: + case TYint: + case TYuint: + case TYlong: + case TYulong: + case TYllong: + case TYullong: + case TYnullptr: +#if JHANDLE + case TYjhandle: +#endif +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: + case TYhptr: + case TYfptr: + case TYvptr: +#endif + case TYnptr: + case TYbool: + case TYwchar_t: + case TYdchar: + if (el_tolong(e) != 1) + goto nomatch; + break; + case TYldouble: + case TYildouble: + if (e->EV.Vldouble != 1) + goto nomatch; + break; + case TYdouble: + case TYidouble: + case TYdouble_alias: + if (e->EV.Vdouble != 1) + goto nomatch; + break; + case TYfloat: + case TYifloat: + if (e->EV.Vfloat != 1) + goto nomatch; + break; + default: + goto nomatch; + } + return TRUE; + } + +nomatch: + return FALSE; +} + +/************************* + * Return TRUE if elem is the constant -1. + */ + +int elemisnegone(elem *e) +{ + if (e->Eoper == OPconst) + { switch (tybasic(e->Ety)) + { + case TYchar: + case TYuchar: + case TYschar: + case TYchar16: + case TYshort: + case TYushort: + case TYint: + case TYuint: + case TYlong: + case TYulong: + case TYllong: + case TYullong: + case TYnullptr: + case TYnptr: +#if JHANDLE + case TYjhandle: +#endif +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: + case TYhptr: + case TYfptr: + case TYvptr: +#endif + case TYbool: + case TYwchar_t: + case TYdchar: + if (el_tolong(e) != -1) + goto nomatch; + break; + case TYldouble: + //case TYildouble: + if (e->EV.Vldouble != -1) + goto nomatch; + break; + case TYdouble: + //case TYidouble: + case TYdouble_alias: + if (e->EV.Vdouble != -1) + goto nomatch; + break; + case TYfloat: + //case TYifloat: + if (e->EV.Vfloat != -1) + goto nomatch; + break; + default: + goto nomatch; + } + return TRUE; + } + +nomatch: + return FALSE; +} + +/********************************** + * Swap relational operators (like if we swapped the leaves). + */ + +unsigned swaprel(unsigned op) +{ + assert(op < (unsigned) OPMAX); + if (OTrel(op)) + op = rel_swap(op); + return op; +} + +/************************** + * Replace e1 by t=e1, replace e2 by t. + */ + +STATIC void fixside(elem **pe1,elem **pe2) +{ tym_t tym; + elem *tmp,*e2; + + tym = (*pe1)->Ety; + tmp = el_alloctmp(tym); + *pe1 = el_bin(OPeq,tym,tmp,*pe1); + e2 = el_copytree(tmp); + el_free(*pe2); + *pe2 = e2; +} + + + +/**************************** + * Compute the 'cost' of evaluating a elem. Could be done + * as Sethi-Ullman numbers, but that ain't worth the bother. + * We'll fake it. + */ + +#define cost(n) (opcost[n->Eoper]) + +/******************************* + * For floating point expressions, the cost would be the number + * of registers in the FPU stack needed. + */ + +int fcost(elem *e) +{ + int cost; + int cost1; + int cost2; + + //printf("fcost()\n"); + switch (e->Eoper) + { + case OPadd: + case OPmin: + case OPmul: + case OPdiv: + cost1 = fcost(e->E1); + cost2 = fcost(e->E2); + cost = cost2 + 1; + if (cost1 > cost) + cost = cost1; + break; + + case OPcall: + case OPucall: + cost = 8; + break; + + case OPneg: + case OPabs: + return fcost(e->E1); + + case OPvar: + case OPconst: + case OPind: + default: + return 1; + } + if (cost > 8) + cost = 8; + return cost; +} + +/******************************* + * The lvalue of an op= is a conversion operator. Since the code + * generator cannot handle this, we will have to fix it here. The + * general strategy is: + * (conv) e1 op= e2 => e1 = (conv) e1 op e2 + * Since e1 can only be evaluated once, if it is an expression we + * must use a temporary. + */ + +STATIC elem *fixconvop(elem *e) +{ elem *e1,*e2,*ed,*T; + elem *ex; + elem **pe; + unsigned cop,icop,op; + tym_t tycop,tym,tyme; + static unsigned char invconvtab[] = + { + OPbool, // OPb_8 + OPs32_d, // OPd_s32 + OPd_s32, // OPs32_d + OPs16_d, /* OPd_s16 */ + OPd_s16, /* OPs16_d */ + OPu16_d, // OPd_u16 + OPd_u16, // OPu16_d + OPu32_d, /* OPd_u32 */ + OPd_u32, /* OPu32_d */ + OPs64_d, // OPd_s64 + OPd_s64, // OPs64_d + OPu64_d, // OPd_u64 + OPd_u64, // OPu64_d + OPf_d, // OPd_f + OPd_f, // OPf_d + OP32_16, // OPs16_32 + OP32_16, // OPu16_32 + OPs16_32, // OP32_16 + OP16_8, // OPu8_16 + OP16_8, // OPs8_16 + OPs8_16, // OP16_8 + OP64_32, // OPu32_64 + OP64_32, // OPs32_64 + OPs32_64, // OP64_32 + OP128_64, // OPu64_128 + OP128_64, // OPs64_128 + OPs64_128, // OP128_64 +#if TARGET_SEGMENTED + 0, /* OPvp_fp */ + 0, /* OPcvp_fp */ + OPnp_fp, /* OPoffset */ + OPoffset, /* OPnp_fp */ + OPf16p_np, /* OPnp_f16p */ + OPnp_f16p, /* OPf16p_np */ +#endif + OPd_ld, // OPld_d + OPld_d, // OPd_ld + OPu64_d, // OPld_u64 + }; + +//dbg_printf("fixconvop before\n"); +//elem_print(e); + assert(arraysize(invconvtab) == CNVOPMAX - CNVOPMIN + 1); + assert(e); + tyme = e->Ety; + cop = e->E1->Eoper; /* the conversion operator */ + assert(cop <= CNVOPMAX); + + if (e->E1->E1->Eoper == OPcomma) + { /* conv(a,b) op= e2 + * => + * a, (conv(b) op= e2) + */ + elem *ecomma = e->E1->E1; + e->E1->E1 = ecomma->E2; + e->E1->E1->Ety = ecomma->Ety; + ecomma->E2 = e; + ecomma->Ety = e->Ety; + return optelem(ecomma, TRUE); + } + + tycop = e->E1->Ety; + tym = e->E1->E1->Ety; + e->E1 = el_selecte1(e->E1); /* dump it for now */ + e1 = e->E1; + e1->Ety = tym; + e2 = e->E2; + assert(e1 && e2); + /* select inverse conversion operator */ + icop = invconvtab[convidx(cop)]; + + /* First, let's see if we can just throw it away. */ + /* (unslng or shtlng) e op= e2 => e op= (lngsht) e2 */ + if (OTwid(e->Eoper) && + (cop == OPs16_32 || cop == OPu16_32 || + cop == OPu8_16 || cop == OPs8_16)) + { if (e->Eoper != OPshlass && e->Eoper != OPshrass && e->Eoper != OPashrass) + e->E2 = el_una(icop,tym,e2); +//dbg_printf("after1\n"); +//elem_print(e); + return e; + } + + /* Oh well, just split up the op and the =. */ + op = opeqtoop(e->Eoper); /* convert op= to op */ + e->Eoper = OPeq; /* just plain = */ + ed = el_copytree(e1); /* duplicate e1 */ + /* make: e1 = (icop) ((cop) ed op e2)*/ + e->E2 = el_una(icop,e1->Ety, + el_bin(op,tycop,el_una(cop,tycop,ed), + e2)); + +//printf("after1\n"); +//elem_print(e); + + if (op == OPdiv && + tybasic(e2->Ety) == TYcdouble) + { + if (tycop == TYdouble) + { + e->E2->E1->Ety = tybasic(e2->Ety); + e->E2->E1 = el_una(OPc_r, tycop, e->E2->E1); + } + else if (tycop == TYidouble) + { + e->E2->E1->Ety = tybasic(e2->Ety); + e->E2->E1 = el_una(OPc_i, tycop, e->E2->E1); + } + } + + if (op == OPdiv && + tybasic(e2->Ety) == TYcfloat) + { + if (tycop == TYfloat) + { + e->E2->E1->Ety = tybasic(e2->Ety); + e->E2->E1 = el_una(OPc_r, tycop, e->E2->E1); + } + else if (tycop == TYifloat) + { + e->E2->E1->Ety = tybasic(e2->Ety); + e->E2->E1 = el_una(OPc_i, tycop, e->E2->E1); + } + } + + /* Handle case of multiple conversion operators on lvalue */ + /* (such as (intdbl 8int char += double)) */ + ex = e; + pe = &e; + while (OTconv(ed->Eoper)) + { unsigned copx = ed->Eoper; + tym_t tymx; + + icop = invconvtab[convidx(copx)]; + tymx = ex->E1->E1->Ety; + ex->E1 = el_selecte1(ex->E1); // dump it for now + e1 = ex->E1; + e1->Ety = tymx; + ex->E2 = el_una(icop,e1->Ety,ex->E2); + ex->Ety = tymx; + tym = tymx; + + if (ex->Ety != tyme) + { *pe = el_una(copx, ed->Ety, ex); + pe = &(*pe)->E1; + } + + ed = ed->E1; + } +//dbg_printf("after2\n"); +//elem_print(e); + + e->Ety = tym; + if (tym != tyme && + !(tyintegral(tym) && tyintegral(tyme) && tysize(tym) == tysize(tyme))) + e = el_una(cop, tyme, e); + + if (ed->Eoper == OPbit) /* special handling */ + { + ed = ed->E1; + e1 = e1->E1; /* go down one */ + } + /* If we have a *, must assign a temporary to the expression */ + /* underneath it (even if it's a var, as e2 may modify the var). */ + if (ed->Eoper == OPind) + { T = el_alloctmp(ed->E1->Ety); /* make temporary */ + ed->E1 = el_bin(OPeq,T->Ety,T,ed->E1); /* ed: *(T=e) */ + el_free(e1->E1); + e1->E1 = el_copytree(T); + } +//dbg_printf("after3\n"); +//elem_print(e); + return e; +} + +STATIC elem * elerr(elem *e) +{ +#ifdef DEBUG + elem_print(e); +#endif + assert(0); + return (elem *)NULL; +} + +/* For ops with no optimizations */ + +STATIC elem * elzot(elem *e) +{ return e; } + +/**************************** + */ + +STATIC elem * elstring(elem *e) +{ +#if 0 // now handled by el_convert() + if (!OPTIMIZER) + el_convstring(e); // convert string to OPrelconst +#endif + return e; +} + +/************************ + */ + +#if TX86 + +/************************ + * Convert far pointer to pointer. + */ + +STATIC void eltonear(elem **pe) +{ tym_t ty; + elem *e = *pe; + + ty = e->E1->Ety; + e = el_selecte1(e); + e->Ety = ty; + *pe = optelem(e,TRUE); +} + +/************************ + */ + +STATIC elem * elstrcpy(elem *e) +{ tym_t ty; + + elem_debug(e); + switch (e->E2->Eoper) + { +#if TARGET_SEGMENTED + case OPnp_fp: + if (OPTIMIZER) + { + eltonear(&e->E2); + e = optelem(e,TRUE); + } + break; +#endif + case OPstring: + /* Replace strcpy(e1,"string") with memcpy(e1,"string",sizeof("string")) */ +#if 0 + // As memcpy + e->Eoper = OPmemcpy; + elem *en = el_long(TYsize_t, strlen(e->E2->EV.ss.Vstring) + 1); + e->E2 = el_bin(OPparam,TYvoid,e->E2,en); +#else + // As streq + e->Eoper = OPstreq; + type *t = type_allocn(TYarray, tschar); + t->Tdim = strlen(e->E2->EV.ss.Vstring) + 1; + e->ET = t; + t->Tcount++; + e->E1 = el_una(OPind,TYstruct,e->E1); + e->E2 = el_una(OPind,TYstruct,e->E2); + + e = el_bin(OPcomma,e->Ety,e,el_copytree(e->E1->E1)); + if (el_sideeffect(e->E2)) + fixside(&e->E1->E1->E1,&e->E2); +#endif + e = optelem(e,TRUE); + break; + } + return e; +} + +/************************ + */ + +STATIC elem * elstrcmp(elem *e) +{ + elem_debug(e); + if (OPTIMIZER) + { +#if TARGET_SEGMENTED + if (e->E1->Eoper == OPnp_fp) + eltonear(&e->E1); +#endif + switch (e->E2->Eoper) + { +#if TARGET_SEGMENTED + case OPnp_fp: + eltonear(&e->E2); + break; +#endif + + case OPstring: + // Replace strcmp(e1,"string") with memcmp(e1,"string",sizeof("string")) + e->Eoper = OPparam; + e = el_bin(OPmemcmp,e->Ety,e,el_long(TYint,strlen(e->E2->EV.ss.Vstring) + 1)); + e = optelem(e,TRUE); + break; + } + } + return e; +} + +/**************************** + * For OPmemcmp, OPmemcpy, OPmemset. + */ + +STATIC elem * elmemxxx(elem *e) +{ + elem_debug(e); + if (OPTIMIZER) + { elem *ex; + + ex = e->E1; + switch (e->Eoper) + { case OPmemcmp: +#if TARGET_SEGMENTED + if (ex->E1->Eoper == OPnp_fp) + eltonear(&ex->E1); + if (ex->E2->Eoper == OPnp_fp) + eltonear(&ex->E2); +#endif + break; + + case OPmemset: +#if TARGET_SEGMENTED + if (ex->Eoper == OPnp_fp) + eltonear(&ex); + else +#endif + { + // lvalue OPmemset (nbytes param value) + elem *enbytes = e->E2->E1; + elem *evalue = e->E2->E2; + +#if MARS && TX86 + if (enbytes->Eoper == OPconst && evalue->Eoper == OPconst + /* && tybasic(e->E1->Ety) == TYstruct*/) + { tym_t tym; + tym_t ety; + int nbytes = el_tolong(enbytes); + targ_llong value = el_tolong(evalue); + elem *e1 = e->E1; + elem *tmp; + + if (e1->Eoper == OPcomma || OTassign(e1->Eoper)) + return cgel_lvalue(e); // replace (e,v)op=e2 with e,(v op= e2) + + switch (nbytes) + { + case CHARSIZE: tym = TYchar; goto L1; + case SHORTSIZE: tym = TYshort; goto L1; + case LONGSIZE: tym = TYlong; goto L1; +#if LONGLONG + case LLONGSIZE: if (intsize == 2) + goto Ldefault; + tym = TYllong; goto L1; +#endif + L1: + ety = e->Ety; + memset(&value, value & 0xFF, sizeof(value)); + evalue->EV.Vullong = value; + evalue->Ety = tym; + e->Eoper = OPeq; + e->Ety = (e->Ety & ~mTYbasic) | tym; + if (tybasic(e1->Ety) == TYstruct) + e1->Ety = tym; + else + e->E1 = el_una(OPind, tym, e1); + tmp = el_same(&e->E1); + tmp = el_una(OPaddr, ety, tmp); + e->E2->Ety = tym; + e->E2 = el_selecte2(e->E2); + e = el_combine(e, tmp); + e = optelem(e,TRUE); + break; + + default: + Ldefault: + break; + } + } +#endif + } + break; + + case OPmemcpy: +#if TARGET_SEGMENTED + if (ex->Eoper == OPnp_fp) + eltonear(&e->E1); +#endif + ex = e->E2; +#if TARGET_SEGMENTED + if (ex->E1->Eoper == OPnp_fp) + eltonear(&ex->E1); +#endif + if (ex->E2->Eoper == OPconst) + { + if (!boolres(ex->E2)) + { // Copying 0 bytes, so remove memcpy + e->E2 = e->E1; + e->E1 = ex->E1; + ex->E1 = NULL; + e->Eoper = OPcomma; + el_free(ex); + return optelem(e, TRUE); + } +#if 1 + // Convert OPmemcpy to OPstreq + e->Eoper = OPstreq; + type *t = type_allocn(TYarray, tschar); + t->Tdim = el_tolong(ex->E2); + e->ET = t; + t->Tcount++; + e->E1 = el_una(OPind,TYstruct,e->E1); + e->E2 = el_una(OPind,TYstruct,ex->E1); + ex->E1 = NULL; + el_free(ex); + ex = el_copytree(e->E1->E1); +#if TARGET_SEGMENTED + if (tysize(e->Ety) > tysize(ex->Ety)) + ex = el_una(OPnp_fp,e->Ety,ex); +#endif + e = el_bin(OPcomma,e->Ety,e,ex); + if (el_sideeffect(e->E2)) + fixside(&e->E1->E1->E1,&e->E2); + return optelem(e,TRUE); +#endif + } + break; + + default: + assert(0); + } + } + return e; +} + +#endif + +/*********************** + * + # (combine offsets with addresses) + * / \ => | + * # c v,c + * | + * v + */ + +STATIC elem * eladd(elem *e) +{ elem *e1,*e2; + int sz; + + //printf("eladd(%p)\n",e); + targ_size_t ptrmask = ~(targ_size_t)0; + if (NPTRSIZE <= 4) + ptrmask = 0xFFFFFFFF; +L1: + e1 = e->E1; + e2 = e->E2; + if (e2->Eoper == OPconst) + { +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (e1->Eoper == OPrelconst && e1->EV.sp.Vsym->Sfl == FLgot) + goto ret; +#endif + if (e1->Eoper == OPrelconst /* if (&v) + c */ + || e1->Eoper == OPstring + ) + { + e1->EV.sp.Voffset += e2->EV.Vpointer; + e1->EV.sp.Voffset &= ptrmask; + e = el_selecte1(e); + goto ret; + } + } + else if (e1->Eoper == OPconst) + { +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (e2->Eoper == OPrelconst && e2->EV.sp.Vsym->Sfl == FLgot) + goto ret; +#endif + if (e2->Eoper == OPrelconst /* if c + (&v) */ + || e2->Eoper == OPstring + ) + { + e2->EV.sp.Voffset += e1->EV.Vpointer; + e2->EV.sp.Voffset &= ptrmask; + e = el_selecte2(e); + goto ret; + } + } + + if (!OPTIMIZER) + goto ret; + + /* Replace ((e + &v) + c) with (e + (&v+c)) */ + if (e2->Eoper == OPconst && e1->Eoper == OPadd && + (e1->E2->Eoper == OPrelconst || e1->E2->Eoper == OPstring)) + { + e1->E2->EV.sp.Voffset += e2->EV.Vpointer; + e1->E2->EV.sp.Voffset &= ptrmask; + e = el_selecte1(e); + goto L1; + } + /* Replace ((e + c) + &v) with (e + (&v+c)) */ + else if ((e2->Eoper == OPrelconst || e2->Eoper == OPstring) && + e1->Eoper == OPadd && cnst(e1->E2)) + { + e2->EV.sp.Voffset += e1->E2->EV.Vpointer; + e2->EV.sp.Voffset &= ptrmask; + e->E1 = el_selecte1(e1); + goto L1; /* try and find some more */ + } + /* Replace (e1 + -e) with (e1 - e) */ + else if (e2->Eoper == OPneg) + { e->E2 = el_selecte1(e2); + e->Eoper = OPmin; + again = 1; + return e; + } + /* Replace (-v + e) with (e + -v) */ + else if (e1->Eoper == OPneg && OTleaf(e1->E1->Eoper)) + { e->E1 = e2; + e->E2 = e1; /* swap leaves */ + goto L1; + } + /* Replace ((e - e2) + e2) with (e) */ + /* The optimizer sometimes generates this case */ + else if (!tyfloating(e->Ety) && /* no floating bugs */ + e1->Eoper == OPmin && + el_match(e1->E2,e2) && + !el_sideeffect(e2)) + { tym_t tym = e->Ety; + + e = el_selecte1(el_selecte1(e)); + e->Ety = tym; /* retain original type */ + return e; + } + /* Replace ((e - #v+c1) + #v+c2) with ((e - c1) + c2) */ + else if (e2->Eoper == OPrelconst && + e1->Eoper == OPmin && + e1->E2->Eoper == OPrelconst && + e1->E2->EV.sp.Vsym == e2->EV.sp.Vsym) + { e2->Eoper = OPconst; + e2->Ety = TYint; + e1->Ety = e1->E1->Ety; + e1->E2->Eoper = OPconst; + e1->E2->Ety = TYint; + { +#if TARGET_SEGMENTED + /* Watch out for pointer types changing, requiring a conversion */ + tym_t ety,e11ty; + + ety = tybasic(e->Ety); + e11ty = tybasic(e1->E1->Ety); + if (typtr(ety) && typtr(e11ty) && + tysize[ety] != tysize[e11ty]) + { + e = el_una((tysize[ety] > tysize[e11ty]) ? OPnp_fp : OPoffset, + e->Ety,e); + e->E1->Ety = e1->Ety; + } +#endif + } + again = 1; + return e; + } + /* Replace (e + e) with (e * 2) */ + else if (el_match(e1,e2) && !el_sideeffect(e1) && !tyfloating(e->Ety)) + { + e->Eoper = OPmul; + el_free(e2); + e->E2 = el_long(e->Ety,2); + again = 1; + return e; + } + // Replace ((e11 + c) + e2) with ((e11 + e2) + c) + if (e1->Eoper == OPadd && e1->E2->Eoper == OPconst && + (e2->Eoper == OPvar || !OTleaf(e2->Eoper)) && + tysize(e1->Ety) == tysize(e2->Ety) && + tysize(e1->E2->Ety) == tysize(e2->Ety)) + { + e->E2 = e1->E2; + e1->E2 = e2; + e1->Ety = e->Ety; + goto ret; + } + + // Replace ((e11 - e12) + e2) with ((e11 + e2) - e12) + // (this should increase the number of LEA possibilities) + sz = tysize(e->Ety); + if (e1->Eoper == OPmin && + tysize(e1->Ety) == sz && + tysize(e2->Ety) == sz && + tysize(e1->E1->Ety) == sz && + tysize(e1->E2->Ety) == sz && + !tyfloating(e->Ety) + ) + { + e->Eoper = OPmin; + e->E2 = e1->E2; + e1->E2 = e2; + e1->Eoper = OPadd; + } + +ret: + return e; +} + + +/************************ + * Multiply (for OPmul && OPmulass) + * e * (c**2) => e << c ;replace multiply by power of 2 with shift + */ + +STATIC elem * elmul(elem *e) +{ + tym_t tym = e->Ety; + + if (OPTIMIZER) + { + // Replace -a*-b with a*b. + // This is valid for all floating point types as well as integers. + if (tyarithmetic(tym) && e->E2->Eoper == OPneg && e->E1->Eoper == OPneg) + { + e->E1 = el_selecte1(e->E1); + e->E2 = el_selecte1(e->E2); + } + } + + elem *e2 = e->E2; + if (e2->Eoper == OPconst) /* try to replace multiplies with shifts */ + { + if (OPTIMIZER) + { + elem *e1 = e->E1; + unsigned op1 = e1->Eoper; + + if (tyintegral(tym) && // skip floating types + OTbinary(op1) && + e1->E2->Eoper == OPconst + ) + { + /* Attempt to replace ((e + c1) * c2) with (e * c2 + (c1 * c2)) + * because the + can be frequently folded out (merged into an + * array offset, for example. + */ + if (op1 == OPadd) + { + e->Eoper = OPadd; + e1->Eoper = OPmul; + e->E2 = el_bin(OPmul,tym,e1->E2,e2); + e1->E2 = el_copytree(e2); + again = 1; + return e; + } + + // ((e << c1) * c2) => e * ((1 << c1) * c2) + if (op1 == OPshl) + { + e2->EV.Vullong *= (targ_ullong)1 << el_tolong(e1->E2); + e1->E2->EV.Vullong = 0; + again = 1; + return e; + } + } + + if (elemisnegone(e2)) + { + e->Eoper = (e->Eoper == OPmul) ? OPneg : OPnegass; + e->E2 = NULL; + el_free(e2); + return e; + } + } + + if (tyintegral(tym)) + { int i; + + i = ispow2(el_tolong(e2)); /* check for power of 2 */ + if (i != -1) /* if it is a power of 2 */ + { e2->EV.Vint = i; + e2->Ety = TYint; + e->Eoper = (e->Eoper == OPmul) /* convert to shift left */ + ? OPshl : OPshlass; + again = 1; + return e; + } + else if (el_allbits(e2,-1)) + goto Lneg; + } + else if (elemisnegone(e2) && !tycomplex(e->E1->Ety)) + { + goto Lneg; + } + } + return e; + +Lneg: + e->Eoper = (e->Eoper == OPmul) /* convert to negate */ + ? OPneg : OPnegass; + el_free(e->E2); + e->E2 = NULL; + again = 1; + return e; +} + +/************************ + * Subtract + * - + + * / \ => / \ (propagate minuses) + * e c e -c + */ + +STATIC elem * elmin(elem *e) +{ elem *e2; + +L1: + e2 = e->E2; + + if (OPTIMIZER) + { + elem *e1; + tym_t tym; + + tym = e->Ety; + e1 = e->E1; + if (e2->Eoper == OPrelconst) + { if (e1->Eoper == OPrelconst && e1->EV.sp.Vsym == e2->EV.sp.Vsym) + { e->Eoper = OPconst; + e->EV.Vint = e1->EV.sp.Voffset - e2->EV.sp.Voffset; + el_free(e1); + el_free(e2); + return e; + } + } + + /* Convert subtraction of long pointers to subtraction of integers */ + if (tyfv(e2->Ety) && tyfv(e1->Ety)) + { e->E1 = el_una(OP32_16,tym,e1); + e->E2 = el_una(OP32_16,tym,e2); + return optelem(e,TRUE); + } + + /* Replace (0 - e2) with (-e2) */ + if (cnst(e1) && !boolres(e1)) + { el_free(e1); + e->E1 = e2; + e->E2 = NULL; + e->Eoper = OPneg; + return optelem(e,TRUE); + } + + /* Replace (e - e) with (0) */ + if (el_match(e1,e2) && !el_sideeffect(e1)) + { el_free(e); + e = el_calloc(); + e->Eoper = OPconst; + e->Ety = tym; + return e; + } + + /* Replace (e1 + c1) - (e2 + c2) with (e1 - e2) + (c1 - c2), but not */ + /* for floating or far or huge pointers! */ + if (e1->Eoper == OPadd && e2->Eoper == OPadd && + cnst(e1->E2) && cnst(e2->E2) && + (tyintegral(tym) || tybasic(tym) == TYjhandle || tybasic(tym) == TYnptr +#if TARGET_SEGMENTED + || tybasic(tym) == TYsptr +#endif + )) + { elem *tmp; + + e->Eoper = OPadd; + e1->Eoper = OPmin; + e2->Eoper = OPmin; + tmp = e1->E2; + e1->E2 = e2->E1; + e2->E1 = tmp; + return optelem(e,TRUE); + } + } + +#if TX86 && !(MARS) + if (tybasic(e2->Ety) == TYhptr && tybasic(e->E1->Ety) == TYhptr) + { // Convert to _aNahdiff(e1,e2) + static symbol hdiff = SYMBOLY(FLfunc,mBX|mCX|mSI|mDI|mBP|mES,"_aNahdiff",0); + + if (LARGECODE) + hdiff.Sident[2] = 'F'; + hdiff.Stype = tsclib; + e->Eoper = OPcall; + e->E2 = el_bin(OPparam,TYint,e2,e->E1); + e->E1 = el_var(&hdiff); + return e; + } +#endif + + /* Disallow the optimization on doubles. The - operator is not */ + /* rearrangable by K+R, and can cause floating point problems if */ + /* converted to an add ((a + 1.0) - 1.0 shouldn't be folded). */ + if (cnst(e2) && !tyfloating(e2->Ety)) + { e->E2 = el_una(OPneg,e2->Ety,e2); + e->Eoper = OPadd; + return optelem(e,TRUE); + } + return e; +} + +/***************************** + * Attempt to 'shrink' bitwise expressions. + * Good for & | ^. + * This should be expanded to include long type stuff. + */ + +STATIC elem * elbitwise(elem *e) +{ elem *e1,*e2; + targ_short i; + targ_ulong ul; + int op; + unsigned sz; + + e2 = e->E2; + e1 = e->E1; + op = e1->Eoper; + if (e2->Eoper == OPconst) + { + sz = tysize(e2->Ety); + switch (sz) + { + tym_t tym; + case CHARSIZE: + /* Replace (c & 0xFF) with (c) */ + if (OPTIMIZER && e2->EV.Vuchar == CHARMASK) + { + L1: + switch (e->Eoper) + { case OPand: /* (c & 0xFF) => (c) */ + return el_selecte1(e); + case OPor: /* (c | 0xFF) => (0xFF) */ + return el_selecte2(e); + case OPxor: /* (c ^ 0xFF) => (~c) */ + tym = e->Ety; + return el_una(OPcom,tym,el_selecte1(e)); + default: + assert(0); + } + } + break; + + case LONGSIZE: + if (!OPTIMIZER) + break; + ul = e2->EV.Vulong; + + if (ul == 0xFFFFFFFF) /* if e1 & 0xFFFFFFFF */ + goto L1; + /* (x >> 16) & 0xFFFF => ((unsigned long)x >> 16) */ + if (ul == 0xFFFF && e->Eoper == OPand && (op == OPshr || op == OPashr) && + e1->E2->Eoper == OPconst && el_tolong(e1->E2) == 16) + { elem *e11 = e1->E1; + + e11->Ety = touns(e11->Ety) | (e11->Ety & ~mTYbasic); + goto L1; + } + + /* Replace (L & 0x0000XXXX) with (unslng)((lngsht) & 0xXXXX) */ + if (intsize < LONGSIZE && + e->Eoper == OPand && + ul <= SHORTMASK) + { tym = e->Ety; + e->E1 = el_una(OP32_16,TYushort,e->E1); + e->E2 = el_una(OP32_16,TYushort,e->E2); + e->Ety = TYushort; + e = el_una(OPu16_32,tym,e); + goto Lopt; + } + + // Replace ((s8sht)L & 0xFF) with (u8sht)L + if (ul == 0xFF && intsize == LONGSIZE && e->Eoper == OPand && + (op == OPs8_16 || op == OPu8_16) + ) + { + e1->Eoper = OPu8_16; + e = el_selecte1(e); + goto Lopt; + } + + break; + + case SHORTSIZE: + i = e2->EV.Vshort; + if (i == (targ_short)SHORTMASK) // e2 & 0xFFFF + goto L1; + + /* (x >> 8) & 0xFF => ((unsigned short)x >> 8) */ + if (OPTIMIZER && i == 0xFF && e->Eoper == OPand && + (op == OPshr || op == OPashr) && e1->E2->Eoper == OPconst && e1->E2->EV.Vint == 8) + { elem *e11 = e1->E1; + + e11->Ety = touns(e11->Ety) | (e11->Ety & ~mTYbasic); + goto L1; + } + + // (s8_16(e) & 0xFF) => u8_16(e) + if (OPTIMIZER && op == OPs8_16 && e->Eoper == OPand && + i == 0xFF) + { + e1->Eoper = OPu8_16; + e = el_selecte1(e); + goto Lopt; + } + + if ( + /* OK for unsigned if AND or high bits of i are 0 */ + op == OPu8_16 && (e->Eoper == OPand || !(i & ~0xFF)) || + /* OK for signed if i is 'sign-extended' */ + op == OPs8_16 && (targ_short)(targ_schar)i == i + ) + { + /* Convert ((u8int) e) & i) to (u8int)(e & (int8) i) */ + /* or similar for s8int */ + e = el_una(e1->Eoper,e->Ety,e); + e->E1->Ety = e1->Ety = e1->E1->Ety; + e->E1->E1 = el_selecte1(e1); + e->E1->E2 = el_una(OP16_8,e->E1->Ety,e->E1->E2); + goto Lopt; + } + break; + +#if __INTSIZE == 4 + case LLONGSIZE: + if (OPTIMIZER) + { + if (e2->EV.Vullong == LLONGMASK) + goto L1; + } + break; +#endif + } + if (OPTIMIZER && sz < 16) + { targ_ullong ul = el_tolong(e2); + + if (e->Eoper == OPor && op == OPand && e1->E2->Eoper == OPconst) + { + // ((x & c1) | c2) => (x | c2) + targ_ullong c3; + + c3 = ul | e1->E2->EV.Vullong; + switch (sz) + { case CHARSIZE: + if ((c3 & CHARMASK) == CHARMASK) + goto L2; + break; + case SHORTSIZE: + if ((c3 & SHORTMASK) == SHORTMASK) + goto L2; + break; + case LONGSIZE: + if ((c3 & LONGMASK) == LONGMASK) + { + L2: + e1->E2->EV.Vullong = c3; + e->E1 = elbitwise(e1); + goto Lopt; + } + break; +#if __INTSIZE == 4 + case LLONGSIZE: + if ((c3 & LLONGMASK) == LLONGMASK) + goto L2; + break; +#endif + default: + assert(0); + } + } + +#if __INTSIZE == 4 + if (op == OPs16_32 && (ul & 0xFFFFFFFFFFFF8000LL) == 0 || + op == OPu16_32 && (ul & 0xFFFFFFFFFFFF0000LL) == 0 || + op == OPs8_16 && (ul & 0xFFFFFFFFFFFFFF80LL) == 0 || + op == OPu8_16 && (ul & 0xFFFFFFFFFFFFFF00LL) == 0 || + op == OPs32_64 && (ul & 0xFFFFFFFF80000000LL) == 0 || + op == OPu32_64 && (ul & 0xFFFFFFFF00000000LL) == 0 + ) +#else + if (op == OPs16_32 && (ul & 0xFFFF8000) == 0 || + op == OPu16_32 && (ul & 0xFFFF0000) == 0 || + op == OPs8_16 && (ul & 0xFFFFFF80) == 0 || + op == OPu8_16 && (ul & 0xFFFFFF00) == 0) +#endif + { + if (e->Eoper == OPand) + { if (op == OPs16_32 && (ul & 0x8000) == 0) + e1->Eoper = OPu16_32; + else if (op == OPs8_16 && (ul & 0x80) == 0) + e1->Eoper = OPu8_16; +#if __INTSIZE == 4 + else if (op == OPs32_64 && (ul & 0x80000000) == 0) + e1->Eoper = OPu32_64; +#endif + } + + // ((shtlng)s & c) => ((shtlng)(s & c) + e1->Ety = e->Ety; + e->Ety = e2->Ety = e1->E1->Ety; + e->E1 = e1->E1; + e1->E1 = e; + e = e1; + goto Lopt; + } + + // Replace (((a & b) ^ c) & d) with ((a ^ c) & e), where + // e is (b&d). + if (e->Eoper == OPand && op == OPxor && e1->E1->Eoper == OPand && + e1->E1->E2->Eoper == OPconst) + { + e2->EV.Vullong &= e1->E1->E2->EV.Vullong; + e1->E1 = el_selecte1(e1->E1); + goto Lopt; + } + } + } + return e; + +Lopt: +#ifdef DEBUG + static int nest; + nest++; + if (nest > 100) + { elem_print(e); + assert(0); + } + e = optelem(e,TRUE); + nest--; + return e; +#endif + return optelem(e,TRUE); +} + + +/************************************* + * Replace shift|shift with rotate. + */ + +STATIC elem *elor(elem *e) +{ + /* ROL: (a << shift) | (a >> (sizeof(a) * 8 - shift)) + * ROR: (a >> shift) | (a << (sizeof(a) * 8 - shift)) + */ + elem *e1 = e->E1; + elem *e2 = e->E2; + unsigned sz = tysize(e->Ety); + if (sz <= intsize) + { + if (e1->Eoper == OPshl && e2->Eoper == OPshr && + tyuns(e2->E1->Ety) && e2->E2->Eoper == OPmin && + e2->E2->E1->Eoper == OPconst && + el_tolong(e2->E2->E1) == sz * 8 && + el_match5(e1->E1, e2->E1) && + el_match5(e1->E2, e2->E2->E2) && + !el_sideeffect(e) + ) + { + e1->Eoper = OProl; + return el_selecte1(e); + } + if (e1->Eoper == OPshr && e2->Eoper == OPshl && + tyuns(e1->E1->Ety) && e2->E2->Eoper == OPmin && + e2->E2->E1->Eoper == OPconst && + el_tolong(e2->E2->E1) == sz * 8 && + el_match5(e1->E1, e2->E1) && + el_match5(e1->E2, e2->E2->E2) && + !el_sideeffect(e) + ) + { + e1->Eoper = OPror; + return el_selecte1(e); + } + } + return elbitwise(e); +} + +/************************************* + */ + +STATIC elem *elxor(elem *e) +{ + if (OPTIMIZER) + { + elem *e1 = e->E1; + elem *e2 = e->E2; + + /* Recognize: + * (a & c) ^ (b & c) => (a ^ b) & c + */ + if (e1->Eoper == OPand && e2->Eoper == OPand && + el_match5(e1->E2, e2->E2) && + (e2->E2->Eoper == OPconst || (!el_sideeffect(e2->E1) && !el_sideeffect(e2->E2)))) + { + el_free(e1->E2); + e1->E2 = e2->E1; + e1->Eoper = OPxor; + e->Eoper = OPand; + e->E2 = e2->E2; + e2->E1 = NULL; + e2->E2 = NULL; + el_free(e2); + return optelem(e, TRUE); + } + } + return elbitwise(e); +} + +/************************** + * Optimize nots. + * ! ! e => bool e + * ! bool e => ! e + * ! OTrel => !OTrel (invert the condition) + * ! OTconv => ! + */ + +STATIC elem * elnot(elem *e) +{ elem *e1; + unsigned op; + + e1 = e->E1; + op = e1->Eoper; + switch (op) + { case OPnot: // ! ! e => bool e + case OPbool: // ! bool e => ! e + e1->Eoper = op ^ (OPbool ^ OPnot); + /* That was a clever substitute for the following: */ + /* e->Eoper = (op == OPnot) ? OPbool : OPnot; */ + goto L1; + + default: + if (OTrel(op)) /* ! OTrel => !OTrel */ + { + /* Find the logical negation of the operator */ + op = rel_not(op); + if (!tyfloating(e1->E1->Ety)) + { op = rel_integral(op); + assert(OTrel(op)); + } + e1->Eoper = op; + + L1: e = optelem(el_selecte1(e),TRUE); + } + else if (tybasic(e1->Ety) == TYbool && tysize(e->Ety) == 1) + { + // !e1 => (e1 ^ 1) + e->Eoper = OPxor; + e->E2 = el_long(e1->Ety,1); + e = optelem(e,TRUE); + } +#if 0 +// Can't use this because what if OPd_s32? +// Note: !(long)(.1) != !(.1) + else if (OTconv(op)) // don't use case because of differ target + { // conversion operators + e1->Eoper = e->Eoper; + goto L1; + } +#endif + break; + + case OPs32_d: + case OPs16_d: + case OPu16_d: + case OPu32_d: + case OPf_d: + case OPs16_32: + case OPu16_32: + case OPu8_16: + case OPs8_16: + case OPu32_64: + case OPs32_64: +#if TARGET_SEGMENTED + case OPvp_fp: + case OPcvp_fp: + case OPnp_fp: +#endif + e1->Eoper = e->Eoper; + goto L1; + + case OPcomma: + /* !(a,b) => (a,!b) */ + e->Eoper = OPcomma; + e->E1 = e1->E1; // a + e->E2 = e1; // ! + e1->Eoper = OPnot; + e1->Ety = e->Ety; + e1->E1 = e1->E2; // b + e1->E2 = NULL; + e = optelem(e,TRUE); + break; + } + return e; +} + +/************************* + * Complement + * ~ ~ e => e + */ + +STATIC elem * elcom(elem *e) +{ elem *e1; + + e1 = e->E1; + if (e1->Eoper == OPcom) /* ~ ~ e => e */ + /* Typing problem here */ + e = el_selecte1(el_selecte1(e)); + return e; +} + +/************************* + * If it is a conditional of a constant + * then we know which exp to evaluate. + * BUG: + * doesn't detect ("string" ? et : ef) + */ + +STATIC elem * elcond(elem *e) +{ elem *e1; + elem *ex; + + e1 = e->E1; + switch (e1->Eoper) + { case OPconst: + if (boolres(e1)) + L1: + e = el_selecte1(el_selecte2(e)); + else + e = el_selecte2(el_selecte2(e)); + break; + case OPrelconst: + case OPstring: + goto L1; + + case OPcomma: + // ((a,b) ? c) => (a,(b ? c)) + e->Eoper = OPcomma; + e->E1 = e1->E1; + e1->E1 = e1->E2; + e1->E2 = e->E2; + e->E2 = e1; + e1->Eoper = OPcond; + e1->Ety = e->Ety; + return optelem(e,TRUE); + + case OPnot: + // (!a ? b : c) => (a ? c : b) + ex = e->E2->E1; + e->E2->E1 = e->E2->E2; + e->E2->E2 = ex; + goto L2; + + default: + if (OTboolnop(e1->Eoper)) + { + L2: + e->E1 = e1->E1; + e1->E1 = NULL; + el_free(e1); + return elcond(e); + } + { + if (OPTIMIZER) + { + elem *ec1,*ec2; + tym_t ty = e->Ety; + + ec1 = e->E2->E1; + ec2 = e->E2->E2; + if (tyintegral(ty) && ec1->Eoper == OPconst && ec2->Eoper == OPconst) + { targ_llong i1,i2; + targ_llong b; + + i1 = el_tolong(ec1); + i2 = el_tolong(ec2); + + /* If b is an integer with only 1 bit set then */ + /* replace ((a & b) ? b : 0) with (a & b) */ + /* replace ((a & b) ? 0 : b) with ((a & b) ^ b) */ + if (e1->Eoper == OPand && e1->E2->Eoper == OPconst && + tysize(ty) == tysize(ec1->Ety)) + { + b = el_tolong(e1->E2); + if (ispow2(b) != -1) /* if only 1 bit is set */ + { + if (b == i1 && i2 == 0) + { e = el_selecte1(e); + e->E1->Ety = ty; + e->E2->Ety = ty; + e->E2->EV.Vllong = b; + return optelem(e,TRUE); + } + else if (i1 == 0 && b == i2) + { + e1->Ety = ty; + e1->E1->Ety = ty; + e1->E2->Ety = ty; + e1->E2->EV.Vllong = b; + e->E1 = el_bin(OPxor,ty,e1,el_long(ty,b)); + e = el_selecte1(e); + return optelem(e,TRUE); + } + } + } + + /* Replace ((a relop b) ? 1 : 0) with (a relop b) */ + else if (OTrel(e1->Eoper) && + tysize(ty) <= tysize[TYint]) + { + if (i1 == 1 && i2 == 0) + e = el_selecte1(e); + else if (i1 == 0 && i2 == 1) + { + e->E1 = el_una(OPnot,ty,e1); + e = optelem(el_selecte1(e),TRUE); + } + } +#if TX86 + // The next two optimizations attempt to replace with an + // unsigned compare, which the code generator can generate + // code for without using jumps. + + // Try to replace (!e1) with (e1 < 1) + else if (e1->Eoper == OPnot && !OTrel(e1->E1->Eoper)) + { + e->E1 = el_bin(OPlt,TYint,e1->E1,el_long(touns(e1->E1->Ety),1)); + e1->E1 = NULL; + el_free(e1); + } + // Try to replace (e1) with (e1 >= 1) + else if (!OTrel(e1->Eoper)) + { + if (tyfv(e1->Ety)) + { + if (tysize(e->Ety) == tysize[TYint]) + { + if (i1 == 1 && i2 == 0) + { e->Eoper = OPbool; + el_free(e->E2); + e->E2 = NULL; + } + else if (i1 == 0 && i2 == 1) + { e->Eoper = OPnot; + el_free(e->E2); + e->E2 = NULL; + } + } + } + else if(tyintegral(e1->Ety)) + e->E1 = el_bin(OPge,TYint,e1,el_long(touns(e1->Ety),1)); + } +#endif + } + + // Try to detect absolute value expression + // (a < 0) -a : a + if ((e1->Eoper == OPlt || e1->Eoper == OPle) && + e1->E2->Eoper == OPconst && + !boolres(e1->E2) && + !tyuns(e1->E1->Ety) && + !tyuns(e1->E2->Ety) && + ec1->Eoper == OPneg && + !el_sideeffect(ec2) && + el_match(e->E1->E1,ec2) && + el_match(ec1->E1,ec2) && + tysize(ty) >= intsize + ) + { e->E2->E2 = NULL; + el_free(e); + e = el_una(OPabs,ty,ec2); + } + // (a >= 0) a : -a + else if ((e1->Eoper == OPge || e1->Eoper == OPgt) && + e1->E2->Eoper == OPconst && + !boolres(e1->E2) && + !tyuns(e1->E1->Ety) && + !tyuns(e1->E2->Ety) && + ec2->Eoper == OPneg && + !el_sideeffect(ec1) && + el_match(e->E1->E1,ec1) && + el_match(ec2->E1,ec1) && + tysize(ty) >= intsize + ) + { e->E2->E1 = NULL; + el_free(e); + e = el_una(OPabs,ty,ec1); + } + break; + } + } + } + return e; +} + + +/**************************** + * Comma operator. + * , e + * / \ => expression with no effect + * c e + * , , + * / \ => / \ operators with no effect + * + e , e + * / \ / \ + * e e e e + */ + +STATIC elem * elcomma(elem *e) +{ register elem *e1,**pe1; + elem *e2; + int e1op; + int changes; + + changes = -1; +L1: + changes++; +L2: + //printf("elcomma()\n"); + e2 = e->E2; + pe1 = &(e->E1); + e1 = *pe1; + e1op = e1->Eoper; + + /* c,e => e */ + if (OTleaf(e1op) && !OTsideff(e1op) && !(e1->Ety & mTYvolatile)) + { e2->Ety = e->Ety; + e = el_selecte2(e); + goto Lret; + } + + /* ((a op b),e2) => ((a,b),e2) if op has no side effects */ + if (!el_sideeffect(e1) && e1op != OPcomma && e1op != OPandand && + e1op != OPoror && e1op != OPcond) + { + if (OTunary(e1op)) + *pe1 = el_selecte1(e1); /* get rid of e1 */ + else + { e1->Eoper = OPcomma; + e1->Ety = e1->E2->Ety; + } + goto L1; + } + + if (!OPTIMIZER) + goto Lret; + + /* Replace (a,b),e2 with a,(b,e2) */ + if (e1op == OPcomma) + { + e1->Ety = e->Ety; + e->E1 = e1->E1; + e1->E1 = e1->E2; + e1->E2 = e2; + e->E2 = elcomma(e1); + goto L2; + } + + if ((OTopeq(e1op) || e1op == OPeq) && + (e1->E1->Eoper == OPvar || e1->E1->Eoper == OPind) && + !el_sideeffect(e1->E1) + ) + { + if (el_match(e1->E1,e2)) + // ((a = b),a) => (a = b) + e = el_selecte1(e); + else if (OTrel(e2->Eoper) && + OTleaf(e2->E2->Eoper) && + el_match(e1->E1,e2->E1) + ) + { // ((a = b),(a < 0)) => ((a = b) < 0) + e1->Ety = e2->E1->Ety; + e->E1 = e2->E1; + e2->E1 = e1; + goto L1; + } + else if ((e2->Eoper == OPandand || + e2->Eoper == OPoror || + e2->Eoper == OPcond) && + el_match(e1->E1,e2->E1) + ) + { + /* ((a = b),(a || c)) => ((a = b) || c) */ + e1->Ety = e2->E1->Ety; + e->E1 = e2->E1; + e2->E1 = e1; + e = el_selecte2(e); + changes++; + goto Lret; + } + else if (e1op == OPeq) + { + /* Replace ((a = b),(c = a)) with a,(c = (a = b)) */ + for (; e2->Eoper == OPcomma; e2 = e2->E1) + ; + if ((OTopeq(e2->Eoper) || e2->Eoper == OPeq) && + el_match(e1->E1,e2->E2) && +#if 0 + !(e1->E1->Eoper == OPvar && el_appears(e2->E1,e1->E1->EV.sp.Vsym)) && +#endif + ERTOL(e2)) + { + e->E1 = e2->E2; + e1->Ety = e2->E2->Ety; + e2->E2 = e1; + goto L1; + } + } + else + { +#if 1 // This optimization is undone in eleq(). + // Replace ((a op= b),(a op= c)) with (0,a = (a op b) op c) + for (; e2->Eoper == OPcomma; e2 = e2->E1) + ; + if ((OTopeq(e2->Eoper)) && + el_match(e1->E1,e2->E1)) + { elem *ex; + + e->E1 = el_long(TYint,0); + e1->Eoper = opeqtoop(e1op); + e2->E2 = el_bin(opeqtoop(e2->Eoper),e2->Ety,e1,e2->E2); + e2->Eoper = OPeq; + goto L1; + } +#endif + } + } +Lret: + again = changes != 0; + return e; +} + +/******************************** + */ + +STATIC elem * elremquo(elem *e) +{ +#if 0 && MARS + if (cnst(e->E2) && !boolres(e->E2)) + error(e->Esrcpos.Sfilename, e->Esrcpos.Slinnum, "divide by zero\n"); +#endif + return e; +} + +/******************************** + */ + +STATIC elem * elmod(elem *e) +{ + elem *e1; + elem *e2; + tym_t tym; + + tym = e->E1->Ety; + if (!tyfloating(tym)) + return eldiv(e); + return e; +} + +/***************************** + * Convert divides to >> if power of 2. + * Can handle OPdiv, OPdivass, OPmod. + */ + +STATIC elem * eldiv(elem *e) +{ elem *e2; + tym_t tym; + int uns; + + e2 = e->E2; + tym = e->E1->Ety; + uns = tyuns(tym) | tyuns(e2->Ety); + if (cnst(e2)) + { +#if 0 && MARS + if (!boolres(e2)) + error(e->Esrcpos.Sfilename, e->Esrcpos.Slinnum, "divide by zero\n"); +#endif + if (uns) + { int i; + + e2->Ety = touns(e2->Ety); + i = ispow2(el_tolong(e2)); + if (i != -1) + { int op; + + switch (e->Eoper) + { case OPdiv: + op = OPshr; + goto L1; + case OPdivass: + op = OPshrass; + L1: + e2->EV.Vint = i; + e2->Ety = TYint; + e->E1->Ety = touns(tym); + break; + + case OPmod: + op = OPand; + goto L3; + case OPmodass: + op = OPandass; + L3: + e2->EV.Vullong = el_tolong(e2) - 1; + break; + + default: + assert(0); + } + e->Eoper = op; + return optelem(e,TRUE); + } + } + } + + if (OPTIMIZER) + { + if (tyintegral(tym) && (e->Eoper == OPdiv || e->Eoper == OPmod)) + { int sz = tysize(tym); + + // See if we can replace with OPremquo + if (sz == REGSIZE /*&& !I64*/) // need cent and ucent working for I64 to work + { + // Don't do it if there are special code sequences in the + // code generator (see cdmul()) + int pow2; + if (e->E2->Eoper == OPconst && + sz == REGSIZE && !uns && + (pow2 = ispow2(el_tolong(e->E2))) != -1 && + !(config.target_cpu < TARGET_80286 && pow2 != 1 && e->Eoper == OPdiv) + ) + ; + else + { + assert(sz == 2 || sz == 4 || sz == 8); + int op = OPmsw; + if (e->Eoper == OPdiv) + { + op = (sz == 2) ? OP32_16 : (sz == 4) ? OP64_32 : OP128_64; + } + e->Eoper = OPremquo; + e = el_una(op, tym, e); + e->E1->Ety = (sz == 2) ? TYlong : (sz == 4) ? TYllong : TYcent; + } + } + } + } + + return e; +} + +/************************** + * Convert (a op b) op c to a op (b op c). + */ + +STATIC elem * swaplog(elem *e) +{ elem *e1; + + e1 = e->E1; + e->E1 = e1->E2; + e1->E2 = e; + return optelem(e1,TRUE); +} + +STATIC elem * eloror(elem *e) +{ elem *e1,*e2; + tym_t t; + tym_t ty1,ty2; + + e1 = e->E1; + if (OTboolnop(e1->Eoper)) + { + e->E1 = e1->E1; + e1->E1 = NULL; + el_free(e1); + return eloror(e); + } + e2 = e->E2; + if (OTboolnop(e2->Eoper)) + { + e->E2 = e2->E1; + e2->E1 = NULL; + el_free(e2); + return eloror(e); + } + if (OPTIMIZER) + { + if (e1->Eoper == OPbool) + { ty1 = e1->E1->Ety; + e1 = e->E1 = el_selecte1(e1); + e1->Ety = ty1; + } + if (e1->Eoper == OPoror) + { /* convert (a||b)||c to a||(b||c). This will find more CSEs. */ + return swaplog(e); + } + e2 = elscancommas(e2); + e1 = elscancommas(e1); + } + + t = e->Ety; + if (e2->Eoper == OPconst || e2->Eoper == OPrelconst || e2->Eoper == OPstring) + { + if (boolres(e2)) /* e1 || 1 => e1 , 1 */ + { if (e->E2 == e2) + goto L2; + } + else /* e1 || 0 => bool e1 */ + { if (e->E2 == e2) + { + el_free(e->E2); + e->E2 = NULL; + e->Eoper = OPbool; + goto L3; + } + } + } + + if (e1->Eoper == OPconst || e1->Eoper == OPrelconst || e1->Eoper == OPstring) + { + if (boolres(e1)) /* (x,1) || e2 => (x,1),1 */ + { + L2: + e->Eoper = OPcomma; + el_free(e->E2); + e->E2 = el_int(t,1); + } + else /* (x,0) || e2 => (x,0),(bool e2) */ + { e->Eoper = OPcomma; + if (tybasic(e->E2->Ety) != TYvoid) + e->E2 = el_una(OPbool,t,e->E2); + } + } + else if (OPTIMIZER && + e->E2->Eoper == OPvar && + !OTlogical(e1->Eoper) && + tysize(ty2 = e2->Ety) == tysize(ty1 = e1->Ety) && + tysize(ty1) <= intsize && + !tyfloating(ty2) && + !tyfloating(ty1) && + !(ty2 & mTYvolatile)) + { /* Convert (e1 || e2) => (e1 | e2) */ + e->Eoper = OPor; + e->Ety = ty1; + e = el_una(OPbool,t,e); + } + else if (OPTIMIZER && + e1->Eoper == OPand && e2->Eoper == OPand && + tysize(e1->Ety) == tysize(e2->Ety) && + el_match(e1->E1,e2->E1) && !el_sideeffect(e1->E1) && + !el_sideeffect(e2->E2) + ) + { // Convert ((a & b) || (a & c)) => bool(a & (b | c)) + e->Eoper = OPbool; + e->E2 = NULL; + e2->Eoper = OPor; + el_free(e2->E1); + e2->E1 = e1->E2; + e1->E2 = e2; + } + else + goto L1; +L3: + e = optelem(e,TRUE); +L1: + return e; +} + +STATIC elem * elandand(elem *e) +{ + elem *e1 = e->E1; + if (OTboolnop(e1->Eoper)) + { + e->E1 = e1->E1; + e1->E1 = NULL; + el_free(e1); + return elandand(e); + } + elem *e2 = e->E2; + if (OTboolnop(e2->Eoper)) + { + e->E2 = e2->E1; + e2->E1 = NULL; + el_free(e2); + return elandand(e); + } + if (OPTIMIZER) + { + /* Recognize: (a >= c1 && a < c2) + */ + if ((e1->Eoper == OPge || e1->Eoper == OPgt) && + (e2->Eoper == OPlt || e2->Eoper == OPle) && + e1->E2->Eoper == OPconst && e2->E2->Eoper == OPconst && + !el_sideeffect(e1->E1) && el_match(e1->E1, e2->E1) && + tyintegral(e1->E1->Ety) && + tybasic(e1->E2->Ety) == tybasic(e2->E2->Ety) && + tysize(e1->E1->Ety) == NPTRSIZE) + { + /* Replace with: ((a - c1) < (c2 - c1)) + */ + targ_llong c1 = el_tolong(e1->E2); + if (e1->Eoper == OPgt) + ++c1; + targ_llong c2 = el_tolong(e2->E2); + if (0 <= c1 && c1 <= c2) + { + e1->Eoper = OPmin; + e1->Ety = e1->E1->Ety; + e1->E2->EV.Vllong = c1; + e->E2 = el_long(touns(e2->E2->Ety), c2 - c1); + e->Eoper = e2->Eoper; + el_free(e2); + return optelem(e, TRUE); + } + } + + // Look for (!(e >>> c) && ...) + if (e1->Eoper == OPnot && e1->E1->Eoper == OPshr && + e1->E1->E2->Eoper == OPconst) + { + // Replace (e >>> c) with (e & x) + elem *e11 = e1->E1; + + targ_ullong shift = el_tolong(e11->E2); + if (shift < intsize * 8) + { targ_ullong m; + + m = ~0LL << (int)shift; + e11->Eoper = OPand; + e11->E2->EV.Vullong = m; + e11->E2->Ety = e11->Ety; + return optelem(e,TRUE); + } + } + + if (e1->Eoper == OPbool) + { tym_t t = e1->E1->Ety; + e1 = e->E1 = el_selecte1(e1); + e1->Ety = t; + } + if (e1->Eoper == OPandand) + { /* convert (a&&b)&&c to a&&(b&&c). This will find more CSEs. */ + return swaplog(e); + } + e2 = elscancommas(e2); + + while (1) + { e1 = elscancommas(e1); + if (e1->Eoper == OPeq) + e1 = e1->E2; + else + break; + } + } + + if (e2->Eoper == OPconst || e2->Eoper == OPrelconst || e2->Eoper == OPstring) + { if (boolres(e2)) /* e1 && (x,1) => e1 ? ((x,1),1) : 0 */ + { + if (e2 == e->E2) /* if no x, replace e with (bool e1) */ + { el_free(e2); + e->E2 = NULL; + e->Eoper = OPbool; + goto L3; + } + } + else /* e1 && (x,0) => e1 , (x,0) */ + { if (e2 == e->E2) + { e->Eoper = OPcomma; + goto L3; + } + } + } + + if (e1->Eoper == OPconst || e1->Eoper == OPrelconst || e1->Eoper == OPstring) + { + e->Eoper = OPcomma; + if (boolres(e1)) /* (x,1) && e2 => (x,1),bool e2 */ + { + e->E2 = el_una(OPbool,e->Ety,e->E2); + } + else /* (x,0) && e2 => (x,0),0 */ + { + el_free(e->E2); + e->E2 = el_int(e->Ety,0); + } + } + else + goto L1; +L3: + e = optelem(e,TRUE); +L1: + return e; +} + +/************************** + * Reference to bit field + * bit + * / \ => ((e << c) >> b) & m + * e w,b + * + * Note that this routine can handle long bit fields, though this may + * not be supported later on. + */ + +STATIC elem * elbit(elem *e) +{ unsigned wb,w,b,c; + targ_ullong m; + elem *e2; + tym_t tym1; + unsigned sz; + + tym1 = e->E1->Ety; + sz = tysize(tym1) * 8; + e2 = e->E2; + wb = e2->EV.Vuns; + + w = (wb >> 8) & 0xFF; /* width in bits of field */ + m = ((targ_ullong)1 << w) - 1; // mask w bits wide + b = wb & 0xFF; /* bits to right of field */ + c = 0; + assert(w + b <= sz); + + if (tyuns(tym1)) /* if unsigned bit field */ + { +#if 1 /* Should use a more general solution to this */ + if (w == 8 && sz == 16 && b == 0) + { + e->E1 = el_una(OP16_8,TYuchar,e->E1); + e->Eoper = OPu8_16; + e->E2 = NULL; + el_free(e2); + goto L1; + } +#endif + if (w + b == sz) /* if field is left-justified */ + m = ~(targ_ullong)0; // no need to mask + } + else /* signed bit field */ + { + if (w == 8 && sz == 16 && b == 0) + { +#if 1 + e->E1 = el_una(OP16_8,TYschar,e->E1); + e->Eoper = OPs8_16; + e->E2 = NULL; + el_free(e2); + goto L1; +#endif + } + m = ~(targ_ullong)0; + c = sz - (w + b); + b = sz - w; + } + + e->Eoper = OPand; + + e2->EV.Vullong = m; /* mask w bits wide */ + e2->Ety = e->Ety; + + e->E1 = el_bin(OPshr,tym1, + el_bin(OPshl,tym1,e->E1,el_int(TYint,c)), + el_int(TYint,b)); +L1: + return optelem(e,TRUE); /* optimize result */ +} + +/***************** + * Indirection + * * & e => e + */ + +STATIC elem * elind(elem *e) +{ elem *e1; + tym_t tym; + + tym = e->Ety; + e1 = e->E1; + switch (e1->Eoper) + { case OPrelconst: + { + e->E1->ET = e->ET; + e = el_selecte1(e); + e->Eoper = OPvar; + e->Ety = tym; /* preserve original type */ + } + break; + case OPadd: +#if TARGET_SEGMENTED + if (OPTIMIZER) + { /* Try to convert far pointer to stack pointer */ + elem *e12 = e1->E2; + + if (e12->Eoper == OPrelconst && + tybasic(e12->Ety) == TYfptr && + /* If symbol is located on the stack */ + sytab[e12->EV.sp.Vsym->Sclass] & SCSS) + { e1->Ety = (e1->Ety & (mTYconst | mTYvolatile | mTYimmutable | mTYshared | mTYLINK)) | TYsptr; + e12->Ety = (e12->Ety & (mTYconst | mTYvolatile | mTYimmutable | mTYshared | mTYLINK)) | TYsptr; + } + } +#endif + break; + case OPcomma: + // Replace (*(ea,eb)) with (ea,*eb) + e->E1->ET = e->ET; + type *t = e->ET; + e = el_selecte1(e); + e->Ety = tym; + e->E2 = el_una(OPind,tym,e->E2); + e->E2->ET = t; + again = 1; + return e; + } + return e; +} + +/***************** + * Address of. + * & v => &v + * & * e => e + * & (v1 = v2) => ((v1 = v2), &v1) + */ + +STATIC elem * eladdr(elem *e) +{ elem *e1; + tym_t tym; + + tym = e->Ety; + e1 = e->E1; + elem_debug(e1); + switch (e1->Eoper) + { + case OPvar: + e1->Eoper = OPrelconst; + e1->EV.sp.Vsym->Sflags &= ~(SFLunambig | GTregcand); + e1->Ety = tym; + e = optelem(el_selecte1(e),TRUE); + break; + case OPind: + { tym_t tym2; + int sz; + + tym2 = e1->E1->Ety; + +#if TARGET_SEGMENTED + /* Watch out for conversions between near and far pointers */ + sz = tysize(tym) - tysize(tym2); + if (sz != 0) + { int op; + + if (sz > 0) /* if &far * near */ + op = OPnp_fp; + else /* else &near * far */ + op = OPoffset; + e->Ety = tym2; + e = el_una(op,tym,e); + goto L1; + } +#endif + e = el_selecte1(el_selecte1(e)); + e->Ety = tym; + break; + } + case OPcomma: + /* Replace (&(ea,eb)) with (ea,&eb) */ + e = el_selecte1(e); + e->Ety = tym; + e->E2 = el_una(OPaddr,tym,e->E2); + L1: + e = optelem(e,TRUE); + break; + case OPnegass: + assert(0); + default: + if (OTassign(e1->Eoper)) + { + case OPstreq: + // & (v1 = e) => ((v1 = e), &v1) + if (e1->E1->Eoper == OPvar) + { elem *ex; + + e->Eoper = OPcomma; + e->E2 = el_una(OPaddr,tym,el_copytree(e1->E1)); + goto L1; + } + // & (*p1 = e) => ((*(t = p1) = e), t) + else if (e1->E1->Eoper == OPind) + { tym_t tym; + elem *tmp; + + tym = e1->E1->E1->Ety; + tmp = el_alloctmp(tym); + e1->E1->E1 = el_bin(OPeq,tym,tmp,e1->E1->E1); + e->Eoper = OPcomma; + e->E2 = el_copytree(tmp); + goto L1; + } + } + break; + case OPcond: + { /* Replace &(x ? y : z) with (x ? &y : &z) */ + elem *ecolon; + + ecolon = e1->E2; + ecolon->Ety = tym; + ecolon->E1 = el_una(OPaddr,tym,ecolon->E1); + ecolon->E2 = el_una(OPaddr,tym,ecolon->E2); + e = el_selecte1(e); + e = optelem(e,TRUE); + break; + } + } + return e; +} + +STATIC elem * elneg(elem *e) +{ + if (e->E1->Eoper == OPneg) + { e = el_selecte1(e); + e = el_selecte1(e); + } + else + e = evalu8(e); + return e; +} + +STATIC elem * elcall(elem *e) +{ + if (e->E1->Eoper == OPcomma || OTassign(e->E1->Eoper)) + e = cgel_lvalue(e); + return e; +} + +/*************************** + * Walk tree, converting types to tym. + */ + +STATIC void elstructwalk(elem *e,tym_t tym) +{ + tym_t ety; + + while ((ety = tybasic(e->Ety)) == TYstruct || + ety == TYarray) + { elem_debug(e); + e->Ety = (e->Ety & ~mTYbasic) | tym; + switch (e->Eoper) + { case OPcomma: + case OPcond: + case OPinfo: + break; + case OPeq: + case OPcolon: + case OPcolon2: + elstructwalk(e->E1,tym); + break; + default: + return; + } + e = e->E2; + } +} + +/******************************* + * See if we can replace struct operations with simpler ones. + * For OPstreq and OPstrpar. + */ + +CEXTERN elem * elstruct(elem *e) +{ tym_t tym; + elem *e2; + elem **pe2; + + //printf("elstruct(%p)\n", e); + if (e->Eoper == OPstreq && (e->E1->Eoper == OPcomma || OTassign(e->E1->Eoper))) + return cgel_lvalue(e); + //printf("\tnumbytes = %d\n", (int)e->Enumbytes); + if (e->ET) + switch ((int) type_size(e->ET)) + { +#if TX86 + case CHARSIZE: tym = TYchar; goto L1; + case SHORTSIZE: tym = TYshort; goto L1; + case LONGSIZE: tym = TYlong; goto L1; +#if LONGLONG + case LLONGSIZE: if (intsize == 2) + goto Ldefault; + tym = TYllong; goto L1; +#endif + L1: + switch (e->Eoper) + { case OPstreq: + e->Eoper = OPeq; + e->Ety = (e->Ety & ~mTYbasic) | tym; + elstructwalk(e->E1,tym); + elstructwalk(e->E2,tym); + e = optelem(e,TRUE); + break; + case OPstrpar: + e = el_selecte1(e); + /* FALL-THROUGH */ + default: /* called by doptelem() */ + e2 = e; + elstructwalk(e2,tym); + break; + } + break; +#endif + case 0: + if (e->Eoper == OPstreq) + { e->Eoper = OPcomma; + e = optelem(e,TRUE); + again = 1; + } + else + goto Ldefault; + break; + + default: + Ldefault: + if (e->Eoper == OPstreq) + pe2 = &e->E2; + else if (e->Eoper == OPstrpar) + pe2 = &e->E1; + else + break; + while ((*pe2)->Eoper == OPcomma) + pe2 = &(*pe2)->E2; + e2 = *pe2; + + // Convert (x streq (a?y:z)) to (x streq *(a ? &y : &z)) + if (e2->Eoper == OPcond) + { tym_t ty2 = e2->Ety; + tym_t typ; + + /* We should do the analysis to see if we can use + something simpler than TYfptr. + */ +#if TARGET_SEGMENTED + typ = (intsize == LONGSIZE) ? TYnptr : TYfptr; +#else + typ = TYnptr; +#endif + e2 = el_una(OPaddr,typ,e2); + e2 = optelem(e2,TRUE); /* distribute & to x and y leaves */ + *pe2 = el_una(OPind,ty2,e2); + break; + } + break; + } + return e; +} + +/************************** + * Assignment. Replace bit field assignment with + * equivalent tree. + * = + * / \ + * / r + * bit + * / \ + * l w,b + * + * becomes: + * , + * / \ + * = (r&m) + * / \ + * l | + * / \ + * (r&m)<E1; + + if (e1->Eoper == OPcomma || OTassign(e1->Eoper)) + return cgel_lvalue(e); + +#if 0 // Doesn't work too well, removed + // Replace (*p++ = e2) with ((*p = e2),*p++) + if (OPTIMIZER && e1->Eoper == OPind && + (e1->E1->Eoper == OPpostinc || e1->E1->Eoper == OPpostdec) && + !el_sideeffect(e1->E1->E1) + ) + { + e = el_bin(OPcomma,e->Ety,e,e1); + e->E1->E1 = el_una(OPind,e1->Ety,el_copytree(e1->E1->E1)); + return optelem(e,TRUE); + } +#endif + +#if 0 && LNGDBLSIZE == 12 + /* On Linux, long doubles are 12 bytes rather than 10. + * This means, on assignment, we need to set 12 bytes, + * so that garbage doesn't creep into the extra 2 bytes + * and throw off compares. + */ + tyl = tybasic(e1->Ety); + if (e1->Eoper == OPvar && (tyl == TYldouble || tyl == TYildouble || tyl == TYcldouble)) + { +#if 1 + elem *ex = el_copytree(e1); + ex->EV.sp.Voffset += 10; + ex = el_bin(OPeq, TYshort, ex, el_long(TYshort, 0)); + e = el_combine(ex, e); + if (tyl == TYcldouble) + { + ex = el_copytree(e1); + ex->EV.sp.Voffset += 10 + 12; + ex = el_bin(OPeq, TYshort, ex, el_long(TYshort, 0)); + e = el_combine(ex, e); + } + return optelem(e, TRUE); +#else + e->Eoper = OPstreq; + e->Enumbytes = tysize(tyl); + return elstruct(e); +#endif + } +#endif + + if (OPTIMIZER) + { elem *e2 = e->E2; + elem *ei; + int op2 = e2->Eoper; + + // Replace (e1 = *p++) with (e1 = *p, p++, e1) + ei = e2; + if (e1->Eoper == OPvar && + (op2 == OPind || (OTunary(op2) && (ei = e2->E1)->Eoper == OPind)) && + (ei->E1->Eoper == OPpostinc || ei->E1->Eoper == OPpostdec) && + !el_sideeffect(e1) && + !el_sideeffect(ei->E1->E1) + ) + { + e = el_bin(OPcomma,e->Ety, + e, + el_bin(OPcomma,e->Ety,ei->E1,el_copytree(e1))); + ei->E1 = el_copytree(ei->E1->E1); // copy p + return optelem(e,TRUE); + } + + /* Replace (e = e) with (e,e) */ + if (el_match(e1,e2)) + { e->Eoper = OPcomma; + L1: + return optelem(e,TRUE); + } + + // Replace (e1 = (e21 , e22)) with (e21 , (e1 = e22)) + if (op2 == OPcomma) + { + e2->Ety = e->Ety; + e->E2 = e2->E2; + e2->E2 = e; + e = e2; + goto L1; + } + + if (OTop(op2) && !el_sideeffect(e1) + && op2 != OPdiv && op2 != OPmod + ) + { tym_t ty; + int op3; + + // Replace (e1 = e1 op e) with (e1 op= e) + if (el_match(e1,e2->E1)) + { ty = e2->E2->Ety; + e->E2 = el_selecte2(e2); + L2: + e->E2->Ety = ty; + e->Eoper = optoopeq(op2); + goto L1; + } + if (OTcommut(op2)) + { + /* Replace (e1 = e op e1) with (e1 op= e) */ + if (el_match(e1,e2->E2)) + { ty = e2->E1->Ety; + e->E2 = el_selecte1(e2); + goto L2; + } + } + +#if 0 +// Note that this optimization is undone in elcomma(), this results in an +// infinite loop. This optimization is preferable if e1 winds up a register +// variable, the inverse in elcomma() is preferable if e1 winds up in memory. + // Replace (e1 = (e1 op3 ea) op2 eb) with (e1 op3= ea),(e1 op2= eb) + op3 = e2->E1->Eoper; + if (OTop(op3) && el_match(e1,e2->E1->E1) && !el_depends(e1,e2->E2)) + { + e->Eoper = OPcomma; + e->E1 = e2->E1; + e->E1->Eoper = optoopeq(op3); + e2->E1 = e1; + e1->Ety = e->E1->Ety; + e2->Eoper = optoopeq(op2); + e2->Ety = e->Ety; + goto L1; + } +#endif + } + + if (op2 == OPneg && el_match(e1,e2->E1) && !el_sideeffect(e1)) + { int offset; + + Ldef: + // Replace (i = -i) with (negass i) + e->Eoper = OPnegass; + e->E2 = NULL; + el_free(e2); + return optelem(e, TRUE); + } + + // Replace (x = (y ? z : x)) with ((y && (x = z)),x) + if (op2 == OPcond && el_match(e1,e2->E2->E2)) + { elem *e22 = e2->E2; // e22 is the OPcond + + e->Eoper = OPcomma; + e->E2 = e1; + e->E1 = e2; + e2->Eoper = OPandand; + e2->Ety = TYint; + e22->Eoper = OPeq; + e22->Ety = e->Ety; + e1 = e22->E1; + e22->E1 = e22->E2; + e22->E2 = e1; + return optelem(e,TRUE); + } + + // Replace (x = (y ? x : z)) with ((y || (x = z)),x) + if (op2 == OPcond && el_match(e1,e2->E2->E1)) + { elem *e22 = e2->E2; // e22 is the OPcond + + e->Eoper = OPcomma; + e->E2 = e1; + e->E1 = e2; + e2->Eoper = OPoror; + e2->Ety = TYint; + e22->Eoper = OPeq; + e22->Ety = e->Ety; + return optelem(e,TRUE); + } + + // If floating point, replace (x = -y) with (x = y ^ signbit) + if (op2 == OPneg && (tyreal(e2->Ety) || tyimaginary(e2->Ety)) && + (e2->E1->Eoper == OPvar || e2->E1->Eoper == OPind) && + /* Turned off for XMM registers because they don't play well with + * int registers. + */ + !config.fpxmmregs) + { elem *es; + tym_t ty; + + es = el_calloc(); + es->Eoper = OPconst; + switch (tysize(e2->Ety)) + { + case FLOATSIZE: + ty = TYlong; + es->EV.Vlong = 0x80000000; + break; + case DOUBLESIZE: +#if LONGLONG + if (I32) + { ty = TYllong; + es->EV.Vllong = 0x8000000000000000LL; + break; + } +#endif + default: + el_free(es); + goto L8; + } + es->Ety = ty; + e1->Ety = ty; + e2->Ety = ty; + e2->E1->Ety = ty; + e2->E2 = es; + e2->Eoper = OPxor; + return optelem(e,TRUE); + } + L8: ; + } + + if (e1->Eoper == OPcomma) + return cgel_lvalue(e); +#if MARS + // No bit fields to deal with + return e; +#else + if (e1->Eoper != OPbit) + return e; + if (e1->E1->Eoper == OPcomma || OTassign(e1->E1->Eoper)) + return cgel_lvalue(e); + t = e->Ety; + l = e1->E1; /* lvalue */ + r = e->E2; + tyl = l->Ety; + sz = tysize(tyl) * 8; + w = (e1->E2->EV.Vuns >> 8); /* width in bits of field */ + m = ((targ_ullong)1 << w) - 1; // mask w bits wide + b = e1->E2->EV.Vuns & 0xFF; /* bits to shift */ + + eres = el_bin(OPeq,t, + l, + el_bin(OPor,t, + el_bin(OPshl,t, + (r2 = el_bin(OPand,t,r,el_long(t,m))), + el_int(TYint,b) + ), + el_bin(OPand,t, + (l2 = el_copytree(l)), + el_long(t,~(m << b)) + ) + ) + ); + eres->Esrcpos = e->Esrcpos; // save line information + if (OPTIMIZER && w + b == sz) + r2->E2->EV.Vllong = ~ZEROLL; // no need to mask if left justified + if (wantres) + { unsigned c; + elem **pe; + elem *e2; + + r = el_copytree(r); + if (tyuns(tyl)) /* unsigned bit field */ + { + e2 = el_bin(OPand,t,r,el_long(t,m)); + pe = &e2->E1; + } + else /* signed bit field */ + { + c = sz - w; /* e2 = (r << c) >> c */ + e2 = el_bin(OPshr,t,el_bin(OPshl,tyl,r,el_long(TYint,c)),el_long(TYint,c)); + pe = &e2->E1->E1; + } + eres = el_bin(OPcomma,t,eres,e2); + if (EOP(r)) + fixside(&(r2->E1),pe); + } + + if (EOP(l) && EOP(l->E1)) + fixside(&(l2->E1),&(l->E1)); + e1->E1 = e->E2 = NULL; + el_free(e); + return optelem(eres,TRUE); +#endif +} + +/********************************** + */ + +STATIC elem * elnegass(elem *e) +{ + e = cgel_lvalue(e); + return e; +} + +/************************** + * Add assignment. Replace bit field assignment with + * equivalent tree. + * += + * / \ + * / r + * bit + * / \ + * l w,b + * + * becomes: + * = + * / \ + * l | + * / \ + * << \ + * / \ \ + * & b & + * / \ / \ + * op m l ~(m<> m + * / \ + * l b + */ + +STATIC elem * elopass(elem *e) +{ targ_llong m; + unsigned w,b,op; + tym_t t; + tym_t tyl; + elem *l,*r,*e1,*l2,*l3,*op2,*eres; + + e1 = e->E1; + if (OTconv(e1->Eoper)) + { e = fixconvop(e); + return optelem(e,TRUE); + } +#if SCPP // have bit fields to worry about? + int wantres = expgoal; + if (e1->Eoper == OPbit) + { + op = opeqtoop(e->Eoper); + + // Make sure t is unsigned + // so >> doesn't have to be masked + t = touns(e->Ety); + + assert(tyintegral(t)); + l = e1->E1; // lvalue + tyl = l->Ety; + r = e->E2; + w = (e1->E2->EV.Vuns >> 8) & 0xFF; // width in bits of field + m = ((targ_llong)1 << w) - 1; // mask w bits wide + b = e1->E2->EV.Vuns & 0xFF; // bits to shift + + if (tyuns(tyl)) + { + eres = el_bin(OPeq,t, + l, + el_bin(OPor,t, + (op2=el_bin(OPshl,t, + el_bin(OPand,t, + el_bin(op,t, + el_bin(OPand,t, + el_bin(OPshr,t, + (l2=el_copytree(l)), + el_long(TYint,b) + ), + el_long(t,m) + ), + r + ), + el_long(t,m) + ), + el_long(TYint,b) + )), + el_bin(OPand,t, + l3=el_copytree(l), + el_long(t,~(m << b)) + ) + ) + ); + + if (wantres) + { eres = el_bin(OPcomma,t,eres,el_copytree(op2->E1)); + fixside(&(op2->E1),&(eres->E2)); + } + } + else + { /* signed bit field + rewrite to: (l bit w,b) = ((l bit w,b) op r) + */ + e->Eoper = OPeq; + e->E2 = el_bin(op,t,el_copytree(e1),r); + if (l->Eoper == OPind) + fixside(&e->E2->E1->E1->E1,&l->E1); + eres = e; + goto ret; + } + + if (EOP(l) && EOP(l->E1)) + { fixside(&(l2->E1),&(l->E1)); + el_free(l3->E1); + l3->E1 = el_copytree(l->E1); + } + + e1->E1 = e->E2 = NULL; + el_free(e); + ret: + e = optelem(eres,TRUE); + } + else +#endif + { + if (e1->Eoper == OPcomma || OTassign(e1->Eoper)) + e = cgel_lvalue(e); // replace (e,v)op=e2 with e,(v op= e2) + else + { + switch (e->Eoper) + { case OPmulass: + e = elmul(e); + break; + case OPdivass: + // Replace r/=c with r=r/c + if (tycomplex(e->E2->Ety) && !tycomplex(e1->Ety)) + { elem *ed; + + e->Eoper = OPeq; + if (e1->Eoper == OPind) + { // ed: *(tmp=e1->E1) + // e1: *tmp + elem *tmp; + + tmp = el_alloctmp(e1->E1->Ety); + ed = el_bin(OPeq, tmp->Ety, tmp, e1->E1); + e1->E1 = el_copytree(tmp); + ed = el_una(OPind, e1->Ety, ed); + } + else + ed = el_copytree(e1); + // e: e1=ed/e2 + e->E2 = el_bin(OPdiv, e->E2->Ety, ed, e->E2); + if (tyreal(e1->Ety)) + e->E2 = el_una(OPc_r, e1->Ety, e->E2); + else + e->E2 = el_una(OPc_i, e1->Ety, e->E2); + return optelem(e, TRUE); + } + // Repace x/=y with x=x/y + if (OPTIMIZER && + tyintegral(e->E1->Ety) && + e->E1->Eoper == OPvar && + !el_sideeffect(e->E1)) + { + e->Eoper = OPeq; + e->E2 = el_bin(OPdiv, e->E2->Ety, el_copytree(e->E1), e->E2); + return optelem(e, TRUE); + } + e = eldiv(e); + break; + + case OPmodass: + // Repace x%=y with x=x%y + if (OPTIMIZER && + tyintegral(e->E1->Ety) && + e->E1->Eoper == OPvar && + !el_sideeffect(e->E1)) + { + e->Eoper = OPeq; + e->E2 = el_bin(OPmod, e->E2->Ety, el_copytree(e->E1), e->E2); + return optelem(e, TRUE); + } + break; + } + } + } + return e; +} + +/************************** + * Add assignment. Replace bit field post assignment with + * equivalent tree. + * (l bit w,b) ++ r + * becomes: + * (((l bit w,b) += r) - r) & m + */ + +STATIC elem * elpost(elem *e) +{ targ_llong r; + tym_t ty; + elem *e1; + targ_llong m; + unsigned w,b; + + e1 = e->E1; + if (e1->Eoper != OPbit) + { if (e1->Eoper == OPcomma || OTassign(e1->Eoper)) + return cgel_lvalue(e); // replace (e,v)op=e2 with e,(v op= e2) + return e; + } + + assert(e->E2->Eoper == OPconst); + r = el_tolong(e->E2); + + w = (e1->E2->EV.Vuns >> 8) & 0xFF; /* width in bits of field */ + m = ((targ_llong)1 << w) - 1; /* mask w bits wide */ + + ty = e->Ety; + e->Eoper = (e->Eoper == OPpostinc) ? OPaddass : ((r = -r), OPminass); + e = el_bin(OPmin,ty,e,el_long(ty,r)); + if (tyuns(e1->E1->Ety)) /* if unsigned bit field */ + e = el_bin(OPand,ty,e,el_long(ty,m)); + return optelem(e,TRUE); +} + +/*************************** + * Take care of compares. + * (e == 0) => (!e) + * (e != 0) => (bool e) + */ + +STATIC elem * elcmp(elem *e) +{ elem *e2 = e->E2; + elem *e1 = e->E1; + int uns; + + //printf("elcmp(%p)\n",e); elem_print(e); + +L1: + if (OPTIMIZER) + { + int op = e->Eoper; + + /* Convert comparison of OPrelconsts of the same symbol to comparisons */ + /* of their offsets. */ + if (e1->Eoper == OPrelconst && e2->Eoper == OPrelconst && + e1->EV.sp.Vsym == e2->EV.sp.Vsym) + { + e1->Eoper = OPconst; + e1->Ety = TYptrdiff; + e2->Eoper = OPconst; + e2->Ety = TYptrdiff; + return optelem(e,TRUE); + } + + // Convert comparison of long pointers to comparison of integers + if ((op == OPlt || op == OPle || op == OPgt || op == OPge) && + tyfv(e2->Ety) && tyfv(e1->Ety)) + { + e->E1 = el_una(OP32_16,e->Ety,e1); + e->E2 = el_una(OP32_16,e->Ety,e2); + return optelem(e,TRUE); + } + + // Convert ((e & 1) == 1) => (e & 1) + if (op == OPeqeq && e2->Eoper == OPconst && e1->Eoper == OPand) + { elem *e12 = e1->E2; + + if (e12->Eoper == OPconst && el_tolong(e2) == 1 && el_tolong(e12) == 1) + { tym_t ty1; + tym_t ty; + int sz1; + int sz; + + ty = e->Ety; + ty1 = e1->Ety; + e = el_selecte1(e); + e->Ety = ty1; + sz = tysize(ty); + for (sz1 = tysize(ty1); sz1 != sz; sz1 = tysize(e->Ety)) + { + switch (sz1) + { + case 1: + e = el_una(OPu8_16,TYshort,e); + break; + case 2: + if (sz > 2) + e = el_una(OPu16_32,TYlong,e); + else + e = el_una(OP16_8,TYuchar,e); + break; + case 4: + if (sz > 2) + e = el_una(OPu32_64,TYshort,e); + else + e = el_una(OP32_16,TYshort,e); + break; + case 8: + e = el_una(OP64_32,TYlong,e); + break; + default: + assert(0); + } + } + e->Ety = ty; + return optelem(e,TRUE); + } + } + } + + uns = tyuns(e1->Ety) | tyuns(e2->Ety); + if (cnst(e2)) + { + tym_t tym; + int sz; + + if (e1->Eoper == OPu16_32 && e2->EV.Vulong <= (targ_ulong) SHORTMASK || + e1->Eoper == OPs16_32 && + e2->EV.Vlong == (targ_short) e2->EV.Vlong) + { + tym = (uns || e1->Eoper == OPu16_32) ? TYushort : TYshort; + e->E2 = el_una(OP32_16,tym,e2); + goto L2; + } + + /* Try to convert to byte/word comparison for ((x & c)==d) + when mask c essentially casts x to a smaller type + */ + if (OPTIMIZER && + e1->Eoper == OPand && + e1->E2->Eoper == OPconst && + (sz = tysize(e2->Ety)) > CHARSIZE) + { int op; + + assert(tyintegral(e2->Ety) || typtr(e2->Ety)); +#if TX86 /* ending up with byte ops in A regs */ + if (!(el_tolong(e2) & ~CHARMASK) && + !(el_tolong(e1->E2) & ~CHARMASK) + ) + { + if (sz == LLONGSIZE) + { e1->E1 = el_una(OP64_32,TYulong,e1->E1); + e1->E1 = el_una(OP32_16,TYushort,e1->E1); + } + else if (sz == LONGSIZE) + e1->E1 = el_una(OP32_16,TYushort,e1->E1); + tym = TYuchar; + op = OP16_8; + goto L4; + } +#endif + if ( +#if TX86 + intsize == SHORTSIZE && /* not a win when regs are long */ +#endif + sz == LONGSIZE && + !(e2->EV.Vulong & ~SHORTMASK) && + !(e1->E2->EV.Vulong & ~SHORTMASK) + ) + { + tym = TYushort; + op = OP32_16; + L4: + e2->Ety = tym; + e1->Ety = tym; + e1->E2->Ety = tym; + e1->E1 = el_una(op,tym,e1->E1); + e = optelem(e,TRUE); + goto ret; + } + } + + if (e1->Eoper == OPu8_16 && e2->EV.Vuns < 256 || + e1->Eoper == OPs8_16 && + e2->EV.Vint == (targ_schar) e2->EV.Vint) + { + tym = (uns || e1->Eoper == OPu8_16) ? TYuchar : TYschar; + e->E2 = el_una(OP16_8,tym,e2); + L2: + tym |= e1->Ety & ~mTYbasic; + e->E1 = el_selecte1(e1); + e->E1->Ety = tym; + e = optelem(e,TRUE); + } + else if (!boolres(e2)) + { + switch (e->Eoper) + { + targ_int i; + + case OPle: /* (u <= 0) becomes (u == 0) */ + if (!uns) + break; + /* FALL-THROUGH */ + case OPeqeq: + e->Eoper = OPnot; + goto L5; + case OPgt: /* (u > 0) becomes (u != 0) */ + if (!uns) + break; + /* FALL-THROUGH */ + case OPne: + e->Eoper = OPbool; + L5: el_free(e2); + e->E2 = NULL; + e = optelem(e,TRUE); + break; + + case OPge: + i = 1; /* (u >= 0) becomes (u,1) */ + goto L3; + case OPlt: /* (u < 0) becomes (u,0) */ + i = 0; + L3: + if (uns) + { + e2->EV.Vint = i; + e2->Ety = TYint; + e->Eoper = OPcomma; + e = optelem(e,TRUE); + } + break; + } + } + else if (OPTIMIZER && uns && tysize(e2->Ety) == 2 && + (unsigned short)e2->EV.Vuns == 0x8000 && + (e->Eoper == OPlt || e->Eoper == OPge) + ) + { // Convert to signed comparison against 0 + tym_t ty; + + ty = tybasic(e2->Ety); + switch (tysize[ty]) + { case 1: ty = TYschar; break; + case 2: ty = TYshort; break; + default: assert(0); + } + e->Eoper ^= (OPlt ^ OPge); // switch between them + e2->EV.Vuns = 0; + e2->Ety = ty | (e2->Ety & ~mTYbasic); + e1->Ety = ty | (e1->Ety & ~mTYbasic); + } + else if (OPTIMIZER && e1->Eoper == OPeq && + e1->E2->Eoper == OPconst) + { // Convert ((x = c1) rel c2) to ((x = c1),(c1 rel c2) + elem *ec; + + ec = el_copytree(e1->E2); + ec->Ety = e1->Ety; + e->E1 = ec; + e = el_bin(OPcomma,e->Ety,e1,e); + e = optelem(e,TRUE); + } + } + else if (( + (e1->Eoper == OPu8_16 || e1->Eoper == OPs8_16) + || (e1->Eoper == OPu16_32 || e1->Eoper == OPs16_32) + ) && e1->Eoper == e2->Eoper) + { if (uns) + { e1->E1->Ety = touns(e1->E1->Ety); + e2->E1->Ety = touns(e2->E1->Ety); + } + e1->Ety = e1->E1->Ety; + e2->Ety = e2->E1->Ety; + e->E1 = el_selecte1(e1); + e->E2 = el_selecte1(e2); + e = optelem(e,TRUE); + } +ret: + return e; +} + +/***************************** + * Boolean operator. + * bool c => (bool c) + * bool logical_operator e => logical_operator e + */ + +STATIC elem * elbool(elem *e) +{ + if (OTlogical(e->E1->Eoper) || + (tybasic(e->E1->Ety) == TYbool && tysize(e->Ety) == 1) + ) + return el_selecte1(e); + if (OPTIMIZER) + { + // Replace bool(x,1) with (x,1),1 + elem *e1 = elscancommas(e->E1); + if (cnst(e1) || e1->Eoper == OPrelconst) + { + int i = boolres(e1) != 0; + e->Eoper = OPcomma; + e->E2 = el_int(e->Ety,i); + e = optelem(e,TRUE); + } + + // Replace bool(e & 1) with (unsigned char)(e & 1) + else if (e->E1->Eoper == OPand && e->E1->E2->Eoper == OPconst && el_tolong(e->E1->E2) == 1) + { unsigned sz = tysize(e->E1->Ety); + tym_t ty = e->Ety; + switch (sz) + { + case 1: + e = el_selecte1(e); + break; + case 2: + e->Eoper = OP16_8; + break; + case 4: + e->Eoper = OP32_16; + e->Ety = TYushort; + e = el_una(OP16_8, ty, e); + break; + case 8: + e->Eoper = OP64_32; + e->Ety = TYulong; + e = el_una(OP32_16, TYushort, e); + e = el_una(OP16_8, ty, e); + break; + default: + assert(0); + } + e = optelem(e,TRUE); + } + + // Replace bool(e % 2) with (unsigned char)(e & 1) + else if (e->E1->Eoper == OPmod && e->E1->E2->Eoper == OPconst && el_tolong(e->E1->E2) == 2) + { unsigned sz = tysize(e->E1->Ety); + tym_t ty = e->Ety; + e->E1->Eoper = OPand; + e->E1->E2->EV.Vullong = 1; + switch (sz) + { + case 1: + e = el_selecte1(e); + break; + case 2: + e->Eoper = OP16_8; + break; + case 4: + e->Eoper = OP32_16; + e->Ety = TYushort; + e = el_una(OP16_8, ty, e); + break; + case 8: + e->Eoper = OP64_32; + e->Ety = TYulong; + e = el_una(OP32_16, TYushort, e); + e = el_una(OP16_8, ty, e); + break; + default: + assert(0); + } + e = optelem(e,TRUE); + } + } + return e; +} + + +#if TARGET_SEGMENTED +/********************************* + * Conversions of pointers to far pointers. + */ + +STATIC elem * elptrlptr(elem *e) +{ + if (e->E1->Eoper == OPrelconst || e->E1->Eoper == OPstring) + { + e->E1->Ety = e->Ety; + e = el_selecte1(e); + } + return e; +} + +/********************************* + * Conversions of handle pointers to far pointers. + */ +STATIC elem * elvptrfptr(elem *e) +{ elem *e1; + elem *e12; + int op; + + e1 = e->E1; + if (e1->Eoper == OPadd || e1->Eoper == OPmin) + { + e12 = e1->E2; + if (tybasic(e12->Ety) != TYvptr) + { + /* Rewrite (vtof(e11 + e12)) to (vtof(e11) + e12) */ + op = e->Eoper; + e->Eoper = e1->Eoper; + e->E2 = e12; + e1->Ety = e->Ety; + e1->Eoper = op; + e1->E2 = NULL; + e = optelem(e,TRUE); + } + } + return e; +} + +#endif + +/************************ + * Optimize conversions of longs to ints. + * Also used for (OPoffset) (TYfptr|TYvptr). + * Also used for conversions of ints to bytes. + */ + +STATIC elem * ellngsht(elem *e) +{ elem *e1; + tym_t ty; + + ty = e->Ety; + e1 = e->E1; + switch (e1->Eoper) + { case OPs16_32: + case OPu16_32: + case OPu8_16: + case OPs8_16: + /* This fix is not quite right. For example, it fails */ + /* if e->Ety != e->E1->E1->Ety. The difference is when */ + /* one is unsigned and the other isn't. */ + if (tysize(ty) != tysize(e->E1->E1->Ety)) + break; + e = el_selecte1(el_selecte1(e)); + e->Ety = ty; + return e; + case OPvar: /* simply paint type of variable */ + /* Do not paint type of ints into bytes, as this causes */ + /* many CSEs to be missed, resulting in bad code. */ + /* Loading a word anyway is just as fast as loading a byte. */ + /* for 68000 byte is swapped, load byte != load word */ + if (e->Eoper == OP16_8) + { + /* Mark symbol as being used sometimes as a byte to */ + /* 80X86 - preclude using SI or DI */ + /* 68000 - preclude using An */ + e1->EV.sp.Vsym->Sflags |= GTbyte; + } + else + e1->Ety = ty; + e = el_selecte1(e); + break; + case OPind: + e = el_selecte1(e); + break; + +#if TARGET_SEGMENTED + case OPnp_fp: + if (e->Eoper != OPoffset) + goto case_default; + // Replace (offset)(ptrlptr)e11 with e11 + e = el_selecte1(el_selecte1(e)); + e->Ety = ty; // retain original type + break; +#endif + + default: /* operator */ + case_default: + /* Attempt to replace (lngsht)(a op b) with */ + /* ((lngsht)a op (lngsht)b). */ + /* op is now an integer op, which is cheaper. */ + if (OTwid(e1->Eoper) && !OTassign(e1->Eoper)) + { tym_t ty1; + + ty1 = e1->E1->Ety; + switch (e->Eoper) + { case OP16_8: + /* Make sure e1->E1 is of the type we're converting from */ + if (tysize(ty1) <= intsize) + { + ty1 = (tyuns(ty1) ? TYuchar : TYschar) | + (ty1 & ~mTYbasic); + e1->E1 = el_una(e->Eoper,ty1,e1->E1); + } + /* Rvalue may be an int if it is a shift operator */ + if (OTbinary(e1->Eoper)) + { tym_t ty2 = e1->E2->Ety; + + if (tysize(ty2) <= intsize) + { + ty2 = (tyuns(ty2) ? TYuchar : TYschar) | + (ty2 & ~mTYbasic); + e1->E2 = el_una(e->Eoper,ty2,e1->E2); + } + } + break; +#if TARGET_SEGMENTED + case OPoffset: + if (intsize == LONGSIZE) + { + /* Make sure e1->E1 is of the type we're converting from */ + if (tysize(ty1) > LONGSIZE) + { + ty1 = (tyuns(ty1) ? TYuint : TYint) | (ty1 & ~mTYbasic); + e1->E1 = el_una(e->Eoper,ty1,e1->E1); + } + /* Rvalue may be an int if it is a shift operator */ + if (OTbinary(e1->Eoper)) + { tym_t ty2 = e1->E2->Ety; + + if (tysize(ty2) > LONGSIZE) + { + ty2 = (tyuns(ty2) ? TYuint : TYint) | + (ty2 & ~mTYbasic); + e1->E2 = el_una(e->Eoper,ty2,e1->E2); + } + } + break; + } + /* FALL-THROUGH */ +#endif + case OP32_16: + /* Make sure e1->E1 is of the type we're converting from */ + if (tysize(ty1) == LONGSIZE) + { + ty1 = (tyuns(ty1) ? TYushort : TYshort) | (ty1 & ~mTYbasic); + e1->E1 = el_una(e->Eoper,ty1,e1->E1); + } + /* Rvalue may be an int if it is a shift operator */ + if (OTbinary(e1->Eoper)) + { tym_t ty2 = e1->E2->Ety; + + if (tysize(ty2) == LONGSIZE) + { + ty2 = (tyuns(ty2) ? TYushort : TYshort) | + (ty2 & ~mTYbasic); + e1->E2 = el_una(e->Eoper,ty2,e1->E2); + } + } + break; + default: + assert(0); + } + e1->Ety = ty; + e = el_selecte1(e); + again = 1; + return e; + } + break; + } + return e; +} + + +/************************ + * Optimize conversions of long longs to ints. + * OP64_32, OP128_64 + */ + +STATIC elem * el64_32(elem *e) +{ + tym_t ty = e->Ety; + elem *e1 = e->E1; + switch (e1->Eoper) + { + case OPs32_64: + case OPu32_64: + case OPs64_128: + case OPu64_128: + case OPpair: + if (tysize(ty) != tysize(e->E1->E1->Ety)) + break; + e = el_selecte1(el_selecte1(e)); + e->Ety = ty; + break; + + case OPrpair: + if (tysize(ty) != tysize(e->E1->E2->Ety)) + break; + e = el_selecte2(el_selecte1(e)); + e->Ety = ty; + break; + + case OPvar: // simply paint type of variable + case OPind: + e = el_selecte1(e); + break; + + case OPshr: // OP64_32(x >> 32) => OPmsw(x) + if (e1->E2->Eoper == OPconst && + (e->Eoper == OP64_32 && el_tolong(e1->E2) == 32 && !I64 || + e->Eoper == OP128_64 && el_tolong(e1->E2) == 64 && I64) + ) + { + e->Eoper = OPmsw; + e->E1 = el_selecte1(e->E1); + } + break; + } + return e; +} + + +/******************************* + * Convert complex to real. + */ + +STATIC elem *elc_r(elem *e) +{ + elem *e1 = e->E1; + + if (e1->Eoper == OPvar || e1->Eoper == OPind) + { + e1->Ety = e->Ety; + e = el_selecte1(e); + } + return e; +} + +/******************************* + * Convert complex to imaginary. + */ + +STATIC elem *elc_i(elem *e) +{ + elem *e1 = e->E1; + + if (e1->Eoper == OPvar) + { + e1->Ety = e->Ety; + e1->EV.sp.Voffset += tysize(e->Ety); + e = el_selecte1(e); + } + else if (e1->Eoper == OPind) + { + e1->Ety = e->Ety; + e = el_selecte1(e); + e->E1 = el_bin(OPadd, e->E1->Ety, e->E1, el_long(TYint, tysize(e->Ety))); + return optelem(e, TRUE); + } + + return e; +} + +/****************************** + * Handle OPu8_16 and OPs8_16. + */ + +STATIC elem * elbyteint(elem *e) +{ + if (OTlogical(e->E1->Eoper)) + { + e->E1->Ety = e->Ety; + e = el_selecte1(e); + } + return e; +} + +/************************ + * Handle <<, OProl and OPror + */ + +STATIC elem *elshl(elem *e) +{ + if (e->E1->Eoper == OPconst && !boolres(e->E1)) // if e1 is 0 + { e->E1->Ety = e->Ety; + e = el_selecte1(e); // (0 << e2) => 0 + } + return e; +} + +/************************ + * Handle >> + * OPshr, OPashr + */ + +STATIC elem * elshr(elem *e) +{ +#if TX86 + tym_t ty = e->Ety; + elem *e1 = e->E1; + elem *e2 = e->E2; + + // (x >> 16) replaced with ((shtlng) x+2) + if (OPTIMIZER && + e2->Eoper == OPconst && e2->EV.Vshort == SHORTSIZE * 8 && + tysize(ty) == LONGSIZE) + { + if (e1->Eoper == OPvar) + { + Symbol *s = e1->EV.sp.Vsym; + + if (s->Sclass != SCfastpar) + { + e1->EV.sp.Voffset += SHORTSIZE; // address high word in long + if (I32) + // Cannot independently address high word of register + s->Sflags &= ~GTregcand; + goto L1; + } + } + else if (e1->Eoper == OPind) + { + /* Replace (*p >> 16) with (shtlng)(*(&*p + 2)) */ + e->E1 = el_una(OPind,TYshort, + el_bin(OPadd,e1->E1->Ety, + el_una(OPaddr,e1->E1->Ety,e1), + el_int(TYint,SHORTSIZE))); + L1: + e->Eoper = tyuns(e1->Ety) ? OPu16_32 : OPs16_32; + el_free(e2); + e->E2 = NULL; + e1->Ety = TYshort; + e = optelem(e,TRUE); + } + } + + // (x >> 32) replaced with ((lngllng) x+4) + if (e2->Eoper == OPconst && e2->EV.Vlong == LONGSIZE * 8 && + tysize(ty) == LLONGSIZE) + { + if (e1->Eoper == OPvar) + { + e1->EV.sp.Voffset += LONGSIZE; // address high dword in longlong + if (I64) + // Cannot independently address high word of register + e1->EV.sp.Vsym->Sflags &= ~GTregcand; + goto L2; + } + else if (e1->Eoper == OPind) + { + // Replace (*p >> 32) with (lngllng)(*(&*p + 4)) + e->E1 = el_una(OPind,TYlong, + el_bin(OPadd,e1->E1->Ety, + el_una(OPaddr,e1->E1->Ety,e1), + el_int(TYint,LONGSIZE))); + L2: + e->Eoper = tyuns(e1->Ety) ? OPu32_64 : OPs32_64; + el_free(e2); + e->E2 = NULL; + e1->Ety = TYlong; + e = optelem(e,TRUE); + } + } +#endif + return e; +} + +/*********************************** + * Handle OPpair, OPrpair. + */ + +elem *elpair(elem *e) +{ + elem *e1; + + //printf("elpair()\n"); + e1 = e->E1; + if (e1->Eoper == OPconst) + { + e->E1 = e->E2; + e->E2 = e1; + e->Eoper ^= OPpair ^ OPrpair; + } + return e; +} + +/******************************** + * Handle OPddtor + */ + +elem *elddtor(elem *e) +{ + return e; +} + +/******************************** + * Handle OPinfo, OPmark, OPctor, OPdtor + */ + +STATIC elem * elinfo(elem *e) +{ + //printf("elinfo()\n"); +#if NTEXCEPTIONS && SCPP + if (funcsym_p->Sfunc->Fflags3 & Fnteh) + { // Eliminate cleanup info if using NT structured EH + if (e->Eoper == OPinfo) + e = el_selecte2(e); + else + { el_free(e); + e = el_long(TYint,0); + } + } +#endif + return e; +} + +/******************************************** + */ + +STATIC elem * elhstring(elem *e) +{ + return e; +} + +/******************************************** + */ + +STATIC elem * elnullcheck(elem *e) +{ + return e; +} + + +/******************************************** + */ + +STATIC elem * elclassinit(elem *e) +{ + return e; +} + +/******************************************** + */ + +STATIC elem * elnewarray(elem *e) +{ + return e; +} + +/******************************************** + */ + +STATIC elem * elmultinewarray(elem *e) +{ + return e; +} + +/******************************************** + */ + +STATIC elem * elinstanceof(elem *e) +{ + return e; +} + +/******************************************** + */ + +STATIC elem * elfinalinstanceof(elem *e) +{ + return e; +} + +/******************************************** + */ + +STATIC elem * elcheckcast(elem *e) +{ + return e; +} + +/******************************************** + */ + +STATIC elem * elarraylength(elem *e) +{ + return e; +} + +/******************************************** + */ + +STATIC elem * elarray(elem *e) +{ + return e; +} + +/******************************************** + */ + +STATIC elem * elfield(elem *e) +{ + return e; +} + +/****************************************** + * OPparam + */ + +STATIC void elparamx(elem *e) +{ + //printf("elparam()\n"); + if (e->E1->Eoper == OPrpair) + { + e->E1->Eoper = OPparam; + } + else if (e->E1->Eoper == OPpair && !el_sideeffect(e->E1)) + { + e->E1->Eoper = OPparam; + elem *ex = e->E1->E2; + e->E1->E2 = e->E1->E1; + e->E1->E1 = ex; + } +#if 0 + // Unfortunately, these don't work because if the last parameter + // is a pair, and it is a D function, the last parameter will get + // passed in EAX. + else if (e->E2->Eoper == OPrpair) + { + e->E2->Eoper = OPparam; + } + else if (e->E2->Eoper == OPpair) + { + e->E2->Eoper = OPparam; + elem *ex = e->E2->E2; + e->E2->E2 = e->E2->E1; + e->E2->E1 = ex; + } +#endif +} + +STATIC elem * elparam(elem *e) +{ + if (!OPTIMIZER) + { + if (!I64) + elparamx(e); + } + return e; +} + +/******************************** + * Optimize an element. This routine is recursive! + * Be careful not to do this if VBEs have been done (else the VBE + * work will be undone), or if DAGs have been built (will crash if + * there is more than one parent for an elem). + * If (goal) + * we care about the result. + */ + +STATIC elem * optelem(elem *e,HINT goal) +{ elem *e1,*e2; + unsigned op; +#include "elxxx.c" /* jump table */ + +beg: +#if MARS + util_progress(); +#else + if (controlc_saw) + util_exit(EXIT_BREAK); +#endif + //{ printf("xoptelem: %p ",e); WROP(e->Eoper); dbg_printf(" goal %d\n", goal); } + assert(e); + elem_debug(e); + assert(e->Ecount == 0); // no CSEs + + if (OPTIMIZER) + { + if (goal) + e->Nflags &= ~NFLnogoal; + else + e->Nflags |= NFLnogoal; + } + + op = e->Eoper; + if (OTleaf(op)) // if not an operator node + { + if (goal || OTsideff(op) || e->Ety & mTYvolatile) + { + return e; + } + else + { + retnull: + el_free(e); + return NULL; + } + } + else if (OTbinary(op)) // if binary operator + { HINT leftgoal = 1; + HINT rightgoal; + + /* Determine goals for left and right subtrees */ + rightgoal = (goal || OTsideff(op)); + switch (op) + { case OPcomma: + e1 = e->E1 = optelem(e->E1,FALSE); +// if (e1 && !OTsideff(e1->Eoper)) +// e1 = e->E1 = optelem(e1, FALSE); + e2 = e->E2 = optelem(e->E2,rightgoal); + if (!e1) + { if (!e2) + goto retnull; + if (!goal) + e->Ety = e->E2->Ety; + e = el_selecte2(e); + return e; + } + if (!e2) + { e->Ety = e->E1->Ety; + return el_selecte1(e); + } + if (!goal) + e->Ety = e2->Ety; + return e; + + case OPcond: + if (!goal) + { // Transform x?y:z into x&&y or x||z + if (!el_sideeffect(e->E2->E1)) + { e->Eoper = OPoror; + e->E2 = el_selecte2(e->E2); + e->Ety = TYint; + goto beg; + } + else if (!el_sideeffect(e->E2->E2)) + { e->Eoper = OPandand; + e->E2 = el_selecte1(e->E2); + e->Ety = TYint; + goto beg; + } + } + goto Llog; + + case OPandand: + case OPoror: /* case (c || f()) with no goal */ + Llog: + if (goal || el_sideeffect(e->E2)) + leftgoal = TRUE; + break; + + default: + leftgoal = rightgoal; + break; + case OPcolon: + case OPcolon2: + if (!goal && !el_sideeffect(e)) + goto retnull; + leftgoal = rightgoal; + break; +#if TX86 + case OPmemcmp: + if (!goal) + { // So OPmemcmp is removed cleanly + assert(e->E1->Eoper == OPparam); + e->E1->Eoper = OPcomma; + } + leftgoal = rightgoal; + break; +#endif + } + + e1 = e->E1; + if (OTassign(op)) + { elem *ex = e1; + + while (OTconv(ex->Eoper)) + ex = ex->E1; + if (ex->Eoper == OPbit) + ex->E1 = optelem(ex->E1, leftgoal); + else + e1 = e->E1 = optelem(e1,leftgoal); + } + else + e1 = e->E1 = optelem(e1,leftgoal); + + e2 = e->E2 = optelem(e->E2,rightgoal); + if (!e1) + { if (!e2) + goto retnull; + return el_selecte2(e); + } + if (!e2) + { + if (!leftgoal) + e->Ety = e1->Ety; + return el_selecte1(e); + } + if (op == OPparam && !goal) + e->Eoper = OPcomma; // DMD bug 6733 + + if (cnst(e1) && cnst(e2)) + { + e = evalu8(e); + return e; + } + if (OPTIMIZER) + { + if (OTassoc(op)) + { + /* Replace (a op1 (b op2 c)) with ((a op2 b) op1 c) + (this must come before the leaf swapping, or we could cause + infinite loops) + */ + if (e2->Eoper == op && + e2->E2->Eoper == OPconst && + tysize(e2->E1->Ety) == tysize(e2->E2->Ety) && + (!tyfloating(e1->Ety) || e1->Ety == e2->Ety) + ) + { + e->E1 = e2; + e->E2 = e2->E2; + e2->E2 = e2->E1; + e2->E1 = e1; + if (op == OPadd) /* fix types */ + { + e1 = e->E1; + if (typtr(e1->E2->Ety)) + e1->Ety = e1->E2->Ety; + else + /* suppose a and b are ints, and c is a pointer */ + /* then this will fix the type of op2 to be int */ + e1->Ety = e1->E1->Ety; + } + goto beg; + } + + // Replace ((a op c1) op c2) with (a op (c2 op c1)) + if (e1->Eoper == op && + e2->Eoper == OPconst && + e1->E2->Eoper == OPconst && + e1->E1->Eoper != OPconst && + tysize(e2->Ety) == tysize(e1->E2->Ety)) + { + e->E1 = e1->E1; + e1->E1 = e2; + e1->Ety = e2->Ety; + e->E2 = e1; + + if (tyfloating(e1->Ety)) + { + e1 = evalu8(e1); + if (EOP(e1)) // if failed to fold the constants + { // Undo the changes so we don't infinite loop + e->E2 = e1->E1; + e1->E1 = e->E1; + e->E1 = e1; + } + else + { e->E2 = e1; + goto beg; + } + } + else + goto beg; + } + } + + if (!OTrtol(op) && op != OPparam && op != OPcolon && op != OPcolon2 && + e1->Eoper == OPcomma) + { // Convert ((a,b) op c) to (a,(b op c)) + e1->Ety = e->Ety; + e1->ET = e->ET; + e->E1 = e1->E2; + e1->E2 = e; + e = e1; + goto beg; + } + } + + if (OTcommut(op)) // if commutative + { + /* see if we should swap the leaves */ +#if 0 + if (tyfloating(e1->Ety)) + { + if (fcost(e2) > fcost(e1)) + { e->E1 = e2; + e2 = e->E2 = e1; + e1 = e->E1; // reverse the leaves + op = e->Eoper = swaprel(op); + } + } + else +#endif + if ( +#if MARS + cost(e2) > cost(e1) + /* Swap only if order of evaluation can be proved + * to not matter, as we must evaluate Left-to-Right + */ + && (e1->Eoper == OPconst || + e1->Eoper == OPrelconst || + /* Local variables that are not aliased + * and are not assigned to in e2 + */ + (e1->Eoper == OPvar && e1->EV.sp.Vsym->Sflags & SFLunambig && !el_appears(e2,e1->EV.sp.Vsym)) || + !(el_sideeffect(e1) || el_sideeffect(e2)) + ) +#else + cost(e2) > cost(e1) +#endif + ) + { + e->E1 = e2; + e2 = e->E2 = e1; + e1 = e->E1; // reverse the leaves + op = e->Eoper = swaprel(op); + } + if (OTassoc(op)) // if commutative and associative + { + if (EOP(e1) && + op == e1->Eoper && + e1->E2->Eoper == OPconst && + e->Ety == e1->Ety && + tysize(e1->E2->Ety) == tysize(e2->Ety) +#if MARS + // Reordering floating point can change the semantics + && !tyfloating(e1->Ety) +#endif + ) + { + // look for ((e op c1) op c2), + // replace with (e op (c1 op c2)) + if (e2->Eoper == OPconst) + { + e->E1 = e1->E1; + e->E2 = e1; + e1->E1 = e1->E2; + e1->E2 = e2; + e1->Ety = e2->Ety; + + e1 = e->E1; + e2 = e->E2 = evalu8(e->E2); + } + else + { // Replace ((e op c) op e2) with ((e op e2) op c) + e->E2 = e1->E2; + e1->E2 = e2; + e2 = e->E2; + } + } + } + } + + if (e2->Eoper == OPconst && // if right operand is a constant + !(OTopeq(op) && OTconv(e1->Eoper)) + ) + { +#ifdef DEBUG + assert(!(OTeop0e(op) && (OTeop00(op)))); +#endif + if (OTeop0e(op)) /* if e1 op 0 => e1 */ + { + if (!boolres(e2)) /* if e2 is 0 */ + { + // Don't do it for ANSI floating point + if (tyfloating(e1->Ety) && !(config.flags4 & CFG4fastfloat)) + ; + // Don't do it if we're assembling a complex value + else if ((tytab[e->E1->Ety & 0xFF] ^ + tytab[e->E2->Ety & 0xFF]) == (TYFLreal | TYFLimaginary)) + ; + else + return optelem(el_selecte1(e),goal); + } + } + else if (OTeop00(op) && !boolres(e2) && !tyfloating(e->Ety)) + { if (OTassign(op)) + op = e->Eoper = OPeq; + else + op = e->Eoper = OPcomma; + } + if (OTeop1e(op)) /* if e1 op 1 => e1 */ + { + if (elemisone(e2) && !tyimaginary(e2->Ety)) + return optelem(el_selecte1(e),goal); + } + } + + if (OTpost(op) && !goal) + { + op = e->Eoper = (op == OPpostinc) ? OPaddass : OPminass; + } + } + else /* unary operator */ + { + assert(!e->E2 || op == OPinfo || op == OParraylength || op == OPddtor); + if (!goal && !OTsideff(op)) + { + tym_t tym = e->E1->Ety; + + e = el_selecte1(e); + e->Ety = tym; + return optelem(e,FALSE); + } + + e1 = e->E1 = optelem(e->E1,TRUE); + if (e1->Eoper == OPconst) + { +#if TARGET_SEGMENTED + if (!(op == OPnp_fp && el_tolong(e1) != 0)) +#endif + return evalu8(e); + } + e2 = NULL; + } + +L1: +#ifdef DEBUG +// if (debugb) +// { dbg_printf("optelem: %p ",e); WROP(op); dbg_printf("\n"); } +#endif + + expgoal = goal; +#if 0 + { dbg_printf("xoptelem: %p ",e); WROP(e->Eoper); dbg_printf("\n"); } + elem_print(e); + e = (*elxxx[op])(e); + printf("After:\n"); + elem_print(e); + return e; +#else + return (*elxxx[op])(e); +#endif +} + +/******************************** + * Optimize and canonicalize an expression tree. + * Fiddle with double operators so that the rvalue is a pointer + * (this is needed by the 8086 code generator). + * + * op op + * / \ / \ + * e1 e2 e1 , + * / \ + * = & + * / \ \ + * fr e2 fr + * + * e1 op (*p) e1 op p + * e1 op c e1 op &dc + * e1 op v e1 op &v + */ + +elem *doptelem(elem *e,HINT goal) +{ + //printf("doptelem(e = %p, goal = %d)\n", e, goal); + cgelem_goal = goal; + + assert(!PARSER); + do + { again = 0; + e = optelem(e,goal & (GOALflags | GOALvalue | GOALnone)); + } while (again && goal & GOALagain && e); + + /* If entire expression is a struct, and we can replace it with */ + /* something simpler, do so. */ + if (goal & GOALstruct && e && tybasic(e->Ety) == TYstruct) + e = elstruct(e); + + return e; +} + +/**************************************** + * Do optimizations after bltailrecursion() and before common subexpressions. + */ + +void postoptelem(elem *e) +{ + int linnum = 0; + const char *filename = NULL; + + elem_debug(e); + while (1) + { + if (OTunary(e->Eoper)) + { + /* This is necessary as the optimizer tends to lose this information + */ +#if MARS + if (e->Esrcpos.Slinnum > linnum) + { linnum = e->Esrcpos.Slinnum; + filename = e->Esrcpos.Sfilename; + } +#endif + if (e->Eoper == OPind) + { +#if MARS + if (e->E1->Eoper == OPconst && + el_tolong(e->E1) >= 0 && el_tolong(e->E1) < 4096) + { + error(filename, linnum, "null dereference in function %s", funcsym_p->Sident); + e->E1->EV.Vlong = 4096; // suppress redundant messages + } +#endif + } + e = e->E1; + } + else if (OTbinary(e->Eoper)) + { +#if MARS + /* This is necessary as the optimizer tends to lose this information + */ + if (e->Esrcpos.Slinnum > linnum) + { linnum = e->Esrcpos.Slinnum; + filename = e->Esrcpos.Sfilename; + } +#endif + if (e->Eoper == OPparam) + { + if (!I64) + elparamx(e); + } + postoptelem(e->E2); + e = e->E1; + } + else + break; + } +} + +#endif // !SPP diff --git a/backend/cgen.c b/backend/cgen.c new file mode 100644 index 00000000..ae0be342 --- /dev/null +++ b/backend/cgen.c @@ -0,0 +1,781 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include +#include "cc.h" +#include "el.h" +#include "oper.h" +#include "code.h" +#include "type.h" +#include "global.h" +#include "aa.h" +#include "dt.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +/************************************* + * Handy function to answer the question: who the heck is generating this piece of code? + */ +inline void ccheck(code *cs) +{ +// if (cs->Iop == LEA && (cs->Irm & 0x3F) == 0x34 && cs->Isib == 7) *(char*)0=0; +// if (cs->Iop == 0x31) *(char*)0=0; +// if (cs->Irm == 0x3D) *(char*)0=0; +} + +/***************************** + * Find last code in list. + */ + +code *code_last(code *c) +{ + if (c) + { while (c->next) + c = c->next; + } + return c; +} + +/***************************** + * Set flag bits on last code in list. + */ + +void code_orflag(code *c,unsigned flag) +{ + if (flag && c) + { while (c->next) + c = c->next; + c->Iflags |= flag; + } +} + +/***************************** + * Set rex bits on last code in list. + */ + +void code_orrex(code *c,unsigned rex) +{ + if (rex && c) + { while (c->next) + c = c->next; + c->Irex |= rex; + } +} + +/************************************** + * Set the opcode fields in cs. + */ +code *setOpcode(code *c, code *cs, unsigned op) +{ + cs->Iop = op; + return c; +} + +/***************************** + * Concatenate two code lists together. Return pointer to result. + */ + +#if TX86 && __INTSIZE == 4 && __SC__ +__declspec(naked) code * __pascal cat(code *c1,code *c2) +{ + _asm + { + mov EAX,c1-4[ESP] + mov ECX,c2-4[ESP] + test EAX,EAX + jne L6D + mov EAX,ECX + ret 8 + +L6D: mov EDX,EAX + cmp dword ptr [EAX],0 + je L7B +L74: mov EDX,[EDX] + cmp dword ptr [EDX],0 + jne L74 +L7B: mov [EDX],ECX + ret 8 + } +} +#else +code * __pascal cat(code *c1,code *c2) +{ code **pc; + + if (!c1) + return c2; + for (pc = &code_next(c1); *pc; pc = &code_next(*pc)) + ; + *pc = c2; + return c1; +} +#endif + +code * cat3(code *c1,code *c2,code *c3) +{ code **pc; + + for (pc = &c1; *pc; pc = &code_next(*pc)) + ; + for (*pc = c2; *pc; pc = &code_next(*pc)) + ; + *pc = c3; + return c1; +} + +code * cat4(code *c1,code *c2,code *c3,code *c4) +{ code **pc; + + for (pc = &c1; *pc; pc = &code_next(*pc)) + ; + for (*pc = c2; *pc; pc = &code_next(*pc)) + ; + for (*pc = c3; *pc; pc = &code_next(*pc)) + ; + *pc = c4; + return c1; +} + +code * cat6(code *c1,code *c2,code *c3,code *c4,code *c5,code *c6) +{ return cat(cat4(c1,c2,c3,c4),cat(c5,c6)); } + +/***************************** + * Add code to end of linked list. + * Note that unused operands are garbage. + * gen1() and gen2() are shortcut routines. + * Input: + * c -> linked list that code is to be added to end of + * cs -> data for the code + * Returns: + * pointer to start of code list + */ + +code *gen(code *c,code *cs) +{ code *ce,*cstart; + unsigned reg; + +#ifdef DEBUG /* this is a high usage routine */ + assert(cs); +#endif + assert(I64 || cs->Irex == 0); + ce = code_calloc(); + *ce = *cs; + //printf("ce = %p %02x\n", ce, ce->Iop); + ccheck(ce); + if (config.flags4 & CFG4optimized && + (ce->Iop == 0x81 || ce->Iop == 0x80) && + ce->IFL2 == FLconst && + reghasvalue((ce->Iop == 0x80) ? BYTEREGS : ALLREGS,I64 ? ce->IEV2.Vsize_t : ce->IEV2.Vlong,®) && + !(ce->Iflags & CFopsize && I16) + ) + { // See if we can replace immediate instruction with register instruction + static unsigned char regop[8] = + { 0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38 }; + + //printf("replacing 0x%02x, val = x%lx\n",ce->Iop,ce->IEV2.Vlong); + ce->Iop = regop[(ce->Irm & modregrm(0,7,0)) >> 3] | (ce->Iop & 1); + code_newreg(ce, reg); + } + code_next(ce) = CNIL; + if (c) + { cstart = c; + while (code_next(c)) c = code_next(c); /* find end of list */ + code_next(c) = ce; /* link into list */ + return cstart; + } + return ce; +} + +code *gen1(code *c,unsigned op) +{ code *ce,*cstart; + + ce = code_calloc(); + ce->Iop = op; + ccheck(ce); + assert(op != LEA); + if (c) + { cstart = c; + while (code_next(c)) c = code_next(c); /* find end of list */ + code_next(c) = ce; /* link into list */ + return cstart; + } + return ce; +} + +code *gen2(code *c,unsigned op,unsigned rm) +{ code *ce,*cstart; + + cstart = ce = code_calloc(); + /*cxcalloc++;*/ + ce->Iop = op; + ce->Iea = rm; + ccheck(ce); + if (c) + { cstart = c; + while (code_next(c)) c = code_next(c); /* find end of list */ + code_next(c) = ce; /* link into list */ + } + return cstart; +} + +code *gen2sib(code *c,unsigned op,unsigned rm,unsigned sib) +{ code *ce,*cstart; + + cstart = ce = code_calloc(); + /*cxcalloc++;*/ + ce->Iop = op; + ce->Irm = rm; + ce->Isib = sib; + ce->Irex = (rm | (sib & (REX_B << 16))) >> 16; + if (sib & (REX_R << 16)) + ce->Irex |= REX_X; + ccheck(ce); + if (c) + { cstart = c; + while (code_next(c)) c = code_next(c); /* find end of list */ + code_next(c) = ce; /* link into list */ + } + return cstart; +} + +/******************************** + * Generate an ASM sequence. + */ + +code *genasm(code *c,char *s,unsigned slen) +{ code *ce; + + ce = code_calloc(); + ce->Iop = ASM; + ce->IFL1 = FLasm; + ce->IEV1.as.len = slen; + ce->IEV1.as.bytes = (char *) mem_malloc(slen); + memcpy(ce->IEV1.as.bytes,s,slen); + return cat(c,ce); +} + +code *gencs(code *c,unsigned op,unsigned ea,unsigned FL2,symbol *s) +{ code cs; + + cs.Iop = op; + cs.Iea = ea; + ccheck(&cs); + cs.Iflags = 0; + cs.IFL2 = FL2; + cs.IEVsym2 = s; + cs.IEVoffset2 = 0; + + return gen(c,&cs); +} + +code *genc2(code *c,unsigned op,unsigned ea,targ_size_t EV2) +{ code cs; + + cs.Iop = op; + cs.Iea = ea; + ccheck(&cs); + cs.Iflags = CFoff; + cs.IFL2 = FLconst; + cs.IEV2.Vsize_t = EV2; + return gen(c,&cs); +} + +/***************** + * Generate code. + */ + +code *genc1(code *c,unsigned op,unsigned ea,unsigned FL1,targ_size_t EV1) +{ code cs; + + assert(FL1 < FLMAX); + cs.Iop = op; + cs.Iflags = CFoff; + cs.Iea = ea; + ccheck(&cs); + cs.IFL1 = FL1; + cs.IEV1.Vsize_t = EV1; + return gen(c,&cs); +} + +/***************** + * Generate code. + */ + +code *genc(code *c,unsigned op,unsigned ea,unsigned FL1,targ_size_t EV1,unsigned FL2,targ_size_t EV2) +{ code cs; + + assert(FL1 < FLMAX); + cs.Iop = op; + cs.Iea = ea; + ccheck(&cs); + cs.Iflags = CFoff; + cs.IFL1 = FL1; + cs.IEV1.Vsize_t = EV1; + assert(FL2 < FLMAX); + cs.IFL2 = FL2; + cs.IEV2.Vsize_t = EV2; + return gen(c,&cs); +} + +/******************************** + * Generate 'instruction' which is actually a line number. + */ + +code *genlinnum(code *c,Srcpos srcpos) +{ code cs; + +#if 0 + srcpos.print("genlinnum"); +#endif + cs.Iop = ESCAPE | ESClinnum; + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL1 = 0; + cs.IFL2 = 0; + cs.IEV1.Vsrcpos = srcpos; + return gen(c,&cs); +} + +/****************************** + * Append line number to existing code. + */ + +void cgen_linnum(code **pc,Srcpos srcpos) +{ + *pc = genlinnum(*pc,srcpos); +} + +/***************************** + * Prepend line number to existing code. + */ + +void cgen_prelinnum(code **pc,Srcpos srcpos) +{ + *pc = cat(genlinnum(NULL,srcpos),*pc); +} + +/******************************** + * Generate 'instruction' which tells the address resolver that the stack has + * changed. + */ + +code *genadjesp(code *c, int offset) +{ code cs; + + if (!I16 && offset) + { + cs.Iop = ESCAPE | ESCadjesp; + cs.Iflags = 0; + cs.Irex = 0; + cs.IEV1.Vint = offset; + return gen(c,&cs); + } + else + return c; +} + +/******************************** + * Generate 'instruction' which tells the scheduler that the fpu stack has + * changed. + */ + +code *genadjfpu(code *c, int offset) +{ code cs; + + if (!I16 && offset) + { + cs.Iop = ESCAPE | ESCadjfpu; + cs.Iflags = 0; + cs.Irex = 0; + cs.IEV1.Vint = offset; + return gen(c,&cs); + } + else + return c; +} + +/******************************** + * Generate 'nop' + */ + +code *gennop(code *c) +{ + return gen1(c,NOP); +} + + +/**************************************** + * Clean stack after call to codelem(). + */ + +code *gencodelem(code *c,elem *e,regm_t *pretregs,bool constflag) +{ + if (e) + { + unsigned stackpushsave; + int stackcleansave; + + stackpushsave = stackpush; + stackcleansave = cgstate.stackclean; + cgstate.stackclean = 0; // defer cleaning of stack + c = cat(c,codelem(e,pretregs,constflag)); + assert(cgstate.stackclean == 0); + cgstate.stackclean = stackcleansave; + c = genstackclean(c,stackpush - stackpushsave,*pretregs); // do defered cleaning + } + return c; +} + +/********************************** + * Determine if one of the registers in regm has value in it. + * If so, return !=0 and set *preg to which register it is. + */ + +bool reghasvalue(regm_t regm,targ_size_t value,unsigned *preg) +{ + //printf("reghasvalue(%s, %llx)\n", regm_str(regm), (unsigned long long)value); + /* See if another register has the right value */ + unsigned r = 0; + for (regm_t mreg = regcon.immed.mval; mreg; mreg >>= 1) + { + if (mreg & regm & 1 && regcon.immed.value[r] == value) + { *preg = r; + return TRUE; + } + r++; + regm >>= 1; + } + return FALSE; +} + +/************************************** + * Load a register from the mask regm with value. + * Output: + * *preg the register selected + */ + +code *regwithvalue(code *c,regm_t regm,targ_size_t value,unsigned *preg,regm_t flags) +{ unsigned reg; + + if (!preg) + preg = ® + + /* If we don't already have a register with the right value in it */ + if (!reghasvalue(regm,value,preg)) + { regm_t save; + + save = regcon.immed.mval; + c = cat(c,allocreg(®m,preg,TYint)); // allocate register + regcon.immed.mval = save; + c = movregconst(c,*preg,value,flags); // store value into reg + } + return c; +} + +/************************ + * When we don't know whether a function symbol is defined or not + * within this module, we stuff it in this linked list of references + * to be fixed up later. + */ + +struct fixlist +{ //symbol *Lsymbol; // symbol we don't know about + int Lseg; // where the fixup is going (CODE or DATA, never UDATA) + int Lflags; // CFxxxx + targ_size_t Loffset; // addr of reference to symbol + targ_size_t Lval; // value to add into location +#if TARGET_OSX + symbol *Lfuncsym; // function the symbol goes in +#endif + fixlist *Lnext; // next in threaded list + + static AArray *start; + static int nodel; // don't delete from within searchfixlist +}; + +AArray *fixlist::start = NULL; +int fixlist::nodel = 0; + +/* The AArray, being hashed on the pointer value of the symbol s, is in a different + * order from run to run. This plays havoc with trying to compare the .obj file output. + * When needing to do that, set FLARRAY to 1. This will replace the AArray with a + * simple (and very slow) linear array. Handy for tracking down compiler issues, though. + */ +#define FLARRAY 0 +#if FLARRAY +struct Flarray +{ + symbol *s; + fixlist *fl; +}; + +Flarray *flarray; +size_t flarray_dim; +size_t flarray_max; +#endif + +/**************************** + * Add to the fix list. + */ + +void addtofixlist(symbol *s,targ_size_t soffset,int seg,targ_size_t val,int flags) +{ fixlist *ln; + static char zeros[8]; + int numbytes; + + //printf("addtofixlist(%p '%s')\n",s,s->Sident); + assert(flags); + ln = (fixlist *) mem_calloc(sizeof(fixlist)); + //ln->Lsymbol = s; + ln->Loffset = soffset; + ln->Lseg = seg; + ln->Lflags = flags; + ln->Lval = val; +#if TARGET_OSX + ln->Lfuncsym = funcsym_p; +#endif + +#if FLARRAY + fixlist **pv; + for (size_t i = 0; 1; i++) + { + if (i == flarray_dim) + { + if (flarray_dim == flarray_max) + { + flarray_max = flarray_max * 2 + 1000; + flarray = (Flarray *)mem_realloc(flarray, flarray_max * sizeof(flarray[0])); + } + flarray_dim += 1; + flarray[i].s = s; + flarray[i].fl = NULL; + pv = &flarray[i].fl; + break; + } + if (flarray[i].s == s) + { + pv = &flarray[i].fl; + break; + } + } +#else + if (!fixlist::start) + fixlist::start = new AArray(&ti_pvoid, sizeof(fixlist *)); + fixlist **pv = (fixlist **)fixlist::start->get(&s); +#endif + ln->Lnext = *pv; + *pv = ln; + +#if TARGET_SEGMENTED + switch (flags & (CFoff | CFseg)) + { + case CFoff: numbytes = tysize[TYnptr]; break; + case CFseg: numbytes = 2; break; + case CFoff | CFseg: numbytes = tysize[TYfptr]; break; + default: assert(0); + } +#else + numbytes = tysize[TYnptr]; + if (I64 && !(flags & CFoffset64)) + numbytes = 4; + assert(!(flags & CFseg)); +#endif +#ifdef DEBUG + assert(numbytes <= sizeof(zeros)); +#endif + obj_bytes(seg,soffset,numbytes,zeros); +} + +/**************************** + * Given a function symbol we've just defined the offset for, + * search for it in the fixlist, and resolve any matches we find. + * Input: + * s function symbol just defined + */ + +void searchfixlist(symbol *s) +{ + //printf("searchfixlist(%s)\n",s->Sident); + if (fixlist::start) + { +#if FLARRAY + fixlist **lp = NULL; + size_t i; + for (i = 0; i < flarray_dim; i++) + { + if (flarray[i].s == s) + { lp = &flarray[i].fl; + break; + } + } +#else + fixlist **lp = (fixlist **)fixlist::start->in(&s); +#endif + if (lp) + { fixlist *p; + while ((p = *lp) != NULL) + { + //dbg_printf("Found reference at x%lx\n",p->Loffset); + + // Determine if it is a self-relative fixup we can + // resolve directly. + if (s->Sseg == p->Lseg && + (s->Sclass == SCstatic || +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + (!(config.flags3 & CFG3pic) && s->Sclass == SCglobal)) && +#else + s->Sclass == SCglobal) && +#endif + s->Sxtrnnum == 0 && p->Lflags & CFselfrel) + { targ_size_t ad; + + //printf("Soffset = x%lx, Loffset = x%lx, Lval = x%lx\n",s->Soffset,p->Loffset,p->Lval); + ad = s->Soffset - p->Loffset - REGSIZE + p->Lval; + obj_bytes(p->Lseg,p->Loffset,REGSIZE,&ad); + } + else + { +#if TARGET_OSX + symbol *funcsymsave = funcsym_p; + funcsym_p = p->Lfuncsym; + reftoident(p->Lseg,p->Loffset,s,p->Lval,p->Lflags); + funcsym_p = funcsymsave; +#else + reftoident(p->Lseg,p->Loffset,s,p->Lval,p->Lflags); +#endif + } + *lp = p->Lnext; + mem_free(p); /* remove from list */ + } + if (!fixlist::nodel) + { +#if FLARRAY + flarray[i].s = NULL; + if (i + 1 == flarray_dim) + flarray_dim -= 1; +#else + fixlist::start->del(&s); +#endif + } + } + } +} + +/**************************** + * End of module. Output remaining fixlist elements as references + * to external symbols. + */ + +STATIC int outfixlist_dg(void *parameter, void *pkey, void *pvalue) +{ + //printf("outfixlist_dg(pkey = %p, pvalue = %p)\n", pkey, pvalue); + symbol *s = *(symbol **)pkey; + + fixlist **plnext = (fixlist **)pvalue; + + while (*plnext) + { + fixlist *ln = *plnext; + + symbol_debug(s); + //printf("outfixlist '%s' offset %04x\n",s->Sident,ln->Loffset); + +#if TARGET_SEGMENTED + if (tybasic(s->ty()) == TYf16func) + { + obj_far16thunk(s); /* make it into a thunk */ + searchfixlist(s); + } + else +#endif + { + if (s->Sxtrnnum == 0) + { if (s->Sclass == SCstatic) + { +#if SCPP + if (s->Sdt) + { + outdata(s); + searchfixlist(s); + continue; + } + + synerr(EM_no_static_def,prettyident(s)); // no definition found for static +#else // MARS + printf("Error: no definition for static %s\n",prettyident(s)); // no definition found for static + err_exit(); // BUG: do better +#endif + } + if (s->Sflags & SFLwasstatic) + { + // Put it in BSS + s->Sclass = SCstatic; + s->Sfl = FLunde; + dtnzeros(&s->Sdt,type_size(s->Stype)); + outdata(s); + searchfixlist(s); + continue; + } + s->Sclass = SCextern; /* make it external */ + objextern(s); + if (s->Sflags & SFLweak) + { + obj_wkext(s, NULL); + } + } +#if TARGET_OSX + symbol *funcsymsave = funcsym_p; + funcsym_p = ln->Lfuncsym; + reftoident(ln->Lseg,ln->Loffset,s,ln->Lval,ln->Lflags); + funcsym_p = funcsymsave; +#else + reftoident(ln->Lseg,ln->Loffset,s,ln->Lval,ln->Lflags); +#endif + *plnext = ln->Lnext; +#if TERMCODE + mem_free(ln); +#endif + } + } + s->Sxtrnnum = 0; + return 0; +} + +void outfixlist() +{ + //printf("outfixlist()\n"); +#if FLARRAY + for (size_t i = 0; i < flarray_dim; i++) + { + fixlist::nodel++; + outfixlist_dg(NULL, &flarray[i].s, &flarray[i].fl); + fixlist::nodel--; + } +#else + if (fixlist::start) + { + fixlist::nodel++; + fixlist::start->apply(NULL, &outfixlist_dg); + fixlist::nodel--; +#if TERMCODE + delete fixlist::start; +#endif + fixlist::start = NULL; + } +#endif +} + +#endif // !SPP diff --git a/backend/cgobj.c b/backend/cgobj.c new file mode 100644 index 00000000..60d253b9 --- /dev/null +++ b/backend/cgobj.c @@ -0,0 +1,3680 @@ +// Copyright (C) 1984-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !HTOD && (SCPP || MARS) + +#include +#include +#include +#include +#include +#include + +#include "filespec.h" + +#include "cc.h" +#include "global.h" +#include "cgcv.h" +#include "code.h" +#include "type.h" +#include "outbuf.h" + +#include "md5.h" + +#if MARS +struct Loc +{ + char *filename; + unsigned linnum; + + Loc(int x) + { + linnum = x; + filename = NULL; + } +}; + +void error(Loc loc, const char *format, ...); +#endif + +#if OMFOBJ + +static char __file__[] = __FILE__; // for tassert.h +#include "tassert.h" + +#define MULTISCOPE 1 /* account for bug in MultiScope debugger + where it cannot handle a line number + with multiple offsets. We use a bit vector + to filter out the extra offsets. + */ + +#define TOOFFSET(a,b) (I32 ? TOLONG(a,b) : TOWORD(a,b)) + +/************************** + * Record types: + */ + +#define RHEADR 0x6E +#define REGINT 0x70 +#define REDATA 0x72 +#define RIDATA 0x74 +#define OVLDEF 0x76 +#define ENDREC 0x78 +#define BLKDEF 0x7A +#define BLKEND 0x7C +//#define DEBSYM 0x7E +#define THEADR 0x80 +#define LHEADR 0x82 +#define PEDATA 0x84 +#define PIDATA 0x86 +#define COMENT 0x88 +#define MODEND 0x8A +#define EXTDEF 0x8C +#define TYPDEF 0x8E +#define PUBDEF 0x90 +#define PUB386 0x91 +#define LOCSYM 0x92 +#define LINNUM 0x94 +#define LNAMES 0x96 +#define SEGDEF 0x98 +#define SEG386 0x99 +#define GRPDEF 0x9A +#define FIXUPP 0x9C +#define FIX386 0x9D +#define LEDATA 0xA0 +#define LED386 0xA1 +#define LIDATA 0xA2 +#define LID386 0xA3 +#define LIBHED 0xA4 +#define LIBNAM 0xA6 +#define LIBLOC 0xA8 +#define LIBDIC 0xAA +#define COMDEF 0xB0 +#define LEXTDEF 0xB4 +#define LPUBDEF 0xB6 +#define LCOMDEF 0xB8 +#define CEXTDEF 0xBC +#define COMDAT 0xC2 +#define LINSYM 0xC4 +#define ALIAS 0xC6 +#define LLNAMES 0xCA + +// Some definitions for .OBJ files. Trial and error to determine which +// one to use when. Page #s refer to Intel spec on .OBJ files. + +// Values for LOCAT byte: (pg. 71) +#define LOCATselfrel 0x8000 +#define LOCATsegrel 0xC000 +// OR'd with one of the following: +#define LOClobyte 0x0000 +#define LOCbase 0x0800 +#define LOChibyte 0x1000 +#define LOCloader_resolved 0x1400 + +// Unfortunately, the fixup stuff is different for EASY OMF and Microsoft +#define EASY_LOCoffset 0x1400 // 32 bit offset +#define EASY_LOCpointer 0x1800 // 48 bit seg/offset + +#define LOC32offset 0x2400 +#define LOC32tlsoffset 0x2800 +#define LOC32pointer 0x2C00 + +#define LOC16offset 0x0400 +#define LOC16pointer 0x0C00 + +#define LOCxx 0x3C00 + +// FDxxxx are constants for the FIXDAT byte in fixup records (pg. 72) + +#define FD_F0 0x00 // segment index +#define FD_F1 0x10 // group index +#define FD_F2 0x20 // external index +#define FD_F4 0x40 // canonic frame of LSEG that contains Location +#define FD_F5 0x50 // Target determines the frame + +#define FD_T0 0 // segment index +#define FD_T1 1 // group index +#define FD_T2 2 // external index +#define FD_T4 4 // segment index, 0 displacement +#define FD_T5 5 // group index, 0 displacement +#define FD_T6 6 // external index, 0 displacement + +/*************** + * Fixup list. + */ + +struct FIXUP +{ + struct FIXUP *FUnext; + targ_size_t FUoffset; // offset from start of ledata + unsigned short FUlcfd; // LCxxxx | FDxxxx + unsigned short FUframedatum; + unsigned short FUtargetdatum; +}; + +#define list_fixup(fl) ((struct FIXUP *)list_ptr(fl)) + +#define seg_is_comdat(seg) ((seg) < 0) + +/***************************** + * Ledata records + */ + +#define LEDATAMAX (1024-14) + +struct Ledatarec +{ + char header[14]; // big enough to handle COMDAT header + char data[LEDATAMAX]; + int lseg; // segment value + unsigned i; // number of bytes in data + targ_size_t offset; // segment offset of start of data + struct FIXUP *fixuplist; // fixups for this ledata + + // For COMDATs + unsigned char flags; // flags byte of COMDAT + unsigned char alloctyp; // allocation type of COMDAT + unsigned char align; // align type + int typidx; + int pubbase; + int pubnamidx; +}; + +/***************************** + * For defining segments. + */ + +#define SEG_ATTR(A,C,B,P) (((A) << 5) | ((C) << 2) | ((B) << 1) | (P)) + +// Segment alignment A +#define SEG_ALIGN0 0 // absolute segment +#define SEG_ALIGN1 1 // byte align +#define SEG_ALIGN2 2 // word align +#define SEG_ALIGN16 3 // paragraph align +#define SEG_ALIGN4K 4 // 4Kb page align +#define SEG_ALIGN4 5 // dword align + +// Segment combine types C +#define SEG_C_ABS 0 +#define SEG_C_PUBLIC 2 +#define SEG_C_STACK 5 +#define SEG_C_COMMON 6 + +// Segment type P +#define USE16 0 +#define USE32 1 + +#define USE32_CODE (4+2) // use32 + execute/read +#define USE32_DATA (4+3) // use32 + read/write + +/***************************** + * Line number support. + */ + +#define LINNUMMAX 512 + +struct Linnum +{ +#if MARS + const char *filename; // source file name +#else + Sfile *filptr; // file pointer +#endif + int cseg; // our internal segment number + int seg; // segment/public index + int i; // used in data[] + char data[LINNUMMAX]; // linnum/offset data +}; + +#define LINRECMAX (2 + 255 * 2) // room for 255 line numbers + +/************************************ + * State of object file. + */ + +struct Objstate +{ + const char *modname; + char *csegname; + Outbuffer *buf; // output buffer + + int fdsegattr; // far data segment attribute + int csegattr; // code segment attribute + + int lastfardatasegi; // SegData[] index of last far data seg + + int LOCoffset; + int LOCpointer; + + int mlidata; + int mpubdef; + int mfixupp; + int mmodend; + + int lnameidx; // index of next LNAMES record + int segidx; // index of next SEGDEF record + int extidx; // index of next EXTDEF record + int pubnamidx; // index of COMDAT public name index + + Symbol *startaddress; // if !NULL, then Symbol is start address + +#ifdef DEBUG + int fixup_count; +#endif + + size_t ledatai; // max index used in ledatas[] + + // Line numbers + list_t linnum_list; + char *linrec; // line number record + unsigned linreci; // index of next avail in linrec[] + unsigned linrecheader; // size of line record header + unsigned linrecnum; // number of line record entries + list_t linreclist; // list of line records + int mlinnum; + int recseg; + int term; +#if MULTISCOPE + vec_t linvec; // bit vector of line numbers used + vec_t offvec; // and offsets used +#endif + + int fisegi; // SegData[] index of FI segment + +#if MARS + int fmsegi; // SegData[] of FM segment +#endif + + int tlssegi; // SegData[] of tls segment + int fardataidx; + + char pubdata[1024]; + int pubdatai; + + char extdata[1024]; + int extdatai; + + // For obj_far16thunk + int code16segi; // SegData[] index + targ_size_t CODE16offset; + + int fltused; + int nullext; +}; + +Ledatarec **ledatas; +size_t ledatamax; + +seg_data **SegData; +static int seg_count; +static int seg_max; + +static Objstate obj; + +STATIC void obj_defaultlib(); +STATIC void objheader (char *csegname); +STATIC char * objmodtoseg (const char *modname); +STATIC void obj_browse_flush(); +STATIC int obj_newfarseg (targ_size_t size,int); +STATIC void linnum_flush(void); +STATIC void linnum_term(void); +STATIC void objsegdef (int attr,targ_size_t size,int segnamidx,int classnamidx); +STATIC void obj_modend(); +STATIC void objfixupp (struct FIXUP *); +STATIC void outextdata(); +STATIC void outpubdata(); +STATIC Ledatarec *ledata_new(int seg,targ_size_t offset); + +char *id_compress(char *id, int idlen); + +/******************************* + * Output an object file data record. + * Input: + * rectyp = record type + * record -> the data + * reclen = # of bytes in record + */ + +void objrecord(unsigned rectyp,const char *record,unsigned reclen) +{ Outbuffer *o = obj.buf; + + //printf("rectyp = x%x, record[0] = x%x, reclen = x%x\n",rectyp,record[0],reclen); + o->reserve(reclen + 4); + o->writeByten(rectyp); + o->writeWordn(reclen + 1); // record length includes checksum + o->writen(record,reclen); + o->writeByten(0); // use 0 for checksum +} + + +/************************** + * Insert an index number. + * Input: + * p -> where to put the 1 or 2 byte index + * index = the 15 bit index + * Returns: + * # of bytes stored + */ + +extern void error(const char *filename, unsigned linnum, const char *format, ...); +extern void fatal(); + +void too_many_symbols() +{ +#if SCPP + err_fatal(EM_too_many_symbols, 0x7FFF); +#else // MARS + error(NULL, 0, "more than %d symbols in object file", 0x7FFF); + fatal(); +#endif +} + +#if !DEBUG && TX86 && __INTSIZE == 4 && !defined(_MSC_VER) +__declspec(naked) int __pascal insidx(char *p,unsigned index) +{ +#undef AL +#undef AH +#undef DL +#undef DH + _asm + { + mov EAX,index - 4[ESP] + mov ECX,p - 4[ESP] + cmp EAX,0x7F + jae L1 + mov [ECX],AL + mov EAX,1 + ret 8 + + + L1: + cmp EAX,0x7FFF + ja L2 + + mov 1[ECX],AL + or EAX,0x8000 + mov [ECX],AH + mov EAX,2 + ret 8 + } + L2: + too_many_symbols(); +} +#else +__inline int insidx(char *p,unsigned index) +{ + //if (index > 0x7FFF) printf("index = x%x\n",index); + /* OFM spec says it could be <=0x7F, but that seems to cause + * "library is corrupted" messages. Unverified. See Bugzilla 3601 + */ + if (index < 0x7F) + { *p = index; + return 1; + } + else if (index <= 0x7FFF) + { + *(p + 1) = index; + *p = (index >> 8) | 0x80; + return 2; + } + else + { too_many_symbols(); + return 0; + } +} +#endif + +/************************** + * Insert a type index number. + * Input: + * p -> where to put the 1 or 2 byte index + * index = the 15 bit index + * Returns: + * # of bytes stored + */ + +__inline int instypidx(char *p,unsigned index) +{ + if (index <= 127) + { *p = index; + return 1; + } + else if (index <= 0x7FFF) + { *(p + 1) = index; + *p = (index >> 8) | 0x80; + return 2; + } + else // overflow + { *p = 0; // the linker ignores this field anyway + return 1; + } +} + +/**************************** + * Read index. + */ + +#define getindex(p) ((*(p) & 0x80) \ + ? ((*(unsigned char *)(p) & 0x7F) << 8) | *((unsigned char *)(p) + 1) \ + : *(unsigned char *)(p)) + +/***************************** + * Returns: + * # of bytes stored + */ + +#define ONS_OHD 4 // max # of extra bytes added by obj_namestring() + +STATIC int obj_namestring(char *p,const char *name) +{ unsigned len; + + len = strlen(name); + if (len > 255) + { p[0] = 0xFF; + p[1] = 0; +#ifdef DEBUG + assert(len <= 0xFFFF); +#endif + TOWORD(p + 2,len); + memcpy(p + 4,name,len); + len += ONS_OHD; + } + else + { p[0] = len; + memcpy(p + 1,name,len); + len++; + } + return len; +} + +/****************************** + * Allocate a new segment. + * Return index for the new segment. + */ + +seg_data *getsegment() +{ + int seg = ++seg_count; + if (seg_count == seg_max) + { + seg_max += 10; + SegData = (seg_data **)mem_realloc(SegData, seg_max * sizeof(seg_data *)); + memset(&SegData[seg_count], 0, 10 * sizeof(seg_data *)); + } + assert(seg_count < seg_max); + if (SegData[seg]) + memset(SegData[seg], 0, sizeof(seg_data)); + else + SegData[seg] = (seg_data *)mem_calloc(sizeof(seg_data)); + + seg_data *pseg = SegData[seg]; + pseg->SDseg = seg; + pseg->segidx = 0; + return pseg; +} + + +/****************************** + * Perform initialization that applies to all .obj output files. + * Input: + * filename source file name + * csegname code segment name (can be NULL) + */ + +void obj_init(Outbuffer *objbuf, const char *filename, const char *csegname) +{ + memset(&obj,0,sizeof(obj)); + + obj.buf = objbuf; + obj.buf->reserve(40000); + + obj.lastfardatasegi = -1; + + obj.mlidata = LIDATA; + obj.mpubdef = PUBDEF; + obj.mfixupp = FIXUPP; + obj.mmodend = MODEND; + obj.mlinnum = LINNUM; + + + // Reset for different OBJ file formats + if (I32) + { if (config.flags & CFGeasyomf) + { obj.LOCoffset = EASY_LOCoffset; + obj.LOCpointer = EASY_LOCpointer; + } + else + { + obj.mlidata = LID386; + obj.mpubdef = PUB386; + obj.mfixupp = FIX386; + obj.mmodend = MODEND + 1; + obj.LOCoffset = LOC32offset; + obj.LOCpointer = LOC32pointer; + } + obj.fdsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32); + obj.csegattr = SEG_ATTR(SEG_ALIGN4, SEG_C_PUBLIC,0,USE32); + } + else + { + obj.LOCoffset = LOC16offset; + obj.LOCpointer = LOC16pointer; + obj.fdsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE16); + obj.csegattr = SEG_ATTR(SEG_ALIGN2, SEG_C_PUBLIC,0,USE16); + } + + if (config.flags4 & CFG4speed && // if optimized for speed + config.target_cpu == TARGET_80486) + // 486 is only CPU that really benefits from alignment + obj.csegattr = I32 ? SEG_ATTR(SEG_ALIGN16, SEG_C_PUBLIC,0,USE32) + : SEG_ATTR(SEG_ALIGN16, SEG_C_PUBLIC,0,USE16); + + if (!SegData) + { seg_max = UDATA + 10; + SegData = (seg_data **)mem_calloc(seg_max * sizeof(seg_data *)); + } + + for (int i = 0; i < seg_max; i++) + { + if (SegData[i]) + memset(SegData[i], 0, sizeof(seg_data)); + else + SegData[i] = (seg_data *)mem_calloc(sizeof(seg_data)); + } + + SegData[CODE]->SDseg = CODE; + SegData[DATA]->SDseg = DATA; + SegData[CDATA]->SDseg = CDATA; + SegData[UDATA]->SDseg = UDATA; + + SegData[CODE]->segidx = CODE; + SegData[DATA]->segidx = DATA; + SegData[CDATA]->segidx = CDATA; + SegData[UDATA]->segidx = UDATA; + + seg_count = UDATA; + + if (config.fulltypes) + { + SegData[DEBSYM]->SDseg = DEBSYM; + SegData[DEBTYP]->SDseg = DEBTYP; + + SegData[DEBSYM]->segidx = DEBSYM; + SegData[DEBTYP]->segidx = DEBTYP; + + seg_count = DEBTYP; + } + + obj_theadr(filename); + obj.modname = filename; + if (!csegname || !*csegname) // if no code seg name supplied + obj.csegname = objmodtoseg(obj.modname); // generate one + else + obj.csegname = mem_strdup(csegname); // our own copy + objheader(obj.csegname); + objseggrp(0,0,0,0); // obj seg and grp info + ledata_new(cseg,0); // so ledata is never NULL + if (config.fulltypes) // if full typing information + cv_init(); // initialize debug output code +} + +/************************** + * Initialize the start of object output for this particular .obj file. + */ + +void obj_initfile(const char *filename,const char *csegname, const char *modname) +{ +} + +/*************************** + * Fixup and terminate object file. + */ + +void obj_termfile() +{ +} + +/********************************* + * Terminate package. + */ + +void obj_term() +{ + //printf("obj_term()\n"); + list_t dl; + unsigned long size; + +#if SCPP + if (!errcnt) +#endif + { obj_defaultlib(); + outfixlist(); // backpatches + } + + if (config.fulltypes) + cv_term(); // write out final debug info + outextdata(); // finish writing EXTDEFs + outpubdata(); // finish writing PUBDEFs + + // Put out LEDATA records and associated fixups + for (size_t i = 0; i < obj.ledatai; i++) + { Ledatarec *d = ledatas[i]; + + if (d->i) // if any data in this record + { // Fill in header + int headersize; + int rectyp; + assert(d->lseg > 0 && d->lseg <= seg_count); + int lseg = SegData[d->lseg]->segidx; + char header[sizeof(d->header)]; + + if (seg_is_comdat(lseg)) // if COMDAT + { + header[0] = d->flags | (d->offset ? 1 : 0); // continuation flag + header[1] = d->alloctyp; + header[2] = d->align; + TOOFFSET(header + 3,d->offset); + headersize = 3 + intsize; + headersize += instypidx(header + headersize,d->typidx); + if ((header[1] & 0x0F) == 0) + { // Group index + header[headersize] = (d->pubbase == DATA) ? 1 : 0; + headersize++; + + // Segment index + headersize += insidx(header + headersize,d->pubbase); + } + headersize += insidx(header + headersize,d->pubnamidx); + + rectyp = I32 ? COMDAT + 1 : COMDAT; + } + else + { + rectyp = LEDATA; + headersize = insidx(header,lseg); + if (intsize == LONGSIZE || d->offset & ~0xFFFFL) + { if (!(config.flags & CFGeasyomf)) + rectyp++; + TOLONG(header + headersize,d->offset); + headersize += 4; + } + else + { + TOWORD(header + headersize,d->offset); + headersize += 2; + } + } + assert(headersize <= sizeof(d->header)); + + // Right-justify data in d->header[] + memcpy(d->header + sizeof(d->header) - headersize,header,headersize); + //printf("objrecord(rectyp=x%02x, d=%p, p=%p, size = %d)\n", + //rectyp,d,d->header + (sizeof(d->header) - headersize),d->i + headersize); + + objrecord(rectyp,d->header + (sizeof(d->header) - headersize), + d->i + headersize); + objfixupp(d->fixuplist); + } + } +#if TERMCODE + //list_free(&obj.ledata_list,mem_freefp); +#endif + + linnum_term(); + obj_modend(); + + size = obj.buf->size(); + obj.buf->setsize(0); // rewind file + obj_theadr(obj.modname); + objheader(obj.csegname); + mem_free(obj.csegname); + objseggrp(SegData[CODE]->SDoffset,Doffset,0,SegData[UDATA]->SDoffset); // do real sizes + + // Update any out-of-date far segment sizes + for (size_t i = 0; i <= seg_count; i++) + { seg_data *f = SegData[i]; + if (f->isfarseg && f->origsize != f->SDoffset) + { obj.buf->setsize(f->seek); + objsegdef(f->attr,f->SDoffset,f->lnameidx,f->classidx); + } + } + //mem_free(obj.farseg); + + //printf("Ledata max = %d\n", obj.ledatai); +#if 0 + printf("Max # of fixups = %d\n",obj.fixup_count); +#endif + + obj.buf->setsize(size); +} + +/***************************** + * Line number support. + */ + +/*************************** + * Record line number linnum at offset. + * Input: + * cseg current code segment (negative for COMDAT segments) + * pubnamidx + * obj.mlinnum LINNUM or LINSYM + */ + +void objlinnum(Srcpos srcpos,targ_size_t offset) +{ + unsigned linnum = srcpos.Slinnum; + +#if 0 +#if MARS || SCPP + printf("objlinnum(cseg=%d, offset=0x%lx) ", cseg, offset); +#endif + srcpos.print(""); +#endif + + char linos2 = config.exe == EX_OS2 && !seg_is_comdat(SegData[cseg]->segidx); + +#if MARS + if (!obj.term && + (seg_is_comdat(SegData[cseg]->segidx) || (srcpos.Sfilename && srcpos.Sfilename != obj.modname))) +#else + if (!srcpos.Sfilptr) + return; + sfile_debug(&srcpos_sfile(srcpos)); + if (!obj.term && (!(srcpos_sfile(srcpos).SFflags & SFtop) || (seg_is_comdat(SegData[cseg]->segidx) && !obj.term))) +#endif + { // Not original source file, or a COMDAT. + // Save data away and deal with it at close of compile. + // It is done this way because presumably 99% of the lines + // will be in the original source file, so we wish to minimize + // memory consumption and maximize speed. + list_t ll; + struct Linnum *ln; + + if (linos2) + return; // BUG: not supported under OS/2 + for (ll = obj.linnum_list; 1; ll = list_next(ll)) + { + if (!ll) + { + ln = (struct Linnum *) mem_calloc(sizeof(struct Linnum)); +#if MARS + ln->filename = srcpos.Sfilename; +#else + ln->filptr = *srcpos.Sfilptr; +#endif + ln->cseg = cseg; + ln->seg = obj.pubnamidx; + list_prepend(&obj.linnum_list,ln); + break; + } + ln = (Linnum *)list_ptr(ll); + if ( +#if MARS + (ln->filename == srcpos.Sfilename) && +#endif +#if SCPP + (ln->filptr == *srcpos.Sfilptr) && +#endif + ln->cseg == cseg && + ln->i < LINNUMMAX - 6) + break; + } + TOWORD(&ln->data[ln->i],linnum); + TOOFFSET(&ln->data[ln->i + 2],offset); + ln->i += 2 + intsize; + } + else + { + if (linos2 && obj.linreci > LINRECMAX - 8) + obj.linrec = NULL; // allocate a new one + else if (cseg != obj.recseg) + linnum_flush(); + + if (!obj.linrec) // if not allocated + { obj.linrec = (char *) mem_calloc(LINRECMAX); + obj.linrec[0] = 0; // base group / flags + obj.linrecheader = 1 + insidx(obj.linrec + 1,seg_is_comdat(SegData[cseg]->segidx) ? obj.pubnamidx : SegData[cseg]->segidx); + obj.linreci = obj.linrecheader; + obj.recseg = cseg; +#if MULTISCOPE + if (!obj.linvec) + { obj.linvec = vec_calloc(1000); + obj.offvec = vec_calloc(1000); + } +#endif + if (linos2) + { + if (!obj.linreclist) // if first line number record + obj.linreci += 8; // leave room for header + list_append(&obj.linreclist,obj.linrec); + } + + // Select record type to use + obj.mlinnum = seg_is_comdat(SegData[cseg]->segidx) ? LINSYM : LINNUM; + if (I32 && !(config.flags & CFGeasyomf)) + obj.mlinnum++; + } + else if (obj.linreci > LINRECMAX - (2 + intsize)) + { objrecord(obj.mlinnum,obj.linrec,obj.linreci); // output data + obj.linreci = obj.linrecheader; + if (seg_is_comdat(SegData[cseg]->segidx)) // if LINSYM record + obj.linrec[0] |= 1; // continuation bit + } +#if MULTISCOPE + if (linnum >= vec_numbits(obj.linvec)) + obj.linvec = vec_realloc(obj.linvec,linnum + 1000); + if (offset >= vec_numbits(obj.offvec)) + { +#if __INTSIZE == 2 + unsigned newsize = (unsigned)offset * 2; + + if (offset >= 0x8000) + { newsize = 0xFF00; + assert(offset < newsize); + } + if (newsize != vec_numbits(obj.offvec)) + obj.offvec = vec_realloc(obj.offvec,newsize); +#else + if (offset < 0xFF00) // otherwise we overflow ph_malloc() + obj.offvec = vec_realloc(obj.offvec,offset * 2); +#endif + } + if ( +#if 1 // disallow multiple offsets per line + !vec_testbit(linnum,obj.linvec) && // if linnum not already used +#endif + // disallow multiple lines per offset + (offset >= 0xFF00 || !vec_testbit(offset,obj.offvec))) // and offset not already used +#endif + { +#if MULTISCOPE + vec_setbit(linnum,obj.linvec); // mark linnum as used + if (offset < 0xFF00) + vec_setbit(offset,obj.offvec); // mark offset as used +#endif + TOWORD(obj.linrec + obj.linreci,linnum); + if (linos2) + { obj.linrec[obj.linreci + 2] = 1; // source file index + TOLONG(obj.linrec + obj.linreci + 4,offset); + obj.linrecnum++; + obj.linreci += 8; + } + else + { + TOOFFSET(obj.linrec + obj.linreci + 2,offset); + obj.linreci += 2 + intsize; + } + } + } +} + +/*************************** + * Flush any pending line number records. + */ + +STATIC void linnum_flush() +{ + if (obj.linreclist) + { list_t list; + size_t len; + + obj.linrec = (char *) list_ptr(obj.linreclist); + TOWORD(obj.linrec + 6,obj.linrecnum); + list = obj.linreclist; + while (1) + { obj.linrec = (char *) list_ptr(list); + + list = list_next(list); + if (list) + { objrecord(obj.mlinnum,obj.linrec,LINRECMAX); + mem_free(obj.linrec); + } + else + { objrecord(obj.mlinnum,obj.linrec,obj.linreci); + break; + } + } + list_free(&obj.linreclist,FPNULL); + + // Put out File Names Table + TOLONG(obj.linrec + 2,0); // record no. of start of source (???) + TOLONG(obj.linrec + 6,obj.linrecnum); // number of primary source records + TOLONG(obj.linrec + 10,1); // number of source and listing files + len = obj_namestring(obj.linrec + 14,obj.modname); + assert(14 + len <= LINRECMAX); + objrecord(obj.mlinnum,obj.linrec,14 + len); + + mem_free(obj.linrec); + obj.linrec = NULL; + } + else if (obj.linrec) // if some line numbers to send + { objrecord(obj.mlinnum,obj.linrec,obj.linreci); + mem_free(obj.linrec); + obj.linrec = NULL; + } +#if MULTISCOPE + vec_clear(obj.linvec); + vec_clear(obj.offvec); +#endif +} + +/************************************* + * Terminate line numbers. + */ + +STATIC void linnum_term() +{ list_t ll; +#if SCPP + Sfile *lastfilptr = NULL; +#endif +#if MARS + const char *lastfilename = NULL; +#endif + int csegsave = cseg; + + linnum_flush(); + obj.term = 1; + while (obj.linnum_list) + { struct Linnum *ln; + unsigned u; + Srcpos srcpos; + targ_size_t offset; + + ll = obj.linnum_list; + ln = (struct Linnum *) list_ptr(ll); +#if SCPP + Sfile *filptr = ln->filptr; + if (filptr != lastfilptr) + { obj_theadr(filptr->SFname); + lastfilptr = filptr; + } +#endif +#if MARS + const char *filename = ln->filename; + if (filename != lastfilename) + { + if (filename) + obj_theadr(filename); + lastfilename = filename; + } +#endif + while (1) + { + cseg = ln->cseg; + assert(cseg > 0); + obj.pubnamidx = ln->seg; +#if MARS + srcpos.Sfilename = ln->filename; +#else + srcpos.Sfilptr = &ln->filptr; +#endif + for (u = 0; u < ln->i; ) + { + srcpos.Slinnum = *(unsigned short *)&ln->data[u]; + u += 2; + if (I32) + offset = *(unsigned long *)&ln->data[u]; + else + offset = *(unsigned short *)&ln->data[u]; + objlinnum(srcpos,offset); + u += intsize; + } + linnum_flush(); + ll = list_next(ll); + list_subtract(&obj.linnum_list,ln); + mem_free(ln); + L1: + if (!ll) + break; + ln = (struct Linnum *) list_ptr(ll); +#if SCPP + if (filptr != ln->filptr) +#else + if (filename != ln->filename) +#endif + { ll = list_next(ll); + goto L1; + } + } + } + cseg = csegsave; + assert(cseg > 0); +#if MULTISCOPE + vec_free(obj.linvec); + vec_free(obj.offvec); +#endif +} + +/******************************* + * Set start address + */ + +void obj_startaddress(Symbol *s) +{ + obj.startaddress = s; +} + +/******************************* + * Output DOSSEG coment record. + */ + +void obj_dosseg() +{ static const char dosseg[] = { 0x80,0x9E }; + + objrecord(COMENT,dosseg,sizeof(dosseg)); +} + +/******************************* + * Embed comment record. + */ + +STATIC void obj_comment(unsigned char x, const char *string, size_t len) +{ + char __ss *library; + + library = (char __ss *) alloca(2 + len); + library[0] = 0; + library[1] = x; + memcpy(library + 2,string,len); + objrecord(COMENT,library,len + 2); +} + +/******************************* + * Output library name. + * Output: + * name is modified + */ + +void obj_includelib(const char *name) +{ const char *p; + size_t len = strlen(name); + + p = filespecdotext(name); + if (!filespeccmp(p,".lib")) + len -= strlen(p); // lop off .LIB extension + obj_comment(0x9F, name, len); +} + +/************************** + * Embed string in executable. + */ + +void obj_exestr(const char *p) +{ + obj_comment(0xA4,p, strlen(p)); +} + +/************************** + * Embed string in obj. + */ + +void obj_user(const char *p) +{ + obj_comment(0xDF,p, strlen(p)); +} + +/********************************* + * Put out default library name. + */ + +STATIC void obj_defaultlib() +{ + char library[4]; // default library + static const char model[MEMMODELS] = "SMCLV"; + +#if MARS + memcpy(library,"SM?",4); +#else + memcpy(library,"SD?",4); +#endif + switch (config.exe) + { + case EX_OS2: + library[2] = 'F'; + case EX_OS1: + library[1] = 'O'; + break; + case EX_NT: +#if MARS + library[1] = 'M'; +#else + library[1] = 'N'; +#endif + library[2] = (config.flags4 & CFG4dllrtl) ? 'D' : 'N'; + break; + case EX_DOSX: + case EX_PHARLAP: + library[2] = 'X'; + break; + default: + library[2] = model[config.memmodel]; + if (config.wflags & WFwindows) + library[1] = 'W'; + break; + } + + if (!(config.flags2 & CFG2nodeflib)) + { + obj_includelib(configv.deflibname ? configv.deflibname : library); + } +} + +/******************************* + * Output a weak extern record. + * s1 is the weak extern, s2 is its default resolution. + */ + +void obj_wkext(Symbol *s1,Symbol *s2) +{ char buffer[2+2+2]; + int i; + int x2; + + printf("obj_wkext(%s)\n", s1->Sident); + if (s2) + x2 = s2->Sxtrnnum; + else + { + if (!obj.nullext) + { + obj.nullext = objextdef("__nullext"); + } + x2 = obj.nullext; + } + outextdata(); + buffer[0] = 0x80; + buffer[1] = 0xA8; + i = 2; + i += insidx(&buffer[2],s1->Sxtrnnum); + i += insidx(&buffer[i],x2); + objrecord(COMENT,buffer,i); +} + +/******************************* + * Output a lazy extern record. + * s1 is the lazy extern, s2 is its default resolution. + */ + +void obj_lzext(Symbol *s1,Symbol *s2) +{ char buffer[2+2+2]; + int i; + + outextdata(); + buffer[0] = 0x80; + buffer[1] = 0xA9; + i = 2; + i += insidx(&buffer[2],s1->Sxtrnnum); + i += insidx(&buffer[i],s2->Sxtrnnum); + objrecord(COMENT,buffer,i); +} + +/******************************* + * Output an alias definition record. + */ + +void obj_alias(const char *n1,const char *n2) +{ unsigned len; + char *buffer; + + buffer = (char *) alloca(strlen(n1) + strlen(n2) + 2 * ONS_OHD); + len = obj_namestring(buffer,n1); + len += obj_namestring(buffer + len,n2); + objrecord(ALIAS,buffer,len); +} + +/******************************* + * Output module name record. + */ + +void obj_theadr(const char *modname) +{ + //printf("obj_theadr(%s)\n", modname); + + // Convert to absolute file name, so debugger can find it anywhere + char absname[260]; + if (config.fulltypes && + modname[0] != '\\' && modname[0] != '/' && !(modname[0] && modname[1] == ':')) + { + if (getcwd(absname, sizeof(absname))) + { + int len = strlen(absname); + if(absname[len - 1] != '\\' && absname[len - 1] != '/') + absname[len++] = '\\'; + strcpy(absname + len, modname); + modname = absname; + } + } + + char *theadr = (char *)alloca(ONS_OHD + strlen(modname)); + int i = obj_namestring(theadr,modname); + objrecord(THEADR,theadr,i); // module name record +} + +/******************************* + * Embed compiler version in .obj file. + */ + +void obj_compiler() +{ + static const char compiler[] = "\0\xDBDigital Mars C/C++" + VERSION + ; // compiled by ... + + objrecord(COMENT,compiler,sizeof(compiler) - 1); +} + +/******************************* + * Output header stuff for object files. + * Input: + * csegname Name to use for code segment (NULL if use default) + */ + +STATIC void objheader(char *csegname) +{ + char *nam; + static char lnames[] = + "\0\06DGROUP\05_TEXT\04CODE\05_DATA\04DATA\05CONST\04_BSS\03BSS\ +\07$$TYPES\06DEBTYP\011$$SYMBOLS\06DEBSYM"; + +#define DATACLASS 6 // data class lname index +#define BSSCLASS 9 // BSS class lname index + + // Include debug segment names if inserting type information + int lnamesize = config.fulltypes ? sizeof(lnames) - 1 : sizeof(lnames) - 1 - 32; + int texti = 8; // index of _TEXT + + static char comment[] = {0,0x9D,'0','?','O'}; // memory model + static char model[MEMMODELS] = "smclv"; + static char exten[] = {0,0xA1,1,'C','V'}; // extended format + static char pmdeb[] = {0x80,0xA1,1,'H','L','L',0}; // IBM PM debug format + + if (I32) + { if (config.flags & CFGeasyomf) + { + // Indicate we're in EASY OMF (hah!) format + static const char easy_omf[] = { 0x80,0xAA,'8','0','3','8','6' }; + objrecord(COMENT,easy_omf,sizeof(easy_omf)); + } + } + + // Send out a comment record showing what memory model was used + comment[2] = config.target_cpu + '0'; + comment[3] = model[config.memmodel]; + if (I32) + { if (config.exe == EX_NT) + comment[3] = 'n'; + else if (config.exe == EX_OS2) + comment[3] = 'f'; + else + comment[3] = 'x'; + } + objrecord(COMENT,comment,sizeof(comment)); + + // Send out comment indicating we're using extensions to .OBJ format + if (config.exe == EX_OS2) + objrecord(COMENT,pmdeb,sizeof(pmdeb)); + else + objrecord(COMENT,exten,sizeof(exten)); + + // Change DGROUP to FLAT if we are doing flat memory model + // (Watch out, objheader() is called twice!) + if (config.exe & EX_flat) + { + if (lnames[2] != 'F') // do not do this twice + { memcpy(lnames + 1,"\04FLAT",5); + memmove(lnames + 6,lnames + 8,sizeof(lnames) - 8); + } + lnamesize -= 2; + texti -= 2; + } + + // Put out segment and group names + if (csegname) + { char *p; + size_t i; + + // Replace the module name _TEXT with the new code segment name + i = strlen(csegname); + p = (char *)alloca(lnamesize + i - 5); + memcpy(p,lnames,8); + p[texti] = i; + texti++; + memcpy(p + texti,csegname,i); + memcpy(p + texti + i,lnames + texti + 5,lnamesize - (texti + 5)); + objrecord(LNAMES,p,lnamesize + i - 5); + } + else + objrecord(LNAMES,lnames,lnamesize); +} + +/******************************** + * Convert module name to code segment name. + * Output: + * mem_malloc'd code seg name + */ + +STATIC char * objmodtoseg(const char *modname) +{ char *csegname = NULL; + + if (LARGECODE) // if need to add in module name + { int i; + char *m; + static const char suffix[] = "_TEXT"; + + // Prepend the module name to the beginning of the _TEXT + m = filespecgetroot(filespecname(modname)); + strupr(m); + i = strlen(m); + csegname = (char *)mem_malloc(i + sizeof(suffix)); + strcpy(csegname,m); + strcat(csegname,suffix); + mem_free(m); + } + return csegname; +} + +/********************************* + * Put out a segment definition. + */ + +STATIC void objsegdef(int attr,targ_size_t size,int segnamidx,int classnamidx) +{ + unsigned reclen; + char sd[1+4+2+2+2+1]; + + //printf("objsegdef(attr=x%x, size=x%x, segnamidx=x%x, classnamidx=x%x)\n", + //attr,size,segnamidx,classnamidx); + sd[0] = attr; + if (attr & 1 || config.flags & CFGeasyomf) + { TOLONG(sd + 1,size); // store segment size + reclen = 5; + } + else + { +#ifdef DEBUG + assert(size <= 0xFFFF); +#endif + TOWORD(sd + 1,size); + reclen = 3; + } + reclen += insidx(sd + reclen,segnamidx); // segment name index + reclen += insidx(sd + reclen,classnamidx); // class name index + sd[reclen] = 1; // overlay name index + reclen++; + if (attr & 1) // if USE32 + { + if (config.flags & CFGeasyomf) + { // Translate to Pharlap format + sd[0] &= ~1; // turn off P bit + + // Translate A: 4->6 + attr &= SEG_ATTR(7,0,0,0); + if (attr == SEG_ATTR(4,0,0,0)) + sd[0] ^= SEG_ATTR(4 ^ 6,0,0,0); + + // 2 is execute/read + // 3 is read/write + // 4 is use32 + sd[reclen] = (classnamidx == 4) ? (4+2) : (4+3); + reclen++; + } + } + else // 16 bit segment + { +#if MARS + assert(0); +#else + if (size & ~0xFFFFL) + { if (size == 0x10000) // if exactly 64Kb + sd[0] |= 2; // set "B" bit + else + synerr(EM_seg_gt_64k,size); // segment exceeds 64Kb + } +//printf("attr = %x\n", attr); +#endif + } +#ifdef DEBUG + assert(reclen <= sizeof(sd)); +#endif + objrecord(SEGDEF + (sd[0] & 1),sd,reclen); +} + +/********************************* + * Output segment and group definitions. + * Input: + * codesize size of code segment + * datasize size of initialized data segment + * cdatasize size of initialized const data segment + * udatasize size of uninitialized data segment + */ + +void objseggrp(targ_size_t codesize,targ_size_t datasize, + targ_size_t cdatasize,targ_size_t udatasize) +{ + int dsegattr; + int dsymattr; + + // Group into DGROUP the segments CONST, _BSS and _DATA + // For FLAT model, it's just GROUP FLAT + static const char grpdef[] = {2,0xFF,2,0xFF,3,0xFF,4}; + + objsegdef(obj.csegattr,codesize,3,4); // seg _TEXT, class CODE + +#if MARS + dsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32); +#else + dsegattr = I32 + ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32) + : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16); +#endif + + objsegdef(dsegattr,datasize,5,DATACLASS); // seg _DATA, class DATA + objsegdef(dsegattr,cdatasize,7,7); // seg CONST, class CONST + objsegdef(dsegattr,udatasize,8,9); // seg _BSS, class BSS + + obj.lnameidx = 10; // next lname index + obj.segidx = 5; // next segment index + + if (config.fulltypes) + { + dsymattr = I32 + ? SEG_ATTR(SEG_ALIGN1,SEG_C_ABS,0,USE32) + : SEG_ATTR(SEG_ALIGN1,SEG_C_ABS,0,USE16); + + if (config.exe & EX_flat) + { // IBM's version of CV uses dword aligned segments + dsymattr = SEG_ATTR(SEG_ALIGN4,SEG_C_ABS,0,USE32); + } + else if (config.fulltypes == CV4) + { // Always use 32 bit segments + dsymattr |= USE32; + assert(!(config.flags & CFGeasyomf)); + } + objsegdef(dsymattr,SegData[DEBSYM]->SDoffset,0x0C,0x0D); + objsegdef(dsymattr,SegData[DEBTYP]->SDoffset,0x0A,0x0B); + obj.lnameidx += 4; // next lname index + obj.segidx += 2; // next segment index + } + + objrecord(GRPDEF,grpdef,(config.exe & EX_flat) ? 1 : sizeof(grpdef)); +#if 0 + // Define fixup threads, we don't use them + { static const char thread[] = { 0,3,1,2,2,1,3,4,0x40,1,0x45,1 }; + objrecord(obj.mfixupp,thread,sizeof(thread)); + } + // This comment appears to indicate that no more PUBDEFs, EXTDEFs, + // or COMDEFs are coming. + { static const char cv[] = {0,0xA2,1}; + objrecord(COMENT,cv,sizeof(cv)); + } +#endif +} + +//#if NEWSTATICDTOR + +/************************************** + * Symbol is the function that calls the static constructors. + * Put a pointer to it into a special segment that the startup code + * looks at. + * Input: + * s static constructor function + * dtor number of static destructors + * seg 1: user + * 2: lib + * 3: compiler + */ + +void obj_staticctor(Symbol *s,int dtor,int seg) +{ + // We need to always put out the segments in triples, so that the + // linker will put them in the correct order. + static char lnamector[] = "\05XIFCB\04XIFU\04XIFL\04XIFM\05XIFCE"; + static char lnamedtor[] = "\04XOFB\03XOF\04XOFE"; + static char lnamedtorf[] = "\03XOB\02XO\03XOE"; + + symbol_debug(s); + + // Determine if near or far function + assert(I32 || tyfarfunc(s->ty())); + + // Put out LNAMES record + objrecord(LNAMES,lnamector,sizeof(lnamector) - 1); + + int dsegattr = I32 + ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32) + : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16); + + for (int i = 0; i < 5; i++) + { int sz; + + sz = (i == seg) ? 4 : 0; + + // Put out segment definition record + objsegdef(dsegattr,sz,obj.lnameidx,DATACLASS); + + if (i == seg) + { + seg_data *pseg = getsegment(); + pseg->segidx = obj.segidx; + reftoident(pseg->SDseg,0,s,0,0); // put out function pointer + } + + obj.segidx++; + obj.lnameidx++; + } + + if (dtor) + { // Leave space in XOF segment so that __fatexit() can insert a + // pointer to the static destructor in XOF. + + // Put out LNAMES record + if (LARGEDATA) + objrecord(LNAMES,lnamedtorf,sizeof(lnamedtorf) - 1); + else + objrecord(LNAMES,lnamedtor,sizeof(lnamedtor) - 1); + + // Put out beginning segment + objsegdef(dsegattr,0,obj.lnameidx,BSSCLASS); + + // Put out segment definition record + objsegdef(dsegattr,4 * dtor,obj.lnameidx + 1,BSSCLASS); + + // Put out ending segment + objsegdef(dsegattr,0,obj.lnameidx + 2,BSSCLASS); + + obj.lnameidx += 3; // for next time + obj.segidx += 3; + } +} + +//#else + +/*************************************** + * Stuff pointer to function in its own segment. + * Used for static ctor and dtor lists. + */ + +void obj_funcptr(Symbol *s) +{ + // We need to always put out the segments in triples, so that the + // linker will put them in the correct order. + static char lnames[4][5+4+5] = + { "\03XIB\02XI\03XIE", // near constructor + "\03XCB\02XC\03XCE", // near destructor + "\04XIFB\03XIF\04XIFE", // far constructor + "\04XCFB\03XCF\04XCFE", // far destructor + }; + // Size of each of the above strings + static int lnamesize[4] = { 4+3+4,4+3+4,5+4+5,5+4+5 }; + + int dsegattr; + int i; + + symbol_debug(s); +#ifdef DEBUG + assert(memcmp(s->Sident,"_ST",3) == 0); +#endif + + // Determine if constructor or destructor + // _STI... is a constructor, _STD... is a destructor + i = s->Sident[3] == 'D'; + // Determine if near or far function + if (tyfarfunc(s->Stype->Tty)) + i += 2; + + // Put out LNAMES record + objrecord(LNAMES,lnames[i],lnamesize[i]); + + dsegattr = I32 + ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32) + : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16); + + // Put out beginning segment + objsegdef(dsegattr,0,obj.lnameidx,DATACLASS); + obj.segidx++; + + // Put out segment definition record + // size is NPTRSIZE or FPTRSIZE + objsegdef(dsegattr,(i & 2) + tysize[TYnptr],obj.lnameidx + 1,DATACLASS); + seg_data *pseg = getsegment(); + pseg->segidx = obj.segidx; + reftoident(pseg->SDseg,0,s,0,0); // put out function pointer + obj.segidx++; + + // Put out ending segment + objsegdef(dsegattr,0,obj.lnameidx + 2,DATACLASS); + obj.segidx++; + + obj.lnameidx += 3; // for next time +} + +//#endif + +/*************************************** + * Stuff pointer to function in its own segment. + * Used for static ctor and dtor lists. + */ + +void obj_ehtables(Symbol *sfunc,targ_size_t size,Symbol *ehsym) +{ + // We need to always put out the segments in triples, so that the + // linker will put them in the correct order. + static char lnames[] = + { "\03FIB\02FI\03FIE" // near constructor + }; + int i; + int dsegattr; + targ_size_t offset; + + symbol_debug(sfunc); + + if (obj.fisegi == 0) + { + // Put out LNAMES record + objrecord(LNAMES,lnames,sizeof(lnames) - 1); + + dsegattr = I32 + ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32) + : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16); + + // Put out beginning segment + objsegdef(dsegattr,0,obj.lnameidx,DATACLASS); + obj.lnameidx++; + obj.segidx++; + + // Put out segment definition record + obj.fisegi = obj_newfarseg(0,DATACLASS); + objsegdef(dsegattr,0,obj.lnameidx,DATACLASS); + SegData[obj.fisegi]->attr = dsegattr; + assert(SegData[obj.fisegi]->segidx == obj.segidx); + + // Put out ending segment + objsegdef(dsegattr,0,obj.lnameidx + 1,DATACLASS); + + obj.lnameidx += 2; // for next time + obj.segidx += 2; + } + offset = SegData[obj.fisegi]->SDoffset; + offset += reftoident(obj.fisegi,offset,sfunc,0,LARGECODE ? CFoff | CFseg : CFoff); // put out function pointer + offset += reftoident(obj.fisegi,offset,ehsym,0,0); // pointer to data + obj_bytes(obj.fisegi,offset,intsize,&size); // size of function + SegData[obj.fisegi]->SDoffset = offset + intsize; +} + +/*************************************** + * Append pointer to ModuleInfo to "FM" segment. + * The FM segment is bracketed by the empty FMB and FME segments. + */ + +#if MARS + +void obj_moduleinfo(Symbol *scc) +{ + // We need to always put out the segments in triples, so that the + // linker will put them in the correct order. + static char lnames[] = + { "\03FMB\02FM\03FME" + }; + + symbol_debug(scc); + + if (obj.fmsegi == 0) + { + // Put out LNAMES record + objrecord(LNAMES,lnames,sizeof(lnames) - 1); + + int dsegattr = I32 + ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32) + : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16); + + // Put out beginning segment + objsegdef(dsegattr,0,obj.lnameidx,DATACLASS); + obj.lnameidx++; + obj.segidx++; + + // Put out segment definition record + obj.fmsegi = obj_newfarseg(0,DATACLASS); + objsegdef(dsegattr,0,obj.lnameidx,DATACLASS); + SegData[obj.fmsegi]->attr = dsegattr; + assert(SegData[obj.fmsegi]->segidx == obj.segidx); + + // Put out ending segment + objsegdef(dsegattr,0,obj.lnameidx + 1,DATACLASS); + + obj.lnameidx += 2; // for next time + obj.segidx += 2; + } + + targ_size_t offset = SegData[obj.fmsegi]->SDoffset; + offset += reftoident(obj.fmsegi,offset,scc,0,LARGECODE ? CFoff | CFseg : CFoff); // put out function pointer + SegData[obj.fmsegi]->SDoffset = offset; +} + +#endif + + +/********************************* + * Setup for Symbol s to go into a COMDAT segment. + * Output (if s is a function): + * cseg segment index of new current code segment + * Coffset starting offset in cseg + * Returns: + * "segment index" of COMDAT (which will be a negative value to + * distinguish it from regular segments). + */ + +int obj_comdat(Symbol *s) +{ char lnames[IDMAX+IDOHD+1]; // +1 to allow room for strcpy() terminating 0 + char cextdef[2+2]; + char __ss *p; + size_t lnamesize; + unsigned ti; + int isfunc; + tym_t ty; + + symbol_debug(s); + ty = s->ty(); + isfunc = tyfunc(ty) != 0; + + // Put out LNAME for name of Symbol + lnamesize = obj_mangle(s,lnames); + objrecord((s->Sclass == SCstatic ? LLNAMES : LNAMES),lnames,lnamesize); + + // Put out CEXTDEF for name of Symbol + outextdata(); + p = cextdef; + p += insidx(p,obj.lnameidx++); + ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0; + p += instypidx(p,ti); + objrecord(CEXTDEF,cextdef,p - cextdef); + s->Sxtrnnum = ++obj.extidx; + + seg_data *pseg = getsegment(); + pseg->segidx = -obj.extidx; + assert(pseg->SDseg > 0); + + // Start new LEDATA record for this COMDAT + Ledatarec *lr = ledata_new(pseg->SDseg,0); + lr->typidx = ti; + lr->pubnamidx = obj.lnameidx - 1; + if (isfunc) + { lr->pubbase = SegData[cseg]->segidx; + if (s->Sclass == SCcomdat || s->Sclass == SCinline) + lr->alloctyp = 0x10 | 0x00; // pick any instance | explicit allocation + cseg = lr->lseg; + assert(cseg > 0 && cseg <= seg_count); + obj.pubnamidx = obj.lnameidx - 1; + Coffset = 0; + if (tyfarfunc(ty) && strcmp(s->Sident,"main") == 0) + lr->alloctyp |= 1; // because MS does for unknown reasons + } + else + { unsigned char atyp; + + switch (ty & mTYLINK) + { case 0: + case mTYnear: lr->pubbase = DATA; +#if 0 + atyp = 0; // only one instance is allowed +#else + atyp = 0x10; // pick any (also means it is + // not searched for in a library) +#endif + break; + +#if TARGET_SEGMENTED + case mTYcs: lr->flags |= 0x08; // data in code seg + atyp = 0x11; break; + + case mTYfar: atyp = 0x12; break; +#endif + case mTYthread: lr->pubbase = obj_tlsseg()->segidx; + atyp = 0x10; // pick any (also means it is + // not searched for in a library) + break; + + default: assert(0); + } + lr->alloctyp = atyp; + } + if (s->Sclass == SCstatic) + lr->flags |= 0x04; // local bit (make it an "LCOMDAT") + return pseg->SDseg; +} + +/********************************** + * Reset code seg to existing seg. + * Used after a COMDAT for a function is done. + */ + +void obj_setcodeseg(int seg,targ_size_t offset) +{ + assert(0 < seg && seg <= seg_count); + cseg = seg; + Coffset = offset; +} + +/******************************** + * Define a new code segment. + * Input: + * name name of segment, if NULL then revert to default + * suffix 0 use name as is + * 1 append "_TEXT" to name + * Output: + * cseg segment index of new current code segment + * Coffset starting offset in cseg + * Returns: + * segment index of newly created code segment + */ + +int obj_codeseg(char *name,int suffix) +{ + if (!name) + { + if (cseg != CODE) + { + cseg = CODE; + } + return cseg; + } + + // Put out LNAMES record + size_t lnamesize = strlen(name) + suffix * 5; + char *lnames = (char *) alloca(1 + lnamesize + 1); + lnames[0] = lnamesize; + assert(lnamesize <= (255 - 2 - sizeof(int)*3)); + strcpy(lnames + 1,name); + if (suffix) + strcat(lnames + 1,"_TEXT"); + objrecord(LNAMES,lnames,lnamesize + 1); + + cseg = obj_newfarseg(0,4); + SegData[cseg]->attr = obj.csegattr; + SegData[cseg]->segidx = obj.segidx; + assert(cseg > 0); + obj.segidx++; + Coffset = 0; + + objsegdef(obj.csegattr,0,obj.lnameidx++,4); + + return cseg; +} + +/********************************* + * Define segment for Thread Local Storage. + * Output: + * tlsseg set to segment number for TLS segment. + * Returns: + * segment for TLS segment + */ + +seg_data *obj_tlsseg_bss() { return obj_tlsseg(); } + +seg_data *obj_tlsseg() +{ //static char tlssegname[] = "\04$TLS\04$TLS"; + //static char tlssegname[] = "\05.tls$\03tls"; + static const char tlssegname[] = "\05.tls$\03tls\04.tls\010.tls$ZZZ"; + + if (obj.tlssegi == 0) + { int segattr; + + objrecord(LNAMES,tlssegname,sizeof(tlssegname) - 1); + +#if MARS + segattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32); +#else + segattr = I32 + ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32) + : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16); +#endif + + // Put out beginning segment (.tls) + objsegdef(segattr,0,obj.lnameidx + 2,obj.lnameidx + 1); + obj.segidx++; + + // Put out .tls$ segment definition record + obj.tlssegi = obj_newfarseg(0,obj.lnameidx + 1); + objsegdef(segattr,0,obj.lnameidx,obj.lnameidx + 1); + SegData[obj.tlssegi]->attr = segattr; + SegData[obj.tlssegi]->segidx = obj.segidx; + + // Put out ending segment (.tls$ZZZ) + objsegdef(segattr,0,obj.lnameidx + 3,obj.lnameidx + 1); + + obj.lnameidx += 4; + obj.segidx += 2; + } + return SegData[obj.tlssegi]; +} + + +/******************************** + * Define a far data segment. + * Input: + * name Name of module + * size Size of the segment to be created + * Returns: + * segment index of far data segment created + * *poffset start of the data for the far data segment + */ + +int obj_fardata(char *name,targ_size_t size,targ_size_t *poffset) +{ + static char fardataclass[] = "\010FAR_DATA"; + int len; + int i; + char *buffer; + + // See if we can use existing far segment, and just bump its size + i = obj.lastfardatasegi; + if (i != -1 + && (intsize != 2 || (unsigned long) SegData[i]->SDoffset + size < 0x8000) + ) + { *poffset = SegData[i]->SDoffset; // BUG: should align this + SegData[i]->SDoffset += size; + return i; + } + + // No. We need to build a new far segment + + if (obj.fardataidx == 0) // if haven't put out far data lname + { // Put out class lname + objrecord(LNAMES,fardataclass,sizeof(fardataclass) - 1); + obj.fardataidx = obj.lnameidx++; + } + + // Generate name based on module name + name = strupr(filespecgetroot(filespecname(obj.modname))); + + // Generate name for this far segment + len = 1 + strlen(name) + 3 + 5 + 1; + buffer = (char *)alloca(len); + sprintf(buffer + 1,"%s%d_DATA",name,obj.segidx); + len = strlen(buffer + 1); + buffer[0] = len; + assert(len <= 255); + objrecord(LNAMES,buffer,len + 1); + + mem_free(name); + + // Construct a new SegData[] entry + obj.lastfardatasegi = obj_newfarseg(size,obj.fardataidx); + + // Generate segment definition + objsegdef(obj.fdsegattr,size,obj.lnameidx++,obj.fardataidx); + obj.segidx++; + + *poffset = 0; + return SegData[obj.lastfardatasegi]->SDseg; +} + +/************************************ + * Remember where we put a far segment so we can adjust + * its size later. + * Input: + * obj.segidx + * lnameidx + * Returns: + * index of SegData[] + */ + +STATIC int obj_newfarseg(targ_size_t size,int classidx) +{ + seg_data *f = getsegment(); + f->isfarseg = true; + f->seek = obj.buf->size(); + f->attr = obj.fdsegattr; + f->origsize = size; + f->SDoffset = size; + f->segidx = obj.segidx; + f->lnameidx = obj.lnameidx; + f->classidx = classidx; + return f->SDseg; +} + +/****************************** + * Convert reference to imported name. + */ + +void obj_import(elem *e) +{ +#if MARS + assert(0); +#else + Symbol *s; + Symbol *simp; + + elem_debug(e); + if ((e->Eoper == OPvar || e->Eoper == OPrelconst) && + (s = e->EV.sp.Vsym)->ty() & mTYimport && + (s->Sclass == SCextern || s->Sclass == SCinline) + ) + { char *name; + char *p; + size_t len; + char buffer[IDMAX + IDOHD + 1]; + + // Create import name + len = obj_mangle(s,buffer); + if (buffer[0] == (char)0xFF && buffer[1] == 0) + { name = buffer + 4; + len -= 4; + } + else + { name = buffer + 1; + len -= 1; + } + if (config.flags4 & CFG4underscore) + { p = (char *) alloca(5 + len + 1); + memcpy(p,"_imp_",5); + memcpy(p + 5,name,len); + p[5 + len] = 0; + } + else + { p = (char *) alloca(6 + len + 1); + memcpy(p,"__imp_",6); + memcpy(p + 6,name,len); + p[6 + len] = 0; + } + simp = scope_search(p,SCTglobal); + if (!simp) + { type *t; + + simp = scope_define(p,SCTglobal,SCextern); + simp->Ssequence = 0; + simp->Sfl = FLextern; + simp->Simport = s; + t = newpointer(s->Stype); + t->Tmangle = mTYman_c; + t->Tcount++; + simp->Stype = t; + } + assert(!e->EV.sp.Voffset); + if (e->Eoper == OPrelconst) + { + e->Eoper = OPvar; + e->EV.sp.Vsym = simp; + } + else // OPvar + { + e->Eoper = OPind; + e->E1 = el_var(simp); + e->E2 = NULL; + } + } +#endif +} + +/******************************* + * Mangle a name. + * Returns: + * length of mangled name + */ + +size_t obj_mangle(Symbol *s,char *dest) +{ size_t len; + size_t ilen; + char *name; + char *name2 = NULL; + + //printf("obj_mangle('%s'), mangle = x%x\n",s->Sident,type_mangle(s->Stype)); +#if SCPP + name = CPP ? cpp_mangle(s) : s->Sident; +#elif MARS + name = cpp_mangle(s); +#else + name = s->Sident; +#endif + len = strlen(name); // # of bytes in name + + // Use as max length the max length lib.exe can handle + // Use 5 as length of _ + @nnn + #define LIBIDMAX ((512 - 0x25 - 3 - 4) - 5) +#define LIBIDMAX 128 + if (len > LIBIDMAX) + //if (len > IDMAX) + { + size_t len2; + + // Attempt to compress the name + name2 = id_compress(name, len); + len2 = strlen(name2); +#if MARS + if (len2 > LIBIDMAX) // still too long + { + /* Form md5 digest of the name and store it in the + * last 32 bytes of the name. + */ + MD5_CTX mdContext; + MD5Init(&mdContext); + MD5Update(&mdContext, (unsigned char *)name, len); + MD5Final(&mdContext); + memcpy(name2, name, LIBIDMAX - 32); + for (int i = 0; i < 16; i++) + { unsigned char c = mdContext.digest[i]; + unsigned char c1 = (c >> 4) & 0x0F; + unsigned char c2 = c & 0x0F; + c1 += (c1 < 10) ? '0' : 'A' - 10; + name2[LIBIDMAX - 32 + i * 2] = c1; + c2 += (c2 < 10) ? '0' : 'A' - 10; + name2[LIBIDMAX - 32 + i * 2 + 1] = c2; + } + name = name2; + len = LIBIDMAX; + name[len] = 0; + //printf("name = '%s', len = %d, strlen = %d\n", name, len, strlen(name)); + } +#else + if (len2 > IDMAX) // still too long + { +#if SCPP + synerr(EM_identifier_too_long, name, len - IDMAX, IDMAX); +#elif MARS +// error(0, "identifier %s is too long by %d characters", name, len - IDMAX); +#else + assert(0); +#endif + len = IDMAX; + } +#endif + else + { + name = name2; + len = len2; + } + } + ilen = len; + if (ilen > (255-2-sizeof(int)*3)) + dest += 3; + switch (type_mangle(s->Stype)) + { case mTYman_pas: // if upper case + case mTYman_for: + memcpy(dest + 1,name,len); // copy in name + dest[1 + len] = 0; + strupr(dest + 1); // to upper case + break; +#if SCPP || MARS + case mTYman_cpp: +#if NEWMANGLE + memcpy(dest + 1,name,len); + break; +#endif +#endif + case mTYman_std: + if (!(config.flags4 & CFG4oldstdmangle) && + config.exe == EX_NT && tyfunc(s->ty()) && + !variadic(s->Stype)) + { + dest[1] = '_'; + memcpy(dest + 2,name,len); + dest[1 + 1 + len] = '@'; + itoa(type_paramsize(s->Stype),dest + 3 + len,10); + len = strlen(dest + 1); + assert(isdigit(dest[len])); + break; + } + case mTYman_c: + if (config.flags4 & CFG4underscore) + { + dest[1] = '_'; // leading _ in name + memcpy(&dest[2],name,len); // copy in name + len++; + break; + } + case mTYman_d: + case mTYman_sys: + memcpy(dest + 1, name, len); // no mangling + dest[1 + len] = 0; + break; + default: +#ifdef DEBUG + symbol_print(s); +#endif + assert(0); + } + if (ilen > (255-2-sizeof(int)*3)) + { dest -= 3; + dest[0] = 0xFF; + dest[1] = 0; +#ifdef DEBUG + assert(len <= 0xFFFF); +#endif + TOWORD(dest + 2,len); + len += 4; + } + else + { *dest = len; + len++; + } + if (name2) + free(name2); + assert(len <= IDMAX + IDOHD); + return len; +} + +/******************************* + * Export a function name. + */ + +void obj_export(Symbol *s,unsigned argsize) +{ char *coment; + size_t len; + + coment = (char *) alloca(4 + 1 + (IDMAX + IDOHD) + 1); // allow extra byte for mangling + len = obj_mangle(s,&coment[4]); + assert(len <= IDMAX + IDOHD); + coment[1] = 0xA0; // comment class + coment[2] = 2; // why??? who knows + if (argsize >= 64) // we only have a 5 bit field + argsize = 0; // hope we don't need callgate + coment[3] = (argsize + 1) >> 1; // # words on stack + coment[4 + len] = 0; // no internal name + objrecord(COMENT,coment,4 + len + 1); // module name record +} + +/******************************* + * Update data information about symbol + * align for output and assign segment + * if not already specified. + * + * Input: + * sdata data symbol + * datasize output size + * seg default seg if not known + * Returns: + * actual seg + */ + +int elf_data_start(Symbol *sdata, targ_size_t datasize, int seg) +{ + targ_size_t alignbytes; + //printf("elf_data_start(%s,size %llx,seg %d)\n",sdata->Sident,datasize,seg); + //symbol_print(sdata); + + if (sdata->Sseg == UNKNOWN) // if we don't know then there + sdata->Sseg = seg; // wasn't any segment override + else + seg = sdata->Sseg; + targ_size_t offset = SegData[seg]->SDoffset; + alignbytes = align(datasize, offset) - offset; + if (alignbytes) + obj_lidata(seg, offset, alignbytes); + sdata->Soffset = offset + alignbytes; + return seg; +} + +/******************************** + * Output a public definition. + * Input: + * seg = segment index that symbol is defined in + * s -> symbol + * offset = offset of name + */ + +STATIC void outpubdata() +{ + if (obj.pubdatai) + { objrecord(obj.mpubdef,obj.pubdata,obj.pubdatai); + obj.pubdatai = 0; + } +} + +void objpubdef(int seg,Symbol *s,targ_size_t offset) +{ unsigned reclen,len; + char *p; + unsigned ti; + + int idx = SegData[seg]->segidx; + if (obj.pubdatai + 1 + (IDMAX + IDOHD) + 4 + 2 > sizeof(obj.pubdata) || + idx != getindex(obj.pubdata + 1)) + outpubdata(); + if (obj.pubdatai == 0) + { + obj.pubdata[0] = (seg == DATA || seg == UDATA) ? 1 : 0; // group index + obj.pubdatai += 1 + insidx(obj.pubdata + 1,idx); // segment index + } + p = &obj.pubdata[obj.pubdatai]; + len = obj_mangle(s,p); // mangle in name + reclen = len + intsize; + p += len; + TOOFFSET(p,offset); + p += intsize; + ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0; + reclen += instypidx(p,ti); + obj.pubdatai += reclen; +} + +/******************************* + * Output an external definition. + * Input: + * name -> external identifier + * Returns: + * External index of the definition (1,2,...) + */ + +STATIC void outextdata() +{ + if (obj.extdatai) + { objrecord(EXTDEF,obj.extdata,obj.extdatai); + obj.extdatai = 0; + } +} + +int objextdef(const char *name) +{ unsigned len; + char *e; + + //dbg_printf("objextdef('%s')\n",name); + assert(name); + len = strlen(name); // length of identifier + if (obj.extdatai + len + ONS_OHD + 1 > sizeof(obj.extdata)) + outextdata(); + + e = &obj.extdata[obj.extdatai]; + len = obj_namestring(e,name); + e[len] = 0; // typidx = 0 + obj.extdatai += len + 1; + assert(obj.extdatai <= sizeof(obj.extdata)); + return ++obj.extidx; +} + +/******************************* + * Output an external definition. + * Input: + * s Symbol to do EXTDEF on + * Returns: + * External index of the definition (1,2,...) + */ + +int objextern(Symbol *s) +{ + //dbg_printf("objextern('%s')\n",s->Sident); + symbol_debug(s); + if (obj.extdatai + (IDMAX + IDOHD) + 3 > sizeof(obj.extdata)) + outextdata(); + + char *e = &obj.extdata[obj.extdatai]; + unsigned len = obj_mangle(s,e); + e[len] = 0; // typidx = 0 + obj.extdatai += len + 1; + s->Sxtrnnum = ++obj.extidx; + return obj.extidx; +} + +/******************************* + * Output a common block definition. + * Input: + * p -> external identifier + * flag TRUE: in default data segment + * FALSE: not in default data segment + * size size in bytes of each elem + * count number of elems + * Returns: + * External index of the definition (1,2,...) + */ + +// Helper for obj_comdef() + +static unsigned storelength(unsigned long length,unsigned i) +{ + obj.extdata[i] = length; + if (length >= 128) // Microsoft docs say 129, but their linker + // won't take >=128, so accommodate it + { obj.extdata[i] = 129; +#ifdef DEBUG + assert(length <= 0xFFFF); +#endif + TOWORD(obj.extdata + i + 1,length); + if (length >= 0x10000) + { obj.extdata[i] = 132; + obj.extdata[i + 3] = length >> 16; + + // Only 386 can generate lengths this big + if (I32 && length >= 0x1000000) + { obj.extdata[i] = 136; + obj.extdata[i + 4] = length >> 24; + i += 4; + } + else + i += 3; + } + else + i += 2; + } + return i + 1; // index past where we stuffed length +} + +int obj_comdef(Symbol *s,int flag,targ_size_t size,targ_size_t count) +{ register unsigned i; + unsigned long length; + unsigned ti; + + //dbg_printf("obj_comdef('%s',%d,%d,%d)\n",s->Sident,flag,size,count); + outextdata(); // borrow the extdata[] storage + i = obj_mangle(s,obj.extdata); + + ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0; + i += instypidx(obj.extdata + i,ti); + + if (flag) // if in default data segment + { + //printf("NEAR comdef\n"); + obj.extdata[i] = 0x62; + length = (unsigned long) size * count; + assert(I32 || length <= 0x10000); + i = storelength(length,i + 1); + } + else + { + //printf("FAR comdef\n"); + obj.extdata[i] = 0x61; + i = storelength((unsigned long) size,i + 1); + i = storelength((unsigned long) count,i); + } + assert(i <= arraysize(obj.extdata)); + objrecord(COMDEF,obj.extdata,i); + return ++obj.extidx; +} + +/*************************************** + * Append an iterated data block of 0s. + * (uninitialized data only) + */ + +void obj_write_zeros(seg_data *pseg, targ_size_t count) +{ + obj_lidata(pseg->SDseg, pseg->SDoffset, count); + //pseg->SDoffset += count; +} + +/*************************************** + * Output an iterated data block of 0s. + * (uninitialized data only) + */ + +void obj_lidata(int seg,targ_size_t offset,targ_size_t count) +{ int i; + unsigned reclen; + static char zero[20]; + char data[20]; + char __ss *di; + + //printf("obj_lidata(seg = %d, offset = x%x, count = %d)\n", seg, offset, count); + + SegData[seg]->SDoffset += count; + + if (seg == UDATA) + return; + int idx = SegData[seg]->segidx; + +Lagain: + if (count <= sizeof(zero)) // if shorter to use ledata + { + obj_bytes(seg,offset,count,zero); + return; + } + + if (seg_is_comdat(idx)) + { + while (count > sizeof(zero)) + { + obj_bytes(seg,offset,sizeof(zero),zero); + offset += sizeof(zero); + count -= sizeof(zero); + } + obj_bytes(seg,offset,count,zero); + return; + } + + i = insidx(data,idx); + di = data + i; + TOOFFSET(di,offset); + + if (config.flags & CFGeasyomf) + { + if (count >= 0x8000) // repeat count can only go to 32k + { + TOWORD(di + 4,(unsigned short)(count / 0x8000)); + TOWORD(di + 4 + 2,1); // 1 data block follows + TOWORD(di + 4 + 2 + 2,0x8000); // repeat count + TOWORD(di + 4 + 2 + 2 + 2,0); // block count + TOWORD(di + 4 + 2 + 2 + 2 + 2,1); // 1 byte of 0 + reclen = i + 4 + 5 * 2; + objrecord(obj.mlidata,data,reclen); + + offset += (count & ~0x7FFFL); + count &= 0x7FFF; + goto Lagain; + } + else + { + TOWORD(di + 4,(unsigned short)count); // repeat count + TOWORD(di + 4 + 2,0); // block count + TOWORD(di + 4 + 2 + 2,1); // 1 byte of 0 + reclen = i + 4 + 2 + 2 + 2; + objrecord(obj.mlidata,data,reclen); + } + } + else + { + TOOFFSET(di + intsize,count); + TOWORD(di + intsize * 2,0); // block count + TOWORD(di + intsize * 2 + 2,1); // repeat 1 byte of 0s + reclen = i + (I32 ? 12 : 8); + objrecord(obj.mlidata,data,reclen); + } + assert(reclen <= sizeof(data)); +} + +/**************************** + * Output a MODEND record. + */ + +STATIC void obj_modend() +{ + if (obj.startaddress) + { char mdata[10]; + int i; + unsigned framedatum,targetdatum; + unsigned char fd; + targ_size_t offset; + int external; // !=0 if identifier is defined externally + tym_t ty; + Symbol *s = obj.startaddress; + + // Turn startaddress into a fixup. + // Borrow heavilly from reftoident() + + symbol_debug(s); + offset = 0; + ty = s->ty(); + + switch (s->Sclass) + { + case SCcomdat: + case_SCcomdat: + case SCextern: + case SCcomdef: + if (s->Sxtrnnum) // identifier is defined somewhere else + external = s->Sxtrnnum; + else + { + Ladd: + s->Sclass = SCextern; + external = objextern(s); + outextdata(); + } + break; + case SCinline: + if (config.flags2 & CFG2comdat) + goto case_SCcomdat; // treat as initialized common block + case SCsinline: + case SCstatic: + case SCglobal: + if (s->Sseg == UNKNOWN) + goto Ladd; + if (seg_is_comdat(SegData[s->Sseg]->segidx)) // if in comdat + goto case_SCcomdat; + case SClocstat: + external = 0; // identifier is static or global + // and we know its offset + offset += s->Soffset; + break; + default: + #ifdef DEBUG + //symbol_print(s); + #endif + assert(0); + } + + if (external) + { fd = FD_T2; + targetdatum = external; + switch (s->Sfl) + { + case FLextern: + if (!(ty & ( +#if TARGET_SEGMENTED + mTYcs | +#endif + mTYthread))) + goto L1; + case FLfunc: +#if TARGET_SEGMENTED + case FLfardata: + case FLcsdata: +#endif + case FLtlsdata: + if (config.exe & EX_flat) + { fd |= FD_F1; + framedatum = 1; + } + else + { + //case FLtlsdata: + fd |= FD_F2; + framedatum = targetdatum; + } + break; + default: + goto L1; + } + } + else + { + fd = FD_T0; // target is always a segment + targetdatum = SegData[s->Sseg]->segidx; + assert(targetdatum != -1); + switch (s->Sfl) + { + case FLextern: + if (!(ty & ( +#if TARGET_SEGMENTED + mTYcs | +#endif + mTYthread))) + goto L1; + case FLfunc: +#if TARGET_SEGMENTED + case FLfardata: + case FLcsdata: +#endif + case FLtlsdata: + if (config.exe & EX_flat) + { fd |= FD_F1; + framedatum = 1; + } + else + { + //case FLtlsdata: + fd |= FD_F0; + framedatum = targetdatum; + } + break; + default: + L1: + fd |= FD_F1; + framedatum = DGROUPIDX; + //if (flags == CFseg) + { fd = FD_F1 | FD_T1; // target is DGROUP + targetdatum = DGROUPIDX; + } + break; + } + } + + // Write the fixup into mdata[] + mdata[0] = 0xC1; + mdata[1] = fd; + i = 2 + insidx(&mdata[2],framedatum); + i += insidx(&mdata[i],targetdatum); + TOOFFSET(mdata + i,offset); + + objrecord(obj.mmodend,mdata,i + intsize); // write mdata[] to .OBJ file + } + else + { static const char modend[] = {0}; + + objrecord(MODEND,modend,sizeof(modend)); + } +} + +/**************************** + * Output the fixups in list fl. + */ + +STATIC void objfixupp(struct FIXUP *f) +{ + unsigned i,j,k; + targ_size_t locat; + struct FIXUP *fn; + +#if 1 // store in one record + char data[1024]; + + i = 0; + for (; f; f = fn) + { unsigned char fd; + + if (i >= sizeof(data) - (3 + 2 + 2)) // if not enough room + { objrecord(obj.mfixupp,data,i); + i = 0; + } + + //printf("f = %p, offset = x%x\n",f,f->FUoffset); + assert(f->FUoffset < 1024); + locat = (f->FUlcfd & 0xFF00) | f->FUoffset; + data[i+0] = locat >> 8; + data[i+1] = locat; + data[i+2] = fd = f->FUlcfd; + k = i; + i += 3 + insidx(&data[i+3],f->FUframedatum); + //printf("FUframedatum = x%x\n", f->FUframedatum); + if ((fd >> 4) == (fd & 3) && f->FUframedatum == f->FUtargetdatum) + { + data[k + 2] = (fd & 15) | FD_F5; + } + else + { i += insidx(&data[i],f->FUtargetdatum); + //printf("FUtargetdatum = x%x\n", f->FUtargetdatum); + } + //printf("[%d]: %02x %02x %02x\n", k, data[k + 0] & 0xFF, data[k + 1] & 0xFF, data[k + 2] & 0xFF); + fn = f->FUnext; + mem_ffree(f); + } + assert(i <= sizeof(data)); + if (i) + objrecord(obj.mfixupp,data,i); +#else // store in multiple records + for (; fl; fl = list_next(fl)) + { + char data[7]; + + assert(f->FUoffset < 1024); + locat = (f->FUlcfd & 0xFF00) | f->FUoffset; + data[0] = locat >> 8; + data[1] = locat; + data[2] = f->FUlcfd; + i = 3 + insidx(&data[3],f->FUframedatum); + i += insidx(&data[i],f->FUtargetdatum); + objrecord(obj.mfixupp,data,i); + } +#endif +} + + +/*************************** + * Add a new fixup to the fixup list. + * Write things out if we overflow the list. + */ + +STATIC void addfixup(Ledatarec *lr, targ_size_t offset,unsigned lcfd, + unsigned framedatum,unsigned targetdatum) +{ struct FIXUP *f; + + assert(offset < 0x1024); +#ifdef DEBUG + assert(targetdatum <= 0x7FFF); + assert(framedatum <= 0x7FFF); +#endif + f = (struct FIXUP *) mem_fmalloc(sizeof(struct FIXUP)); + //printf("f = %p, offset = x%x\n",f,offset); + f->FUoffset = offset; + f->FUlcfd = lcfd; + f->FUframedatum = framedatum; + f->FUtargetdatum = targetdatum; + f->FUnext = lr->fixuplist; // link f into list + lr->fixuplist = f; +#ifdef DEBUG + obj.fixup_count++; // gather statistics +#endif +} + + +/********************************* + * Open up a new ledata record. + * Input: + * seg segment number data is in + * offset starting offset of start of data for this record + */ + +STATIC Ledatarec *ledata_new(int seg,targ_size_t offset) +{ + + //printf("ledata_new(seg = %d, offset = x%lx)\n",seg,offset); + assert(seg > 0 && seg <= seg_count); + + if (obj.ledatai == ledatamax) + { + size_t o = ledatamax; + ledatamax = o * 2 + 100; + ledatas = (Ledatarec **)mem_realloc(ledatas, ledatamax * sizeof(Ledatarec *)); + memset(ledatas + o, 0, (ledatamax - o) * sizeof(Ledatarec *)); + } + Ledatarec *lr = ledatas[obj.ledatai]; + if (!lr) + { lr = (Ledatarec *) mem_malloc(sizeof(Ledatarec)); + ledatas[obj.ledatai] = lr; + } + memset(lr, 0, sizeof(Ledatarec)); + ledatas[obj.ledatai] = lr; + obj.ledatai++; + + lr->lseg = seg; + lr->offset = offset; + + if (seg_is_comdat(SegData[seg]->segidx) && offset) // if continuation of an existing COMDAT + { + Ledatarec *d = SegData[seg]->ledata; + if (d) + { + if (d->lseg == seg) // found existing COMDAT + { lr->flags = d->flags; + lr->alloctyp = d->alloctyp; + lr->align = d->align; + lr->typidx = d->typidx; + lr->pubbase = d->pubbase; + lr->pubnamidx = d->pubnamidx; + } + } + } + SegData[seg]->ledata = lr; + return lr; +} + +/*********************************** + * Append byte to segment. + */ + +void obj_write_byte(seg_data *pseg, unsigned byte) +{ + obj_byte(pseg->SDseg, pseg->SDoffset, byte); + pseg->SDoffset++; +} + +/************************************ + * Output byte to object file. + */ + +void obj_byte(int seg,targ_size_t offset,unsigned byte) +{ unsigned i; + + Ledatarec *lr = SegData[seg]->ledata; + if (!lr) + goto L2; + + if ( + lr->i > LEDATAMAX - 1 || // if it'll overflow + offset < lr->offset || // underflow + offset > lr->offset + lr->i + ) + { + // Try to find an existing ledata + for (size_t i = obj.ledatai; i; ) + { Ledatarec *d = ledatas[--i]; + if (seg == d->lseg && // segments match + offset >= d->offset && + offset + 1 <= d->offset + LEDATAMAX && + offset <= d->offset + d->i + ) + { + lr = SegData[seg]->ledata = d; + goto L1; + } + } +L2: + lr = ledata_new(seg,offset); +L1: ; + } + + i = offset - lr->offset; + if (lr->i <= i) + lr->i = i + 1; + lr->data[i] = byte; // 1st byte of data +} + +/*********************************** + * Append bytes to segment. + */ + +void obj_write_bytes(seg_data *pseg, unsigned nbytes, void *p) +{ + obj_bytes(pseg->SDseg, pseg->SDoffset, nbytes, p); + pseg->SDoffset += nbytes; +} + +/************************************ + * Output bytes to object file. + * Returns: + * nbytes + */ + +unsigned obj_bytes(int seg,targ_size_t offset,unsigned nbytes, void *p) +{ unsigned n = nbytes; + + //dbg_printf("obj_bytes(seg=%d, offset=x%lx, nbytes=x%x, p=%p)\n",seg,offset,nbytes,p); + Ledatarec *lr = SegData[seg]->ledata; + if (!lr) + lr = ledata_new(seg, offset); + L1: + if ( + lr->i + nbytes > LEDATAMAX || // or it'll overflow + offset < lr->offset || // underflow + offset > lr->offset + lr->i + ) + { + while (nbytes) + { obj_byte(seg,offset,*(char *)p); + offset++; + ((char *)p)++; + nbytes--; + lr = SegData[seg]->ledata; + if (lr->i + nbytes <= LEDATAMAX) + goto L1; + } + } + else + { + unsigned i = offset - lr->offset; + if (lr->i < i + nbytes) + lr->i = i + nbytes; + memcpy(lr->data + i,p,nbytes); + } + return n; +} + +/************************************ + * Output word of data. (Two words if segment:offset pair.) + * Input: + * seg CODE, DATA, CDATA, UDATA + * offset offset of start of data + * data word of data + * lcfd LCxxxx | FDxxxx + * if (FD_F2 | FD_T6) + * idx1 = external Symbol # + * else + * idx1 = frame datum + * idx2 = target datum + */ + +void objledata(int seg,targ_size_t offset,targ_size_t data, + unsigned lcfd,unsigned idx1,unsigned idx2) +{ unsigned i; + unsigned size; // number of bytes to output + +#if TARGET_SEGMENTED + unsigned ptrsize = tysize[TYfptr]; +#else + unsigned ptrsize = I64 ? 10 : 6; +#endif + + if ((lcfd & LOCxx) == obj.LOCpointer) + size = ptrsize; + else if ((lcfd & LOCxx) == LOCbase) + size = 2; + else + size = tysize[TYnptr]; + + Ledatarec *lr = SegData[seg]->ledata; + if (!lr) + lr = ledata_new(seg, offset); + assert(seg == lr->lseg); + if ( + lr->i + size > LEDATAMAX || // if it'll overflow + offset < lr->offset || // underflow + offset > lr->offset + lr->i + ) + { + // Try to find an existing ledata +//dbg_printf("seg = %d, offset = x%lx, size = %d\n",seg,offset,size); + for (size_t i = obj.ledatai; i; ) + { Ledatarec *d = ledatas[--i]; + +//dbg_printf("d: seg = %d, offset = x%lx, i = x%x\n",d->lseg,d->offset,d->i); + if (seg == d->lseg && // segments match + offset >= d->offset && + offset + size <= d->offset + LEDATAMAX && + offset <= d->offset + d->i + ) + { +//dbg_printf("match\n"); + lr = SegData[seg]->ledata = d; + goto L1; + } + } + lr = ledata_new(seg,offset); +L1: ; + } + + i = offset - lr->offset; + if (lr->i < i + size) + lr->i = i + size; + if (size == 2 || !I32) + TOWORD(lr->data + i,data); + else + TOLONG(lr->data + i,data); + if (size == ptrsize) // if doing a seg:offset pair + TOWORD(lr->data + i + tysize[TYnptr],0); // segment portion + addfixup(lr, offset - lr->offset,lcfd,idx1,idx2); +} + +/************************************ + * Output long word of data. + * Input: + * seg CODE, DATA, CDATA, UDATA + * offset offset of start of data + * data long word of data + * Present only if size == 2: + * lcfd LCxxxx | FDxxxx + * if (FD_F2 | FD_T6) + * idx1 = external Symbol # + * else + * idx1 = frame datum + * idx2 = target datum + */ + +void obj_long(int seg,targ_size_t offset,unsigned long data, + unsigned lcfd,unsigned idx1,unsigned idx2) +{ +#if TARGET_SEGMENTED + unsigned sz = tysize[TYfptr]; +#else + unsigned sz = I64 ? 10 : 6; +#endif + Ledatarec *lr = SegData[seg]->ledata; + if (!lr) + lr = ledata_new(seg, offset); + if ( + lr->i + sz > LEDATAMAX || // if it'll overflow + offset < lr->offset || // underflow + offset > lr->offset + lr->i + ) + lr = ledata_new(seg,offset); + unsigned i = offset - lr->offset; + if (lr->i < i + sz) + lr->i = i + sz; + TOLONG(lr->data + i,data); + if (I32) // if 6 byte far pointers + TOWORD(lr->data + i + LONGSIZE,0); // fill out seg + addfixup(lr, offset - lr->offset,lcfd,idx1,idx2); +} + +/******************************* + * Refer to address that is in the data segment. + * Input: + * seg = where the address is going + * offset = offset within seg + * val = displacement from address + * targetdatum = DATA, CDATA or UDATA, depending where the address is + * flags = CFoff, CFseg + * Example: + * int *abc = &def[3]; + * to allocate storage: + * reftodatseg(DATA,offset,3 * sizeof(int *),UDATA); + */ + +void reftodatseg(int seg,targ_size_t offset,targ_size_t val, + unsigned targetdatum,int flags) +{ + assert(flags); + + if (flags == 0 || flags & CFoff) + { + // The frame datum is always 1, which is DGROUP + objledata(seg,offset,val, + LOCATsegrel | obj.LOCoffset | FD_F1 | FD_T4,DGROUPIDX,SegData[targetdatum]->segidx); + offset += intsize; + } + + if (flags & CFseg) + { +#if 0 + if (config.wflags & WFdsnedgroup) + warerr(WM_ds_ne_dgroup); +#endif + objledata(seg,offset,0, + LOCATsegrel | LOCbase | FD_F1 | FD_T5,DGROUPIDX,DGROUPIDX); + } +} + +/******************************* + * Refer to address that is in a far segment. + * Input: + * seg = where the address is going + * offset = offset within seg + * val = displacement from address + * farseg = far segment index + * flags = CFoff, CFseg + */ + +void reftofarseg(int seg,targ_size_t offset,targ_size_t val, + int farseg,int flags) +{ + assert(flags); + + int idx = SegData[farseg]->segidx; + if (flags == 0 || flags & CFoff) + { + objledata(seg,offset,val, + LOCATsegrel | obj.LOCoffset | FD_F0 | FD_T4,idx,idx); + offset += intsize; + } + + if (flags & CFseg) + { + objledata(seg,offset,0, + LOCATsegrel | LOCbase | FD_F0 | FD_T4,idx,idx); + } +} + +/******************************* + * Refer to address that is in the code segment. + * Only offsets are output, regardless of the memory model. + * Used to put values in switch address tables. + * Input: + * seg = where the address is going (CODE or DATA) + * offset = offset within seg + * val = displacement from start of this module + */ + +void reftocodseg(int seg,targ_size_t offset,targ_size_t val) +{ unsigned framedatum; + unsigned lcfd; + + int idx = SegData[cseg]->segidx; + if (seg_is_comdat(idx)) // if comdat + { idx = -idx; + framedatum = idx; + lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F2 | FD_T6); + } + else if (config.exe & EX_flat) + { framedatum = 1; + lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F1 | FD_T4); + } + else + { framedatum = idx; + lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F0 | FD_T4); + } + + objledata(seg,offset,val,lcfd,framedatum,idx); +} + +/******************************* + * Refer to an identifier. + * Input: + * seg = where the address is going (CODE or DATA) + * offset = offset within seg + * s -> Symbol table entry for identifier + * val = displacement from identifier + * flags = CFselfrel: self-relative + * CFseg: get segment + * CFoff: get offset + * Returns: + * number of bytes in reference (2 or 4) + * Example: + * extern int def[]; + * int *abc = &def[3]; + * to allocate storage: + * reftodatseg(DATA,offset,3 * sizeof(int *),UDATA); + */ + +int reftoident(int seg,targ_size_t offset,Symbol *s,targ_size_t val, + int flags) +{ + unsigned targetdatum; // which datum the symbol is in + unsigned framedatum; + int lc; + int external; // !=0 if identifier is defined externally + int numbytes; + tym_t ty; + +#if 0 + printf("reftoident('%s' seg %d, offset x%lx, val x%lx, flags x%x)\n", + s->Sident,seg,offset,val,flags); + printf("Sseg = %d, Sxtrnnum = %d\n",s->Sseg,s->Sxtrnnum); + symbol_print(s); +#endif + assert(seg > 0); + + ty = s->ty(); + while (1) + { + switch (flags & (CFseg | CFoff)) + { case 0: + // Select default + flags |= CFoff; + if (tyfunc(ty)) + { + if (tyfarfunc(ty)) + flags |= CFseg; + } + else // DATA + { + if (LARGEDATA) + flags |= CFseg; + } + continue; + case CFoff: + if (I32) + { +#if 1 + if (ty & mTYthread) + { lc = LOC32tlsoffset; + } + else +#endif + lc = obj.LOCoffset; + } + else + { + // The 'loader_resolved' offset is required for VCM + // and Windows support. A fixup of this type is + // relocated by the linker to point to a 'thunk'. + lc = (tyfarfunc(ty) + && !(flags & CFselfrel)) + ? LOCloader_resolved : obj.LOCoffset; + } + numbytes = tysize[TYnptr]; + break; + case CFseg: + lc = LOCbase; + numbytes = 2; + break; + case CFoff | CFseg: + lc = obj.LOCpointer; +#if TARGET_SEGMENTED + numbytes = tysize[TYfptr]; +#else + numbytes = I64 ? 10 : 6; +#endif + break; + } + break; + } + + switch (s->Sclass) + { + case SCcomdat: + case_SCcomdat: + case SCextern: + case SCcomdef: + if (s->Sxtrnnum) // identifier is defined somewhere else + { external = s->Sxtrnnum; +#ifdef DEBUG + if (external > obj.extidx) + symbol_print(s); +#endif + assert(external <= obj.extidx); + } + else + { // Don't know yet, worry about it later + Ladd: + addtofixlist(s,offset,seg,val,flags); + return numbytes; + } + break; + case SCinline: + if (config.flags2 & CFG2comdat) + goto case_SCcomdat; // treat as initialized common block + case SCsinline: + case SCstatic: + case SCglobal: + if (s->Sseg == UNKNOWN) + goto Ladd; + if (seg_is_comdat(SegData[s->Sseg]->segidx)) + goto case_SCcomdat; + case SClocstat: + external = 0; // identifier is static or global + // and we know its offset + if (flags & CFoff) + val += s->Soffset; + break; + default: +#ifdef DEBUG + symbol_print(s); +#endif + assert(0); + } + + lc |= (flags & CFselfrel) ? LOCATselfrel : LOCATsegrel; + if (external) + { lc |= FD_T6; + targetdatum = external; + switch (s->Sfl) + { + case FLextern: + if (!(ty & ( +#if TARGET_SEGMENTED + mTYcs | +#endif + mTYthread))) + goto L1; + case FLfunc: +#if TARGET_SEGMENTED + case FLfardata: + case FLcsdata: +#endif + case FLtlsdata: + if (config.exe & EX_flat) + { lc |= FD_F1; + framedatum = 1; + } + else + { + //case FLtlsdata: + lc |= FD_F2; + framedatum = targetdatum; + } + break; + default: + goto L1; + } + } + else + { + lc |= FD_T4; // target is always a segment + targetdatum = SegData[s->Sseg]->segidx; + assert(s->Sseg != UNKNOWN); + switch (s->Sfl) + { + case FLextern: + if (!(ty & ( +#if TARGET_SEGMENTED + mTYcs | +#endif + mTYthread))) + goto L1; + case FLfunc: +#if TARGET_SEGMENTED + case FLfardata: + case FLcsdata: +#endif + case FLtlsdata: + if (config.exe & EX_flat) + { lc |= FD_F1; + framedatum = 1; + } + else + { + //case FLtlsdata: + lc |= FD_F0; + framedatum = targetdatum; + } + break; + default: + L1: + lc |= FD_F1; + framedatum = DGROUPIDX; + if (flags == CFseg) + { lc = LOCATsegrel | LOCbase | FD_F1 | FD_T5; + targetdatum = DGROUPIDX; + } +#if 0 + if (flags & CFseg && config.wflags & WFdsnedgroup) + warerr(WM_ds_ne_dgroup); +#endif + break; + } + } + + objledata(seg,offset,val,lc,framedatum,targetdatum); + return numbytes; +} + +/***************************************** + * Generate far16 thunk. + * Input: + * s Symbol to generate a thunk for + */ + +void obj_far16thunk(Symbol *s) +{ + static unsigned char cod32_1[] = + { + 0x55, // PUSH EBP + 0x8B,0xEC, // MOV EBP,ESP + 0x83,0xEC,0x04, // SUB ESP,4 + 0x53, // PUSH EBX + 0x57, // PUSH EDI + 0x56, // PUSH ESI + 0x06, // PUSH ES + 0x8C,0xD2, // MOV DX,SS + 0x80,0xE2,0x03, // AND DL,3 + 0x80,0xCA,0x07, // OR DL,7 + 0x89,0x65,0xFC, // MOV -4[EBP],ESP + 0x8C,0xD0, // MOV AX,SS + 0x66,0x3D, // 0x00,0x00 */ /* CMP AX,seg FLAT:_DATA + }; + static unsigned char cod32_2[] = + { 0x0F,0x85,0x10,0x00,0x00,0x00, // JNE L1 + 0x8B,0xC4, // MOV EAX,ESP + 0x66,0x3D,0x00,0x08, // CMP AX,2048 + 0x0F,0x83,0x04,0x00,0x00,0x00, // JAE L1 + 0x66,0x33,0xC0, // XOR AX,AX + 0x94, // XCHG ESP,EAX + // L1: + 0x55, // PUSH EBP + 0x8B,0xC4, // MOV EAX,ESP + 0x16, // PUSH SS + 0x50, // PUSH EAX + 0x8D,0x75,0x08, // LEA ESI,8[EBP] + 0x81,0xEC,0x00,0x00,0x00,0x00, // SUB ESP,numparam + 0x8B,0xFC, // MOV EDI,ESP + 0xB9,0x00,0x00,0x00,0x00, // MOV ECX,numparam + 0x66,0xF3,0xA4, // REP MOVSB + 0x8B,0xC4, // MOV EAX,ESP + 0xC1,0xC8,0x10, // ROR EAX,16 + 0x66,0xC1,0xE0,0x03, // SHL AX,3 + 0x0A,0xC2, // OR AL,DL + 0xC1,0xC0,0x10, // ROL EAX,16 + 0x50, // PUSH EAX + 0x66,0x0F,0xB2,0x24,0x24, // LSS SP,[ESP] + 0x66,0xEA, // 0,0,0,0, */ /* JMPF L3 + }; + static unsigned char cod32_3[] = + { // L2: + 0xC1,0xE0,0x10, // SHL EAX,16 + 0x0F,0xAC,0xD0,0x10, // SHRD EAX,EDX,16 + 0x0F,0xB7,0xE4, // MOVZX ESP,SP + 0x0F,0xB2,0x24,0x24, // LSS ESP,[ESP] + 0x5D, // POP EBP + 0x8B,0x65,0xFC, // MOV ESP,-4[EBP] + 0x07, // POP ES + 0x5E, // POP ESI + 0x5F, // POP EDI + 0x5B, // POP EBX + 0xC9, // LEAVE + 0xC2,0x00,0x00 // RET numparam + }; + + unsigned numparam = 24; + targ_size_t L2offset; + int idx; + + s->Sclass = SCstatic; + s->Sseg = cseg; // identifier is defined in code segment + s->Soffset = Coffset; + + // Store numparam into right places + assert((numparam & 0xFFFF) == numparam); // 2 byte value + TOWORD(&cod32_2[32],numparam); + TOWORD(&cod32_2[32 + 7],numparam); + TOWORD(&cod32_3[sizeof(cod32_3) - 2],numparam); + + //------------------------------------------ + // Generate CODE16 segment if it isn't there already + if (obj.code16segi == 0) + { + // Define CODE16 segment for far16 thunks + + static char lname[] = { "\06CODE16" }; + + // Put out LNAMES record + objrecord(LNAMES,lname,sizeof(lname) - 1); + + obj.code16segi = obj_newfarseg(0,4); + obj.CODE16offset = 0; + + // class CODE + unsigned attr = SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16); + SegData[obj.code16segi]->attr = attr; + objsegdef(attr,0,obj.lnameidx++,4); + obj.segidx++; + } + + //------------------------------------------ + // Output the 32 bit thunk + + obj_bytes(cseg,Coffset,sizeof(cod32_1),cod32_1); + Coffset += sizeof(cod32_1); + + // Put out fixup for SEG FLAT:_DATA + objledata(cseg,Coffset,0,LOCATsegrel|LOCbase|FD_F1|FD_T4, + DGROUPIDX,DATA); + Coffset += 2; + + obj_bytes(cseg,Coffset,sizeof(cod32_2),cod32_2); + Coffset += sizeof(cod32_2); + + // Put out fixup to CODE16 part of thunk + objledata(cseg,Coffset,obj.CODE16offset,LOCATsegrel|LOC16pointer|FD_F0|FD_T4, + SegData[obj.code16segi]->segidx, + SegData[obj.code16segi]->segidx); + Coffset += 4; + + L2offset = Coffset; + obj_bytes(cseg,Coffset,sizeof(cod32_3),cod32_3); + Coffset += sizeof(cod32_3); + + s->Ssize = Coffset - s->Soffset; // size of thunk + + //------------------------------------------ + // Output the 16 bit thunk + + obj_byte(obj.code16segi,obj.CODE16offset++,0x9A); // CALLF function + + // Make function external + idx = objextern(s); // use Pascal name mangling + + // Output fixup for function + objledata(obj.code16segi,obj.CODE16offset,0,LOCATsegrel|LOC16pointer|FD_F2|FD_T6, + idx,idx); + obj.CODE16offset += 4; + + obj_bytes(obj.code16segi,obj.CODE16offset,3,"\x66\x67\xEA"); // JMPF L2 + obj.CODE16offset += 3; + + objledata(obj.code16segi,obj.CODE16offset,L2offset, + LOCATsegrel | LOC32pointer | FD_F1 | FD_T4, + DGROUPIDX, + SegData[cseg]->segidx); + obj.CODE16offset += 6; + + SegData[obj.code16segi]->SDoffset = obj.CODE16offset; +} + +/************************************** + * Mark object file as using floating point. + */ + +void obj_fltused() +{ + if (!obj.fltused) + { + obj.fltused = 1; + if (!(config.flags3 & CFG3wkfloat)) + objextdef("__fltused"); + } +} + + +/**************************************** + * Find longest match of pattern[] in dict[]. + */ + +static int longest_match(char *dict, int dlen, char *pattern, int plen, + int *pmatchoff, int *pmatchlen) +{ + int matchlen = 0; + int matchoff; + + int i; + int j; + + for (i = 0; i < dlen; i++) + { + if (dict[i] == pattern[0]) + { + for (j = 1; 1; j++) + { + if (i + j == dlen || j == plen) + break; + if (dict[i + j] != pattern[j]) + break; + } + if (j >= matchlen) + { + matchlen = j; + matchoff = i; + } + } + } + + if (matchlen > 1) + { + *pmatchlen = matchlen; + *pmatchoff = matchoff; + return 1; // found a match + } + return 0; // no match +} + +/****************************************** + * Compress an identifier. + * Format: if ASCII, then it's just the char + * if high bit set, then it's a length/offset pair + * Returns: + * malloc'd compressed identifier + */ + +char *id_compress(char *id, int idlen) +{ + int i; + int count = 0; + char *p; + + p = (char *)malloc(idlen + 1); + for (i = 0; i < idlen; i++) + { + int matchoff; + int matchlen; + + int j = 0; + if (i > 1023) + j = i - 1023; + + if (longest_match(id + j, i - j, id + i, idlen - i, &matchoff, &matchlen)) + { int off; + + matchoff += j; + off = i - matchoff; + //printf("matchoff = %3d, matchlen = %2d, off = %d\n", matchoff, matchlen, off); + assert(off >= matchlen); + + if (off <= 8 && matchlen <= 8) + { + p[count] = 0xC0 | ((off - 1) << 3) | (matchlen - 1); + count++; + i += matchlen - 1; + continue; + } + else if (matchlen > 2 && off < 1024) + { + if (matchlen >= 1024) + matchlen = 1023; // longest representable match + p[count + 0] = 0x80 | ((matchlen >> 4) & 0x38) | ((off >> 7) & 7); + p[count + 1] = 0x80 | matchlen; + p[count + 2] = 0x80 | off; + count += 3; + i += matchlen - 1; + continue; + } + } + p[count] = id[i]; + count++; + } + p[count] = 0; + //printf("old size = %d, new size = %d\n", idlen, count); + return p; +} + +#endif +#endif diff --git a/backend/cgreg.c b/backend/cgreg.c new file mode 100644 index 00000000..084a6a7c --- /dev/null +++ b/backend/cgreg.c @@ -0,0 +1,1013 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2012 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +#if !SPP + +#include +#include +#include +#include +#include "cc.h" +#include "el.h" +#include "oper.h" +#include "code.h" +#include "global.h" +#include "type.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +STATIC void el_weights(int bi,elem *e,unsigned weight); + +#ifndef __DMC__ +#undef __cdecl +#define __cdecl +#endif + +static int __cdecl weight_compare(const void *e1,const void *e2); + +static int nretblocks; + +static vec_t regrange[REGMAX]; + +static int *weights; +#define WEIGHTS(bi,si) weights[bi * globsym.top + si] + +/****************************************** + */ + +void cgreg_init() +{ + if (!(config.flags4 & CFG4optimized)) + return; + + // Use calloc() instead because sometimes the alloc is too large + //printf("1weights: dfotop = %d, globsym.top = %d\n", dfotop, globsym.top); + weights = (int *) calloc(1,dfotop * globsym.top * sizeof(weights[0])); + assert(weights); + + nretblocks = 0; + for (int bi = 0; bi < dfotop; bi++) + { block *b = dfo[bi]; + if (b->BC == BCret || b->BC == BCretexp) + nretblocks++; + if (b->Belem) + { + //printf("b->Bweight = x%x\n",b->Bweight); + el_weights(bi,b->Belem,b->Bweight); + } + } + memset(regrange,0,sizeof(regrange)); + + // Make adjustments to symbols we might stick in registers + for (size_t i = 0; i < globsym.top; i++) + { unsigned sz; + symbol *s = globsym.tab[i]; + + //printf("candidate '%s' for register\n",s->Sident); + + if (s->Srange) + s->Srange = vec_realloc(s->Srange,dfotop); + + // Determine symbols that are not candidates + if (!(s->Sflags & GTregcand) || + !s->Srange || + (sz = type_size(s->Stype)) == 0 || + (tysize(s->ty()) == -1) || + (I16 && sz > REGSIZE) || + (tyfloating(s->ty()) && !(config.fpxmmregs && tyxmmreg(s->ty()))) + ) + { + #ifdef DEBUG + if (debugr) + printf("not considering variable '%s' for register\n",s->Sident); + #endif + s->Sflags &= ~GTregcand; + continue; + } + + switch (s->Sclass) + { case SCparameter: + case SCfastpar: + // Do not put parameters in registers if they are not used + // more than twice (otherwise we have a net loss). + if (s->Sweight <= 2 && !tyxmmreg(s->ty())) + { + #ifdef DEBUG + if (debugr) + printf("parameter '%s' weight %d is not enough\n",s->Sident,s->Sweight); + #endif + s->Sflags &= ~GTregcand; + continue; + } + break; + } + + if (sz == 1) + s->Sflags |= GTbyte; + + if (!s->Slvreg) + s->Slvreg = vec_calloc(dfotop); + + //printf("dfotop = %d, numbits = %d\n",dfotop,vec_numbits(s->Srange)); + assert(vec_numbits(s->Srange) == dfotop); + } +} + +/****************************************** + */ + +void cgreg_term() +{ + if (config.flags4 & CFG4optimized) + { + for (size_t i = 0; i < globsym.top; i++) + { + Symbol *s = globsym.tab[i]; + vec_free(s->Srange); + vec_free(s->Slvreg); + s->Srange = NULL; + s->Slvreg = NULL; + } + + for (size_t i = 0; i < arraysize(regrange); i++) + { + if (regrange[i]) + { vec_free(regrange[i]); + regrange[i] = NULL; + } + } + + free(weights); + weights = NULL; + } +} + +/********************************* + */ + +void cgreg_reset() +{ + for (size_t j = 0; j < arraysize(regrange); j++) + if (!regrange[j]) + regrange[j] = vec_calloc(dfotop); + else + vec_clear(regrange[j]); +} + +/******************************* + * Registers used in block bi. + */ + +void cgreg_used(unsigned bi,regm_t used) +{ + for (size_t j = 0; used; j++) + { if (used & 1) // if register j is used + vec_setbit(bi,regrange[j]); + used >>= 1; + } +} + +/************************* + * Run through a tree calculating symbol weights. + */ + +STATIC void el_weights(int bi,elem *e,unsigned weight) +{ + while (1) + { elem_debug(e); + + int op = e->Eoper; + if (!OTleaf(op)) + { + // This prevents variable references within common subexpressions + // from adding to the variable's usage count. + if (e->Ecount) + { + if (e->Ecomsub) + weight = 0; + else + e->Ecomsub = 1; + } + + if (OTbinary(op)) + { el_weights(bi,e->E2,weight); + if ((OTopeq(op) || OTpost(op)) && e->E1->Eoper == OPvar) + { + if (weight >= 10) + weight += 10; + else + weight++; + } + } + e = e->E1; + } + else + { + switch (op) + { + case OPvar: + Symbol *s = e->EV.sp.Vsym; + if (s->Ssymnum != -1 && s->Sflags & GTregcand) + { + s->Sweight += weight; + //printf("adding %d weight to '%s' (block %d, Ssymnum %d), giving Sweight %d\n",weight,s->Sident,bi,s->Ssymnum,s->Sweight); + if (weights) + WEIGHTS(bi,s->Ssymnum) += weight; + } + break; + } + return; + } + } +} + +/***************************************** + * Determine 'benefit' of assigning symbol s to register reg. + * Benefit is roughly the number of clocks saved. + * A negative value means that s cannot or should not be assigned to reg. + */ + +int cgreg_benefit(Symbol *s,int reg, Symbol *retsym) +{ + int benefit; + int benefit2; + block *b; + int bi; + int gotoepilog; + int retsym_cnt; + + //printf("cgreg_benefit(s = '%s', reg = %d)\n", s->Sident, reg); + + vec_sub(s->Slvreg,s->Srange,regrange[reg]); + int si = s->Ssymnum; + +Lagain: + //printf("again\n"); + benefit = 0; + retsym_cnt = 0; + + // Make sure we have enough uses to justify + // using a register we must save + if (fregsaved & mask[reg] & mfuncreg) + benefit -= 1 + nretblocks; + + foreach (bi,dfotop,s->Srange) + { int inoutp; + int inout; + + b = dfo[bi]; + switch (b->BC) + { + case BCjcatch: + case BCcatch: + case BC_except: + case BC_finally: + case BC_ret: + s->Sflags &= ~GTregcand; + goto Lcant; // can't assign to register + } + if (vec_testbit(bi,s->Slvreg)) + { benefit += WEIGHTS(bi,si); + //printf("WEIGHTS(%d,%d) = %d, benefit = %d\n",bi,si,WEIGHTS(bi,si),benefit); + inout = 1; + + if (s == retsym && (reg == AX || reg == XMM0) && b->BC == BCretexp) + { benefit += 1; + retsym_cnt++; + //printf("retsym, benefit = %d\n",benefit); + if (s->Sfl == FLreg && !vec_disjoint(s->Srange,regrange[reg])) + goto Lcant; // don't spill if already in register + } + } + else + inout = -1; + + // Look at predecessors to see if we need to load in/out of register + gotoepilog = 0; + L2: + inoutp = 0; + benefit2 = 0; + for (list_t bl = b->Bpred; bl; bl = list_next(bl)) + { + block *bp = list_block(bl); + int bpi = bp->Bdfoidx; + if (!vec_testbit(bpi,s->Srange)) + continue; + if (gotoepilog && bp->BC == BCgoto) + { + if (vec_testbit(bpi,s->Slvreg)) + { + if (inout == -1) + benefit2 -= bp->Bweight; // need to mov into mem + } + else + { + if (inout == 1) + benefit2 -= bp->Bweight; // need to mov into reg + } + } + else if (vec_testbit(bpi,s->Slvreg)) + { + switch (inoutp) + { + case 0: + inoutp = 1; + if (inout != 1) + { if (gotoepilog) + { vec_clearbit(bpi,s->Slvreg); + goto Lagain; + } + benefit2 -= b->Bweight; // need to mov into mem + } + break; + case 1: + break; + case -1: + if (gotoepilog == 0) + { gotoepilog = 1; + goto L2; + } + vec_clearbit(bpi,s->Slvreg); + goto Lagain; + } + } + else + { + switch (inoutp) + { + case 0: + inoutp = -1; + if (inout != -1) + { if (gotoepilog) + { vec_clearbit(bi,s->Slvreg); + goto Lagain; + } + benefit2 -= b->Bweight; // need to mov into reg + } + break; + case 1: + if (gotoepilog == 0) + { gotoepilog = 1; + goto L2; + } + if (inout == 1) + { vec_clearbit(bi,s->Slvreg); + goto Lagain; + } + goto Lcant; + case -1: + break; + } + } + } + //printf("benefit2 = %d\n", benefit2); + benefit += benefit2; + } + +#ifdef DEBUG + //printf("2weights: dfotop = %d, globsym.top = %d\n", dfotop, globsym.top); + if (benefit > s->Sweight + retsym_cnt) + printf("s = '%s', benefit = %d, Sweight = %d, retsym_cnt = x%x\n",s->Sident,benefit,s->Sweight, retsym_cnt); +#endif + assert(benefit <= s->Sweight + retsym_cnt); + return benefit; + +Lcant: + return -1; // can't assign to reg +} + +/********************************************* + * Determine if block gets symbol loaded by predecessor epilog (1), + * or by prolog (0). + */ + +int cgreg_gotoepilog(block *b,Symbol *s) +{ + int bi = b->Bdfoidx; + + int inout; + if (vec_testbit(bi,s->Slvreg)) + inout = 1; + else + inout = -1; + + // Look at predecessors to see if we need to load in/out of register + int gotoepilog = 0; + int inoutp = 0; + for (list_t bl = b->Bpred; bl; bl = list_next(bl)) + { + block *bp = list_block(bl); + int bpi = bp->Bdfoidx; + if (!vec_testbit(bpi,s->Srange)) + continue; + if (vec_testbit(bpi,s->Slvreg)) + { + switch (inoutp) + { + case 0: + inoutp = 1; + if (inout != 1) + { if (gotoepilog) + goto Lcant; + } + break; + case 1: + break; + case -1: + if (gotoepilog == 0) + { gotoepilog = 1; + goto Lret; + } + goto Lcant; + } + } + else + { + switch (inoutp) + { + case 0: + inoutp = -1; + if (inout != -1) + { if (gotoepilog) + goto Lcant; + } + break; + case 1: + if (gotoepilog == 0) + { gotoepilog = 1; + goto Lret; + } + goto Lcant; + case -1: + break; + } + } + } +Lret: + return gotoepilog; + +Lcant: + assert(0); + return -1; // can't assign to reg +} + +/********************************** + * Determine block prolog code - it's either + * assignments to register, or storing register back in memory. + */ + +void cgreg_spillreg_prolog(block *b,Symbol *s,code **pcstore,code **pcload) +{ + list_t bl; + code *cstore = *pcstore; + code *cload = *pcload; + int bi = b->Bdfoidx; + + //printf("cgreg_spillreg_prolog(block %d, s = '%s')\n",bi,s->Sident); + + int inoutp; + if (vec_testbit(bi,s->Slvreg)) + { inoutp = 1; + // If it's startblock, and it's a spilled parameter, we + // need to load it + if (s->Sflags & SFLspill && bi == 0 && + (s->Sclass == SCparameter || s->Sclass == SCfastpar)) + { + goto Lload; + } + } + else + inoutp = -1; + + if (cgreg_gotoepilog(b,s)) + return; + + // Look at predecessors to see if we need to load in/out of register + for (bl = b->Bpred; bl; bl = list_next(bl)) + { + { + block *bp = list_block(bl); + int bpi = bp->Bdfoidx; + + if (!vec_testbit(bpi,s->Srange)) + continue; + if (vec_testbit(bpi,s->Slvreg)) + { + if (inoutp != -1) + continue; + } + else + { + if (inoutp != 1) + continue; + } + } + +Lload: +#ifdef DEBUG + if (debugr) + { + int sz = type_size(s->Stype); + if (inoutp == -1) + printf("B%d: prolog moving %s into '%s'\n",bi,regstring[s->Sreglsw],s->Sident); + else + printf("B%d: prolog moving '%s' into %s:%s\n", + bi, s->Sident, regstring[s->Sregmsw], sz > REGSIZE ? regstring[s->Sreglsw] : ""); + } +#endif + + code* c = gen_spill_reg(s, inoutp == 1); + + if (inoutp == -1) + cstore = cat(cstore,c); + else + cload = cat(cload,c); + break; + } + + // Store old register values before loading in new ones + *pcstore = cstore; + *pcload = cload; +} + +/********************************** + * Determine block epilog code - it's either + * assignments to register, or storing register back in memory. + */ + +void cgreg_spillreg_epilog(block *b,Symbol *s,code **pcstore,code **pcload) +{ + code *cstore = *pcstore; + code *cload = *pcload; + int bi = b->Bdfoidx; + + //printf("cgreg_spillreg_epilog(block %d, s = '%s')\n",bi,s->Sident); + //assert(b->BC == BCgoto); + if (!cgreg_gotoepilog(list_block(b->Bsucc),s)) + return; + + int inoutp; + if (vec_testbit(bi,s->Slvreg)) + inoutp = 1; + else + inoutp = -1; + + // Look at successors to see if we need to load in/out of register + for (list_t bl = b->Bsucc; bl; bl = list_next(bl)) + { + block *bp = list_block(bl); + int bpi = bp->Bdfoidx; + if (!vec_testbit(bpi,s->Srange)) + continue; + if (vec_testbit(bpi,s->Slvreg)) + { + if (inoutp != -1) + continue; + } + else + { + if (inoutp != 1) + continue; + } + +#ifdef DEBUG + if (debugr) + { + if (inoutp == 1) + printf("B%d: epilog moving %s into '%s'\n",bi,regstring[s->Sreglsw],s->Sident); + else + printf("B%d: epilog moving '%s' into %s\n",bi,s->Sident,regstring[s->Sreglsw]); + } +#endif + + code* c = gen_spill_reg(s, inoutp == -1); + + if (inoutp == 1) + cstore = cat(cstore,c); + else + cload = cat(cload,c); + break; + } + + // Store old register values before loading in new ones + *pcstore = cstore; + *pcload = cload; +} + +/*************************** + * Map symbol s into registers [NOREG,reglsw] or [regmsw, reglsw]. + */ + +void cgreg_map(Symbol *s, unsigned regmsw, unsigned reglsw) +{ + //assert(I64 || reglsw < 8); + + if (vec_disjoint(s->Srange,regrange[reglsw]) && + (regmsw == NOREG || vec_disjoint(s->Srange,regrange[regmsw])) + ) + { + s->Sfl = FLreg; + vec_copy(s->Slvreg,s->Srange); + } + else + { + s->Sflags |= SFLspill; + + // Already computed by cgreg_benefit() + //vec_sub(s->Slvreg,s->Srange,regrange[reglsw]); + + if (s->Sfl == FLreg) // if reassigned + { + switch (s->Sclass) + { + case SCauto: + case SCregister: + case SCtmp: + case SCfastpar: + s->Sfl = FLauto; + break; + case SCbprel: + s->Sfl = FLbprel; + break; + case SCparameter: + s->Sfl = FLpara; + break; +#if PSEUDO_REGS + case SCpseudo: + s->Sfl = FLpseudo; + break; +#endif + case SCstack: + s->Sfl = FLstack; + break; + default: +#ifdef DEBUG + symbol_print(s); +#endif + assert(0); + } + } + } + s->Sreglsw = reglsw; + s->Sregm = mask[reglsw]; + mfuncreg &= ~mask[reglsw]; + if (regmsw != NOREG) + vec_subass(s->Slvreg,regrange[regmsw]); + vec_orass(regrange[reglsw],s->Slvreg); + + if (regmsw == NOREG) + { + #if DEBUG + if (debugr) + { + printf("symbol '%s' %s in register %s\n ", + s->Sident, + (s->Sflags & SFLspill) ? "spilled" : "put", + regstring[reglsw]); + vec_println(s->Slvreg); + } + #endif + } + else + { + assert(regmsw < 8); + s->Sregmsw = regmsw; + s->Sregm |= mask[regmsw]; + mfuncreg &= ~mask[regmsw]; + vec_orass(regrange[regmsw],s->Slvreg); + + #if DEBUG + if (debugr) + printf("symbol '%s' %s in register pair %s\n", + s->Sident, + (s->Sflags & SFLspill) ? "spilled" : "put", + regm_str(s->Sregm)); + #endif + } +} + +/******************************************** + * The register variables in this mask can not be in registers. + * "Unregister" them. + */ + +void cgreg_unregister(regm_t conflict) +{ + if (pass == PASSfinal) + pass = PASSreg; // have to codegen at least one more time + for (int i = 0; i < globsym.top; i++) + { symbol *s = globsym.tab[i]; + if (s->Sfl == FLreg && s->Sregm & conflict) + { + s->Sflags |= GTunregister; + } + } +} + +/****************************************** + * Do register assignments. + * Returns: + * !=0 redo code generation + * 0 no more register assignments + */ + +struct Reg // data for trial register assignment +{ + Symbol *sym; + int reglsw; + int regmsw; + int benefit; +}; + +int cgreg_assign(Symbol *retsym) +{ + int flag = FALSE; // assume no changes + + /* First do any 'unregistering' which might have happened in the last + * code gen pass. + */ + for (size_t si = 0; si < globsym.top; si++) + { symbol *s = globsym.tab[si]; + + if (s->Sflags & GTunregister) + { + #if DEBUG + if (debugr) + { + printf("symbol '%s' %s register %s\n ", + s->Sident, + (s->Sflags & SFLspill) ? "unspilled" : "unregistered", + regstring[s->Sreglsw]); + vec_println(s->Slvreg); + } + #endif + flag = TRUE; + s->Sflags &= ~(GTregcand | GTunregister | SFLspill); + if (s->Sfl == FLreg) + { + switch (s->Sclass) + { + case SCauto: + case SCregister: + case SCtmp: + case SCfastpar: + s->Sfl = FLauto; + break; + case SCbprel: + s->Sfl = FLbprel; + break; + case SCparameter: + s->Sfl = FLpara; + break; +#if PSEUDO_REGS + case SCpseudo: + s->Sfl = FLpseudo; + break; +#endif + case SCstack: + s->Sfl = FLstack; + break; + default: +#ifdef DEBUG + symbol_print(s); +#endif + assert(0); + } + } + } + } + + vec_t v = vec_calloc(dfotop); + + // Find symbol t, which is the most 'deserving' symbol that should be + // placed into a register. + Reg t; + t.sym = NULL; + t.benefit = 0; + for (size_t si = 0; si < globsym.top; si++) + { symbol *s = globsym.tab[si]; + + Reg u; + u.sym = s; + if (!(s->Sflags & GTregcand) || + s->Sflags & SFLspill || + // Keep trying to reassign retsym into AX + (s->Sfl == FLreg && !(s == retsym && s->Sregm != mAX && s->Sregm != mXMM0)) + ) + { + #ifdef DEBUG + if (debugr) + if (s->Sfl == FLreg) + printf("symbol '%s' is in reg %s\n",s->Sident,regm_str(s->Sregm)); + else if (s->Sflags & SFLspill) + printf("symbol '%s' spilled in reg %s\n",s->Sident,regm_str(s->Sregm)); + else if (!(s->Sflags & GTregcand)) + printf("symbol '%s' is not a reg candidate\n",s->Sident); + else + printf("symbol '%s' is not a candidate\n",s->Sident); + #endif + continue; + } + + tym_t ty = s->ty(); + unsigned sz = tysize(ty); + + #ifdef DEBUG + if (debugr) + { printf("symbol '%3s', ty x%x weight x%x sz %d\n ", + s->Sident,ty,s->Sweight,(int)sz); + vec_println(s->Srange); + } + #endif + + // Select sequence of registers to try to map s onto + char *pseq; // sequence to try for LSW + char *pseqmsw = NULL; // sequence to try for MSW, NULL if none + if (tyxmmreg(ty)) + { + static char sequence[] = {XMM0,XMM1,XMM2,XMM3,XMM4,XMM5,XMM6,XMM7,NOREG}; + pseq = sequence; + } + else if (I64) + { + if (sz == REGSIZE * 2) + { + static char seqmsw[] = {CX,DX,NOREG}; + static char seqlsw[] = {AX,BX,SI,DI,NOREG}; + pseq = seqlsw; + pseqmsw = seqmsw; + } + else + { // R10 is reserved for the static link + static char sequence[] = {AX,CX,DX,SI,DI,R8,R9,R11,BX,R12,R13,R14,R15,BP,NOREG}; + pseq = sequence; + } + } + else if (I32) + { + if (sz == REGSIZE * 2) + { + static char seqlsw[] = {AX,BX,SI,DI,NOREG}; + static char seqmsw[] = {CX,DX,NOREG}; + pseq = seqlsw; + pseqmsw = seqmsw; + } + else + { + static char sequence[] = {AX,CX,DX,BX,SI,DI,BP,NOREG}; + pseq = sequence; + } + } + else + { assert(I16); + if (typtr(ty)) + { + // For pointer types, try to pick index register first + static char seqidx[] = {BX,SI,DI,AX,CX,DX,BP,NOREG}; + pseq = seqidx; + } + else + { + // Otherwise, try to pick index registers last + static char sequence[] = {AX,CX,DX,BX,SI,DI,BP,NOREG}; + pseq = sequence; + } + } + + u.benefit = 0; + for (int i = 0; pseq[i] != NOREG; i++) + { + unsigned reg = pseq[i]; + + // Symbols used as return values should only be mapped into return value registers + if (s == retsym && !(mask[reg] & (mAX | mXMM0))) + continue; + + // If BP isn't available, can't assign to it + if (reg == BP && !(allregs & mBP)) + continue; + +#if 0 && TARGET_LINUX + // Need EBX for static pointer + if (reg == BX && !(allregs & mBX)) + continue; +#endif + + if (s->Sflags & GTbyte && + !(mask[reg] & BYTEREGS)) + continue; + + int benefit = cgreg_benefit(s,reg,retsym); + + #ifdef DEBUG + if (debugr) + { printf(" %s",regstring[reg]); + vec_print(regrange[reg]); + printf(" %d\n",benefit); + } + #endif + + if (benefit > u.benefit) + { // successful assigning of lsw + unsigned regmsw = NOREG; + + // Now assign MSW + if (pseqmsw) + { + for (unsigned regj = 0; 1; regj++) + { + regmsw = pseqmsw[regj]; + if (regmsw == NOREG) + goto Ltried; // tried and failed to assign MSW + if (regmsw == reg) // can't assign msw and lsw to same reg + continue; + #ifdef DEBUG + if (debugr) + { printf(".%s",regstring[regmsw]); + vec_println(regrange[regmsw]); + } + #endif + if (vec_disjoint(s->Slvreg,regrange[regmsw])) + break; + } + } + vec_copy(v,s->Slvreg); + u.benefit = benefit; + u.reglsw = reg; + u.regmsw = regmsw; + } +Ltried: ; + } + + if (u.benefit > t.benefit) + { t = u; + vec_copy(t.sym->Slvreg,v); + } + } + + if (t.sym && t.benefit > 0) + { + cgreg_map(t.sym,t.regmsw,t.reglsw); + flag = TRUE; + } + + /* See if any scratch registers have become available that we can use. + * Scratch registers are cheaper, as they don't need save/restore. + * All floating point registers are scratch registers, so no need + * to do this for them. + */ + if ((I32 || I64) && // not worth the bother for 16 bit code + !flag && // if haven't already assigned registers in this pass + (mfuncreg & ~fregsaved) & ALLREGS && // if unused non-floating scratch registers + !(funcsym_p->Sflags & SFLexit)) // don't need save/restore if function never returns + { + for (size_t si = 0; si < globsym.top; si++) + { symbol *s = globsym.tab[si]; + + if (s->Sfl == FLreg && // if assigned to register + mask[s->Sreglsw] & fregsaved && // and that register is not scratch + type_size(s->Stype) <= REGSIZE && // don't bother with register pairs + !tyfloating(s->ty())) // don't assign floating regs to non-floating regs + { + s->Sreglsw = findreg((mfuncreg & ~fregsaved) & ALLREGS); + s->Sregm = mask[s->Sreglsw]; + flag = TRUE; +#ifdef DEBUG + if (debugr) + printf("re-assigned '%s' to %s\n",s->Sident,regstring[s->Sreglsw]); +#endif + break; + } + } + } + vec_free(v); + + return flag; +} + +////////////////////////////////////// +// Qsort() comparison routine for array of pointers to Symbol's. + +static int __cdecl weight_compare(const void *e1,const void *e2) +{ Symbol **psp1; + Symbol **psp2; + + psp1 = (Symbol **)e1; + psp2 = (Symbol **)e2; + + return (*psp2)->Sweight - (*psp1)->Sweight; +} + + +#endif diff --git a/backend/cgsched.c b/backend/cgsched.c new file mode 100644 index 00000000..e3edf170 --- /dev/null +++ b/backend/cgsched.c @@ -0,0 +1,3206 @@ +// Copyright (C) 1995-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include + +#include "cc.h" +#include "el.h" +#include "code.h" +#include "oper.h" +#include "global.h" +#include "type.h" +#include "exh.h" +#include "list.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +// If we use Pentium Pro scheduler +#if 0 +#define PRO (config.target_scheduler >= TARGET_PentiumPro) +#else +#define PRO (config.target_cpu >= TARGET_PentiumPro) +#endif + +// Struct where we gather information about an instruction +struct Cinfo +{ + code *c; // the instruction + unsigned char pair; // pairing information + unsigned char sz; // operand size + unsigned char isz; // instruction size + + // For floating point scheduling + unsigned char fxch_pre; + unsigned char fxch_post; + unsigned char fp_op; + #define FPfstp 1 // FSTP mem + #define FPfld 2 // FLD mem + #define FPfop 3 // Fop ST0,mem or Fop ST0 + + unsigned char flags; +#define CIFLarraybounds 1 // this instruction is a jmp to array bounds +#define CIFLea 2 // this instruction has a memory-referencing + // modregrm EA byte +#define CIFLnostage 4 // don't stage these instructions +#define CIFLpush 8 // it's a push we can swap around + + unsigned r; // read mask + unsigned w; // write mask + unsigned a; // registers used in addressing mode + unsigned char reg; // reg field of modregrm byte + unsigned char uops; // Pentium Pro micro-ops + unsigned sibmodrm; // (sib << 8) + mod__rm byte + unsigned spadjust; // if !=0, then amount ESP changes as a result of this + // instruction being executed + int fpuadjust; // if !=0, then amount FPU stack changes as a result + // of this instruction being executed +#if DEBUG + void print(); // pretty-printer +#endif +}; + +code *simpleops(code *c,regm_t scratch); +code *schedule(code *c,regm_t scratch); +code *peephole(code *c,regm_t scratch); + +/***************************************** + * Do Pentium optimizations. + * Input: + * scratch scratch registers we can use + */ + +void cgsched_pentium(code **pc,regm_t scratch) +{ + //printf("scratch = x%02x\n",scratch); + if (config.target_scheduler >= TARGET_80486) + { + if (!I64) + *pc = peephole(*pc,0); + if (I32) // forget about 16 bit code + { + if (config.target_cpu == TARGET_Pentium || + config.target_cpu == TARGET_PentiumMMX) + *pc = simpleops(*pc,scratch); + *pc = schedule(*pc,0); + } + } +} + +void cgsched_block(block* b) +{ + if (config.flags4 & CFG4speed && + config.target_cpu >= TARGET_Pentium && + b->BC != BCasm) + { + regm_t scratch = allregs; + + scratch &= ~(b->Bregcon.used | b->Bregcon.params | mfuncreg); + scratch &= ~(b->Bregcon.immed.mval | b->Bregcon.cse.mval); + cgsched_pentium(&b->Bcode,scratch); + //printf("after schedule:\n"); WRcodlst(b->Bcode); + } +} + +#define NP 0 // not pairable +#define PU 1 // pairable in U only, never executed in V +#define PV 2 // pairable in V only +#define UV (PU|PV) // pairable in both U and V +#define PE 4 // register contention exception +#define PF 8 // flags contention exception +#define FX 0x10 // pairable with FXCH instruction + +static unsigned char pentcycl[256] = +{ + UV,UV,UV,UV, UV,UV,NP,NP, // 0 + UV,UV,UV,UV, UV,UV,NP,NP, // 8 + PU,PU,PU,PU, PU,PU,NP,NP, // 10 + PU,PU,PU,PU, PU,PU,NP,NP, // 18 + UV,UV,UV,UV, UV,UV,NP,NP, // 20 + UV,UV,UV,UV, UV,UV,NP,NP, // 28 + UV,UV,UV,UV, UV,UV,NP,NP, // 30 + UV,UV,UV,UV, UV,UV,NP,NP, // 38 + + UV,UV,UV,UV, UV,UV,UV,UV, // 40 + UV,UV,UV,UV, UV,UV,UV,UV, // 48 + PE|UV,PE|UV,PE|UV,PE|UV, PE|UV,PE|UV,PE|UV,PE|UV, // 50 PUSH reg + PE|UV,PE|UV,PE|UV,PE|UV, PE|UV,PE|UV,PE|UV,PE|UV, // 58 POP reg + NP,NP,NP,NP, NP,NP,NP,NP, // 60 + PE|UV,NP,PE|UV,NP, NP,NP,NP,NP, // 68 + PV|PF,PV|PF,PV|PF,PV|PF, PV|PF,PV|PF,PV|PF,PV|PF, // 70 Jcc rel8 + PV|PF,PV|PF,PV|PF,PV|PF, PV|PF,PV|PF,PV|PF,PV|PF, // 78 Jcc rel8 + + NP,NP,NP,NP, NP,NP,NP,NP, // 80 + UV,UV,UV,UV, NP,UV,NP,NP, // 88 + NP,NP,NP,NP, NP,NP,NP,NP, // 90 + NP,NP,NP,NP, NP,NP,NP,NP, // 98 + UV,UV,UV,UV, NP,NP,NP,NP, // A0 + UV,UV,NP,NP, NP,NP,NP,NP, // A8 + UV,UV,UV,UV, UV,UV,UV,UV, // B0 + UV,UV,UV,UV, UV,UV,UV,UV, // B8 + + NP,NP,NP,NP, NP,NP,NP,NP, // C0 + NP,NP,NP,NP, NP,NP,NP,NP, // C8 + PU,PU,NP,NP, NP,NP,NP,NP, // D0 + FX,NP,FX,FX, NP,NP,FX,NP, // D8 all floating point + NP,NP,NP,NP, NP,NP,NP,NP, // E0 + PE|PV,PV,NP,PV, NP,NP,NP,NP, // E8 + NP,NP,NP,NP, NP,NP,NP,NP, // F0 + NP,NP,NP,NP, NP,NP,NP,NP, // F8 +}; + +/******************************************** + * For each opcode, determine read [0] and written [1] masks. + */ + +#define EA 0x100000 +#define R 0x200000 // register (reg of modregrm field) +#define N 0x400000 // other things modified, not swappable +#define B 0x800000 // it's a byte operation +#define C 0x1000000 // floating point flags +#define mMEM 0x2000000 // memory +#define S 0x4000000 // floating point stack +#define F 0x8000000 // flags + +static unsigned oprw[256][2] = +{ + // 00 + EA|R|B, F|EA|B, // ADD + EA|R, F|EA, + EA|R|B, F|R|B, + EA|R, F|R, + mAX, F|mAX, + mAX, F|mAX, + N, N, // PUSH ES + N, N, // POP ES + + // 08 + EA|R|B, F|EA|B, // OR + EA|R, F|EA, + EA|R|B, F|R|B, + EA|R, F|R, + mAX, F|mAX, + mAX, F|mAX, + N, N, // PUSH CS + N, N, // 2 byte escape + + // 10 + F|EA|R|B,F|EA|B, // ADC + F|EA|R, F|EA, + F|EA|R|B,F|R|B, + F|EA|R, F|R, + F|mAX, F|mAX, + F|mAX, F|mAX, + N, N, // PUSH SS + N, N, // POP SS + + // 18 + F|EA|R|B,F|EA|B, // SBB + F|EA|R, F|EA, + F|EA|R|B,F|R|B, + F|EA|R, F|R, + F|mAX, F|mAX, + F|mAX, F|mAX, + N, N, // PUSH DS + N, N, // POP DS + + // 20 + EA|R|B, F|EA|B, // AND + EA|R, F|EA, + EA|R|B, F|R|B, + EA|R, F|R, + mAX, F|mAX, + mAX, F|mAX, + N, N, // SEG ES + F|mAX, F|mAX, // DAA + + // 28 + EA|R|B, F|EA|B, // SUB + EA|R, F|EA, + EA|R|B, F|R|B, + EA|R, F|R, + mAX, F|mAX, + mAX, F|mAX, + N, N, // SEG CS + F|mAX, F|mAX, // DAS + + // 30 + EA|R|B, F|EA|B, // XOR + EA|R, F|EA, + EA|R|B, F|R|B, + EA|R, F|R, + mAX, F|mAX, + mAX, F|mAX, + N, N, // SEG SS + F|mAX, F|mAX, // AAA + + // 38 + EA|R|B, F, // CMP + EA|R, F, + EA|R|B, F, + EA|R, F, + mAX, F, // CMP AL,imm8 + mAX, F, // CMP EAX,imm16/32 + N, N, // SEG DS + N, N, // AAS + + // 40 + mAX, F|mAX, // INC EAX + mCX, F|mCX, + mDX, F|mDX, + mBX, F|mBX, + mSP, F|mSP, + mBP, F|mBP, + mSI, F|mSI, + mDI, F|mDI, + + // 48 + mAX, F|mAX, // DEC EAX + mCX, F|mCX, + mDX, F|mDX, + mBX, F|mBX, + mSP, F|mSP, + mBP, F|mBP, + mSI, F|mSI, + mDI, F|mDI, + + // 50 + mAX|mSP, mSP|mMEM, // PUSH EAX + mCX|mSP, mSP|mMEM, + mDX|mSP, mSP|mMEM, + mBX|mSP, mSP|mMEM, + mSP|mSP, mSP|mMEM, + mBP|mSP, mSP|mMEM, + mSI|mSP, mSP|mMEM, + mDI|mSP, mSP|mMEM, + + // 58 + mSP|mMEM, mAX|mSP, // POP EAX + mSP|mMEM, mCX|mSP, + mSP|mMEM, mDX|mSP, + mSP|mMEM, mBX|mSP, + mSP|mMEM, mSP|mSP, + mSP|mMEM, mBP|mSP, + mSP|mMEM, mSI|mSP, + mSP|mMEM, mDI|mSP, + + // 60 + N, N, // PUSHA + N, N, // POPA + N, N, // BOUND Gv,Ma + N, N, // ARPL Ew,Rw + N, N, // SEG FS + N, N, // SEG GS + N, N, // operand size prefix + N, N, // address size prefix + + // 68 + mSP, mSP|mMEM, // PUSH immed16/32 + EA, F|R, // IMUL Gv,Ev,lv + mSP, mSP|mMEM, // PUSH immed8 + EA, F|R, // IMUL Gv,Ev,lb + N, N, // INSB Yb,DX + N, N, // INSW/D Yv,DX + N, N, // OUTSB DX,Xb + N, N, // OUTSW/D DX,Xv + + // 70 + F|N, N, + F|N, N, + F|N, N, + F|N, N, + F|N, N, + F|N, N, + F|N, N, + F|N, N, + + // 78 + F|N, N, + F|N, N, + F|N, N, + F|N, N, + F|N, N, + F|N, N, + F|N, N, + F|N, N, + + // 80 + N, N, + N, N, + N, N, + N, N, + EA|R, F, // TEST EA,r8 + EA|R, F, // TEST EA,r16/32 + EA|R, EA|R, // XCHG EA,r8 + EA|R, EA|R, // XCHG EA,r16/32 + + // 88 + R|B, EA|B, // MOV EA8,r8 + R, EA, // MOV EA,r16/32 + EA|B, R|B, // MOV r8,EA8 + EA, R, // MOV r16/32,EA + N, N, // MOV EA,segreg + EA, R, // LEA r16/32,EA + N, N, // MOV segreg,EA + mSP|mMEM, EA|mSP, // POP mem16/32 + + // 90 + 0, 0, // NOP + mAX|mCX, mAX|mCX, + mAX|mDX, mAX|mDX, + mAX|mBX, mAX|mBX, + mAX|mSP, mAX|mSP, + mAX|mBP, mAX|mBP, + mAX|mSI, mAX|mSI, + mAX|mDI, mAX|mDI, + + // 98 + mAX, mAX, // CBW + mAX, mDX, // CWD + N, N|F, // CALL far ptr + N, N, // WAIT + F|mSP, mSP|mMEM, // PUSHF + mSP|mMEM, F|mSP, // POPF + mAX, F, // SAHF + F, mAX, // LAHF + + // A0 + mMEM, mAX, // MOV AL,moffs8 + mMEM, mAX, // MOV EAX,moffs32 + mAX, mMEM, // MOV moffs8,AL + mAX, mMEM, // MOV moffs32,EAX + N, N, // MOVSB + N, N, // MOVSW/D + N, N, // CMPSB + N, N, // CMPSW/D + + // A8 + mAX, F, // TEST AL,imm8 + mAX, F, // TEST AX,imm16 + N, N, // STOSB + N, N, // STOSW/D + N, N, // LODSB + N, N, // LODSW/D + N, N, // SCASB + N, N, // SCASW/D + + // B0 + 0, mAX, // MOV AL,imm8 + 0, mCX, + 0, mDX, + 0, mBX, + 0, mAX, + 0, mCX, + 0, mDX, + 0, mBX, + + // B8 + 0, mAX, // MOV AX,imm16 + 0, mCX, + 0, mDX, + 0, mBX, + 0, mSP, + 0, mBP, + 0, mSI, + 0, mDI, + + // C0 + EA, F|EA, // Shift Eb,Ib + EA, F|EA, + N, N, + N, N, + N, N, + N, N, + 0, EA|B, // MOV EA8,imm8 + 0, EA, // MOV EA,imm16 + + // C8 + N, N, // ENTER + N, N, // LEAVE + N, N, // RETF lw + N, N, // RETF + N, N, // INT 3 + N, N, // INT lb + N, N, // INTO + N, N, // IRET + + // D0 + EA, F|EA, // Shift EA,1 + EA, F|EA, + EA|mCX, F|EA, // Shift EA,CL + EA|mCX, F|EA, + mAX, F|mAX, // AAM + mAX, F|mAX, // AAD + N, N, // reserved + mAX|mBX|mMEM, mAX, // XLAT + + // D8 + N, N, + N, N, + N, N, + N, N, + N, N, + N, N, + N, N, + N, N, + + // E0 + F|mCX|N,mCX|N, // LOOPNE jb + F|mCX|N,mCX|N, // LOOPE jb + mCX|N, mCX|N, // LOOP jb + mCX|N, N, // JCXZ jb + N, N, // IN AL,lb + N, N, // IN EAX,lb + N, N, // OUT lb,AL + N, N, // OUT lb,EAX + + // E8 + N, N|F, // CALL jv + N, N, // JMP Jv + N, N, // JMP Ab + N, N, // JMP jb + N|mDX, N|mAX, // IN AL,DX + N|mDX, N|mAX, // IN AX,DX + N|mAX|mDX,N, // OUT DX,AL + N|mAX|mDX,N, // OUT DX,AX + + // F0 + N, N, // LOCK + N, N, // reserved + N, N, // REPNE + N, N, // REP,REPE + N, N, // HLT + F, F, // CMC + N, N, + N, N, + + // F8 + 0, F, // CLC + 0, F, // STC + N, N, // CLI + N, N, // STI + N, N, // CLD + N, N, // STD + EA, F|EA, // INC/DEC + N, N, +}; + +/**************************************** + * Same thing, but for groups. + */ + +static unsigned grprw[8][8][2] = +{ + // Grp 1 + EA, F|EA, // ADD + EA, F|EA, // OR + F|EA, F|EA, // ADC + F|EA, F|EA, // SBB + EA, F|EA, // AND + EA, F|EA, // SUB + EA, F|EA, // XOR + EA, F, // CMP + + // Grp 3 + EA, F, // TEST EA,imm + N, N, // reserved + EA, EA, // NOT + EA, F|EA, // NEG + mAX|EA, F|mAX|mDX, // MUL + mAX|EA, F|mAX|mDX, // IMUL + mAX|mDX|EA, F|mAX|mDX, // DIV +#if 0 + // Could generate an exception we want to catch + mAX|mDX|EA|N, F|mAX|mDX|N, // IDIV +#else + mAX|mDX|EA, F|mAX|mDX, // IDIV +#endif + + // Grp 5 + EA, F|EA, // INC Ev + EA, F|EA, // DEC Ev + N|EA, N, // CALL Ev + N|EA, N, // CALL eP + N|EA, N, // JMP Ev + N|EA, N, // JMP Ep + mSP|EA, mSP|mMEM, // PUSH Ev + N, N, // reserved + + // Grp 3, byte version + EA|B, F, // TEST EA,imm + N, N, // reserved + EA|B, EA|B, // NOT + EA|B, F|EA|B, // NEG + mAX|EA, F|mAX, // MUL + mAX|EA, F|mAX, // IMUL + mAX|EA, F|mAX, // DIV +#if 0 + // Could generate an exception we want to catch + mAX|EA|N, F|mAX|N, // IDIV +#else + mAX|EA, F|mAX, // IDIV +#endif + +}; + +/******************************************** + * For floating point opcodes 0xD8..0xDF, with Irm < 0xC0. + * [][][0] = read + * [1] = write + */ + +static unsigned grpf1[8][8][2] = +{ + // 0xD8 + EA|S, S|C, // FADD float + EA|S, S|C, // FMUL float + EA|S, C, // FCOM float + EA|S, S|C, // FCOMP float + EA|S, S|C, // FSUB float + EA|S, S|C, // FSUBR float + EA|S, S|C, // FDIV float + EA|S, S|C, // FDIVR float + + // 0xD9 + EA, S|C, // FLD float + N, N, // + S, EA|C, // FST float + S, EA|S|C, // FSTP float + N, N, // FLDENV + N, N, // FLDCW + N, N, // FSTENV + N, N, // FSTCW + + // 0xDA + EA|S, S|C, // FIADD long + EA|S, S|C, // FIMUL long + EA|S, C, // FICOM long + EA|S, S|C, // FICOMP long + EA|S, S|C, // FISUB long + EA|S, S|C, // FISUBR long + EA|S, S|C, // FIDIV long + EA|S, S|C, // FIDIVR long + + // 0xDB + EA, S|C, // FILD long + S, EA|S|C, // FISTTP int + S, EA|C, // FIST long + S, EA|S|C, // FISTP long + N, N, // + EA, S|C, // FLD real80 + N, N, // + S, EA|S|C, // FSTP real80 + + // 0xDC + EA|S, S|C, // FADD double + EA|S, S|C, // FMUL double + EA|S, C, // FCOM double + EA|S, S|C, // FCOMP double + EA|S, S|C, // FSUB double + EA|S, S|C, // FSUBR double + EA|S, S|C, // FDIV double + EA|S, S|C, // FDIVR double + + // 0xDD + EA, S|C, // FLD double + S, EA|S|C, // FISTTP long + S, EA|C, // FST double + S, EA|S|C, // FSTP double + N, N, // FRSTOR + N, N, // + N, N, // FSAVE + C, EA, // FSTSW + + // 0xDE + EA|S, S|C, // FIADD short + EA|S, S|C, // FIMUL short + EA|S, C, // FICOM short + EA|S, S|C, // FICOMP short + EA|S, S|C, // FISUB short + EA|S, S|C, // FISUBR short + EA|S, S|C, // FIDIV short + EA|S, S|C, // FIDIVR short + + // 0xDF + EA, S|C, // FILD short + S, EA|S|C, // FISTTP short + S, EA|C, // FIST short + S, EA|S|C, // FISTP short + EA, S|C, // FBLD packed BCD + EA, S|C, // FILD long long + S, EA|S|C, // FBSTP packed BCD + S, EA|S|C, // FISTP long long +}; + + +/******************************************** + * Micro-ops for floating point opcodes 0xD8..0xDF, with Irm < 0xC0. + */ + +static unsigned char uopsgrpf1[8][8] = +{ + // 0xD8 + 2, // FADD float + 2, // FMUL float + 2, // FCOM float + 2, // FCOMP float + 2, // FSUB float + 2, // FSUBR float + 2, // FDIV float + 2, // FDIVR float + + // 0xD9 + 1, // FLD float + 0, // + 2, // FST float + 2, // FSTP float + 5, // FLDENV + 3, // FLDCW + 5, // FSTENV + 5, // FSTCW + + // 0xDA + 5, // FIADD long + 5, // FIMUL long + 5, // FICOM long + 5, // FICOMP long + 5, // FISUB long + 5, // FISUBR long + 5, // FIDIV long + 5, // FIDIVR long + + // 0xDB + 4, // FILD long + 0, // + 4, // FIST long + 4, // FISTP long + 0, // + 4, // FLD real80 + 0, // + 5, // FSTP real80 + + // 0xDC + 2, // FADD double + 2, // FMUL double + 2, // FCOM double + 2, // FCOMP double + 2, // FSUB double + 2, // FSUBR double + 2, // FDIV double + 2, // FDIVR double + + // 0xDD + 1, // FLD double + 0, // + 2, // FST double + 2, // FSTP double + 5, // FRSTOR + 0, // + 5, // FSAVE + 5, // FSTSW + + // 0xDE + 5, // FIADD short + 5, // FIMUL short + 5, // FICOM short + 5, // FICOMP short + 5, // FISUB short + 5, // FISUBR short + 5, // FIDIV short + 5, // FIDIVR short + + // 0xDF + 4, // FILD short + 0, // + 4, // FIST short + 4, // FISTP short + 5, // FBLD packed BCD + 4, // FILD long long + 5, // FBSTP packed BCD + 4, // FISTP long long +}; + +/************************************************** + * Determine number of micro-ops for Pentium Pro and Pentium II processors. + * 0 means special case, + * 5 means 'complex' + */ + +static const unsigned char insuops[256] = +{ 0,0,0,0, 1,1,4,5, /* 00 */ + 0,0,0,0, 1,1,4,0, /* 08 */ + 0,0,0,0, 2,2,4,5, /* 10 */ + 0,0,0,0, 2,2,4,5, /* 18 */ + 0,0,0,0, 1,1,0,1, /* 20 */ + 0,0,0,0, 1,1,0,1, /* 28 */ + 0,0,0,0, 1,1,0,1, /* 30 */ + 0,0,0,0, 1,1,0,1, /* 38 */ + 1,1,1,1, 1,1,1,1, /* 40 */ + 1,1,1,1, 1,1,1,1, /* 48 */ + 3,3,3,3, 3,3,3,3, /* 50 */ + 2,2,2,2, 3,2,2,2, /* 58 */ + 5,5,5,5, 0,0,0,0, /* 60 */ + 3,3,0,0, 5,5,5,5, /* 68 */ + 1,1,1,1, 1,1,1,1, /* 70 */ + 1,1,1,1, 1,1,1,1, /* 78 */ + 0,0,0,0, 0,0,0,0, /* 80 */ + 0,0,0,0, 0,1,4,0, /* 88 */ + 1,3,3,3, 3,3,3,3, /* 90 */ + 1,1,5,0, 5,5,1,1, /* 98 */ + 1,1,2,2, 5,5,5,5, /* A0 */ + 1,1,3,3, 2,2,3,3, /* A8 */ + 1,1,1,1, 1,1,1,1, /* B0 */ + 1,1,1,1, 1,1,1,1, /* B8 */ + 0,0,5,4, 0,0,0,0, /* C0 */ + 5,3,5,5, 5,3,5,5, /* C8 */ + 0,0,0,0, 4,3,0,2, /* D0 */ + 0,0,0,0, 0,0,0,0, /* D8 */ + 4,4,4,2, 5,5,5,5, /* E0 */ + 4,1,5,1, 5,5,5,5, /* E8 */ + 0,0,5,5, 5,1,0,0, /* F0 */ + 1,1,5,5, 4,4,0,0, /* F8 */ +}; + +static unsigned char uopsx[8] = { 1,1,2,5,1,1,1,5 }; + +/************************************************ + * Determine number of micro-ops for Pentium Pro and Pentium II processors. + * 5 means 'complex'. + * Doesn't currently handle: + * floating point + * MMX + * 0F opcodes + * prefix bytes + */ + +STATIC int uops(code *c) +{ int n; + int op; + int op2; + + op = c->Iop & 0xFF; + if ((c->Iop & 0xFF00) == 0x0F00) + op = 0x0F; + n = insuops[op]; + if (!n) // if special case + { unsigned char irm,mod,reg,rm; + + irm = c->Irm; + mod = (irm >> 6) & 3; + reg = (irm >> 3) & 7; + rm = irm & 7; + + switch (op) + { + case 0x10: + case 0x11: // ADC rm,r + case 0x18: + case 0x19: // SBB rm,r + n = (mod == 3) ? 2 : 4; + break; + + case 0x12: + case 0x13: // ADC r,rm + case 0x1A: + case 0x1B: // SBB r,rm + n = (mod == 3) ? 2 : 3; + break; + + case 0x00: + case 0x01: // ADD rm,r + case 0x08: + case 0x09: // OR rm,r + case 0x20: + case 0x21: // AND rm,r + case 0x28: + case 0x29: // SUB rm,r + case 0x30: + case 0x31: // XOR rm,r + n = (mod == 3) ? 1 : 4; + break; + + case 0x02: + case 0x03: // ADD r,rm + case 0x0A: + case 0x0B: // OR r,rm + case 0x22: + case 0x23: // AND r,rm + case 0x2A: + case 0x2B: // SUB r,rm + case 0x32: + case 0x33: // XOR r,rm + case 0x38: + case 0x39: // CMP rm,r + case 0x3A: + case 0x3B: // CMP r,rm + case 0x69: // IMUL rm,r,imm + case 0x6B: // IMUL rm,r,imm8 + case 0x84: + case 0x85: // TEST rm,r + n = (mod == 3) ? 1 : 2; + break; + + case 0x80: + case 0x81: + case 0x82: + case 0x83: + if (reg == 2 || reg == 3) // ADC/SBB rm,imm + n = (mod == 3) ? 2 : 4; + else if (reg == 7) // CMP rm,imm + n = (mod == 3) ? 1 : 2; + else + n = (mod == 3) ? 1 : 4; + break; + + case 0x86: + case 0x87: // XCHG rm,r + n = (mod == 3) ? 3 : 5; + break; + + case 0x88: + case 0x89: // MOV rm,r + n = (mod == 3) ? 1 : 2; + break; + + case 0x8A: + case 0x8B: // MOV r,rm + n = 1; + break; + + case 0x8C: // MOV Sreg,rm + n = (mod == 3) ? 1 : 3; + break; + + case 0x8F: + if (reg == 0) // POP m + n = 5; + break; + + case 0xC6: + case 0xC7: + if (reg == 0) // MOV rm,imm + n = (mod == 3) ? 1 : 2; + break; + + case 0xD0: + case 0xD1: + if (reg == 2 || reg == 3) // RCL/RCR rm,1 + n = (mod == 3) ? 2 : 4; + else + n = (mod == 3) ? 1 : 4; + break; + + case 0xC0: + case 0xC1: // RCL/RCR rm,imm8 + case 0xD2: + case 0xD3: + if (reg == 2 || reg == 3) // RCL/RCR rm,CL + n = 5; + else + n = (mod == 3) ? 1 : 4; + break; + + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + // Floating point opcodes + if (irm < 0xC0) + { n = uopsgrpf1[op - 0xD8][reg]; + break; + } + n = uopsx[op - 0xD8]; + switch (op) + { + case 0xD9: + switch (irm) + { + case 0xE0: // FCHS + n = 3; + break; + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + n = 2; + break; + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF8: + case 0xF9: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + n = 5; + break; + } + break; + case 0xDE: + if (irm == 0xD9) // FCOMPP + n = 2; + break; + } + break; + + case 0xF6: + if (reg == 6 || reg == 7) // DIV AL,rm8 + n = (mod == 3) ? 3 : 4; + else if (reg == 4 || reg == 5 || reg == 0) // MUL/IMUL/TEST rm8 + n = (mod == 3) ? 1 : 2; + else if (reg == 2 || reg == 3) // NOT/NEG rm + n = (mod == 3) ? 1 : 4; + break; + + case 0xF7: + if (reg == 6 || reg == 7) // DIV EAX,rm + n = 4; + else if (reg == 4 || reg == 5) // MUL/IMUL rm + n = (mod == 3) ? 3 : 4; + else if (reg == 2 || reg == 3) // NOT/NEG rm + n = (mod == 3) ? 1 : 4; + break; + + case 0xFF: + if (reg == 2 || reg == 3 || // CALL rm, CALL m,rm + reg == 5) // JMP seg:offset + n = 5; + else if (reg == 4) + n = (mod == 3) ? 1 : 2; + else if (reg == 0 || reg == 1) // INC/DEC rm + n = (mod == 3) ? 1 : 4; + else if (reg == 6) // PUSH rm + n = (mod == 3) ? 3 : 4; + break; + + case 0x0F: + op2 = c->Iop & 0xFF; + if ((op2 & 0xF0) == 0x80) // Jcc + { n = 1; + break; + } + if ((op2 & 0xF0) == 0x90) // SETcc + { n = (mod == 3) ? 1 : 3; + break; + } + if (op2 == 0xB6 || op2 == 0xB7 || // MOVZX + op2 == 0xBE || op2 == 0xBF) // MOVSX + { n = 1; + break; + } + if (op2 == 0xAF) // IMUL r,m + { n = (mod == 3) ? 1 : 2; + break; + } + break; + } + } + if (n == 0) + n = 5; // copout for now + return n; +} + +/****************************************** + * Determine pairing classification. + * Don't deal with floating point, just assume they are all NP (Not Pairable). + * Returns: + * NP,UV,PU,PV optionally OR'd with PE + */ + +STATIC int pair_class(code *c) +{ unsigned char op; + unsigned char irm,mod,reg,rm; + unsigned a32; + int pc; + + // Of course, with Intel this is *never* simple, and Intel's + // documentation is vague about the specifics. + + op = c->Iop & 0xFF; + if ((c->Iop & 0xFF00) == 0x0F00) + op = 0x0F; + pc = pentcycl[op]; + a32 = I32; + if (c->Iflags & CFaddrsize) + a32 ^= 1; + irm = c->Irm; + mod = (irm >> 6) & 3; + reg = (irm >> 3) & 7; + rm = irm & 7; + switch (op) + { + case 0x0F: // 2 byte opcode + if ((c->Iop & 0xF0) == 0x80) // if Jcc + pc = PV | PF; + break; + + case 0x80: + case 0x81: + case 0x83: + if (reg == 2 || // ADC EA,immed + reg == 3) // SBB EA,immed + { pc = PU; + goto L2; + } + goto L1; // AND/OR/XOR/ADD/SUB/CMP EA,immed + + case 0x84: + case 0x85: // TEST EA,reg + if (mod == 3) // TEST reg,reg + pc = UV; + break; + + case 0xC0: + case 0xC1: + if (reg >= 4) + pc = PU; + break; + + case 0xC6: + case 0xC7: + if (reg == 0) // MOV EA,immed + { + L1: + pc = UV; + L2: + // if EA contains a displacement then + // can't execute in V, or pair in U + switch (mod) + { case 0: + if (a32) + { if (rm == 5 || + (rm == 4 && (c->Isib & 7) == 5) + ) + pc = NP; + } + else if (rm == 6) + pc = NP; + break; + case 1: + case 2: + pc = NP; + break; + } + } + break; + + case 0xD9: + if (irm < 0xC0) + { + if (reg == 0) + pc = FX; + } + else if (irm < 0xC8) + pc = FX; + else if (irm < 0xD0) + pc = PV; + else + { + switch (irm) + { + case 0xE0: + case 0xE1: + case 0xE4: + pc = FX; + break; + } + } + break; + + case 0xDB: + if (irm < 0xC0 && (reg == 0 || reg == 5)) + pc = FX; + break; + + case 0xDD: + if (irm < 0xC0) + { + if (reg == 0) + pc = FX; + } + else if (irm >= 0xE0 && irm < 0xF0) + pc = FX; + break; + + case 0xDF: + if (irm < 0xC0 && (reg == 0 || reg == 5)) + pc = FX; + break; + + case 0xFE: + if (reg == 0 || reg == 1) // INC/DEC EA + pc = UV; + break; + case 0xFF: + if (reg == 0 || reg == 1) // INC/DEC EA + pc = UV; + else if (reg == 2 || reg == 4) // CALL/JMP near ptr EA + pc = PE|PV; + else if (reg == 6 && mod == 3) // PUSH reg + pc = PE | UV; + break; + } + if (c->Iflags & CFPREFIX && pc == UV) // if prefix byte + pc = PU; + return pc; +} + +/****************************************** + * For an instruction, determine what is read + * and what is written, and what is used for addressing. + * Determine operand size if EA (larger is ok). + */ + +STATIC void getinfo(Cinfo *ci,code *c) +{ + memset(ci,0,sizeof(Cinfo)); + if (!c) + return; + ci->c = c; + + if (PRO) + { + ci->uops = uops(c); + ci->isz = calccodsize(c); + } + else + ci->pair = pair_class(c); + + unsigned char op; + unsigned char op2; + unsigned char irm,mod,reg,rm; + unsigned a32; + int pc; + unsigned r,w; + int sz = I32 ? 4 : 2; + + ci->r = 0; + ci->w = 0; + ci->a = 0; + op = c->Iop & 0xFF; + if ((c->Iop & 0xFF00) == 0x0F00) + op = 0x0F; + //printf("\tgetinfo %x, op %x \n",c,op); + pc = pentcycl[op]; + a32 = I32; + if (c->Iflags & CFaddrsize) + a32 ^= 1; + if (c->Iflags & CFopsize) + sz ^= 2 | 4; + irm = c->Irm; + mod = (irm >> 6) & 3; + reg = (irm >> 3) & 7; + rm = irm & 7; + + r = oprw[op][0]; + w = oprw[op][1]; + + switch (op) + { + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x55: + case 0x56: + case 0x57: // PUSH reg + ci->flags |= CIFLpush; + case 0x54: // PUSH ESP + case 0x6A: // PUSH imm8 + case 0x68: // PUSH imm + case 0x0E: + case 0x16: + case 0x1E: + case 0x06: + case 0x9C: + Lpush: + ci->spadjust = -sz; + ci->a |= mSP; + break; + + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: // POP reg + case 0x1F: + case 0x07: + case 0x17: + case 0x9D: // POPF + Lpop: + ci->spadjust = sz; + ci->a |= mSP; + break; + + case 0x80: + if (reg == 7) // CMP + c->Iflags |= CFpsw; + r = B | grprw[0][reg][0]; // Grp 1 (byte) + w = B | grprw[0][reg][1]; + break; + + case 0x81: + case 0x83: + if (reg == 7) // CMP + c->Iflags |= CFpsw; + else if (irm == modregrm(3,0,SP)) // ADD ESP,imm + { + assert(c->IFL2 == FLconst); + ci->spadjust = (op == 0x81) ? c->IEV2.Vint : (signed char)c->IEV2.Vint; + } + else if (irm == modregrm(3,5,SP)) // SUB ESP,imm + { + assert(c->IFL2 == FLconst); + ci->spadjust = (op == 0x81) ? -c->IEV2.Vint : -(signed char)c->IEV2.Vint; + } + r = grprw[0][reg][0]; // Grp 1 + w = grprw[0][reg][1]; + break; + + case 0x8F: + if (reg == 0) // POP rm + goto Lpop; + break; + + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + // Fake having an EA to simplify code in conflict() + ci->flags |= CIFLea; + ci->reg = 0; + ci->sibmodrm = a32 ? modregrm(0,0,5) : modregrm(0,0,6); + c->IFL1 = c->IFL2; + c->IEV1 = c->IEV2; + break; + + case 0xC2: + case 0xC3: + case 0xCA: + case 0xCB: // RET + ci->a |= mSP; + break; + + case 0xE8: + if (c->Iflags & CFclassinit) // call to __j_classinit + { r = 0; + w = F; +#if CLASSINIT2 + ci->pair = UV; // it is patched to CMP EAX,0 +#else + ci->pair = NP; +#endif + } + break; + + case 0xF6: + r = grprw[3][reg][0]; // Grp 3, byte version + w = grprw[3][reg][1]; + break; + + case 0xF7: + r = grprw[1][reg][0]; // Grp 3 + w = grprw[1][reg][1]; + break; + + case 0x0F: + op2 = c->Iop & 0xFF; + if ((op2 & 0xF0) == 0x80) // if Jxx instructions + { + ci->r = F | N; + ci->w = N; + goto Lret; + } + ci->r = N; + ci->w = N; // copout for now + goto Lret; + + case 0xD7: // XLAT + ci->a = mAX | mBX; + break; + + case 0xFF: + r = grprw[2][reg][0]; // Grp 5 + w = grprw[2][reg][1]; + if (reg == 6) // PUSH rm + goto Lpush; + break; + + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: // CMP AL,imm8 + case 0x3D: // CMP EAX,imm32 + // For CMP opcodes, always test for flags + c->Iflags |= CFpsw; + break; + + case ESCAPE: + if (c->Iop == (ESCAPE | ESCadjfpu)) + ci->fpuadjust = c->IEV1.Vint; + break; + + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xC0: + case 0xC1: + if (reg == 2 || reg == 3) // if RCL or RCR + c->Iflags |= CFpsw; // always test for flags + break; + + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + if (irm < 0xC0) + { r = grpf1[op - 0xD8][reg][0]; + w = grpf1[op - 0xD8][reg][1]; + switch (op) + { + case 0xD8: + if (reg == 3) // if FCOMP + ci->fpuadjust = -1; + else + ci->fp_op = FPfop; + break; + + case 0xD9: + if (reg == 0) // if FLD float + { ci->fpuadjust = 1; + ci->fp_op = FPfld; + } + else if (reg == 3) // if FSTP float + { ci->fpuadjust = -1; + ci->fp_op = FPfstp; + } + else if (reg == 5 || reg == 7) + sz = 2; + else if (reg == 4 || reg == 6) + sz = 28; + break; + case 0xDA: + if (reg == 3) // if FICOMP + ci->fpuadjust = -1; + break; + case 0xDB: + if (reg == 0 || reg == 5) + { ci->fpuadjust = 1; + ci->fp_op = FPfld; // FILD / FLD long double + } + if (reg == 3 || reg == 7) + ci->fpuadjust = -1; + if (reg == 7) + ci->fp_op = FPfstp; // FSTP long double + if (reg == 5 || reg == 7) + sz = 10; + break; + case 0xDC: + sz = 8; + if (reg == 3) // if FCOMP + ci->fpuadjust = -1; + else + ci->fp_op = FPfop; + break; + case 0xDD: + if (reg == 0) // if FLD double + { ci->fpuadjust = 1; + ci->fp_op = FPfld; + } + if (reg == 3) // if FSTP double + { ci->fpuadjust = -1; + ci->fp_op = FPfstp; + } + if (reg == 7) + sz = 2; + else if (reg == 4 || reg == 6) + sz = 108; + else + sz = 8; + break; + case 0xDE: + sz = 2; + if (reg == 3) // if FICOMP + ci->fpuadjust = -1; + break; + case 0xDF: + sz = 2; + if (reg == 4 || reg == 6) + sz = 10; + else if (reg == 5 || reg == 7) + sz = 8; + if (reg == 0 || reg == 4 || reg == 5) + ci->fpuadjust = 1; + else if (reg == 3 || reg == 6 || reg == 7) + ci->fpuadjust = -1; + break; + } + break; + } + else if (op == 0xDE) + { ci->fpuadjust = -1; // pop versions of Fop's + if (irm == 0xD9) + ci->fpuadjust = -2; // FCOMPP + } + + // Most floating point opcodes aren't staged, but are + // sent right through, in order to make use of the large + // latencies with floating point instructions. + if (ci->fp_op == FPfld || + (op == 0xD9 && (irm & 0xF8) == 0xC0)) + ; // FLD ST(i) + else + ci->flags |= CIFLnostage; + + switch (op) + { + case 0xD8: + r = S; + w = C; + if ((irm & ~7) == 0xD0) + w |= S; + break; + case 0xD9: + // FCHS or FABS or FSQRT + if (irm == 0xE0 || irm == 0xE1 || irm == 0xFA) + ci->fp_op = FPfop; + r = S; + w = S|C; + break; + case 0xDA: + if (irm == 0xE9) // FUCOMPP + { r = S; + w = S|C; + break; + } + break; + case 0xDB: + if (irm == 0xE2) // FCLEX + { r = 0; + w = C; + break; + } + if (irm == 0xE3) // FINIT + { r = 0; + w = S|C; + break; + } + break; + case 0xDC: + case 0xDE: + if ((irm & 0xF0) != 0xD0) + { r = S; + w = S|C; + break; + } + break; + case 0xDD: + // Not entirely correct, but conservative + r = S; + w = S|C; + break; + case 0xDF: + if (irm == 0xE0) // FSTSW AX + { r = C; + w = mAX; + break; + } + break; + } + break; +#if DEBUG + default: + //printf("\t\tNo special case\n"); + break; +#endif + } + + if ((r | w) & B) // if byte operation + sz = 1; // operand size is 1 + + ci->r = r & ~(R | EA); + ci->w = w & ~(R | EA); + if (r & R) + ci->r |= mask[(r & B) ? (reg & 3) : reg]; + if (w & R) + ci->w |= mask[(w & B) ? (reg & 3) : reg]; + + // OR in bits for EA addressing mode + if ((r | w) & EA) + { unsigned char sib; + + sib = 0; + switch (mod) + { + case 0: + if (a32) + { + if (rm == 4) + { sib = c->Isib; + if ((sib & modregrm(0,7,0)) != modregrm(0,4,0)) + ci->a |= mask[(sib >> 3) & 7]; // index register + if ((sib & 7) != 5) + ci->a |= mask[sib & 7]; // base register + } + else if (rm != 5) + ci->a |= mask[rm]; + } + else + { static unsigned char ea16[8] = {mBX|mSI,mBX|mDI,mBP|mSI,mBP|mDI,mSI,mDI,0,mBX}; + ci->a |= ea16[rm]; + } + goto Lmem; + + case 1: + case 2: + if (a32) + { + if (rm == 4) + { sib = c->Isib; + if ((sib & modregrm(0,7,0)) != modregrm(0,4,0)) + ci->a |= mask[(sib >> 3) & 7]; // index register + ci->a |= mask[sib & 7]; // base register + } + else + ci->a |= mask[rm]; + } + else + { static unsigned char ea16[8] = {mBX|mSI,mBX|mDI,mBP|mSI,mBP|mDI,mSI,mDI,mBP,mBX}; + ci->a |= ea16[rm]; + } + + Lmem: + if (r & EA) + ci->r |= mMEM; + if (w & EA) + ci->w |= mMEM; + ci->flags |= CIFLea; + break; + + case 3: + if (r & EA) + ci->r |= mask[(r & B) ? (rm & 3) : rm]; + if (w & EA) + ci->w |= mask[(w & B) ? (rm & 3) : rm]; + break; + } + // Adjust sibmodrm so that addressing modes can be compared simply + irm &= modregrm(3,0,7); + if (a32) + { + if (irm != modregrm(0,0,5)) + { + switch (mod) + { case 0: + if ((sib & 7) != 5) // if not disp32[index] + { c->IFL1 = FLconst; + c->IEVpointer1 = 0; + irm |= 0x80; + } + break; + case 1: + c->IEVpointer1 = (signed char) c->IEVpointer1; + irm = modregrm(2,0,rm); + break; + } + } + } + else + { + if (irm != modregrm(0,0,6)) + { + switch (mod) + { case 0: + c->IFL1 = FLconst; + c->IEVpointer1 = 0; + irm |= 0x80; + break; + case 1: + c->IEVpointer1 = (signed char) c->IEVpointer1; + irm = modregrm(2,0,rm); + break; + } + } + } + + ci->r |= ci->a; + ci->reg = reg; + ci->sibmodrm = (sib << 8) | irm; + } +Lret: + if (ci->w & mSP) // if stack pointer is modified + ci->w |= mMEM; // then we are implicitly writing to memory + if (op == 0x8D) // if LEA + ci->r &= ~mMEM; // memory is not actually read + ci->sz = sz; +#if DEBUG + //printf("\t\t"); ci->print(); +#endif +} + +/****************************************** + * Determine if two instructions can pair. + * Assume that in general, cu can pair in the U pipe and cv in the V. + * Look for things like register contentions. + * Input: + * cu instruction for U pipe + * cv instruction for V pipe + * Returns: + * !=0 if they can pair + */ + +STATIC int pair_test(Cinfo *cu,Cinfo *cv) +{ unsigned pcu; + unsigned pcv; + unsigned r1,w1; + unsigned r2,w2; + unsigned x; + + pcu = cu->pair; + if (!(pcu & PU)) + { + // See if pairs with FXCH and cv is FXCH + if (pcu & FX && cv->c->Iop == 0xD9 && (cv->c->Irm & ~7) == 0xC8) + goto Lpair; + goto Lnopair; + } + pcv = cv->pair; + if (!(pcv & PV)) + goto Lnopair; + + r1 = cu->r; + w1 = cu->w; + r2 = cv->r; + w2 = cv->w; + + x = w1 & (r2 | w2) & ~(F|mMEM); // register contention + if (x && // if register contention + !(x == mSP && pcu & pcv & PE) // and not exception + ) + goto Lnopair; + + // Look for flags contention + if (w1 & r2 & F && !(pcv & PF)) + goto Lnopair; + +Lpair: + return 1; + +Lnopair: + return 0; +} + +/****************************************** + * Determine if two instructions have an AGI or register contention. + * Returns: + * !=0 if they have an AGI + */ + +STATIC int pair_agi(Cinfo *c1,Cinfo *c2) +{ unsigned x; + + x = c1->w & c2->a; + return x && !(x == mSP && c1->pair & c2->pair & PE); +} + +/******************************************** + * Determine if three instructions can decode simultaneously + * in Pentium Pro and Pentium II. + * Input: + * c0,c1,c2 candidates for decoders 0,1,2 + * c2 can be NULL + * Returns: + * !=0 if they can decode simultaneously + */ + +STATIC int triple_test(Cinfo *c0,Cinfo *c1,Cinfo *c2) +{ int c2isz; + + assert(c0); + if (!c1) + goto Lnopair; + c2isz = c2 ? c2->isz : 0; + if (c0->isz > 7 || c1->isz > 7 || c2isz > 7 || + c0->isz + c1->isz + c2isz > 16) + goto Lnopair; + + // 4-1-1 decode + if (c1->uops > 1 || + (c2 && c2->uops > 1)) + goto Lnopair; + +Lpair: + return 1; + +Lnopair: + return 0; +} + +/******************************************** + * Get next instruction worth looking at for scheduling. + * Returns: + * NULL no more instructions + */ + +STATIC code * cnext(code *c) +{ + while (1) + { + c = code_next(c); + if (!c) + break; + if (c->Iflags & (CFtarg | CFtarg2)) + break; + if (!(c->Iop == NOP || + c->Iop == (ESCAPE | ESClinnum))) + break; + } + return c; +} + +/****************************************** + * Instruction scheduler. + * Input: + * c list of instructions to schedule + * scratch scratch registers we can use + * Returns: + * revised list of scheduled instructions + */ + +/////////////////////////////////// +// Determine if c1 and c2 are swappable. +// c1 comes before c2. +// If they do not conflict +// return 0 +// If they do conflict +// return 0x100 + delay_clocks +// Input: +// fpsched if 1, then adjust fxch_pre and fxch_post to swap, +// then return 0 +// if 2, then adjust ci1 as well as ci2 + +STATIC int conflict(Cinfo *ci1,Cinfo *ci2,int fpsched) +{ + code *c1; + code *c2; + unsigned r1,w1,a1; + unsigned r2,w2,a2; + int sz1,sz2; + int i = 0; + int delay_clocks; + + c1 = ci1->c; + c2 = ci2->c; + + //printf("conflict %x %x\n",c1,c2); + + r1 = ci1->r; + w1 = ci1->w; + a1 = ci1->a; + sz1 = ci1->sz; + + r2 = ci2->r; + w2 = ci2->w; + a2 = ci2->a; + sz2 = ci2->sz; + + //printf("r1 %lx w1 %lx a1 %lx sz1 %x\n",r1,w1,a1,sz1); + //printf("r2 %lx w2 %lx a2 %lx sz2 %x\n",r2,w2,a2,sz2); + + if ((c1->Iflags | c2->Iflags) & CFvolatile) + goto Lconflict; + + // Determine if we should handle FPU register conflicts separately + //if (fpsched) printf("fp_op %d,%d:\n",ci1->fp_op,ci2->fp_op); + if (fpsched && ci1->fp_op && ci2->fp_op) + { + w1 &= ~(S|C); + r1 &= ~(S|C); + w2 &= ~(S|C); + r2 &= ~(S|C); + } + else + fpsched = 0; + + if ((r1 | r2) & N) + { + goto Lconflict; + } + +#if 0 + if (c1->Iop == 0xFF && c2->Iop == 0x8B) + { c1->print(); c2->print(); i = 1; + printf("r1=%lx, w1=%lx, a1=%lx, sz1=%d, r2=%lx, w2=%lx, a2=%lx, sz2=%d\n",r1,w1,a1,sz1,r2,w2,a2,sz2); + } +#endif +L1: + if (w1 & r2 || (r1 | w1) & w2) + { unsigned char ifl1,ifl2; + +if (i) printf("test\n"); + +#if 0 +if (c1->IFL1 != c2->IFL1) printf("t1\n"); +if ((c1->Irm & modregrm(3,0,7)) != (c2->Irm & modregrm(3,0,7))) printf("t2\n"); +if ((issib(c1->Irm) && c1->Isib != c2->Isib)) printf("t3\n"); +if (c1->IEVpointer1 + sz1 <= c2->IEVpointer1) printf("t4\n"); +if (c2->IEVpointer1 + sz2 <= c1->IEVpointer1) printf("t5\n"); +#endif + +#if 1 // make sure CFpsw is reliably set + if (w1 & w2 & F && // if both instructions write to flags + w1 != F && + w2 != F && + !((r1 | r2) & F) && // but neither instruction reads them + !((c1->Iflags | c2->Iflags) & CFpsw)) // and we don't care about flags + { + w1 &= ~F; + w2 &= ~F; // remove conflict + goto L1; // and try again + } +#endif + // If other than the memory reference is a conflict + if (w1 & r2 & ~mMEM || (r1 | w1) & w2 & ~mMEM) + { if (i) printf("\t1\n"); + if (i) printf("r1=%x, w1=%x, a1=%x, sz1=%d, r2=%x, w2=%x, a2=%x, sz2=%d\n",r1,w1,a1,sz1,r2,w2,a2,sz2); + goto Lconflict; + } + + // If referring to distinct types, then no dependency + if (c1->Irex && c2->Irex && c1->Irex != c2->Irex) + goto Lswap; + + ifl1 = c1->IFL1; + ifl2 = c2->IFL1; + + // Special case: Allow indexed references using registers other than + // ESP and EBP to be swapped with PUSH instructions + if (((c1->Iop & ~7) == 0x50 || // PUSH reg + c1->Iop == 0x6A || // PUSH imm8 + c1->Iop == 0x68 || // PUSH imm16/imm32 + (c1->Iop == 0xFF && ci1->reg == 6) // PUSH EA + ) && + ci2->flags & CIFLea && !(a2 & mSP) && + !(a2 & mBP && (long)c2->IEVpointer1 < 0) + ) + { + if (c1->Iop == 0xFF) + { + if (!(w2 & mMEM)) + goto Lswap; + } + else + goto Lswap; + } + + // Special case: Allow indexed references using registers other than + // ESP and EBP to be swapped with PUSH instructions + if (((c2->Iop & ~7) == 0x50 || // PUSH reg + c2->Iop == 0x6A || // PUSH imm8 + c2->Iop == 0x68 || // PUSH imm16/imm32 + (c2->Iop == 0xFF && ci2->reg == 6) // PUSH EA + ) && + ci1->flags & CIFLea && !(a1 & mSP) && + !(a2 & mBP && (long)c2->IEVpointer1 < 0) + ) + { + if (c2->Iop == 0xFF) + { + if (!(w1 & mMEM)) + goto Lswap; + } + else + goto Lswap; + } + + // If not both an EA addressing mode, conflict + if (!(ci1->flags & ci2->flags & CIFLea)) + { if (i) printf("\t2\n"); + goto Lconflict; + } + + if (ci1->sibmodrm == ci2->sibmodrm) + { if (ifl1 != ifl2) + goto Lswap; + switch (ifl1) + { + case FLconst: + if (c1->IEV1.Vint != c2->IEV1.Vint && + (c1->IEV1.Vint + sz1 <= c2->IEV1.Vint || + c2->IEV1.Vint + sz2 <= c1->IEV1.Vint)) + goto Lswap; + break; + case FLdatseg: + if (c1->IEVseg1 != c2->IEVseg1 || + c1->IEV1.Vint + sz1 <= c2->IEV1.Vint || + c2->IEV1.Vint + sz2 <= c1->IEV1.Vint) + goto Lswap; + break; + } + } + + if ((c1->Iflags | c2->Iflags) & CFunambig && + (ifl1 != ifl2 || + ci1->sibmodrm != ci2->sibmodrm || + (c1->IEV1.Vint != c2->IEV1.Vint && + (c1->IEV1.Vint + sz1 <= c2->IEV1.Vint || + c2->IEV1.Vint + sz2 <= c1->IEV1.Vint) + ) + ) + ) + { + // Assume that [EBP] and [ESP] can point to the same location + if (((a1 | a2) & (mBP | mSP)) == (mBP | mSP)) + goto Lconflict; + goto Lswap; + } + + if (i) printf("\t3\n"); + goto Lconflict; + } + +Lswap: + if (fpsched) + { unsigned char a1,b1; + unsigned char a2,b2; + + //printf("\tfpsched %d,%d:\n",ci1->fp_op,ci2->fp_op); + a1 = ci1->fxch_pre; + b1 = ci1->fxch_post; + a2 = ci2->fxch_pre; + b2 = ci2->fxch_post; + + #define X(a,b) ((a << 8) | b) + switch (X(ci1->fp_op,ci2->fp_op)) + { + case X(FPfstp,FPfld): + if (a1 || b1) + goto Lconflict; + if (a2) + goto Lconflict; + if (b2 == 0) + ci2->fxch_post++; + else if (b2 == 1) + { + ci2->fxch_pre++; + ci2->fxch_post++; + } + else + { + goto Lconflict; + } + break; + + case X(FPfstp,FPfop): + if (a1 || b1) + goto Lconflict; + ci2->fxch_pre++; + ci2->fxch_post++; + break; + + case X(FPfop,FPfop): + if (a1 == 0 && b1 == 1 && a2 == 0 && b2 == 0) + { ci2->fxch_pre = 1; + ci2->fxch_post = 1; + break; + } + if (a1 == 0 && b1 == 0 && a2 == 1 && b2 == 1) + break; + goto Lconflict; + + case X(FPfop,FPfld): + if (a1 || b1) + goto Lconflict; + if (a2) + goto Lconflict; + if (b2) + break; + else if (fpsched == 2) + ci1->fxch_post = 1; + ci2->fxch_post = 1; + break; + + default: + goto Lconflict; + } + #undef X + //printf("\tpre = %d, post = %d\n",ci2->fxch_pre,ci2->fxch_post); + } + + //printf("w1 = x%x, w2 = x%x\n",w1,w2); + if (i) printf("no conflict\n\n"); + return 0; + +Lconflict: + //printf("r1=%x, w1=%x, r2=%x, w2=%x\n",r1,w1,r2,w2); + delay_clocks = 0; + + // Determine if AGI + if (!PRO && pair_agi(ci1,ci2)) + delay_clocks = 1; + + // Special delays for floating point + if (fpsched) + { if (ci1->fp_op == FPfld && ci2->fp_op == FPfstp) + delay_clocks = 1; + else if (ci1->fp_op == FPfop && ci2->fp_op == FPfstp) + delay_clocks = 3; + else if (ci1->fp_op == FPfop && ci2->fp_op == FPfop) + delay_clocks = 2; + } + else if (PRO) + { + // Look for partial register write stalls + if (w1 & r2 & ALLREGS && sz1 < sz2) + delay_clocks = 7; + } + else if ((w1 | r1) & (w2 | r2) & (C | S)) + { int reg; + int op; + + op = c1->Iop; + reg = c1->Irm & modregrm(0,7,0); + if (ci1->fp_op == FPfld || + (op == 0xD9 && (c1->Irm & 0xF8) == 0xC0) + ) + ; // FLD + else if (op == 0xD9 && (c1->Irm & 0xF8) == 0xC8) + ; // FXCH + else if (c2->Iop == 0xD9 && (c2->Irm & 0xF8) == 0xC8) + ; // FXCH + else + delay_clocks = 3; + } + + if (i) printf("conflict %d\n\n",delay_clocks); + return 0x100 + delay_clocks; +} + +struct Schedule +{ + #define TBLMAX (2*3*20) // must be divisible by both 2 and 3 + // (U,V pipe in Pentium, 3 decode units + // in Pentium Pro) + + Cinfo *tbl[TBLMAX]; // even numbers are U pipe, odd numbers are V + int tblmax; // max number of slots used + + Cinfo cinfo[TBLMAX]; + int cinfomax; + + list_t stagelist; // list of instructions in staging area + + int fpustackused; // number of slots in FPU stack that are used + + void initialize(int fpustackinit); // initialize scheduler + int stage(code *c); // stage instruction + int insert(Cinfo *ci); // insert c into schedule + code **assemble(code **pc); // reassemble scheduled instructions +}; + +/****************************** + */ + +void Schedule::initialize(int fpustackinit) +{ + //printf("Schedule::initialize(fpustackinit = %d)\n", fpustackinit); + memset(this,0,sizeof(Schedule)); + fpustackused = fpustackinit; +} + +/****************************** + */ + +code **Schedule::assemble(code **pc) +{ int i; + list_t l; + code *c; + +#ifdef DEBUG + if (debugs) printf("assemble:\n"); +#endif + assert(!*pc); + + // Try to insert the rest of the staged instructions + for (l = stagelist; l; l = list_next(l)) + { Cinfo *ci; + + ci = (Cinfo *)list_ptr(l); + if (!insert(ci)) + break; + } + + // Get the instructions out of the schedule table + assert((unsigned)tblmax <= TBLMAX); + for (i = 0; i < tblmax; i++) + { Cinfo *ci; + + ci = tbl[i]; +#ifdef DEBUG + if (debugs) + { + if (PRO) + { static char tbl[3][4] = { "0 "," 1 "," 2" }; + + if (ci) + printf("%s %d ",tbl[i - ((i / 3) * 3)],ci->uops); + else + printf("%s ",tbl[i - ((i / 3) * 3)]); + } + else + { + printf((i & 1) ? " V " : "U "); + } + if (ci) + ci->c->print(); + else + printf("\n"); + } +#endif + if (!ci) + continue; + fpustackused += ci->fpuadjust; + //printf("stage()1: fpustackused = %d\n", fpustackused); + c = ci->c; + if (i == 0) + c->Iflags |= CFtarg; // by definition, first is always a jump target + else + c->Iflags &= ~CFtarg; // the rest are not + + // Put in any FXCH prefix + if (ci->fxch_pre) + { code *cf; + assert(i); + cf = gen2(NULL,0xD9,0xC8 + ci->fxch_pre); + *pc = cf; + pc = &code_next(cf); + } + + *pc = c; + do + { + assert(*pc != code_next(*pc)); + pc = &code_next(*pc); + } while (*pc); + + // Put in any FXCH postfix + if (ci->fxch_post) + { int j; + + for (j = i + 1; j < tblmax; j++) + { if (tbl[j]) + { if (tbl[j]->fxch_pre == ci->fxch_post) + { + tbl[j]->fxch_pre = 0; // they cancel each other out + goto L1; + } + break; + } + } + { code *cf; + cf = gen2(NULL,0xD9,0xC8 + ci->fxch_post); + *pc = cf; + pc = &code_next(cf); + } + } + L1: ; + } + + // Just append any instructions left in the staging area + for (; l; l = list_next(l)) + { Cinfo *ci = (Cinfo *)list_ptr(l); + code *c = ci->c; + +#ifdef DEBUG + if (debugs) { printf("appending: "); c->print(); } +#endif + *pc = c; + do + { + pc = &code_next(*pc); + + } while (*pc); + fpustackused += ci->fpuadjust; + //printf("stage()2: fpustackused = %d\n", fpustackused); + } + list_free(&stagelist); + + return pc; +} + +/****************************** + * Insert c into scheduling table. + * Returns: + * 0 could not be scheduled; have to start a new one + */ + +int Schedule::insert(Cinfo *ci) +{ code *c; + int clocks; + int i; + int ic = 0; + int imin; + targ_size_t offset; + targ_size_t vpointer; + int movesp = 0; + int reg2 = -1; // avoid "may be uninitialized" warning + + //printf("insert "); ci->c->print(); + //printf("insert() %d\n", fpustackused); + c = ci->c; + //printf("\tc->Iop %x\n",c->Iop); + vpointer = c->IEVpointer1; + assert((unsigned)tblmax <= TBLMAX); + if (tblmax == TBLMAX) // if out of space + goto Lnoinsert; + if (tblmax == 0) // if table is empty + { // Just stuff it in the first slot + i = tblmax; + goto Linsert; + } + else if (c->Iflags & (CFtarg | CFtarg2)) + // Jump targets can only be first in the scheduler + goto Lnoinsert; + + // Special case of: + // PUSH reg1 + // MOV reg2,x[ESP] + if (c->Iop == 0x8B && + (c->Irm & modregrm(3,0,7)) == modregrm(1,0,4) && + c->Isib == modregrm(0,4,SP) && + c->IFL1 == FLconst && + ((signed char)c->IEVpointer1) >= REGSIZE + ) + { + movesp = 1; // this is a MOV reg2,offset[ESP] + offset = (signed char)c->IEVpointer1; + reg2 = (c->Irm >> 3) & 7; + } + + + // Start at tblmax, and back up until we get a conflict + ic = -1; + imin = 0; + for (i = tblmax; i >= 0; i--) + { Cinfo *cit; + + cit = tbl[i]; + if (!cit) + continue; + + // Look for special case swap + if (movesp && + (cit->c->Iop & ~7) == 0x50 && // if PUSH reg1 + (cit->c->Iop & 7) != reg2 && // if reg1 != reg2 + ((signed char)c->IEVpointer1) >= -cit->spadjust + ) + { + c->IEVpointer1 += cit->spadjust; + //printf("\t1, spadjust = %d, ptr = x%x\n",cit->spadjust,c->IEVpointer1); + continue; + } + + if (movesp && + cit->c->Iop == 0x83 && + cit->c->Irm == modregrm(3,5,SP) && // if SUB ESP,offset + cit->c->IFL2 == FLconst && + ((signed char)c->IEVpointer1) >= -cit->spadjust + ) + { + //printf("\t2, spadjust = %d\n",cit->spadjust); + c->IEVpointer1 += cit->spadjust; + continue; + } + + clocks = conflict(cit,ci,1); + if (clocks) + { int j; + + ic = i; // where the conflict occurred + clocks &= 0xFF; // convert to delay count + + // Move forward the delay clocks + if (clocks == 0) + j = i + 1; + else if (PRO) + j = (((i + 3) / 3) * 3) + clocks * 3; + else + { j = ((i + 2) & ~1) + clocks * 2; + + // It's possible we skipped over some AGI generating + // instructions due to movesp. + int k; + for (k = i + 1; k < j; k++) + { + if (k >= TBLMAX) + goto Lnoinsert; + if (tbl[k] && pair_agi(tbl[k],ci)) + { + k = ((k + 2) & ~1) + 1; + } + } + j = k; + } + + if (j >= TBLMAX) // exceed table size? + goto Lnoinsert; + imin = j; // first possible slot c can go in + break; + } + } + + + // Scan forward looking for a hole to put it in + for (i = imin; i < TBLMAX; i++) + { + if (tbl[i]) + { + // In case, due to movesp, we skipped over some AGI instructions + if (!PRO && pair_agi(tbl[i],ci)) + { + i = ((i + 2) & ~1) + 1; + if (i >= TBLMAX) + goto Lnoinsert; + } + } + else + { + if (PRO) + { int i0 = (i / 3) * 3; // index of decode unit 0 + Cinfo *ci0; + + assert(((TBLMAX / 3) * 3) == TBLMAX); + switch (i - i0) + { + case 0: // i0 can handle any instruction + goto Linsert; + case 1: + ci0 = tbl[i0]; + if (ci->uops > 1) + { + if (i0 >= imin && ci0->uops == 1) + goto L1; + i++; + break; + } + if (triple_test(ci0,ci,tbl[i0 + 2])) + goto Linsert; + break; + case 2: + ci0 = tbl[i0]; + if (ci->uops > 1) + { + if (i0 >= imin && ci0->uops == 1) + { + if (i >= tblmax) + { if (i + 1 >= TBLMAX) + goto Lnoinsert; + tblmax = i + 1; + } + tbl[i0 + 2] = tbl[i0 + 1]; + tbl[i0 + 1] = ci0; + i = i0; + goto Linsert; + } + break; + } + if (triple_test(ci0,tbl[i0 + 1],ci)) + goto Linsert; + break; + default: + assert(0); + } + } + else + { + assert((TBLMAX & 1) == 0); + if (i & 1) // if V pipe + { + if (pair_test(tbl[i - 1],ci)) + { + goto Linsert; + } + else if (i > imin && pair_test(ci,tbl[i - 1])) + { + L1: + tbl[i] = tbl[i - 1]; + if (i >= tblmax) + tblmax = i + 1; + i--; + //printf("\tswapping with x%02x\n",tbl[i + 1]->c->Iop); + goto Linsert; + } + } + else // will always fit in U pipe + { + assert(!tbl[i + 1]); // because V pipe should be empty + goto Linsert; + } + } + } + } + +Lnoinsert: + //printf("\tnoinsert\n"); + c->IEVpointer1 = vpointer; // reset to original value + return 0; + +Linsert: + // Insert at location i + assert(i < TBLMAX); + assert(tblmax <= TBLMAX); + tbl[i] = ci; + //printf("\tinsert at location %d\n",i); + + // If it's a scheduled floating point code, we have to adjust + // the FXCH values + if (ci->fp_op) + { int j; + + ci->fxch_pre = 0; + ci->fxch_post = 0; // start over again + + int fpu = fpustackused; + for (j = 0; j < tblmax; j++) + { + if (tbl[j]) + { + fpu += tbl[j]->fpuadjust; + if (fpu >= 8) // if FPU stack overflow + { tbl[i] = NULL; + //printf("fpu stack overflow\n"); + goto Lnoinsert; + } + } + } + + for (j = tblmax; j > i; j--) + { + if (j < TBLMAX && tbl[j]) + conflict(tbl[j],ci,2); + } + } + + if (movesp) + { // Adjust [ESP] offsets + int j; + + //printf("\tic = %d, inserting at %d\n",ic,i); + assert((unsigned)tblmax <= TBLMAX); + for (j = ic + 1; j < i; j++) + { Cinfo *cit; + + cit = tbl[j]; + if (cit) + { + c->IEVpointer1 -= cit->spadjust; + //printf("\t3, spadjust = %d, ptr = x%x\n",cit->spadjust,c->IEVpointer1); + } + } + } + if (i >= tblmax) + tblmax = i + 1; + + // Now do a hack. Look back at immediately preceding instructions, + // and see if we can swap with a push. + if (0 && movesp) + { int j; + + while (1) + { + for (j = 1; i > j; j++) + if (tbl[i - j]) + break; + + if (i >= j && tbl[i - j] && + (tbl[i - j]->c->Iop & ~7) == 0x50 && // if PUSH reg1 + (tbl[i - j]->c->Iop & 7) != reg2 && // if reg1 != reg2 + (signed char)c->IEVpointer1 >= REGSIZE) + { + //printf("\t-4 prec, i-j=%d, i=%d\n",i-j,i); + assert((unsigned)i < TBLMAX); + assert((unsigned)(i - j) < TBLMAX); + tbl[i] = tbl[i - j]; + tbl[i - j] = ci; + i -= j; + c->IEVpointer1 -= REGSIZE; + } + else + break; + } + } + + //printf("\tinsert\n"); + return 1; +} + + +/****************************** + * Insert c into staging area. + * Returns: + * 0 could not be scheduled; have to start a new one + */ + +int Schedule::stage(code *c) +{ Cinfo *ci; + list_t l; + list_t ln; + int agi; + + //printf("stage: "); c->print(); + if (cinfomax == TBLMAX) // if out of space + goto Lnostage; + ci = &cinfo[cinfomax++]; + getinfo(ci,c); + + if (c->Iflags & (CFtarg | CFtarg2 | CFvolatile)) + { + // Insert anything in stagelist + for (l = stagelist; l; l = ln) + { Cinfo *cs; + + ln = list_next(l); + cs = (Cinfo *)list_ptr(l); + if (!insert(cs)) + return 0; + list_subtract(&stagelist,cs); + } + return insert(ci); + } + + // Look through stagelist, and insert any AGI conflicting instructions + agi = 0; + for (l = stagelist; l; l = ln) + { Cinfo *cs; + + ln = list_next(l); + cs = (Cinfo *)list_ptr(l); + if (pair_agi(cs,ci)) + { + if (!insert(cs)) + goto Lnostage; + list_subtract(&stagelist,cs); + agi = 1; // we put out an AGI + } + } + + // Look through stagelist, and insert any other conflicting instructions + for (l = stagelist; l; l = ln) + { Cinfo *cs; + + ln = list_next(l); + cs = (Cinfo *)list_ptr(l); + if (conflict(cs,ci,0) && // if conflict + !(cs->flags & ci->flags & CIFLpush)) + { + if (cs->spadjust) + { + // We need to insert all previous adjustments to ESP + list_t la,lan; + + for (la = stagelist; la != l; la = lan) + { Cinfo *ca; + + lan = list_next(la); + ca = (Cinfo *)list_ptr(la); + if (ca->spadjust) + { if (!insert(ca)) + goto Lnostage; + list_subtract(&stagelist,ca); + } + } + } + + if (!insert(cs)) + goto Lnostage; + list_subtract(&stagelist,cs); + } + } + + // If floating point opcode, don't stage it, send it right out + if (!agi && ci->flags & CIFLnostage) + { + if (!insert(ci)) + goto Lnostage; + return 1; + } + + list_append(&stagelist,ci); // append to staging list + return 1; + +Lnostage: + return 0; +} + +/******************************************** + * Snip off tail of instruction sequence. + * Returns: + * next instruction (the tail) or + * NULL for no more instructions + */ + +STATIC code * csnip(code *c) +{ code **pc; + unsigned iflags; + + if (c) + { iflags = c->Iflags & CFclassinit; + while (1) + { + pc = &code_next(c); + c = *pc; + if (!c) + break; + if (c->Iflags & (CFtarg | CFtarg2)) + break; + if (!(c->Iop == NOP || + c->Iop == (ESCAPE | ESClinnum) || + c->Iflags & iflags)) + break; + } + *pc = NULL; + } + return c; +} + + +/****************************** + * Schedule Pentium instructions, + * based on Steve Russell's algorithm. + */ + +code *schedule(code *c,regm_t scratch) +{ + code *cresult = NULL; + code **pctail = &cresult; + Schedule sch; + + sch.initialize(0); // initialize scheduling table + while (c) + { + if ((c->Iop == NOP || + ((c->Iop & 0xFF) == ESCAPE && c->Iop != (ESCAPE | ESCadjfpu)) || + c->Iflags & CFclassinit) && + !(c->Iflags & (CFtarg | CFtarg2))) + { code *cn; + + // Just append this instruction to pctail and go to the next one + *pctail = c; + cn = code_next(c); + code_next(c) = NULL; + pctail = &code_next(c); + c = cn; + continue; + } + + //printf("init\n"); + sch.initialize(sch.fpustackused); // initialize scheduling table + + while (c) + { + //printf("insert %p\n",c); + if (!sch.stage(c)) // store c in scheduling table + break; + c = csnip(c); + } + + //printf("assem %d\n",sch.tblmax); + pctail = sch.assemble(pctail); // reassemble instruction stream + } + + return cresult; +} + +/**************************************************************************/ + +/******************************************** + * Replace any occurrence of r1 in EA with r2. + */ + +STATIC void repEA(code *c,unsigned r1,unsigned r2) +{ + unsigned mod,reg,rm; + unsigned rmn; + + rmn = c->Irm; + mod = rmn & 0xC0; + reg = rmn & modregrm(0,7,0); + rm = rmn & 7; + + if (mod == 0xC0 && rm == r1) + ; //c->Irm = mod | reg | r2; + else if (is32bitaddr(I32,c->Iflags) && + // If not disp32 + (rmn & modregrm(3,0,7)) != modregrm(0,0,5)) + { + if (rm == 4) + { // SIB byte addressing + unsigned sib; + unsigned base; + unsigned index; + + sib = c->Isib; + base = sib & 7; + index = (sib >> 3) & 7; + if (base == r1 && + !(r1 == 5 && mod == 0) && + !(r2 == 5 && mod == 0) + ) + base = r2; + if (index == r1) + index = r2; + c->Isib = (sib & 0xC0) | (index << 3) | base; + } + else if (rm == r1) + { + if (r1 == BP && r2 == SP) + { // Replace [EBP] with [ESP] + c->Irm = mod | reg | 4; + c->Isib = modregrm(0,4,SP); + } + else if (r2 == BP && mod == 0) + { + c->Irm = modregrm(1,0,0) | reg | r2; + c->IFL1 = FLconst; + c->IEV1.Vint = 0; + } + else + c->Irm = mod | reg | r2; + } + } +} + +/****************************************** + * Instruction scheduler. + * Input: + * c list of instructions to schedule + * scratch scratch registers we can use + * Returns: + * revised list of scheduled instructions + */ + +/****************************************** + * Swap c1 and c2. + * c1 comes before c2. + * Swap in place to not disturb addresses of jmp targets + */ + +STATIC void code_swap(code *c1,code *c2) +{ code cs; + + // Special case of: + // PUSH reg1 + // MOV reg2,x[ESP] + //printf("code_swap(%x, %x)\n",c1,c2); + if ((c1->Iop & ~7) == 0x50 && + c2->Iop == 0x8B && + (c2->Irm & modregrm(3,0,7)) == modregrm(1,0,4) && + c2->Isib == modregrm(0,4,SP) && + c2->IFL1 == FLconst && + ((signed char)c2->IEVpointer1) >= REGSIZE && + (c1->Iop & 7) != ((c2->Irm >> 3) & 7) + ) + c2->IEVpointer1 -= REGSIZE; + + + cs = *c2; + *c2 = *c1; + *c1 = cs; + // Retain original CFtarg + c1->Iflags = (c1->Iflags & ~(CFtarg | CFtarg2)) | (c2->Iflags & (CFtarg | CFtarg2)); + c2->Iflags = (c2->Iflags & ~(CFtarg | CFtarg2)) | (cs.Iflags & (CFtarg | CFtarg2)); + + c1->next = c2->next; + c2->next = cs.next; +} + +code *peephole(code *cstart,regm_t scratch) +{ + // Look for cases of: + // MOV r1,r2 + // OP ?,r1 + // we can replace with: + // MOV r1,r2 + // OP ?,r2 + // to improve pairing + code *c; + code *c1; + unsigned r1,r2; + unsigned mod,reg,rm; + + //printf("peephole\n"); + for (c = cstart; c; c = c1) + { unsigned char rmi; + unsigned char rmn; + + //c->print(); + c1 = cnext(c); + Ln: + if (!c1) + break; + if (c1->Iflags & (CFtarg | CFtarg2)) + continue; + + // Do: + // PUSH reg + if (I32 && (c->Iop & ~7) == 0x50) + { unsigned reg = c->Iop & 7; + + // MOV [ESP],reg => NOP + if (c1->Iop == 0x8B && + c1->Irm == modregrm(0,reg,4) && + c1->Isib == modregrm(0,4,SP)) + { c1->Iop = NOP; + continue; + } + + // PUSH [ESP] => PUSH reg + if (c1->Iop == 0xFF && + c1->Irm == modregrm(0,6,4) && + c1->Isib == modregrm(0,4,SP)) + { c1->Iop = 0x50 + reg; + continue; + } + + // CMP [ESP],imm => CMP reg,i,, + if (c1->Iop == 0x83 && + c1->Irm == modregrm(0,7,4) && + c1->Isib == modregrm(0,4,SP)) + { c1->Irm = modregrm(3,7,reg); + if (c1->IFL2 == FLconst && (signed char)c1->IEV2.Vuns == 0) + { // to TEST reg,reg + c1->Iop = (c1->Iop & 1) | 0x84; + c1->Irm = modregrm(3,reg,reg); + } + continue; + } + + } + + rmi = c->Irm; + + // Do: + // MOV reg,[ESP] => PUSH reg + // ADD ESP,4 => NOP + if (I32 && c->Iop == 0x8B && (rmi & 0xC7) == modregrm(0,0,4) && + c->Isib == modregrm(0,4,SP) && + c1->Iop == 0x83 && (c1->Irm & 0xC7) == modregrm(3,0,SP) && + !(c1->Iflags & CFpsw) && c1->IFL2 == FLconst && c1->IEV2.Vint == 4) + { unsigned reg = (rmi >> 3) & 7; + c->Iop = 0x58 + reg; + c1->Iop = NOP; + continue; + } + + if ((rmi & 0xC0) != 0xC0) + { + continue; + } + + // Combine two SUBs of the same register + if (c->Iop == c1->Iop && + c->Iop == 0x83 && + (rmi & modregrm(3,0,7)) == (c1->Irm & modregrm(3,0,7)) && + !(c1->Iflags & CFpsw) && + c->IFL2 == FLconst && c1->IFL2 == FLconst + ) + { int i = (signed char)c->IEV2.Vint; + int i1 = (signed char)c1->IEV2.Vint; + switch ((rmi & modregrm(0,7,0)) | ((c1->Irm & modregrm(0,7,0)) >> 3)) + { + case (0 << 3) | 0: // ADD, ADD + case (5 << 3) | 5: // SUB, SUB + i += i1; + goto Laa; + case (0 << 3) | 5: // ADD, SUB + case (5 << 3) | 0: // SUB, ADD + i -= i1; + goto Laa; + Laa: + if ((signed char)i != i) + c->Iop &= ~2; + c->IEV2.Vint = i; + c1->Iop = NOP; + if (i == 0) + c->Iop = NOP; + continue; + } + } + + if (c->Iop == 0x8B) // MOV r1,EA + { r1 = (rmi >> 3) & 7; + r2 = rmi & 7; + } + else if (c->Iop == 0x89) // MOV EA,r2 + { r1 = rmi & 7; + r2 = (rmi >> 3) & 7; + } + else + { + continue; + } + + rmn = c1->Irm; + mod = rmn & 0xC0; + reg = rmn & modregrm(0,7,0); + rm = rmn & 7; + if (cod3_EA(c1)) + repEA(c1,r1,r2); + switch (c1->Iop) + { + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: // PUSH reg + if ((c1->Iop & 7) == r1) + { c1->Iop = 0x50 | r2; + //printf("schedule PUSH reg\n"); + } + break; + + case 0x81: + case 0x83: + // Look for CMP EA,imm + if (reg == modregrm(0,7,0)) + { + if (mod == 0xC0 && rm == r1) + c1->Irm = mod | reg | r2; + } + break; + + case 0x84: // TEST reg,byte ptr EA + if (r1 >= 4 || r2 >= 4) // if not a byte register + break; + if ((rmn & 0xC0) == 0xC0) + { + if ((rmn & 3) == r1) + { c1->Irm = rmn = (rmn & modregrm(3,7,4)) | r2; + //printf("schedule 1\n"); + } + } + if ((rmn & modregrm(0,3,0)) == modregrm(0,r1,0)) + { c1->Irm = (rmn & modregrm(3,4,7)) | modregrm(0,r2,0); + //printf("schedule 2\n"); + } + break; + case 0x85: // TEST reg,word ptr EA + if ((rmn & 0xC0) == 0xC0) + { + if ((rmn & 7) == r1) + { c1->Irm = rmn = (rmn & modregrm(3,7,0)) | r2; + //printf("schedule 3\n"); + } + } + if ((rmn & modregrm(0,7,0)) == modregrm(0,r1,0)) + { c1->Irm = (rmn & modregrm(3,0,7)) | modregrm(0,r2,0); + //printf("schedule 4\n"); + } + break; + + case 0x89: // MOV EA,reg + if ((rmn & modregrm(0,7,0)) == modregrm(0,r1,0)) + { c1->Irm = (rmn & modregrm(3,0,7)) | modregrm(0,r2,0); + //printf("schedule 5\n"); + if (c1->Irm == modregrm(3,r2,r2)) + goto Lnop; + } + break; + + case 0x8B: // MOV reg,EA + if ((rmn & 0xC0) == 0xC0 && + (rmn & 7) == r1) // if EA == r1 + { c1->Irm = (rmn & modregrm(3,7,0)) | r2; + //printf("schedule 6\n"); + if (c1->Irm == modregrm(3,r2,r2)) + goto Lnop; + } + break; + + case 0x3C: // CMP AL,imm8 + if (r1 == AX && r2 < 4) + { c1->Iop = 0x80; + c1->Irm = modregrm(3,7,r2); + //printf("schedule 7, r2 = %d\n", r2); + } + break; + + case 0x3D: // CMP AX,imm16 + if (r1 == AX) + { c1->Iop = 0x81; + c1->Irm = modregrm(3,7,r2); + if (c1->IFL2 == FLconst && + c1->IEV2.Vuns == (signed char)c1->IEV2.Vuns) + c1->Iop = 0x83; + //printf("schedule 8\n"); + } + break; + } + continue; +Lnop: + c1->Iop = NOP; + c1 = cnext(c1); + goto Ln; + } +L1: ; + return cstart; +} + +/*****************************************************************/ + +/********************************************** + * Replace complex instructions with simple ones more conducive + * to scheduling. + */ + +code *simpleops(code *c,regm_t scratch) +{ code *cstart; + code **pc; + unsigned reg; + code *c2; + + // Worry about using registers not saved yet by prolog + scratch &= ~fregsaved; + + if (!(scratch & (scratch - 1))) // if 0 or 1 registers + return c; + + reg = findreg(scratch); + + cstart = c; + for (pc = &cstart; *pc; pc = &code_next(*pc)) + { + c = *pc; + if (c->Iflags & (CFtarg | CFtarg2 | CFopsize)) + continue; + if (c->Iop == 0x83 && + (c->Irm & modregrm(0,7,0)) == modregrm(0,7,0) && + (c->Irm & modregrm(3,0,0)) != modregrm(3,0,0) + ) + { // Replace CMP mem,imm with: + // MOV reg,mem + // CMP reg,imm + targ_long imm; + + //printf("replacing CMP\n"); + c->Iop = 0x8B; + c->Irm = (c->Irm & modregrm(3,0,7)) | modregrm(0,reg,0); + + c2 = code_calloc(); + if (reg == AX) + c2->Iop = 0x3D; + else + { c2->Iop = 0x83; + c2->Irm = modregrm(3,7,reg); + } + c2->IFL2 = c->IFL2; + c2->IEV2 = c->IEV2; + + // See if c2 should be replaced by a TEST + imm = c2->IEV2.Vuns; + if (!(c2->Iop & 1)) + imm &= 0xFF; + else if (I32 ? c->Iflags & CFopsize : !(c->Iflags & CFopsize)) + imm = (short) imm; + if (imm == 0) + { + c2->Iop = 0x85; // TEST reg,reg + c2->Irm = modregrm(3,reg,reg); + } + goto L1; + } + else if (c->Iop == 0xFF && + (c->Irm & modregrm(0,7,0)) == modregrm(0,6,0) && + (c->Irm & modregrm(3,0,0)) != modregrm(3,0,0) + ) + { // Replace PUSH mem with: + // MOV reg,mem + // PUSH reg + + // printf("replacing PUSH\n"); + c->Iop = 0x8B; + c->Irm = (c->Irm & modregrm(3,0,7)) | modregrm(0,reg,0); + + c2 = gen1(NULL,0x50 + reg); + L1: +//c->print(); +//c2->print(); + c2->next = c->next; + c->next = c2; + + // Switch to another reg + if (scratch & ~mask[reg]) + reg = findreg(scratch & ~mask[reg]); + } + } + return cstart; +} + +#if DEBUG +static const char *fpops[] = {"fstp","fld","fop"}; +void Cinfo::print() +{ + Cinfo *ci = this; + + if (ci == NULL) + { + printf("Cinfo 0\n"); + return; + } + + printf("Cinfo %p: c %p, pair %x, sz %d, isz %d, flags - ", + ci,c,pair,sz,isz); + if (ci->flags & CIFLarraybounds) + printf("arraybounds,"); + if (ci->flags & CIFLea) + printf("ea,"); + if (ci->flags & CIFLnostage) + printf("nostage,"); + if (ci->flags & CIFLpush) + printf("push,"); + if (ci->flags & ~(CIFLarraybounds|CIFLnostage|CIFLpush|CIFLea)) + printf("bad flag,"); + printf("\n\tr %lx w %lx a %lx reg %x uops %x sibmodrm %x spadjust %ld\n", + (long)r,(long)w,(long)a,reg,uops,sibmodrm,(long)spadjust); + if (ci->fp_op) + printf("\tfp_op %s, fxch_pre %x, fxch_post %x\n", + fpops[fp_op-1],fxch_pre,fxch_post); +} +#endif +#endif diff --git a/backend/cgxmm.c b/backend/cgxmm.c new file mode 100644 index 00000000..727b9c0e --- /dev/null +++ b/backend/cgxmm.c @@ -0,0 +1,780 @@ +// Copyright (C) 2011-2012 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include + +#include "cc.h" +#include "oper.h" +#include "el.h" +#include "code.h" +#include "global.h" +#include "type.h" +#if SCPP +#include "exh.h" +#endif +#include "xmm.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +unsigned xmmoperator(tym_t tym, unsigned oper); + +/******************************************* + * Move constant value into xmm register xreg. + */ + +code *movxmmconst(unsigned xreg, unsigned sz, targ_size_t value, regm_t flags) +{ + /* Generate: + * MOV reg,value + * MOV xreg,reg + * Not so efficient. We should at least do a PXOR for 0. + */ + assert(mask[xreg] & XMMREGS); + assert(sz == 4 || sz == 8); + code *c; + if (I32 && sz == 8) + { + unsigned r; + regm_t rm = ALLREGS; + c = allocreg(&rm,&r,TYint); // allocate scratch register + union { targ_size_t s; targ_long l[2]; } u; + u.l[1] = 0; + u.s = value; + targ_long *p = &u.l[0]; + c = movregconst(c,r,p[0],0); + c = genfltreg(c,0x89,r,0); // MOV floatreg,r + c = movregconst(c,r,p[1],0); + c = genfltreg(c,0x89,r,4); // MOV floatreg+4,r + + unsigned op = xmmload(TYdouble); + c = genfltreg(c,op,xreg - XMM0,0); // MOVSD XMMreg,floatreg + } + else + { + unsigned reg; + c = regwithvalue(CNIL,ALLREGS,value,®,(sz == 8) ? 64 : 0); + c = gen2(c,LODD,modregxrmx(3,xreg-XMM0,reg)); // MOVD xreg,reg + if (sz == 8) + code_orrex(c, REX_W); + } + return c; +} + +/*********************************************** + * Do simple orthogonal operators for XMM registers. + */ + +code *orthxmm(elem *e, regm_t *pretregs) +{ elem *e1 = e->E1; + elem *e2 = e->E2; + regm_t retregs = *pretregs & XMMREGS; + if (!retregs) + retregs = XMMREGS; + code *c = codelem(e1,&retregs,FALSE); // eval left leaf + unsigned reg = findreg(retregs); + regm_t rretregs = XMMREGS & ~retregs; + code *cr = scodelem(e2, &rretregs, retregs, TRUE); // eval right leaf + + unsigned op = xmmoperator(e1->Ety, e->Eoper); + unsigned rreg = findreg(rretregs); + + code *cg; + if (OTrel(e->Eoper)) + { + retregs = mPSW; + cg = NULL; + code *cc = gen2(CNIL,op,modregxrmx(3,rreg-XMM0,reg-XMM0)); + return cat4(c,cr,cg,cc); + } + else + cg = getregs(retregs); + + code *co = gen2(CNIL,op,modregxrmx(3,reg-XMM0,rreg-XMM0)); + if (retregs != *pretregs) + co = cat(co,fixresult(e,retregs,pretregs)); + + return cat4(c,cr,cg,co); +} + + +/************************ + * Generate code for an assignment using XMM registers. + */ + +code *xmmeq(elem *e,regm_t *pretregs) +{ + tym_t tymll; + unsigned reg; + int i; + code *cl,*cr,*c,cs; + elem *e11; + bool regvar; /* TRUE means evaluate into register variable */ + regm_t varregm; + unsigned varreg; + targ_int postinc; + + //printf("xmmeq(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); + elem *e1 = e->E1; + elem *e2 = e->E2; + int e2oper = e2->Eoper; + tym_t tyml = tybasic(e1->Ety); /* type of lvalue */ + regm_t retregs = *pretregs; + + if (!(retregs & XMMREGS)) + retregs = XMMREGS; // pick any XMM reg + + cs.Iop = xmmstore(tyml); + regvar = FALSE; + varregm = 0; + if (config.flags4 & CFG4optimized) + { + // Be careful of cases like (x = x+x+x). We cannot evaluate in + // x if x is in a register. + if (isregvar(e1,&varregm,&varreg) && // if lvalue is register variable + doinreg(e1->EV.sp.Vsym,e2) // and we can compute directly into it + ) + { regvar = TRUE; + retregs = varregm; + reg = varreg; /* evaluate directly in target register */ + } + } + if (*pretregs & mPSW && !EOP(e1)) // if evaluating e1 couldn't change flags + { // Be careful that this lines up with jmpopcode() + retregs |= mPSW; + *pretregs &= ~mPSW; + } + cr = scodelem(e2,&retregs,0,TRUE); // get rvalue + + // Look for special case of (*p++ = ...), where p is a register variable + if (e1->Eoper == OPind && + ((e11 = e1->E1)->Eoper == OPpostinc || e11->Eoper == OPpostdec) && + e11->E1->Eoper == OPvar && + e11->E1->EV.sp.Vsym->Sfl == FLreg + ) + { + postinc = e11->E2->EV.Vint; + if (e11->Eoper == OPpostdec) + postinc = -postinc; + cl = getlvalue(&cs,e11,RMstore | retregs); + freenode(e11->E2); + } + else + { postinc = 0; + cl = getlvalue(&cs,e1,RMstore | retregs); // get lvalue (cl == CNIL if regvar) + } + + c = getregs_imm(varregm); + + reg = findreg(retregs & XMMREGS); + cs.Irm |= modregrm(0,(reg - XMM0) & 7,0); + if ((reg - XMM0) & 8) + cs.Irex |= REX_R; + + // Do not generate mov from register onto itself + if (!(regvar && reg == XMM0 + ((cs.Irm & 7) | (cs.Irex & REX_B ? 8 : 0)))) + c = gen(c,&cs); // MOV EA+offset,reg + + if (e1->Ecount || // if lvalue is a CSE or + regvar) // rvalue can't be a CSE + { + c = cat(c,getregs_imm(retregs)); // necessary if both lvalue and + // rvalue are CSEs (since a reg + // can hold only one e at a time) + cssave(e1,retregs,EOP(e1)); // if lvalue is a CSE + } + + c = cat4(cr,cl,c,fixresult(e,retregs,pretregs)); +Lp: + if (postinc) + { + int reg = findreg(idxregm(&cs)); + if (*pretregs & mPSW) + { // Use LEA to avoid touching the flags + unsigned rm = cs.Irm & 7; + if (cs.Irex & REX_B) + rm |= 8; + c = genc1(c,0x8D,buildModregrm(2,reg,rm),FLconst,postinc); + if (tysize(e11->E1->Ety) == 8) + code_orrex(c, REX_W); + } + else if (I64) + { + c = genc2(c,0x81,modregrmx(3,0,reg),postinc); + if (tysize(e11->E1->Ety) == 8) + code_orrex(c, REX_W); + } + else + { + if (postinc == 1) + c = gen1(c,0x40 + reg); // INC reg + else if (postinc == -(targ_int)1) + c = gen1(c,0x48 + reg); // DEC reg + else + { + c = genc2(c,0x81,modregrm(3,0,reg),postinc); + } + } + } + freenode(e1); + return c; +} + +/******************************** + * Generate code for conversion using SSE2 instructions. + * + * OPs32_d + * OPs64_d (64-bit only) + * OPu32_d (64-bit only) + * OPd_f + * OPf_d + * OPd_s32 + * OPd_s64 (64-bit only) + * + */ + +code *xmmcnvt(elem *e,regm_t *pretregs) +{ + code *c; + unsigned op=0, regs; + tym_t ty; + unsigned char rex = 0; + bool zx = false; // zero extend uint + + /* There are no ops for integer <-> float/real conversions + * but there are instructions for them. In order to use these + * try to fuse chained conversions. Be careful not to loose + * precision for real to long. + */ + elem *e1 = e->E1; + switch (e->Eoper) + { + case OPd_f: + if (e1->Eoper == OPs32_d) + ; + else if (I64 && e1->Eoper == OPs64_d) + rex = REX_W; + else if (I64 && e1->Eoper == OPu32_d) + { rex = REX_W; + zx = true; + } + else + { regs = XMMREGS; + op = CVTSD2SS; + ty = TYfloat; + break; + } + // directly use si2ss + regs = ALLREGS; + e1 = e1->E1; + op = CVTSI2SS; + ty = TYfloat; + break; + + case OPs32_d: goto Litod; + case OPs64_d: rex = REX_W; goto Litod; + case OPu32_d: rex = REX_W; zx = true; goto Litod; + Litod: + regs = ALLREGS; + op = CVTSI2SD; + ty = TYdouble; + break; + + case OPd_s32: ty = TYint; goto Ldtoi; + case OPd_s64: ty = TYlong; rex = REX_W; goto Ldtoi; + Ldtoi: + regs = XMMREGS; + switch (e1->Eoper) + { + case OPf_d: + e1 = e1->E1; + op = CVTTSS2SI; + break; + case OPld_d: + if (e->Eoper == OPd_s64) + return cnvt87(e,pretregs); // precision + /* FALL-THROUGH */ + default: + op = CVTTSD2SI; + break; + } + break; + + case OPf_d: + regs = XMMREGS; + op = CVTSS2SD; + ty = TYdouble; + break; + } + assert(op); + + c = codelem(e1, ®s, FALSE); + unsigned reg = findreg(regs); + if (reg >= XMM0) + reg -= XMM0; + else if (zx) + { assert(I64); + c = cat(c,getregs(regs)); + c = genregs(c,0x89,reg,reg); // MOV reg,reg to zero upper 32-bit + code_orflag(c,CFvolatile); + } + + unsigned retregs = *pretregs; + if (tyxmmreg(ty)) // target is XMM + { if (!(*pretregs & XMMREGS)) + retregs = XMMREGS; + } + else // source is XMM + { assert(regs & XMMREGS); + if (!(retregs & ALLREGS)) + retregs = ALLREGS; + } + + unsigned rreg; + c = cat(c,allocreg(&retregs,&rreg,ty)); + if (rreg >= XMM0) + rreg -= XMM0; + + c = gen2(c, op, modregxrmx(3,rreg,reg)); + assert(I64 || !rex); + if (rex) + code_orrex(c, rex); + + if (*pretregs != retregs) + c = cat(c,fixresult(e,retregs,pretregs)); + return c; +} + +/******************************** + * Generate code for op= + */ + +code *xmmopass(elem *e,regm_t *pretregs) +{ elem *e1 = e->E1; + elem *e2 = e->E2; + tym_t ty1 = tybasic(e1->Ety); + unsigned sz1 = tysize[ty1]; + regm_t rretregs = XMMREGS & ~*pretregs; + if (!rretregs) + rretregs = XMMREGS; + + code *cr = codelem(e2,&rretregs,FALSE); // eval right leaf + unsigned rreg = findreg(rretregs); + + code cs; + code *cl,*cg; + + regm_t retregs; + unsigned reg; + bool regvar = FALSE; + if (config.flags4 & CFG4optimized) + { + // Be careful of cases like (x = x+x+x). We cannot evaluate in + // x if x is in a register. + unsigned varreg; + regm_t varregm; + if (isregvar(e1,&varregm,&varreg) && // if lvalue is register variable + doinreg(e1->EV.sp.Vsym,e2) // and we can compute directly into it + ) + { regvar = TRUE; + retregs = varregm; + reg = varreg; // evaluate directly in target register + cl = NULL; + cg = getregs(retregs); // destroy these regs + } + } + + if (!regvar) + { + cl = getlvalue(&cs,e1,rretregs); // get EA + retregs = *pretregs & XMMREGS & ~rretregs; + if (!retregs) + retregs = XMMREGS & ~rretregs; + cg = allocreg(&retregs,®,ty1); + cs.Iop = xmmload(ty1); // MOVSD xmm,xmm_m64 + code_newreg(&cs,reg - XMM0); + cg = gen(cg,&cs); + } + + unsigned op = xmmoperator(e1->Ety, e->Eoper); + code *co = gen2(CNIL,op,modregxrmx(3,reg-XMM0,rreg-XMM0)); + + if (!regvar) + { + cs.Iop = xmmstore(ty1); // reverse operand order of MOVS[SD] + gen(co,&cs); + } + + if (e1->Ecount || // if lvalue is a CSE or + regvar) // rvalue can't be a CSE + { + cl = cat(cl,getregs_imm(retregs)); // necessary if both lvalue and + // rvalue are CSEs (since a reg + // can hold only one e at a time) + cssave(e1,retregs,EOP(e1)); // if lvalue is a CSE + } + + co = cat(co,fixresult(e,retregs,pretregs)); + freenode(e1); + return cat4(cr,cl,cg,co); +} + +/****************** + * Negate operator + */ + +code *xmmneg(elem *e,regm_t *pretregs) +{ + //printf("xmmneg()\n"); + //elem_print(e); + assert(*pretregs); + tym_t tyml = tybasic(e->E1->Ety); + int sz = tysize[tyml]; + + regm_t retregs = *pretregs & XMMREGS; + if (!retregs) + retregs = XMMREGS; + + /* Generate: + * MOV reg,e1 + * MOV rreg,signbit + * XOR reg,rreg + */ + code *cl = codelem(e->E1,&retregs,FALSE); + cl = cat(cl,getregs(retregs)); + unsigned reg = findreg(retregs); + regm_t rretregs = XMMREGS & ~retregs; + unsigned rreg; + cl = cat(cl,allocreg(&rretregs,&rreg,tyml)); + targ_size_t signbit = 0x80000000; + if (sz == 8) + signbit = 0x8000000000000000LL; + code *c = movxmmconst(rreg, sz, signbit, 0); + + code *cg = getregs(retregs); + unsigned op = (sz == 8) ? XORPD : XORPS; // XORPD/S reg,rreg + code *co = gen2(CNIL,op,modregxrmx(3,reg-XMM0,rreg-XMM0)); + co = cat(co,fixresult(e,retregs,pretregs)); + return cat4(cl,c,cg,co); +} + +/***************************** + * Get correct load operator based on type. + * It is important to use the right one even if the number of bits moved is the same, + * as there are performance consequences for using the wrong one. + */ + +unsigned xmmload(tym_t tym) +{ unsigned op; + switch (tybasic(tym)) + { + case TYfloat: + case TYifloat: op = LODSS; break; // MOVSS + case TYdouble: + case TYidouble: op = LODSD; break; // MOVSD + + case TYfloat4: op = LODAPS; break; // MOVAPS + case TYdouble2: op = LODAPD; break; // MOVAPD + case TYschar16: + case TYuchar16: + case TYshort8: + case TYushort8: + case TYlong4: + case TYulong4: + case TYllong2: + case TYullong2: op = LODDQA; break; // MOVDQA + + default: + printf("tym = x%x\n", tym); + assert(0); + } + return op; +} + +/***************************** + * Get correct store operator based on type. + */ + +unsigned xmmstore(tym_t tym) +{ unsigned op; + switch (tybasic(tym)) + { + case TYfloat: + case TYifloat: op = STOSS; break; // MOVSS + case TYdouble: + case TYidouble: + case TYllong: + case TYullong: + case TYuint: + case TYlong: + case TYcfloat: op = STOSD; break; // MOVSD + + case TYfloat4: op = STOAPS; break; // MOVAPS + case TYdouble2: op = STOAPD; break; // MOVAPD + case TYschar16: + case TYuchar16: + case TYshort8: + case TYushort8: + case TYlong4: + case TYulong4: + case TYllong2: + case TYullong2: op = STODQA; break; // MOVDQA + + default: + printf("tym = x%x\n", tym); + assert(0); + } + return op; +} + +/************************************ + * Get correct XMM operator based on type and operator. + */ + +unsigned xmmoperator(tym_t tym, unsigned oper) +{ + tym = tybasic(tym); + unsigned op; + switch (oper) + { + case OPadd: + case OPaddass: + switch (tym) + { + case TYfloat: + case TYifloat: op = ADDSS; break; + case TYdouble: + case TYidouble: op = ADDSD; break; + + // SIMD vector types + case TYfloat4: op = ADDPS; break; + case TYdouble2: op = ADDPD; break; + case TYschar16: + case TYuchar16: op = PADDB; break; + case TYshort8: + case TYushort8: op = PADDW; break; + case TYlong4: + case TYulong4: op = PADDD; break; + case TYllong2: + case TYullong2: op = PADDQ; break; + + default: assert(0); + } + break; + + case OPmin: + case OPminass: + switch (tym) + { + case TYfloat: + case TYifloat: op = SUBSS; break; + case TYdouble: + case TYidouble: op = SUBSD; break; + + // SIMD vector types + case TYfloat4: op = SUBPS; break; + case TYdouble2: op = SUBPD; break; + case TYschar16: + case TYuchar16: op = PSUBB; break; + case TYshort8: + case TYushort8: op = PSUBW; break; + case TYlong4: + case TYulong4: op = PSUBD; break; + case TYllong2: + case TYullong2: op = PSUBQ; break; + + default: assert(0); + } + break; + + case OPmul: + case OPmulass: + switch (tym) + { + case TYfloat: + case TYifloat: op = MULSS; break; + case TYdouble: + case TYidouble: op = MULSD; break; + + // SIMD vector types + case TYfloat4: op = MULPS; break; + case TYdouble2: op = MULPD; break; + case TYshort8: + case TYushort8: op = PMULLW; break; + + default: assert(0); + } + break; + + case OPdiv: + case OPdivass: + switch (tym) + { + case TYfloat: + case TYifloat: op = DIVSS; break; + case TYdouble: + case TYidouble: op = DIVSD; break; + + // SIMD vector types + case TYfloat4: op = DIVPS; break; + case TYdouble2: op = DIVPD; break; + + default: assert(0); + } + break; + + case OPor: + case OPorass: + switch (tym) + { + // SIMD vector types + case TYschar16: + case TYuchar16: + case TYshort8: + case TYushort8: + case TYlong4: + case TYulong4: + case TYllong2: + case TYullong2: op = POR; break; + + default: assert(0); + } + break; + + case OPand: + case OPandass: + switch (tym) + { + // SIMD vector types + case TYschar16: + case TYuchar16: + case TYshort8: + case TYushort8: + case TYlong4: + case TYulong4: + case TYllong2: + case TYullong2: op = PAND; break; + + default: assert(0); + } + break; + + case OPxor: + case OPxorass: + switch (tym) + { + // SIMD vector types + case TYschar16: + case TYuchar16: + case TYshort8: + case TYushort8: + case TYlong4: + case TYulong4: + case TYllong2: + case TYullong2: op = PXOR; break; + + default: assert(0); + } + break; + + case OPlt: + case OPle: + case OPgt: + case OPge: + case OPne: + case OPeqeq: + case OPunord: /* !<>= */ + case OPlg: /* <> */ + case OPleg: /* <>= */ + case OPule: /* !> */ + case OPul: /* !>= */ + case OPuge: /* !< */ + case OPug: /* !<= */ + case OPue: /* !<> */ + case OPngt: + case OPnge: + case OPnlt: + case OPnle: + case OPord: + case OPnlg: + case OPnleg: + case OPnule: + case OPnul: + case OPnuge: + case OPnug: + case OPnue: + switch (tym) + { + case TYfloat: + case TYifloat: op = UCOMISS; break; + case TYdouble: + case TYidouble: op = UCOMISD; break; + + default: assert(0); + } + break; + + default: + assert(0); + } + return op; +} + +code *cdvector(elem *e, regm_t *pretregs) +{ + /* e should look like: + * vector + * | + * param + * / \ + * param op2 + * / \ + * op op1 + */ + + if (!config.fpxmmregs) + { printf("SIMD operations not supported on this platform\n"); + exit(1); + } + + elem *e1 = e->E1; + assert(e1->Eoper == OPparam); + elem *op2 = e1->E2; + e1 = e1->E1; + assert(e1->Eoper == OPparam); + elem *eop = e1->E1; + assert(eop->Eoper == OPconst); + elem *op1 = e1->E2; + + tym_t ty1 = tybasic(op1->Ety); + unsigned sz1 = tysize[ty1]; + assert(sz1 == 16); // float or double + regm_t retregs = *pretregs & XMMREGS; + if (!retregs) + retregs = XMMREGS; + code *c = codelem(op1,&retregs,FALSE); // eval left leaf + unsigned reg = findreg(retregs); + regm_t rretregs = XMMREGS & ~retregs; + code *cr = scodelem(op2, &rretregs, retregs, TRUE); // eval right leaf + unsigned rreg = findreg(rretregs); + code *cg = getregs(retregs); + unsigned op = el_tolong(eop); + code *co = gen2(CNIL,op,modregxrmx(3,reg-XMM0,rreg-XMM0)); + co = cat(co,fixresult(e,retregs,pretregs)); + return cat4(c,cr,cg,co); +} + +#endif // !SPP diff --git a/backend/cod1.c b/backend/cod1.c new file mode 100644 index 00000000..4474989c --- /dev/null +++ b/backend/cod1.c @@ -0,0 +1,3990 @@ +// Copyright (C) 1984-1998 by Symantec +// Copyright (C) 2000-2012 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include + +#if __sun&&__SVR4 +#include +#endif + +#include "cc.h" +#include "el.h" +#include "oper.h" +#include "code.h" +#include "global.h" +#include "type.h" +#include "xmm.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +/* Generate the appropriate ESC instruction */ +#define ESC(MF,b) (0xD8 + ((MF) << 1) + (b)) +enum MF +{ // Values for MF + MFfloat = 0, + MFlong = 1, + MFdouble = 2, + MFword = 3 +}; +code * genf2(code *c,unsigned op,unsigned rm); + +targ_size_t paramsize(elem *e,unsigned stackalign); +STATIC code * funccall (elem *,unsigned,unsigned,regm_t *,regm_t); + +/* array to convert from index register to r/m field */ + /* AX CX DX BX SP BP SI DI */ +static const signed char regtorm32[8] = { 0, 1, 2, 3,-1, 5, 6, 7 }; + signed char regtorm [8] = { -1,-1,-1, 7,-1, 6, 4, 5 }; + +/************************** + * Determine if e is a 32 bit scaled index addressing mode. + * Returns: + * 0 not a scaled index addressing mode + * !=0 the value for ss in the SIB byte + */ + +int isscaledindex(elem *e) +{ targ_uns ss; + + assert(!I16); + while (e->Eoper == OPcomma) + e = e->E2; + if (!(e->Eoper == OPshl && !e->Ecount && + e->E2->Eoper == OPconst && + (ss = e->E2->EV.Vuns) <= 3 + ) + ) + ss = 0; + return ss; +} + +/********************************************* + * Generate code for which isscaledindex(e) returned a non-zero result. + */ + +code *cdisscaledindex(elem *e,regm_t *pidxregs,regm_t keepmsk) +{ code *c; + regm_t r; + + // Load index register with result of e->E1 + c = NULL; + while (e->Eoper == OPcomma) + { + r = 0; + c = cat(c,scodelem(e->E1,&r,keepmsk,TRUE)); + freenode(e); + e = e->E2; + } + assert(e->Eoper == OPshl); + c = cat(c,scodelem(e->E1,pidxregs,keepmsk,TRUE)); + freenode(e->E2); + freenode(e); + return c; +} + +/*********************************** + * Determine index if we can do two LEA instructions as a multiply. + * Returns: + * 0 can't do it + */ + +static struct Ssindex +{ + targ_uns product; + char ss1; + char ss2; + char ssflags; + #define SSFLnobp 1 // can't have EBP in relconst + #define SSFLnobase1 2 // no base register for first LEA + #define SSFLnobase 4 // no base register + #define SSFLlea 8 // can do it in one LEA +} ssindex_array[] = +{ {0, 0,0}, // [0] is a place holder + + {3, 1,0,SSFLnobp | SSFLlea}, + {5, 2,0,SSFLnobp | SSFLlea}, + {9, 3,0,SSFLnobp | SSFLlea}, + + {6, 1,1,SSFLnobase}, + {12,1,2,SSFLnobase}, + {24,1,3,SSFLnobase}, + {10,2,1,SSFLnobase}, + {20,2,2,SSFLnobase}, + {40,2,3,SSFLnobase}, + {18,3,1,SSFLnobase}, + {36,3,2,SSFLnobase}, + {72,3,3,SSFLnobase}, + + {15,2,1,SSFLnobp}, + {25,2,2,SSFLnobp}, + {27,3,1,SSFLnobp}, + {45,3,2,SSFLnobp}, + {81,3,3,SSFLnobp}, + + {16,3,1,SSFLnobase1 | SSFLnobase}, + {32,3,2,SSFLnobase1 | SSFLnobase}, + {64,3,3,SSFLnobase1 | SSFLnobase}, +}; + +int ssindex(int op,targ_uns product) +{ int i; + + if (op == OPshl) + product = 1 << product; + for (i = 1; i < arraysize(ssindex_array); i++) + { + if (ssindex_array[i].product == product) + return i; + } + return 0; +} + +/*************************************** + * Build an EA of the form disp[base][index*scale]. + * Input: + * c struct to fill in + * base base register (-1 if none) + * index index register (-1 if none) + * scale scale factor - 1,2,4,8 + * disp displacement + */ + +void buildEA(code *c,int base,int index,int scale,targ_size_t disp) +{ unsigned char rm; + unsigned char sib; + unsigned char rex = 0; + + sib = 0; + if (!I16) + { unsigned ss; + + assert(index != SP); + + switch (scale) + { case 1: ss = 0; break; + case 2: ss = 1; break; + case 4: ss = 2; break; + case 8: ss = 3; break; + default: assert(0); + } + + if (base == -1) + { + if (index == -1) + rm = modregrm(0,0,5); + else + { + rm = modregrm(0,0,4); + sib = modregrm(ss,index & 7,5); + if (index & 8) + rex |= REX_X; + } + } + else if (index == -1) + { + if (base == SP) + { + rm = modregrm(2,0,4); + sib = modregrm(0,4,SP); + } + else + { rm = modregrm(2,0,base & 7); + if (base & 8) + { rex |= REX_B; + if (base == R12) + { + rm = modregrm(2,0,4); + sib = modregrm(0,4,4); + } + } + } + } + else + { + rm = modregrm(2,0,4); + sib = modregrm(ss,index & 7,base & 7); + if (index & 8) + rex |= REX_X; + if (base & 8) + rex |= REX_B; + } + } + else + { + // -1 AX CX DX BX SP BP SI DI + static unsigned char EA16rm[9][9] = + { + { 0x06,0x09,0x09,0x09,0x87,0x09,0x86,0x84,0x85, }, // -1 + { 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, }, // AX + { 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, }, // CX + { 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, }, // DX + { 0x87,0x09,0x09,0x09,0x09,0x09,0x09,0x80,0x81, }, // BX + { 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, }, // SP + { 0x86,0x09,0x09,0x09,0x09,0x09,0x09,0x82,0x83, }, // BP + { 0x84,0x09,0x09,0x09,0x80,0x09,0x82,0x09,0x09, }, // SI + { 0x85,0x09,0x09,0x09,0x81,0x09,0x83,0x09,0x09, } // DI + }; + + assert(scale == 1); + rm = EA16rm[base + 1][index + 1]; + assert(rm != 9); + } + c->Irm = rm; + c->Isib = sib; + c->Irex = rex; + c->IFL1 = FLconst; + c->IEV1.Vuns = disp; +} + +/********************************************* + * Build REX, modregrm and sib bytes + */ + +unsigned buildModregrm(int mod, int reg, int rm) +{ unsigned m; + if (I16) + m = modregrm(mod, reg, rm); + else + { + if ((rm & 7) == SP && mod != 3) + m = (modregrm(0,4,SP) << 8) | modregrm(mod,reg & 7,4); + else + m = modregrm(mod,reg & 7,rm & 7); + if (reg & 8) + m |= REX_R << 16; + if (rm & 8) + m |= REX_B << 16; + } + return m; +} + +/**************************************** + * Generate code for eecontext + */ + +void genEEcode() +{ regm_t retregs; + code *c; + + eecontext.EEin++; + regcon.immed.mval = 0; + retregs = 0; //regmask(eecontext.EEelem->Ety); + assert(EEoffset >= REGSIZE); + c = genc2(NULL,0x81,modregrm(3,5,SP),EEoffset - REGSIZE); // SUB ESP,EEoffset + gen1(c,0x50 + SI); // PUSH ESI + genadjesp(c,EEoffset); + c = gencodelem(c,eecontext.EEelem,&retregs, FALSE); + assignaddrc(c); + pinholeopt(c,NULL); + jmpaddr(c); + eecontext.EEcode = gen1(c,0xCC); // INT 3 + eecontext.EEin--; +} + +/******************************************** + * Gen a save/restore sequence for mask of registers. + */ + +void gensaverestore2(regm_t regm,code **csave,code **crestore) +{ + code *cs1 = *csave; + code *cs2 = *crestore; + + //printf("gensaverestore2(%s)\n", regm_str(regm)); + regm &= mBP | mES | ALLREGS | XMMREGS | mST0 | mST01; + for (int i = 0; regm; i++) + { + if (regm & 1) + { + if (i == ES) + { + cs1 = gen1(cs1, 0x06); // PUSH ES + cs2 = cat(gen1(CNIL, 0x07),cs2); // POP ES + } + else if (i == ST0 || i == ST01) + { + gensaverestore87(1 << i, &cs1, &cs2); + } + else if (i >= XMM0) + { unsigned idx; + cs1 = regsave.save(cs1, i, &idx); + cs2 = regsave.restore(cs2, i, idx); + } + else + { + cs1 = gen1(cs1,0x50 + (i & 7)); // PUSH i + code *c = gen1(NULL, 0x58 + (i & 7)); // POP i + if (i & 8) + { code_orrex(cs1, REX_B); + code_orrex(c, REX_B); + } + cs2 = cat(c,cs2); + } + } + regm >>= 1; + } + *csave = cs1; + *crestore = cs2; +} + +void gensaverestore(regm_t regm,code **csave,code **crestore) +{ + *csave = NULL; + *crestore = NULL; + gensaverestore2(regm, csave, crestore); +} + +/**************************************** + * Clean parameters off stack. + * Input: + * numpara amount to adjust stack pointer + * keepmsk mask of registers to not destroy + */ + +code *genstackclean(code *c,unsigned numpara,regm_t keepmsk) +{ + //dbg_printf("genstackclean(numpara = %d, stackclean = %d)\n",numpara,cgstate.stackclean); + if (numpara && (cgstate.stackclean || STACKALIGN == 16)) + { +#if 0 // won't work if operand of scodelem + if (numpara == stackpush && // if this is all those pushed + needframe && // and there will be a BP + !config.windows && + !(regcon.mvar & fregsaved) // and no registers will be pushed + ) + c = genregs(c,0x89,BP,SP); // MOV SP,BP + else +#endif + { regm_t scratchm = 0; + + if (numpara == REGSIZE && config.flags4 & CFG4space) + { + scratchm = ALLREGS & ~keepmsk & regcon.used & ~regcon.mvar; + } + + if (scratchm) + { unsigned r; + c = cat(c,allocreg(&scratchm,&r,TYint)); + c = gen1(c,0x58 + r); // POP r + } + else + { c = genc2(c,0x81,modregrm(3,0,SP),numpara); // ADD SP,numpara + if (I64) + code_orrex(c, REX_W); + } + } + stackpush -= numpara; + c = genadjesp(c,-numpara); + } + return c; +} + + +/********************************* + * Generate code for a logical expression. + * Input: + * e elem + * jcond + * bit 1 if TRUE then goto jump address if e + * if FALSE then goto jump address if !e + * 2 don't call save87() + * fltarg FLcode or FLblock, flavor of target if e evaluates to jcond + * targ either code or block pointer to destination + */ + +code *logexp(elem *e,int jcond,unsigned fltarg,code *targ) +{ code *c,*ce,*cnop; + regm_t retregs; + unsigned op; + + //printf("logexp(e = %p, jcond = %d)\n", e, jcond); + int no87 = (jcond & 2) == 0; + _chkstack(); + code *cc = docommas(&e); // scan down commas + cgstate.stackclean++; + + if (EOP(e) && !e->Ecount) /* if operator and not common sub */ + { con_t regconsave; + + switch (e->Eoper) + { case OPoror: + if (jcond & 1) + { c = logexp(e->E1,jcond,fltarg,targ); + regconsave = regcon; + ce = logexp(e->E2,jcond,fltarg,targ); + } + else + { cnop = gennop(CNIL); + c = logexp(e->E1,jcond | 1,FLcode,cnop); + regconsave = regcon; + ce = logexp(e->E2,jcond,fltarg,targ); + ce = cat(ce,cnop); + } + cnop = CNIL; + goto L1; + + case OPandand: + if (jcond & 1) + { cnop = gennop(CNIL); /* a dummy target address */ + c = logexp(e->E1,jcond & ~1,FLcode,cnop); + regconsave = regcon; + ce = logexp(e->E2,jcond,fltarg,targ); + } + else + { c = logexp(e->E1,jcond,fltarg,targ); + regconsave = regcon; + ce = logexp(e->E2,jcond,fltarg,targ); + cnop = CNIL; + } + L1: andregcon(®consave); + freenode(e); + c = cat4(cc,c,ce,cnop); + goto Lret; + + case OPnot: + jcond ^= 1; + case OPbool: + case OPs8_16: + case OPu8_16: + case OPs16_32: + case OPu16_32: + case OPs32_64: + case OPu32_64: + case OPu32_d: + case OPd_ld: + c = logexp(e->E1,jcond,fltarg,targ); + freenode(e); + goto Lretc; + + case OPcond: + { + code *cnop2 = gennop(CNIL); // addresses of start of leaves + cnop = gennop(CNIL); + c = logexp(e->E1,FALSE,FLcode,cnop2); /* eval condition */ + con_t regconold = regcon; + ce = logexp(e->E2->E1,jcond,fltarg,targ); + ce = genjmp(ce,JMP,FLcode,(block *) cnop); /* skip second leaf */ + + regconsave = regcon; + regcon = regconold; + + code_next(cnop2) = logexp(e->E2->E2,jcond,fltarg,targ); + andregcon(®conold); + andregcon(®consave); + freenode(e->E2); + freenode(e); + c = cat6(cc,c,NULL,ce,cnop2,cnop); + goto Lret; + } + } + } + + /* Special code for signed long compare. + * Not necessary for I64 until we do cents. + */ + if (OTrel2(e->Eoper) && /* if < <= >= > */ + !e->Ecount && + ( (I16 && tybasic(e->E1->Ety) == TYlong && tybasic(e->E2->Ety) == TYlong) || + (I32 && tybasic(e->E1->Ety) == TYllong && tybasic(e->E2->Ety) == TYllong)) + ) + { + c = longcmp(e,jcond,fltarg,targ); + goto Lretc; + } + + retregs = mPSW; /* return result in flags */ + op = jmpopcode(e); /* get jump opcode */ + if (!(jcond & 1)) + op ^= 0x101; // toggle jump condition(s) + c = codelem(e,&retregs,TRUE); /* evaluate elem */ + if (no87) + c = cat(c,cse_flush(no87)); // flush CSE's to memory + genjmp(c,op,fltarg,(block *) targ); /* generate jmp instruction */ +Lretc: + c = cat(cc,c); +Lret: + cgstate.stackclean--; + return c; +} + + +/****************************** + * Routine to aid in setting things up for gen(). + * Look for common subexpression. + * Can handle indirection operators, but not if they're common subs. + * Input: + * e -> elem where we get some of the data from + * cs -> partially filled code to add + * op = opcode + * reg = reg field of (mod reg r/m) + * offset = data to be added to Voffset field + * keepmsk = mask of registers we must not destroy + * desmsk = mask of registers destroyed by executing the instruction + * Returns: + * pointer to code generated + */ + +code *loadea(elem *e,code *cs,unsigned op,unsigned reg,targ_size_t offset, + regm_t keepmsk,regm_t desmsk) +{ + code *c,*cg,*cd; + +#ifdef DEBUG + if (debugw) + printf("loadea: e=%p cs=%p op=x%x reg=%d offset=%lld keepmsk=x%x desmsk=x%x\n", + e,cs,op,reg,(unsigned long long)offset,keepmsk,desmsk); +#endif + + assert(e); + cs->Iflags = 0; + cs->Irex = 0; + cs->Iop = op; + tym_t tym = e->Ety; + int sz = tysize(tym); + + /* Determine if location we want to get is in a register. If so, */ + /* substitute the register for the EA. */ + /* Note that operators don't go through this. CSE'd operators are */ + /* picked up by comsub(). */ + if (e->Ecount && /* if cse */ + e->Ecount != e->Ecomsub && /* and cse was generated */ + op != 0x8D && op != 0xC4 && /* and not an LEA or LES */ + (op != 0xFF || reg != 3) && /* and not CALLF MEM16 */ + (op & 0xFFF8) != 0xD8) // and not 8087 opcode + { + assert(!EOP(e)); /* can't handle this */ + regm_t rm = regcon.cse.mval & ~regcon.cse.mops & ~regcon.mvar; // possible regs + if (sz > REGSIZE) // value is in 2 or 4 registers + { + if (I16 && sz == 8) // value is in 4 registers + { static regm_t rmask[4] = { mDX,mCX,mBX,mAX }; + rm &= rmask[offset >> 1]; + } + + else if (offset) + rm &= mMSW; /* only high words */ + else + rm &= mLSW; /* only low words */ + } + for (unsigned i = 0; rm; i++) + { if (mask[i] & rm) + { if (regcon.cse.value[i] == e && // if register has elem + /* watch out for a CWD destroying DX */ + !(i == DX && op == 0xF7 && desmsk & mDX)) + { + /* if ES, then it can only be a load */ + if (i == ES) + { if (op != 0x8B) + goto L1; /* not a load */ + cs->Iop = 0x8C; /* MOV reg,ES */ + cs->Irm = modregrm(3,0,reg & 7); + if (reg & 8) + code_orrex(cs, REX_B); + } + else // XXX reg,i + { + cs->Irm = modregrm(3,reg & 7,i & 7); + if (reg & 8) + cs->Irex |= REX_R; + if (i & 8) + cs->Irex |= REX_B; + if (sz == 1 && I64 && (i >= 4 || reg >= 4)) + cs->Irex |= REX; + if (I64 && (sz == 8 || sz == 16)) + cs->Irex |= REX_W; + } + c = CNIL; + goto L2; + } + rm &= ~mask[i]; + } + } + } + +L1: + c = getlvalue(cs,e,keepmsk); + if (offset == REGSIZE) + getlvalue_msw(cs); + else + cs->IEVoffset1 += offset; + if (I64) + { if (reg >= 4 && sz == 1) // if byte register + // Can only address those 8 bit registers if a REX byte is present + cs->Irex |= REX; + if ((op & 0xFFFFFFF8) == 0xD8) + cs->Irex &= ~REX_W; // not needed for x87 ops + } + code_newreg(cs, reg); // OR in reg field + if (!I16) + { + if (reg == 6 && op == 0xFF || /* don't PUSH a word */ + op == 0x0FB7 || op == 0x0FBF || /* MOVZX/MOVSX */ + (op & 0xFFF8) == 0xD8 || /* 8087 instructions */ + op == 0x8D) /* LEA */ + { + cs->Iflags &= ~CFopsize; + if (reg == 6 && op == 0xFF) // if PUSH + cs->Irex &= ~REX_W; // REX is ignored for PUSH anyway + } + } + else if ((op & 0xFFF8) == 0xD8 && ADDFWAIT()) + cs->Iflags |= CFwait; +L2: + cg = getregs(desmsk); /* save any regs we destroy */ + + /* KLUDGE! fix up DX for divide instructions */ + cd = CNIL; + if (op == 0xF7 && desmsk == (mAX|mDX)) /* if we need to fix DX */ + { if (reg == 7) /* if IDIV */ + { cd = gen1(cd,0x99); // CWD + if (I64 && sz == 8) + code_orrex(cd, REX_W); + } + else if (reg == 6) // if DIV + { cd = genregs(cd,0x33,DX,DX); // XOR DX,DX + if (I64 && sz == 8) + code_orrex(cd, REX_W); + } + } + + // Eliminate MOV reg,reg + if ((cs->Iop & ~3) == 0x88 && + (cs->Irm & 0xC7) == modregrm(3,0,reg & 7)) + { + unsigned r = cs->Irm & 7; + if (cs->Irex & REX_B) + r |= 8; + if (r == reg) + cs->Iop = NOP; + } + + return cat4(c,cg,cd,gen(NULL,cs)); +} + +/************************** + * Get addressing mode. + */ + +unsigned getaddrmode(regm_t idxregs) +{ + unsigned mode; + + if (I16) + { + mode = (idxregs & mBX) ? modregrm(2,0,7) : /* [BX] */ + (idxregs & mDI) ? modregrm(2,0,5): /* [DI] */ + (idxregs & mSI) ? modregrm(2,0,4): /* [SI] */ + (assert(0),1); + } + else + { unsigned reg = findreg(idxregs & (ALLREGS | mBP)); + if (reg == R12) + mode = (REX_B << 16) | (modregrm(0,4,4) << 8) | modregrm(2,0,4); + else + mode = modregrmx(2,0,reg); + } + return mode; +} + +void setaddrmode(code *c, regm_t idxregs) +{ + unsigned mode = getaddrmode(idxregs); + c->Irm = mode & 0xFF; + c->Isib = mode >> 8; + c->Irex &= ~REX_B; + c->Irex |= mode >> 16; +} + +/********************************************** + */ + +void getlvalue_msw(code *c) +{ + if (c->IFL1 == FLreg) + { + unsigned regmsw = c->IEVsym1->Sregmsw; + c->Irm = (c->Irm & ~7) | (regmsw & 7); + if (regmsw & 8) + c->Irex |= REX_B; + else + c->Irex &= ~REX_B; + } + else + c->IEVoffset1 += REGSIZE; +} + +/********************************************** + */ + +void getlvalue_lsw(code *c) +{ + if (c->IFL1 == FLreg) + { + unsigned reglsw = c->IEVsym1->Sreglsw; + c->Irm = (c->Irm & ~7) | (reglsw & 7); + if (reglsw & 8) + c->Irex |= REX_B; + else + c->Irex &= ~REX_B; + } + else + c->IEVoffset1 -= REGSIZE; +} + +/****************** + * Compute addressing mode. + * Generate & return sequence of code (if any). + * Return in cs the info on it. + * Input: + * pcs -> where to store data about addressing mode + * e -> the lvalue elem + * keepmsk mask of registers we must not destroy or use + * if (keepmsk & RMstore), this will be only a store operation + * into the lvalue + * if (keepmsk & RMload), this will be a read operation only + */ + +code *getlvalue(code *pcs,elem *e,regm_t keepmsk) +{ regm_t idxregs; + unsigned fl,f,opsave; + code *c; + elem *e1; + elem *e11; + elem *e12; + bool e1isadd,e1free; + unsigned reg; + tym_t e1ty; + symbol *s; + + //printf("getlvalue(e = %p)\n",e); + //elem_print(e); + assert(e); + elem_debug(e); + if (e->Eoper == OPvar || e->Eoper == OPrelconst) + { s = e->EV.sp.Vsym; + fl = s->Sfl; + if (tyfloating(s->ty())) + obj_fltused(); + } + else + fl = FLoper; + pcs->IFL1 = fl; + pcs->Iflags = CFoff; /* only want offsets */ + pcs->Irex = 0; + pcs->IEVoffset1 = 0; + + tym_t ty = e->Ety; + unsigned sz = tysize(ty); + if (tyfloating(ty)) + obj_fltused(); + if (I64 && (sz == 8 || sz == 16)) + pcs->Irex |= REX_W; + if (!I16 && sz == SHORTSIZE) + pcs->Iflags |= CFopsize; + if (ty & mTYvolatile) + pcs->Iflags |= CFvolatile; + c = CNIL; + switch (fl) + { +#if 0 && TARGET_LINUX + case FLgot: + case FLgotoff: + gotref = 1; + pcs->IEVsym1 = s; + pcs->IEVoffset1 = e->EV.sp.Voffset; + if (e->Eoper == OPvar && fl == FLgot) + { + code *c1; + unsigned saveop = pcs->Iop; + idxregs = allregs & ~keepmsk; // get a scratch register + c = allocreg(&idxregs,®,TYptr); + pcs->Irm = modregrm(2,reg,BX); // BX has GOT + pcs->Isib = 0; + //pcs->Iflags |= CFvolatile; + pcs->Iop = 0x8B; + c = gen(c,pcs); // MOV reg,disp[EBX] + pcs->Irm = modregrm(0,0,reg); + pcs->IEVoffset1 = 0; + pcs->Iop = saveop; + } + else + { + pcs->Irm = modregrm(2,0,BX); // disp[EBX] is addr + pcs->Isib = 0; + } + break; +#endif + case FLoper: +#ifdef DEBUG + if (debugw) printf("getlvalue(e = %p, km = x%x)\n",e,keepmsk); +#endif + switch (e->Eoper) + { + case OPadd: // this way when we want to do LEA + e1 = e; + e1free = FALSE; + e1isadd = TRUE; + break; + case OPind: + case OPpostinc: // when doing (*p++ = ...) + case OPpostdec: // when doing (*p-- = ...) + case OPbt: + case OPbtc: + case OPbtr: + case OPbts: + e1 = e->E1; + e1free = TRUE; + e1isadd = e1->Eoper == OPadd; + break; + default: +#ifdef DEBUG + elem_print(e); +#endif + assert(0); + } + e1ty = tybasic(e1->Ety); + if (e1isadd) + { e12 = e1->E2; + e11 = e1->E1; + } + + /* First see if we can replace *(e+&v) with + * MOV idxreg,e + * EA = [ES:] &v+idxreg + */ + f = FLconst; + if (e1isadd && + ((e12->Eoper == OPrelconst +#if TARGET_SEGMENTED + && (f = el_fl(e12)) != FLfardata +#endif + ) || + (e12->Eoper == OPconst && !I16 && !e1->Ecount && (!I64 || el_signx32(e12)))) && + !(I64 && config.flags3 & CFG3pic) && + e1->Ecount == e1->Ecomsub && +#if TARGET_SEGMENTED + (!e1->Ecount || (~keepmsk & ALLREGS & mMSW) || (e1ty != TYfptr && e1ty != TYhptr)) && +#endif + tysize(e11->Ety) == REGSIZE + ) + { unsigned char t; /* component of r/m field */ + int ss; + int ssi; + +#if !TARGET_SEGMENTED + if (e12->Eoper == OPrelconst) + f = el_fl(e12); +#endif + /*assert(datafl[f]);*/ /* what if addr of func? */ + if (!I16) + { /* Any register can be an index register */ + regm_t idxregs = allregs & ~keepmsk; + assert(idxregs); + + /* See if e1->E1 can be a scaled index */ + ss = isscaledindex(e11); + if (ss) + { + /* Load index register with result of e11->E1 */ + c = cdisscaledindex(e11,&idxregs,keepmsk); + reg = findreg(idxregs); + { + t = stackfl[f] ? 2 : 0; + pcs->Irm = modregrm(t,0,4); + pcs->Isib = modregrm(ss,reg & 7,5); + if (reg & 8) + pcs->Irex |= REX_X; + } + } + else if ((e11->Eoper == OPmul || e11->Eoper == OPshl) && + !e11->Ecount && + e11->E2->Eoper == OPconst && + (ssi = ssindex(e11->Eoper,e11->E2->EV.Vuns)) != 0 + ) + { + regm_t scratchm; + +#if 0 && TARGET_LINUX + assert(f != FLgot && f != FLgotoff); +#endif + char ssflags = ssindex_array[ssi].ssflags; + if (ssflags & SSFLnobp && stackfl[f]) + goto L6; + + // Load index register with result of e11->E1 + c = scodelem(e11->E1,&idxregs,keepmsk,TRUE); + reg = findreg(idxregs); + + int ss1 = ssindex_array[ssi].ss1; + if (ssflags & SSFLlea) + { + assert(!stackfl[f]); + pcs->Irm = modregrm(2,0,4); + pcs->Isib = modregrm(ss1,reg & 7,reg & 7); + if (reg & 8) + pcs->Irex |= REX_X | REX_B; + } + else + { int rbase; + unsigned r; + + scratchm = ALLREGS & ~keepmsk; + c = cat(c,allocreg(&scratchm,&r,TYint)); + + if (ssflags & SSFLnobase1) + { t = 0; + rbase = 5; + } + else + { t = 0; + rbase = reg; + if (rbase == BP || rbase == R13) + { static unsigned imm32[4] = {1+1,2+1,4+1,8+1}; + + // IMUL r,BP,imm32 + c = genc2(c,0x69,modregxrmx(3,r,rbase),imm32[ss1]); + goto L7; + } + } + + c = gen2sib(c,0x8D,modregxrm(t,r,4),modregrm(ss1,reg & 7,rbase & 7)); + if (reg & 8) + code_orrex(c, REX_X); + if (rbase & 8) + code_orrex(c, REX_B); + if (I64) + code_orrex(c, REX_W); + + if (ssflags & SSFLnobase1) + { code_last(c)->IFL1 = FLconst; + code_last(c)->IEV1.Vuns = 0; + } + L7: + if (ssflags & SSFLnobase) + { t = stackfl[f] ? 2 : 0; + rbase = 5; + } + else + { t = 2; + rbase = r; + assert(rbase != BP); + } + pcs->Irm = modregrm(t,0,4); + pcs->Isib = modregrm(ssindex_array[ssi].ss2,r & 7,rbase & 7); + if (r & 8) + pcs->Irex |= REX_X; + if (rbase & 8) + pcs->Irex |= REX_B; + } + freenode(e11->E2); + freenode(e11); + } + else + { + L6: + /* Load index register with result of e11 */ + c = scodelem(e11,&idxregs,keepmsk,TRUE); + setaddrmode(pcs, idxregs); +#if 0 && TARGET_LINUX + if (e12->EV.sp.Vsym->Sfl == FLgot || e12->EV.sp.Vsym->Sfl == FLgotoff) + { + gotref = 1; +#if 1 + reg = findreg(idxregs & (ALLREGS | mBP)); + pcs->Irm = modregrm(2,0,4); + pcs->Isib = modregrm(0,reg,BX); +#else + pcs->Isib = modregrm(0,pcs->Irm,BX); + pcs->Irm = modregrm(2,0,4); +#endif + } + else +#endif + if (stackfl[f]) /* if we need [EBP] too */ + { unsigned idx = pcs->Irm & 7; + if (pcs->Irex & REX_B) + pcs->Irex = (pcs->Irex & ~REX_B) | REX_X; + pcs->Isib = modregrm(0,idx,BP); + pcs->Irm = modregrm(2,0,4); + } + } + } + else + { + idxregs = IDXREGS & ~keepmsk; /* only these can be index regs */ + assert(idxregs); +#if 0 && TARGET_LINUX + assert(f != FLgot && f != FLgotoff); +#endif + if (stackfl[f]) /* if stack data type */ + { idxregs &= mSI | mDI; /* BX can't index off stack */ + if (!idxregs) goto L1; /* index regs aren't avail */ + t = 6; /* [BP+SI+disp] */ + } + else + t = 0; /* [SI + disp] */ + c = scodelem(e11,&idxregs,keepmsk,TRUE); /* load idx reg */ + pcs->Irm = getaddrmode(idxregs) ^ t; + } + if (f == FLpara) + refparam = TRUE; + else if (f == FLauto || f == FLtmp || f == FLbprel || f == FLfltreg) + reflocal = TRUE; +#if TARGET_SEGMENTED + else if (f == FLcsdata || tybasic(e12->Ety) == TYcptr) + pcs->Iflags |= CFcs; +#endif + else + assert(f != FLreg); + pcs->IFL1 = f; + if (f != FLconst) + pcs->IEVsym1 = e12->EV.sp.Vsym; + pcs->IEVoffset1 = e12->EV.sp.Voffset; /* += ??? */ + + /* If e1 is a CSE, we must generate an addressing mode */ + /* but also leave EA in registers so others can use it */ + if (e1->Ecount) + { unsigned flagsave; + + idxregs = IDXREGS & ~keepmsk; + c = cat(c,allocreg(&idxregs,®,TYoffset)); + +#if TARGET_SEGMENTED + /* If desired result is a far pointer, we'll have */ + /* to load another register with the segment of v */ + if (e1ty == TYfptr) + { + unsigned msreg; + + idxregs |= mMSW & ALLREGS & ~keepmsk; + c = cat(c,allocreg(&idxregs,&msreg,TYfptr)); + msreg = findregmsw(idxregs); + /* MOV msreg,segreg */ + c = genregs(c,0x8C,segfl[f],msreg); + } +#endif + opsave = pcs->Iop; + flagsave = pcs->Iflags; + pcs->Iop = 0x8D; + code_newreg(pcs, reg); + if (!I16) + pcs->Iflags &= ~CFopsize; + if (I64) + pcs->Irex |= REX_W; + c = gen(c,pcs); /* LEA idxreg,EA */ + cssave(e1,idxregs,TRUE); + if (!I16) + pcs->Iflags = flagsave; + if (stackfl[f] && (config.wflags & WFssneds)) // if pointer into stack + pcs->Iflags |= CFss; // add SS: override + pcs->Iop = opsave; + pcs->IFL1 = FLoffset; + pcs->IEV1.Vuns = 0; + setaddrmode(pcs, idxregs); + } + freenode(e12); + if (e1free) + freenode(e1); + goto Lptr; + } + + L1: + + /* The rest of the cases could be a far pointer */ + + idxregs = (I16 ? IDXREGS : allregs) & ~keepmsk; // only these can be index regs + assert(idxregs); + if (!I16 && + (sz == REGSIZE || (I64 && sz == 4)) && + keepmsk & RMstore) + idxregs |= regcon.mvar; + +#if TARGET_SEGMENTED + switch (e1ty) + { case TYfptr: /* if far pointer */ + case TYhptr: + idxregs = (mES | IDXREGS) & ~keepmsk; // need segment too + assert(idxregs & mES); + pcs->Iflags |= CFes; /* ES segment override */ + break; + case TYsptr: /* if pointer to stack */ + if (config.wflags & WFssneds) // if SS != DS + pcs->Iflags |= CFss; /* then need SS: override */ + break; + case TYcptr: /* if pointer to code */ + pcs->Iflags |= CFcs; /* then need CS: override */ + break; + } +#endif + pcs->IFL1 = FLoffset; + pcs->IEV1.Vuns = 0; + + /* see if we can replace *(e+c) with + * MOV idxreg,e + * [MOV ES,segment] + * EA = [ES:]c[idxreg] + */ + if (e1isadd && e12->Eoper == OPconst && + (!I64 || el_signx32(e12)) && + (tysize(e12->Ety) == REGSIZE || (I64 && tysize(e12->Ety) == 4)) && + (!e1->Ecount || !e1free) + ) + { int ss; + + pcs->IEV1.Vuns = e12->EV.Vuns; + freenode(e12); + if (e1free) freenode(e1); + if (!I16 && e11->Eoper == OPadd && !e11->Ecount && + tysize(e11->Ety) == REGSIZE) + { + e12 = e11->E2; + e11 = e11->E1; + e1 = e1->E1; + e1free = TRUE; + goto L4; + } + if (!I16 && (ss = isscaledindex(e11)) != 0) + { // (v * scale) + const + c = cdisscaledindex(e11,&idxregs,keepmsk); + reg = findreg(idxregs); + pcs->Irm = modregrm(0,0,4); + pcs->Isib = modregrm(ss,reg & 7,5); + if (reg & 8) + pcs->Irex |= REX_X; + } + else + { + c = scodelem(e11,&idxregs,keepmsk,TRUE); // load index reg + setaddrmode(pcs, idxregs); + } + goto Lptr; + } + + /* Look for *(v1 + v2) + * EA = [v1][v2] + */ + + if (!I16 && e1isadd && (!e1->Ecount || !e1free) && + (tysize[e1ty] == REGSIZE || (I64 && tysize[e1ty] == 4))) + { code *c2; + regm_t idxregs2; + unsigned base,index; + int ss; + + L4: + // Look for *(v1 + v2 << scale) + ss = isscaledindex(e12); + if (ss) + { + c = scodelem(e11,&idxregs,keepmsk,TRUE); + idxregs2 = allregs & ~(idxregs | keepmsk); + c2 = cdisscaledindex(e12,&idxregs2,keepmsk | idxregs); + } + + // Look for *(v1 << scale + v2) + else if ((ss = isscaledindex(e11)) != 0) + { + idxregs2 = idxregs; + c = cdisscaledindex(e11,&idxregs2,keepmsk); + idxregs = allregs & ~(idxregs2 | keepmsk); + c2 = scodelem(e12,&idxregs,keepmsk | idxregs2,TRUE); + } + // Look for *(((v1 << scale) + c1) + v2) + else if (e11->Eoper == OPadd && !e11->Ecount && + e11->E2->Eoper == OPconst && + (ss = isscaledindex(e11->E1)) != 0 + ) + { + pcs->IEV1.Vuns = e11->E2->EV.Vuns; + idxregs2 = idxregs; + c = cdisscaledindex(e11->E1,&idxregs2,keepmsk); + idxregs = allregs & ~(idxregs2 | keepmsk); + c2 = scodelem(e12,&idxregs,keepmsk | idxregs2,TRUE); + freenode(e11->E2); + freenode(e11); + } + else + { + c = scodelem(e11,&idxregs,keepmsk,TRUE); + idxregs2 = allregs & ~(idxregs | keepmsk); + c2 = scodelem(e12,&idxregs2,keepmsk | idxregs,TRUE); + } + c = cat(c,c2); + base = findreg(idxregs); + index = findreg(idxregs2); + pcs->Irm = modregrm(2,0,4); + pcs->Isib = modregrm(ss,index & 7,base & 7); + if (index & 8) + pcs->Irex |= REX_X; + if (base & 8) + pcs->Irex |= REX_B; + if (e1free) freenode(e1); + goto Lptr; + } + + /* give up and replace *e1 with + * MOV idxreg,e + * EA = 0[idxreg] + * pinholeopt() will usually correct the 0, we need it in case + * we have a pointer to a long and need an offset to the second + * word. + */ + + assert(e1free); + c = scodelem(e1,&idxregs,keepmsk,TRUE); /* load index register */ + setaddrmode(pcs, idxregs); + Lptr: + if (config.flags3 & CFG3ptrchk) + cod3_ptrchk(&c,pcs,keepmsk); // validate pointer code + break; + case FLdatseg: + assert(0); +#if 0 + pcs->Irm = modregrm(0,0,BPRM); + pcs->IEVpointer1 = e->EVpointer; + break; +#endif + case FLfltreg: + reflocal = TRUE; + pcs->Irm = modregrm(2,0,BPRM); + pcs->IEV1.Vint = 0; + break; + case FLreg: + goto L2; + case FLpara: + refparam = TRUE; + pcs->Irm = modregrm(2,0,BPRM); + goto L2; + + case FLauto: + if (s->Sclass == SCfastpar && regcon.params & mask[s->Spreg]) + { + if (keepmsk & RMload) + { + if (sz == REGSIZE) // could this be (sz <= REGSIZE) ? + { + pcs->Irm = modregrm(3,0,s->Spreg & 7); + if (s->Spreg & 8) + pcs->Irex |= REX_B; + regcon.used |= mask[s->Spreg]; + break; + } + } + else + regcon.params &= ~mask[s->Spreg]; + } + case FLtmp: + case FLbprel: + reflocal = TRUE; + pcs->Irm = modregrm(2,0,BPRM); + goto L2; + case FLextern: + if (s->Sident[0] == '_' && memcmp(s->Sident + 1,"tls_array",10) == 0) + { +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + // Rewrite as GS:[0000], or FS:[0000] for 64 bit + if (I64) + { + pcs->Irm = modregrm(0, 0, 4); + pcs->Isib = modregrm(0, 4, 5); // don't use [RIP] addressing + pcs->IFL1 = FLconst; + pcs->IEV1.Vuns = 0; + pcs->Iflags = CFfs; + pcs->Irex |= REX_W; + } + else + { + pcs->Irm = modregrm(0, 0, BPRM); + pcs->IFL1 = FLconst; + pcs->IEV1.Vuns = 0; + pcs->Iflags = CFgs; + } + break; +#else + pcs->Iflags |= CFfs; // add FS: override +#endif + } +#if TARGET_SEGMENTED + if (s->ty() & mTYcs && LARGECODE) + goto Lfardata; +#endif + goto L3; + case FLdata: + case FLudata: +#if TARGET_SEGMENTED + case FLcsdata: +#endif +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + case FLgot: + case FLgotoff: + case FLtlsdata: +#endif + L3: + pcs->Irm = modregrm(0,0,BPRM); + L2: + if (fl == FLreg) + { +#ifdef DEBUG + if (!(s->Sregm & regcon.mvar)) symbol_print(s); +#endif + assert(s->Sregm & regcon.mvar); + + /* Attempting to paint a float as an integer or an integer as a float + * will cause serious problems since the EA is loaded separatedly from + * the opcode. The only way to deal with this is to prevent enregistering + * such variables. + */ + if (tyxmmreg(ty) && !(s->Sregm & XMMREGS) || + !tyxmmreg(ty) && (s->Sregm & XMMREGS)) + cgreg_unregister(s->Sregm); + + if ( + s->Sclass == SCregpar || + s->Sclass == SCparameter) + { refparam = TRUE; + reflocal = TRUE; // kludge to set up prolog + } + pcs->Irm = modregrm(3,0,s->Sreglsw & 7); + if (s->Sreglsw & 8) + pcs->Irex |= REX_B; + if (e->EV.sp.Voffset == 1 && sz == 1) + { assert(s->Sregm & BYTEREGS); + assert(s->Sreglsw < 4); + pcs->Irm |= 4; // use 2nd byte of register + } + else + { assert(!e->EV.sp.Voffset); + if (I64 && sz == 1 && s->Sreglsw >= 4) + pcs->Irex |= REX; + } + } +#if TARGET_SEGMENTED + else if (s->ty() & mTYcs && !(fl == FLextern && LARGECODE)) + { + pcs->Iflags |= CFcs | CFoff; + } +#endif +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (I64 && config.flags3 & CFG3pic && + (fl == FLtlsdata || s->ty() & mTYthread)) + { + pcs->Iflags |= CFopsize; + pcs->Irex = 0x48; + } +#endif + pcs->IEVsym1 = s; + pcs->IEVoffset1 = e->EV.sp.Voffset; + if (sz == 1) + { /* Don't use SI or DI for this variable */ + s->Sflags |= GTbyte; + if (e->EV.sp.Voffset > 1) + s->Sflags &= ~GTregcand; + } + else if (e->EV.sp.Voffset) + s->Sflags &= ~GTregcand; + if (!(keepmsk & RMstore)) // if not store only + { s->Sflags |= SFLread; // assume we are doing a read + } + break; + case FLpseudo: +#if MARS + assert(0); +#else + { + unsigned u = s->Sreglsw; + c = getregs(pseudomask[u]); + pcs->Irm = modregrm(3,0,pseudoreg[u] & 7); + break; + } +#endif +#if TARGET_SEGMENTED + case FLfardata: +#endif + case FLfunc: /* reading from code seg */ + if (config.exe & EX_flat) + goto L3; + Lfardata: + { + regm_t regm = ALLREGS & ~keepmsk; // need scratch register + code *c1 = allocreg(®m,®,TYint); + /* MOV mreg,seg of symbol */ + c = gencs(CNIL,0xB8 + reg,0,FLextern,s); + c->Iflags = CFseg; + c = gen2(c,0x8E,modregrmx(3,0,reg)); /* MOV ES,reg */ + c = cat3(c1,getregs(mES),c); + pcs->Iflags |= CFes | CFoff; /* ES segment override */ + goto L3; + } + + case FLstack: + assert(!I16); + pcs->Irm = modregrm(2,0,4); + pcs->Isib = modregrm(0,4,SP); + pcs->IEVsym1 = s; + pcs->IEVoffset1 = e->EV.sp.Voffset; + break; + + default: +#ifdef DEBUG + WRFL((enum FL)fl); + symbol_print(s); +#endif + assert(0); + } + return c; +} + +/***************************** + * Given an opcode and EA in cs, generate code + * for each floating register in turn. + * Input: + * tym either TYdouble or TYfloat + */ + +code *fltregs(code *pcs,tym_t tym) +{ code *c; + + assert(!I64); + tym = tybasic(tym); + if (I32) + { + c = getregs((tym == TYfloat) ? mAX : mAX | mDX); + if (tym != TYfloat) + { + pcs->IEVoffset1 += REGSIZE; + NEWREG(pcs->Irm,DX); + c = gen(c,pcs); + pcs->IEVoffset1 -= REGSIZE; + } + NEWREG(pcs->Irm,AX); + c = gen(c,pcs); + } + else + { + c = getregs((tym == TYfloat) ? FLOATREGS_16 : DOUBLEREGS_16); + pcs->IEVoffset1 += (tym == TYfloat) ? 2 : 6; + if (tym == TYfloat) + NEWREG(pcs->Irm,DX); + else + NEWREG(pcs->Irm,AX); + c = gen(c,pcs); + pcs->IEVoffset1 -= 2; + if (tym == TYfloat) + NEWREG(pcs->Irm,AX); + else + NEWREG(pcs->Irm,BX); + gen(c,pcs); + if (tym != TYfloat) + { pcs->IEVoffset1 -= 2; + NEWREG(pcs->Irm,CX); + gen(c,pcs); + pcs->IEVoffset1 -= 2; /* note that exit is with Voffset unaltered */ + NEWREG(pcs->Irm,DX); + gen(c,pcs); + } + } + return c; +} + + +/***************************** + * Given a result in registers, test it for TRUE or FALSE. + * Will fail if TYfptr and the reg is ES! + * If saveflag is TRUE, preserve the contents of the + * registers. + */ + +code *tstresult(regm_t regm,tym_t tym,unsigned saveflag) +{ + unsigned scrreg; /* scratch register */ + regm_t scrregm; + +#ifdef DEBUG + //if (!(regm & (mBP | ALLREGS))) + // printf("tstresult(regm = %s, tym = x%x, saveflag = %d)\n", + // regm_str(regm),tym,saveflag); +#endif + assert(regm & (XMMREGS | mBP | ALLREGS)); + tym = tybasic(tym); + code *ce = CNIL; + unsigned reg = findreg(regm); + unsigned sz = tysize[tym]; + if (sz == 1) + { assert(regm & BYTEREGS); + ce = genregs(ce,0x84,reg,reg); // TEST regL,regL + if (I64 && reg >= 4) + code_orrex(ce, REX); + return ce; + } + if (regm & XMMREGS) + { + unsigned xreg; + regm_t xregs = XMMREGS & ~regm; + ce = allocreg(&xregs, &xreg, TYdouble); + unsigned op = 0; + if (tym == TYdouble || tym == TYidouble || tym == TYcdouble) + op = 0x660000; + ce = gen2(ce,op | 0x0F57,modregrm(3,xreg-XMM0,xreg-XMM0)); // XORPS xreg,xreg + gen2(ce,op | 0x0F2E,modregrm(3,xreg-XMM0,reg-XMM0)); // UCOMISS xreg,reg + if (tym == TYcfloat || tym == TYcdouble) + { code *cnop = gennop(CNIL); + genjmp(ce,JNE,FLcode,(block *) cnop); // JNE L1 + genjmp(ce,JP, FLcode,(block *) cnop); // JP L1 + reg = findreg(regm & ~mask[reg]); + gen2(ce,op | 0x0F2E,modregrm(3,xreg-XMM0,reg-XMM0)); // UCOMISS xreg,reg + ce = cat(ce, cnop); + } + return ce; + } + if (sz <= REGSIZE) + { + if (!I16) + { + if (tym == TYfloat) + { if (saveflag) + { + scrregm = allregs & ~regm; /* possible scratch regs */ + ce = allocreg(&scrregm,&scrreg,TYoffset); /* allocate scratch reg */ + ce = genmovreg(ce,scrreg,reg); /* MOV scrreg,msreg */ + reg = scrreg; + } + ce = cat(ce,getregs(mask[reg])); + return gen2(ce,0xD1,modregrmx(3,4,reg)); // SHL reg,1 + } + ce = gentstreg(ce,reg); // TEST reg,reg + if (sz == SHORTSIZE) + ce->Iflags |= CFopsize; /* 16 bit operands */ + else if (sz == 8) + code_orrex(ce, REX_W); + } + else + ce = gentstreg(ce,reg); // TEST reg,reg + return ce; + } + if (saveflag || tyfv(tym)) + { + scrregm = ALLREGS & ~regm; /* possible scratch regs */ + ce = allocreg(&scrregm,&scrreg,TYoffset); /* allocate scratch reg */ + if (I32 || sz == REGSIZE * 2) + { code *c; + + assert(regm & mMSW && regm & mLSW); + + reg = findregmsw(regm); + if (I32) + { + if (tyfv(tym)) + { c = genregs(CNIL,0x0FB7,scrreg,reg); // MOVZX scrreg,msreg + ce = cat(ce,c); + } + else + { ce = genmovreg(ce,scrreg,reg); /* MOV scrreg,msreg */ + if (tym == TYdouble || tym == TYdouble_alias) + gen2(ce,0xD1,modregrm(3,4,scrreg)); /* SHL scrreg,1 */ + } + } + else + { + ce = genmovreg(ce,scrreg,reg); /* MOV scrreg,msreg */ + if (tym == TYfloat) + gen2(ce,0xD1,modregrm(3,4,scrreg)); /* SHL scrreg,1 */ + } + reg = findreglsw(regm); + genorreg(ce,scrreg,reg); /* OR scrreg,lsreg */ + } + else if (sz == 8) + { /* !I32 */ + ce = genmovreg(ce,scrreg,AX); /* MOV scrreg,AX */ + if (tym == TYdouble || tym == TYdouble_alias) + gen2(ce,0xD1,modregrm(3,4,scrreg)); // SHL scrreg,1 + genorreg(ce,scrreg,BX); /* OR scrreg,BX */ + genorreg(ce,scrreg,CX); /* OR scrreg,CX */ + genorreg(ce,scrreg,DX); /* OR scrreg,DX */ + } + else + assert(0); + } + else + { + if (I32 || sz == REGSIZE * 2) + { + /* can't test ES:LSW for 0 */ + assert(regm & mMSW & ALLREGS && regm & (mLSW | mBP)); + + reg = findregmsw(regm); + ce = getregs(mask[reg]); /* we're going to trash reg */ + if (tyfloating(tym) && sz == 2 * intsize) + ce = gen2(ce,0xD1,modregrm(3,4,reg)); // SHL reg,1 + ce = genorreg(ce,reg,findreglsw(regm)); // OR reg,reg+1 + if (I64) + code_orrex(ce, REX_W); + } + else if (sz == 8) + { assert(regm == DOUBLEREGS_16); + ce = getregs(mAX); // allocate AX + if (tym == TYdouble || tym == TYdouble_alias) + ce = gen2(ce,0xD1,modregrm(3,4,AX)); // SHL AX,1 + genorreg(ce,AX,BX); // OR AX,BX + genorreg(ce,AX,CX); // OR AX,CX + genorreg(ce,AX,DX); // OR AX,DX + } + else + assert(0); + } + code_orflag(ce,CFpsw); + return ce; +} + + +/****************************** + * Given the result of an expression is in retregs, + * generate necessary code to return result in *pretregs. + */ + +code *fixresult(elem *e,regm_t retregs,regm_t *pretregs) +{ code *c,*ce; + unsigned reg,rreg; + regm_t forccs,forregs; + tym_t tym; + int sz; + + //printf("fixresult(e = %p, retregs = %s, *pretregs = %s)\n",e,regm_str(retregs),regm_str(*pretregs)); + if (*pretregs == 0) return CNIL; /* if don't want result */ + assert(e && retregs); /* need something to work with */ + forccs = *pretregs & mPSW; + forregs = *pretregs & (mST01 | mST0 | mBP | ALLREGS | mES | mSTACK | XMMREGS); + tym = tybasic(e->Ety); +#if TARGET_SEGMENTED + if (tym == TYstruct) + // Hack to support cdstreq() + tym = (forregs & mMSW) ? TYfptr : TYnptr; +#else + if (tym == TYstruct) + { + // Hack to support cdstreq() + assert(!(forregs & mMSW)); + tym = TYnptr; + } +#endif + c = CNIL; + sz = tysize[tym]; + if (sz == 1) + { + assert(retregs & BYTEREGS); + unsigned reg = findreg(retregs); + if (e->Eoper == OPvar && + e->EV.sp.Voffset == 1 && + e->EV.sp.Vsym->Sfl == FLreg) + { + assert(reg < 4); + if (forccs) + c = gen2(c,0x84,modregrm(3,reg | 4,reg | 4)); // TEST regH,regH + forccs = 0; + } + } + if ((retregs & forregs) == retregs) /* if already in right registers */ + *pretregs = retregs; + else if (forregs) /* if return the result in registers */ + { + if (forregs & (mST01 | mST0)) + return fixresult87(e,retregs,pretregs); + ce = CNIL; + unsigned opsflag = FALSE; + if (I16 && sz == 8) + { if (forregs & mSTACK) + { assert(retregs == DOUBLEREGS_16); + /* Push floating regs */ + c = CNIL; + ce = gen1(ce,0x50 + AX); + gen1(ce,0x50 + BX); + gen1(ce,0x50 + CX); + gen1(ce,0x50 + DX); + stackpush += DOUBLESIZE; + } + else if (retregs & mSTACK) + { assert(forregs == DOUBLEREGS_16); + /* Pop floating regs */ + c = getregs(forregs); + ce = gen1(ce,0x58 + DX); + gen1(ce,0x58 + CX); + gen1(ce,0x58 + BX); + gen1(ce,0x58 + AX); + stackpush -= DOUBLESIZE; + retregs = DOUBLEREGS_16; /* for tstresult() below */ + } + else +#ifdef DEBUG + printf("retregs = x%x, forregs = x%x\n",retregs,forregs), +#endif + assert(0); + if (EOP(e)) + opsflag = TRUE; + } + else + { + c = allocreg(pretregs,&rreg,tym); /* allocate return regs */ + if (retregs & XMMREGS) + { + reg = findreg(retregs & XMMREGS); + // MOVSD floatreg, XMM? + ce = genfltreg(ce,xmmstore(tym),reg - XMM0,0); + if (mask[rreg] & XMMREGS) + // MOVSD XMM?, floatreg + ce = genfltreg(ce,xmmload(tym),rreg - XMM0,0); + else + { + // MOV rreg,floatreg + ce = genfltreg(ce,0x8B,rreg,0); + if (sz == 8) + { + if (I32) + { + rreg = findregmsw(*pretregs); + ce = genfltreg(ce,0x8B,rreg,4); + } + else + code_orrex(ce,REX_W); + } + } + } + else if (forregs & XMMREGS) + { + reg = findreg(retregs & (mBP | ALLREGS)); + // MOV floatreg,reg + ce = genfltreg(ce,0x89,reg,0); + if (sz == 8) + { + if (I32) + { + reg = findregmsw(retregs); + ce = genfltreg(ce,0x89,reg,4); + } + else + code_orrex(ce,REX_W); + } + // MOVSS/MOVSD XMMreg,floatreg + ce = genfltreg(ce,xmmload(tym),rreg - XMM0,0); + } + else if (sz > REGSIZE) + { + unsigned msreg = findregmsw(retregs); + unsigned lsreg = findreglsw(retregs); + unsigned msrreg = findregmsw(*pretregs); + unsigned lsrreg = findreglsw(*pretregs); + + ce = genmovreg(ce,msrreg,msreg); /* MOV msrreg,msreg */ + ce = genmovreg(ce,lsrreg,lsreg); /* MOV lsrreg,lsreg */ + } + else + { + assert(!(retregs & XMMREGS)); + assert(!(forregs & XMMREGS)); + reg = findreg(retregs & (mBP | ALLREGS)); + ce = genmovreg(ce,rreg,reg); /* MOV rreg,reg */ + } + } + c = cat(c,ce); + cssave(e,retregs | *pretregs,opsflag); + forregs = 0; /* don't care about result in reg */ + /* cuz we have real result in rreg */ + retregs = *pretregs & ~mPSW; + } + if (forccs) /* if return result in flags */ + c = cat(c,tstresult(retregs,tym,forregs)); + return c; +} + + +/******************************** + * Generate code sequence to call C runtime library support routine. + * clib = CLIBxxxx + * keepmask = mask of registers not to destroy. Currently can + * handle only 1. Should use a temporary rather than + * push/pop for speed. + */ + +int clib_inited = 0; // != 0 if initialized + +code *callclib(elem *e,unsigned clib,regm_t *pretregs,regm_t keepmask) +{ + //printf("callclib(e = %p, clib = %d, *pretregs = %s, keepmask = %s\n", e, clib, regm_str(*pretregs), regm_str(keepmask)); + //elem_print(e); +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + static symbol lib[] = + { +/* Convert destroyed regs into saved regs */ +#define Z(desregs) (~(desregs) & (mBP| mES | ALLREGS)) +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +#define N(name) "_" name +#else +#define N(name) name +#endif + +/* Shorthand to map onto SYMBOLY() */ +#define Y(desregs,name) SYMBOLY(FLfunc,Z(desregs),N(name),0) + + Y(0,"_LCMP__"), // CLIBlcmp + Y(mAX|mCX|mDX,"_LMUL__"), // CLIBlmul +#if 1 + Y(mAX|mBX|mCX|mDX,"_LDIV__"), // CLIBldiv + Y(mAX|mBX|mCX|mDX,"_LDIV__"), // CLIBlmod + Y(mAX|mBX|mCX|mDX,"_ULDIV__"), // CLIBuldiv + Y(mAX|mBX|mCX|mDX,"_ULDIV__"), // CLIBulmod +#else + Y(ALLREGS,"_LDIV__"), // CLIBldiv + Y(ALLREGS,"_LDIV__"), // CLIBlmod + Y(ALLREGS,"_ULDIV__"), // CLIBuldiv + Y(ALLREGS,"_ULDIV__"), // CLIBulmod +#endif +#if 0 + Y(DOUBLEREGS_16,"_DNEG"), + Y(mAX|mBX|mCX|mDX,"_DMUL"), // CLIBdmul + Y(mAX|mBX|mCX|mDX,"_DDIV"), // CLIBddiv + Y(0,"_DTST0"), // CLIBdtst0 + Y(0,"_DTST0EXC"), // CLIBdtst0exc + Y(0,"_DCMP"), // CLIBdcmp + Y(0,"_DCMPEXC"), // CLIBdcmpexc + + Y(mAX|mBX|mCX|mDX,"_DADD"), // CLIBdadd + Y(mAX|mBX|mCX|mDX,"_DSUB"), // CLIBdsub + + Y(mAX|mBX|mCX|mDX,"_FMUL"), // CLIBfmul + Y(mAX|mBX|mCX|mDX,"_FDIV"), // CLIBfdiv + Y(0,"_FTST0"), // CLIBftst0 + Y(0,"_FTST0EXC"), // CLIBftst0exc + Y(0,"_FCMP"), // CLIBfcmp + Y(0,"_FCMPEXC"), // CLIBfcmpexc + Y(FLOATREGS_32,"_FNEG"), // CLIBfneg + Y(mAX|mBX|mCX|mDX,"_FADD"), // CLIBfadd + Y(mAX|mBX|mCX|mDX,"_FSUB"), // CLIBfsub +#endif + Y(DOUBLEREGS_32,"_DBLLNG"), // CLIBdbllng + Y(DOUBLEREGS_32,"_LNGDBL"), // CLIBlngdbl + Y(DOUBLEREGS_32,"_DBLINT"), // CLIBdblint + Y(DOUBLEREGS_32,"_INTDBL"), // CLIBintdbl + Y(DOUBLEREGS_32,"_DBLUNS"), // CLIBdbluns + Y(DOUBLEREGS_32,"_UNSDBL"), // CLIBunsdbl + Y(mAX|mST0,"_DBLULNG"), // CLIBdblulng +#if 0 + {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _ULNGDBL@ ulngdbl +#endif + Y(DOUBLEREGS_32,"_DBLFLT"), // CLIBdblflt + Y(DOUBLEREGS_32,"_FLTDBL"), // CLIBfltdbl + + Y(DOUBLEREGS_32,"_DBLLLNG"), // CLIBdblllng + Y(DOUBLEREGS_32,"_LLNGDBL"), // CLIBllngdbl + Y(DOUBLEREGS_32,"_DBLULLNG"), // CLIBdblullng + Y(DOUBLEREGS_32,"_ULLNGDBL"), // CLIBullngdbl + + Y(0,"_DTST"), // CLIBdtst + Y(mES|mBX,"_HTOFPTR"), // CLIBvptrfptr + Y(mES|mBX,"_HCTOFPTR"), // CLIBcvptrfptr + Y(0,"_87TOPSW"), // CLIB87topsw + Y(mST0,"_FLTTO87"), // CLIBfltto87 + Y(mST0,"_DBLTO87"), // CLIBdblto87 + Y(mST0|mAX,"_DBLINT87"), // CLIBdblint87 + Y(mST0|mAX|mDX,"_DBLLNG87"), // CLIBdbllng87 + Y(0,"_FTST"), // CLIBftst + Y(0,"_FCOMPP"), // CLIBfcompp + Y(0,"_FTEST"), // CLIBftest + Y(0,"_FTEST0"), // CLIBftest0 + Y(mST0|mAX|mBX|mCX|mDX,"_FDIVP"), // CLIBfdiv87 + + Y(mST0|mST01,"Cmul"), // CLIBcmul + Y(mAX|mCX|mDX|mST0|mST01,"Cdiv"), // CLIBcdiv + Y(mAX|mST0|mST01,"Ccmp"), // CLIBccmp + + Y(mST0,"_U64_LDBL"), // CLIBu64_ldbl +#if ELFOBJ || MACHOBJ + Y(mST0|mAX|mDX,"_LDBLULLNG"), // CLIBld_u64 +#else + Y(mST0|mAX|mDX,"__LDBLULLNG"), // CLIBld_u64 +#endif + }; +#else + static symbol lib[CLIBMAX] = + { +/* Convert destroyed regs into saved regs */ +#define Z(desregs) (~(desregs) & (mBP| mES | ALLREGS)) + +/* Shorthand to map onto SYMBOLY() */ +#define Y(desregs,name) SYMBOLY(FLfunc,Z(desregs),name,0) + + Y(0,"_LCMP@"), + Y(mAX|mCX|mDX,"_LMUL@"), + Y(ALLREGS,"_LDIV@"), + Y(ALLREGS,"_LDIV@"), + Y(ALLREGS,"_ULDIV@"), + Y(ALLREGS,"_ULDIV@"), + Y(mAX|mBX|mCX|mDX,"_DMUL@"), + Y(mAX|mBX|mCX|mDX,"_DDIV@"), + Y(0,"_DTST0@"), + Y(0,"_DTST0EXC@"), + Y(0,"_DCMP@"), + Y(0,"_DCMPEXC@"), + + /* _DNEG@ only really destroys EDX, but then EAX would hold */ + /* 2 values, and we can't handle that. */ + + /* _DNEG@ only really destroys AX, but then BX,CX,DX would hold */ + /* 2 values, and we can't handle that. */ + + Y(DOUBLEREGS_16,"_DNEG@"), + Y(mAX|mBX|mCX|mDX,"_DADD@"), + Y(mAX|mBX|mCX|mDX,"_DSUB@"), + + Y(mAX|mBX|mCX|mDX,"_FMUL@"), + Y(mAX|mBX|mCX|mDX,"_FDIV@"), + Y(0,"_FTST0@"), + Y(0,"_FTST0EXC@"), + Y(0,"_FCMP@"), + Y(0,"_FCMPEXC@"), + Y(FLOATREGS_16,"_FNEG@"), + Y(mAX|mBX|mCX|mDX,"_FADD@"), + Y(mAX|mBX|mCX|mDX,"_FSUB@"), + Y(DOUBLEREGS_16,"_DBLLNG@"), + Y(DOUBLEREGS_16,"_LNGDBL@"), + Y(DOUBLEREGS_16,"_DBLINT@"), + Y(DOUBLEREGS_16,"_INTDBL@"), + Y(DOUBLEREGS_16,"_DBLUNS@"), + Y(DOUBLEREGS_16,"_UNSDBL@"), + Y(DOUBLEREGS_16,"_DBLULNG@"), + Y(DOUBLEREGS_16,"_ULNGDBL@"), + Y(DOUBLEREGS_16,"_DBLFLT@"), + Y(ALLREGS,"_FLTDBL@"), + + Y(DOUBLEREGS_16,"_DBLLLNG@"), + Y(DOUBLEREGS_16,"_LLNGDBL@"), +#if 0 + Y(DOUBLEREGS_16,"__DBLULLNG"), +#else + Y(DOUBLEREGS_16,"_DBLULLNG@"), +#endif + Y(DOUBLEREGS_16,"_ULLNGDBL@"), + + Y(0,"_DTST@"), + Y(mES|mBX,"_HTOFPTR@"), // CLIBvptrfptr + Y(mES|mBX,"_HCTOFPTR@"), // CLIBcvptrfptr + Y(0,"_87TOPSW@"), // CLIB87topsw + Y(mST0,"_FLTTO87@"), // CLIBfltto87 + Y(mST0,"_DBLTO87@"), // CLIBdblto87 + Y(mST0|mAX,"_DBLINT87@"), // CLIBdblint87 + Y(mST0|mAX|mDX,"_DBLLNG87@"), // CLIBdbllng87 + Y(0,"_FTST@"), + Y(0,"_FCOMPP@"), // CLIBfcompp + Y(0,"_FTEST@"), // CLIBftest + Y(0,"_FTEST0@"), // CLIBftest0 + Y(mST0|mAX|mBX|mCX|mDX,"_FDIVP"), // CLIBfdiv87 + + // NOTE: desregs is wrong for 16 bit code, mBX should be included + Y(mST0|mST01,"_Cmul"), // CLIBcmul + Y(mAX|mCX|mDX|mST0|mST01,"_Cdiv"), // CLIBcdiv + Y(mAX|mST0|mST01,"_Ccmp"), // CLIBccmp + + Y(mST0,"_U64_LDBL"), // CLIBu64_ldbl + Y(mST0|mAX|mDX,"__LDBLULLNG"), // CLIBld_u64 + }; +#endif + + static struct + { + regm_t retregs16; /* registers that 16 bit result is returned in */ + regm_t retregs32; /* registers that 32 bit result is returned in */ + char pop; /* # of bytes popped off of stack upon return */ + char flags; + #define INF32 1 // if 32 bit only + #define INFfloat 2 // if this is floating point + #define INFwkdone 4 // if weak extern is already done + #define INF64 8 // if 64 bit only + char push87; // # of pushes onto the 8087 stack + char pop87; // # of pops off of the 8087 stack + } info[CLIBMAX] = + { + {0,0,0,0}, /* _LCMP@ lcmp */ + {mDX|mAX,mDX|mAX,0,0}, // _LMUL@ lmul + {mDX|mAX,mDX|mAX,0,0}, // _LDIV@ ldiv + {mCX|mBX,mCX|mBX,0,0}, /* _LDIV@ lmod */ + {mDX|mAX,mDX|mAX,0,0}, /* _ULDIV@ uldiv */ + {mCX|mBX,mCX|mBX,0,0}, /* _ULDIV@ ulmod */ + +#if TARGET_WINDOS + {DOUBLEREGS_16,DOUBLEREGS_32,8,INFfloat,1,1}, // _DMUL@ dmul + {DOUBLEREGS_16,DOUBLEREGS_32,8,INFfloat,1,1}, // _DDIV@ ddiv + {0,0,0,2}, // _DTST0@ + {0,0,0,2}, // _DTST0EXC@ + {0,0,8,INFfloat,1,1}, // _DCMP@ dcmp + {0,0,8,INFfloat,1,1}, // _DCMPEXC@ dcmp + {DOUBLEREGS_16,DOUBLEREGS_32,0,2}, // _DNEG@ dneg + {DOUBLEREGS_16,DOUBLEREGS_32,8,INFfloat,1,1}, // _DADD@ dadd + {DOUBLEREGS_16,DOUBLEREGS_32,8,INFfloat,1,1}, // _DSUB@ dsub + + {FLOATREGS_16,FLOATREGS_32,0,INFfloat,1,1}, // _FMUL@ fmul + {FLOATREGS_16,FLOATREGS_32,0,INFfloat,1,1}, // _FDIV@ fdiv + {0,0,0,2}, // _FTST0@ + {0,0,0,2}, // _FTST0EXC@ + {0,0,0,INFfloat,1,1}, // _FCMP@ fcmp + {0,0,0,INFfloat,1,1}, // _FCMPEXC@ fcmp + {FLOATREGS_16,FLOATREGS_32,0,2}, // _FNEG@ fneg + {FLOATREGS_16,FLOATREGS_32,0,INFfloat,1,1}, // _FADD@ fadd + {FLOATREGS_16,FLOATREGS_32,0,INFfloat,1,1}, // _FSUB@ fsub +#endif + + {mDX|mAX,mAX,0,INFfloat,1,1}, // _DBLLNG@ dbllng + {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _LNGDBL@ lngdbl + {mAX,mAX,0,INFfloat,1,1}, // _DBLINT@ dblint + {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _INTDBL@ intdbl + {mAX,mAX,0,INFfloat,1,1}, // _DBLUNS@ dbluns + {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _UNSDBL@ unsdbl +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + {mDX|mAX,mAX,0,INF32|INFfloat,0,1}, // _DBLULNG@ dblulng +#else + {mDX|mAX,mAX,0,INFfloat,1,1}, // _DBLULNG@ dblulng +#endif +#if TARGET_WINDOS + {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _ULNGDBL@ ulngdbl +#endif + {FLOATREGS_16,FLOATREGS_32,0,INFfloat,1,1}, // _DBLFLT@ dblflt + {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _FLTDBL@ fltdbl + + {DOUBLEREGS_16,mDX|mAX,0,INFfloat,1,1}, // _DBLLLNG@ + {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _LLNGDBL@ +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + {DOUBLEREGS_16,mDX|mAX,0,INFfloat,2,2}, // _DBLULLNG@ +#else + {DOUBLEREGS_16,mDX|mAX,0,INFfloat,1,1}, // _DBLULLNG@ +#endif + {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _ULLNGDBL@ + + {0,0,0,2}, // _DTST@ dtst + {mES|mBX,mES|mBX,0,0}, // _HTOFPTR@ vptrfptr + {mES|mBX,mES|mBX,0,0}, // _HCTOFPTR@ cvptrfptr + {0,0,0,2}, // _87TOPSW@ 87topsw + {mST0,mST0,0,INFfloat,1,0}, // _FLTTO87@ fltto87 + {mST0,mST0,0,INFfloat,1,0}, // _DBLTO87@ dblto87 + {mAX,mAX,0,2}, // _DBLINT87@ dblint87 + {mDX|mAX,mAX,0,2}, // _DBLLNG87@ dbllng87 + {0,0,0,2}, // _FTST@ + {mPSW,mPSW,0,INFfloat,0,2}, // _FCOMPP@ + {mPSW,mPSW,0,2}, // _FTEST@ + {mPSW,mPSW,0,2}, // _FTEST0@ + {mST0,mST0,0,INFfloat,1,1}, // _FDIV@ + + {mST01,mST01,0,INF32|INFfloat,3,5}, // _Cmul + {mST01,mST01,0,INF32|INFfloat,0,2}, // _Cdiv + {mPSW, mPSW, 0,INF32|INFfloat,0,4}, // _Ccmp + + {mST0,mST0,0,INF32|INF64|INFfloat,2,1}, // _U64_LDBL + {0,mDX|mAX,0,INF32|INF64|INFfloat,1,2}, // __LDBLULLNG + }; + + if (!clib_inited) /* if not initialized */ + { + assert(sizeof(lib) / sizeof(lib[0]) == CLIBMAX); + assert(sizeof(info) / sizeof(info[0]) == CLIBMAX); + for (int i = 0; i < CLIBMAX; i++) + { lib[i].Stype = tsclib; +#if MARS + lib[i].Sxtrnnum = 0; + lib[i].Stypidx = 0; +#endif + } + + if (!I16) + { /* Adjust table for 386 */ + lib[CLIBdbllng].Sregsaved = Z(DOUBLEREGS_32); + lib[CLIBlngdbl].Sregsaved = Z(DOUBLEREGS_32); + lib[CLIBdblint].Sregsaved = Z(DOUBLEREGS_32); + lib[CLIBintdbl].Sregsaved = Z(DOUBLEREGS_32); +#if TARGET_WINDOS + lib[CLIBfneg].Sregsaved = Z(FLOATREGS_32); + lib[CLIBdneg].Sregsaved = Z(DOUBLEREGS_32); + lib[CLIBdbluns].Sregsaved = Z(DOUBLEREGS_32); + lib[CLIBunsdbl].Sregsaved = Z(DOUBLEREGS_32); + lib[CLIBdblulng].Sregsaved = Z(DOUBLEREGS_32); + lib[CLIBulngdbl].Sregsaved = Z(DOUBLEREGS_32); +#endif + lib[CLIBdblflt].Sregsaved = Z(DOUBLEREGS_32); + lib[CLIBfltdbl].Sregsaved = Z(DOUBLEREGS_32); + + lib[CLIBdblllng].Sregsaved = Z(DOUBLEREGS_32); + lib[CLIBllngdbl].Sregsaved = Z(DOUBLEREGS_32); + lib[CLIBdblullng].Sregsaved = Z(DOUBLEREGS_32); + lib[CLIBullngdbl].Sregsaved = Z(DOUBLEREGS_32); + + if (I64) + { + info[CLIBullngdbl].retregs32 = mAX; + info[CLIBdblullng].retregs32 = mAX; + } + } + clib_inited++; + } +#undef Z + + assert(clib < CLIBMAX); + symbol *s = &lib[clib]; + if (I16) + assert(!(info[clib].flags & (INF32 | INF64))); + code *cpop = CNIL; + code *c = getregs((~s->Sregsaved & (mES | mBP | ALLREGS)) & ~keepmask); // mask of regs destroyed + keepmask &= ~s->Sregsaved; + int npushed = numbitsset(keepmask); + gensaverestore2(keepmask, &c, &cpop); +#if 0 + while (keepmask) + { unsigned keepreg; + + if (keepmask & (mBP|ALLREGS)) + { keepreg = findreg(keepmask & (mBP|ALLREGS)); + c = gen1(c,0x50 + keepreg); /* PUSH keepreg */ + cpop = cat(gen1(CNIL,0x58 + keepreg),cpop); // POP keepreg + keepmask &= ~mask[keepreg]; + npushed++; + } + if (keepmask & mES) + { c = gen1(c,0x06); /* PUSH ES */ + cpop = cat(gen1(CNIL,0x07),cpop); /* POP ES */ + keepmask &= ~mES; + npushed++; + } + } +#endif + + c = cat(c, save87regs(info[clib].push87)); + for (int i = 0; i < info[clib].push87; i++) + c = cat(c, push87()); + + for (int i = 0; i < info[clib].pop87; i++) + pop87(); + + if (config.target_cpu >= TARGET_80386 && clib == CLIBlmul && !I32) + { static char lmul[] = { + 0x66,0xc1,0xe1,0x10, // shl ECX,16 + 0x8b,0xcb, // mov CX,BX ;ECX = CX,BX + 0x66,0xc1,0xe0,0x10, // shl EAX,16 + 0x66,0x0f,0xac,0xd0,0x10, // shrd EAX,EDX,16 ;EAX = DX,AX + 0x66,0xf7,0xe1, // mul ECX + 0x66,0x0f,0xa4,0xc2,0x10, // shld EDX,EAX,16 ;DX,AX = EAX + }; + + c = genasm(c,lmul,sizeof(lmul)); + } + else + { makeitextern(s); + int nalign = 0; + if (STACKALIGN == 16) + { // Align the stack (assume no args on stack) + int npush = npushed * REGSIZE + stackpush; + if (npush & (STACKALIGN - 1)) + { nalign = STACKALIGN - (npush & (STACKALIGN - 1)); + c = genc2(c,0x81,modregrm(3,5,SP),nalign); // SUB ESP,nalign + if (I64) + code_orrex(c, REX_W); + } + } + c = gencs(c,(LARGECODE) ? 0x9A : 0xE8,0,FLfunc,s); // CALL s + if (nalign) + { c = genc2(c,0x81,modregrm(3,0,SP),nalign); // ADD ESP,nalign + if (I64) + code_orrex(c, REX_W); + } + calledafunc = 1; + + if (I16 && // bug in Optlink for weak references + config.flags3 & CFG3wkfloat && + (info[clib].flags & (INFfloat | INFwkdone)) == INFfloat) + { info[clib].flags |= INFwkdone; + makeitextern(rtlsym[RTLSYM_INTONLY]); + obj_wkext(s,rtlsym[RTLSYM_INTONLY]); + } + } + if (I16) + stackpush -= info[clib].pop; + regm_t retregs = I16 ? info[clib].retregs16 : info[clib].retregs32; + return cat(cat(c,cpop),fixresult(e,retregs,pretregs)); +} + +/************************************************* + * Helper function for converting OPparam's into array of Parameters. + */ +struct Parameter { elem *e; int reg; unsigned numalign; }; + +void fillParameters(elem *e, Parameter *parameters, int *pi) +{ + if (e->Eoper == OPparam) + { + fillParameters(e->E1, parameters, pi); + fillParameters(e->E2, parameters, pi); + freenode(e); + } + else + { + parameters[*pi].e = e; + (*pi)++; + } +} + + +/******************************* + * Generate code sequence for function call. + */ + +code *cdfunc(elem *e,regm_t *pretregs) +{ unsigned numpara = 0; + unsigned stackpushsave; + unsigned preg; + regm_t keepmsk; + unsigned numalign = 0; + code *c; + + //printf("cdfunc()\n"); elem_print(e); + assert(e); + stackpushsave = stackpush; /* so we can compute # of parameters */ + cgstate.stackclean++; + c = CNIL; + keepmsk = 0; + if (OTbinary(e->Eoper)) // if parameters + { + if (I16) + { + c = cat(c, params(e->E2,2)); // push parameters + } + else if (I32) + { + unsigned stackalign = REGSIZE; + tym_t tyf = tybasic(e->E1->Ety); + + // First compute numpara, the total bytes pushed on the stack + switch (tyf) + { +#if TARGET_SEGMENTED + case TYf16func: + stackalign = 2; + goto Ldefault; +#endif + case TYmfunc: + case TYjfunc: + // last parameter goes into register + elem *ep; + for (ep = e->E2; ep->Eoper == OPparam; ep = ep->E2) + { + numpara += paramsize(ep->E1,stackalign); + } + unsigned sz; + if (tyf == TYjfunc && + // This must match type_jparam() + !(tyjparam(ep->Ety) || + ((tybasic(ep->Ety) == TYstruct || tybasic(ep->Ety) == TYarray) && + (sz = type_size(ep->ET)) <= intsize && sz != 3 && sz) + ) + ) + { + numpara += paramsize(ep,stackalign); + } + break; + default: + Ldefault: + numpara += paramsize(e->E2,stackalign); + break; + } + assert((numpara & (REGSIZE - 1)) == 0); + assert((stackpush & (REGSIZE - 1)) == 0); + + /* Special handling for call to __tls_get_addr, we must save registers + * before evaluating the parameter, so that the parameter load and call + * are adjacent. + */ + if (e->E2->Eoper != OPparam && e->E1->Eoper == OPvar) + { symbol *s = e->E1->EV.sp.Vsym; + if (s == tls_get_addr_sym) + c = getregs(~s->Sregsaved & (mBP | ALLREGS | mES | XMMREGS)); + } + + + /* Adjust start of the stack so after all args are pushed, + * the stack will be aligned. + */ + if (STACKALIGN == 16 && (numpara + stackpush) & (STACKALIGN - 1)) + { + numalign = STACKALIGN - ((numpara + stackpush) & (STACKALIGN - 1)); + c = genc2(c,0x81,modregrm(3,5,SP),numalign); // SUB ESP,numalign + if (I64) + code_orrex(c, REX_W); + c = genadjesp(c, numalign); + stackpush += numalign; + stackpushsave += numalign; + } + + switch (tyf) + { +#if TARGET_SEGMENTED + case TYf16func: + stackalign = 2; + goto Ldefault2; +#endif + case TYmfunc: // last parameter goes into ECX + preg = CX; + goto L1; + case TYjfunc: // last parameter goes into EAX + preg = AX; + goto L1; + L1: + { elem *ep; + elem *en; + for (ep = e->E2; ep->Eoper == OPparam; ep = en) + { + c = cat(c,params(ep->E1,stackalign)); + en = ep->E2; + freenode(ep); + } + unsigned sz; + if (tyf == TYjfunc && + // This must match type_jparam() + !(tyjparam(ep->Ety) || + ((tybasic(ep->Ety) == TYstruct || tybasic(ep->Ety) == TYarray) && + (sz = type_size(ep->ET)) <= intsize && sz != 3 && sz) + ) + ) + { + c = cat(c,params(ep,stackalign)); + goto Lret; + } + // preg is the register to put the parameter ep in + keepmsk = mask[preg]; // don't change preg when evaluating func address + regm_t retregs = keepmsk; + if (ep->Eoper == OPstrthis) + { code *c2; + + code *c1 = getregs(retregs); + // LEA preg,np[ESP] + unsigned np = stackpush - ep->EV.Vuns; // stack delta to parameter + c2 = genc1(CNIL,0x8D,(modregrm(0,4,SP) << 8) | modregrm(2,preg,4),FLconst,np); + if (I64) + code_orrex(c2, REX_W); + c = cat3(c,c1,c2); + } + else + { code *cp = codelem(ep,&retregs,FALSE); + c = cat(c,cp); + } + goto Lret; + } + default: + Ldefault2: + c = cat(c, params(e->E2,stackalign)); // push parameters + break; + } + } + else + { assert(I64); + + // Easier to deal with parameters as an array: parameters[0..np] + int np = el_nparams(e->E2); + Parameter *parameters = (Parameter *)alloca(np * sizeof(Parameter)); + + { int n = 0; + fillParameters(e->E2, parameters, &n); + assert(n == np); + } + + /* Special handling for call to __tls_get_addr, we must save registers + * before evaluating the parameter, so that the parameter load and call + * are adjacent. + */ + if (np == 1 && e->E1->Eoper == OPvar) + { symbol *s = e->E1->EV.sp.Vsym; + if (s == tls_get_addr_sym) + c = getregs(~s->Sregsaved & (mBP | ALLREGS | mES | XMMREGS)); + } + + unsigned stackalign = REGSIZE; + + // Figure out which parameters go in registers + // Compute numpara, the total bytes pushed on the stack + int r = 0; + int xmmcnt = XMM0; + for (int i = np; --i >= 0;) + { + static const unsigned char argregs[6] = { DI,SI,DX,CX,R8,R9 }; + elem *ep = parameters[i].e; + tym_t ty = ep->Ety; + if (r < sizeof(argregs)/sizeof(argregs[0])) // if more arg regs + { unsigned sz; + if ( + // This must match type_jparam() + ty64reg(ty) || + ((tybasic(ty) == TYstruct || tybasic(ty) == TYarray) && + ((sz = type_size(ep->ET)) == 1 || sz == 2 || sz == 4 || sz == 8)) + ) + { + parameters[i].reg = argregs[r]; + r++; + continue; // goes in register, not stack + } + } + if (xmmcnt <= XMM7) + { + if (tyxmmreg(ty)) + { + parameters[i].reg = xmmcnt; + xmmcnt++; + continue; // goes in register, not stack + } + } + + // Parameter i goes on the stack + parameters[i].reg = -1; // -1 means no register + unsigned alignsize = el_alignsize(ep); + parameters[i].numalign = 0; + if (alignsize > stackalign) + { unsigned newnumpara = (numpara + (alignsize - 1)) & ~(alignsize - 1); + parameters[i].numalign = newnumpara - numpara; + numpara = newnumpara; + } + numpara += paramsize(ep,stackalign); + } + + assert((numpara & (REGSIZE - 1)) == 0); + assert((stackpush & (REGSIZE - 1)) == 0); + + /* Should consider reordering the order of evaluation of the parameters + * so that args that go into registers are evaluated after args that get + * pushed. We can reorder args that are constants or relconst's. + */ + + /* Adjust start of the stack so after all args are pushed, + * the stack will be aligned. + */ + if (STACKALIGN == 16 && (numpara + stackpush) & (STACKALIGN - 1)) + { + numalign = STACKALIGN - ((numpara + stackpush) & (STACKALIGN - 1)); + c = genc2(c,0x81,(REX_W << 16) | modregrm(3,5,SP),numalign); // SUB RSP,numalign + c = genadjesp(c, numalign); + stackpush += numalign; + stackpushsave += numalign; + } + + int regsaved[XMM7 + 1]; + memset(regsaved, -1, sizeof(regsaved)); + code *crest = NULL; + regm_t saved = 0; + + /* Parameters go into the registers RDI,RSI,RDX,RCX,R8,R9 + * float and double parameters go into XMM0..XMM7 + * For variadic functions, count of XMM registers used goes in AL + */ + for (int i = 0; i < np; i++) + { + elem *ep = parameters[i].e; + int preg = parameters[i].reg; + if (preg == -1) + { + /* Push parameter on stack, but keep track of registers used + * in the process. If they interfere with keepmsk, we'll have + * to save/restore them. + */ + code *csave = NULL; + regm_t overlap = msavereg & keepmsk; + msavereg |= keepmsk; + code *cp = params(ep,stackalign); + regm_t tosave = keepmsk & ~msavereg; + msavereg &= ~keepmsk | overlap; + + // tosave is the mask to save and restore + for (int j = 0; tosave; j++) + { regm_t mi = mask[j]; + assert(j <= XMM7); + if (mi & tosave) + { + unsigned idx; + csave = regsave.save(csave, j, &idx); + crest = regsave.restore(crest, j, idx); + saved |= mi; + keepmsk &= ~mi; // don't need to keep these for rest of params + tosave &= ~mi; + } + } + + c = cat4(c, csave, cp, NULL); + + // Alignment for parameter comes after it got pushed + unsigned numalign = parameters[i].numalign; + if (numalign) + { + c = genc2(c,0x81,(REX_W << 16) | modregrm(3,5,SP),numalign); // SUB RSP,numalign + c = genadjesp(c, numalign); + stackpush += numalign; + } + } + else + { + // Goes in register preg, not stack + regm_t retregs = mask[preg]; + if (ep->Eoper == OPstrthis) + { + code *c1 = getregs(retregs); + // LEA preg,np[RSP] + unsigned np = stackpush - ep->EV.Vuns; // stack delta to parameter + code *c2 = genc1(CNIL,0x8D,(REX_W << 16) | + (modregrm(0,4,SP) << 8) | + modregxrm(2,preg,4), FLconst,np); + c = cat3(c,c1,c2); + } + else + { code *cp = scodelem(ep,&retregs,keepmsk,FALSE); + c = cat(c,cp); + } + keepmsk |= retregs; // don't change preg when evaluating func address + } + } + + // Restore any register parameters we saved + c = cat4(c, getregs(saved), crest, NULL); + keepmsk |= saved; + + // Variadic functions store the number of XMM registers used in AL + if (e->Eflags & EFLAGS_variadic) + { code *c1 = getregs(mAX); + c1 = movregconst(c1,AX,xmmcnt - XMM0,1); + c = cat(c, c1); + keepmsk |= mAX; + } + } + } + else + { + /* Adjust start of the stack so + * the stack will be aligned. + */ + if (STACKALIGN == 16 && (stackpush) & (STACKALIGN - 1)) + { + numalign = STACKALIGN - ((stackpush) & (STACKALIGN - 1)); + c = genc2(NULL,0x81,modregrm(3,5,SP),numalign); // SUB ESP,numalign + if (I64) + code_orrex(c, REX_W); + c = genadjesp(c, numalign); + stackpush += numalign; + stackpushsave += numalign; + } + + // Variadic functions store the number of XMM registers used in AL + if (I64 && e->Eflags & EFLAGS_variadic) + { code *c1 = getregs(mAX); + c1 = movregconst(c1,AX,0,1); + c = cat(c, c1); + keepmsk |= mAX; + } + } +Lret: + cgstate.stackclean--; + if (I16) + numpara = stackpush - stackpushsave; + else + { + if (numpara != stackpush - stackpushsave) + printf("numpara = %d, stackpush = %d, stackpushsave = %d\n", numpara, stackpush, stackpushsave); + assert(numpara == stackpush - stackpushsave); + } + return cat(c,funccall(e,numpara,numalign,pretregs,keepmsk)); +} + +/*********************************** + */ + +code *cdstrthis(elem *e,regm_t *pretregs) +{ + code *c1; + code *c2; + + assert(tysize(e->Ety) == REGSIZE); + unsigned reg = findreg(*pretregs & allregs); + c1 = getregs(mask[reg]); + // LEA reg,np[ESP] + unsigned np = stackpush - e->EV.Vuns; // stack delta to parameter + c2 = genc1(CNIL,0x8D,(modregrm(0,4,SP) << 8) | modregxrm(2,reg,4),FLconst,np); + if (I64) + code_orrex(c2, REX_W); + return cat3(c1,c2,fixresult(e,mask[reg],pretregs)); +} + +/****************************** + * Call function. All parameters are pushed onto the stack, numpara gives + * the size of them all. + */ + +STATIC code * funccall(elem *e,unsigned numpara,unsigned numalign,regm_t *pretregs,regm_t keepmsk) +{ + elem *e1; + code *c,*ce,cs; + tym_t tym1; + char farfunc; + regm_t retregs; + symbol *s; + + //printf("funccall(e = %p, *pretregs = x%x, numpara = %d, numalign = %d)\n",e,*pretregs,numpara,numalign); + calledafunc = 1; + /* Determine if we need frame for function prolog/epilog */ +#if TARGET_WINDOS + if (config.memmodel == Vmodel) + { + if (tyfarfunc(funcsym_p->ty())) + needframe = TRUE; + } +#endif + e1 = e->E1; + tym1 = tybasic(e1->Ety); + farfunc = tyfarfunc(tym1) || tym1 == TYifunc; + c = NULL; + if (e1->Eoper == OPvar) + { /* Call function directly */ + code *c1; + +#ifdef DEBUG + if (!tyfunc(tym1)) WRTYxx(tym1); +#endif + assert(tyfunc(tym1)); + s = e1->EV.sp.Vsym; + if (s->Sflags & SFLexit) + c = NULL; + else if (s != tls_get_addr_sym) + c = save87(); // assume 8087 regs are all trashed + if (s->Sflags & SFLexit) + // Function doesn't return, so don't worry about registers + // it may use + c1 = NULL; + else if (!tyfunc(s->ty()) || !(config.flags4 & CFG4optimized)) + // so we can replace func at runtime + c1 = getregs(~fregsaved & (mBP | ALLREGS | mES | XMMREGS)); + else + c1 = getregs(~s->Sregsaved & (mBP | ALLREGS | mES | XMMREGS)); + if (strcmp(s->Sident,"alloca") == 0) + { +#if 1 + s = rtlsym[RTLSYM_ALLOCA]; + makeitextern(s); + c1 = cat(c1,getregs(mCX)); + c1 = genc(c1,0x8D,modregrm(2,CX,BPRM),FLallocatmp,0,0,0); // LEA CX,&localsize[BP] + if (I64) + code_orrex(c1, REX_W); + usedalloca = 2; // new way +#else + usedalloca = 1; // old way +#endif + } + if (sytab[s->Sclass] & SCSS) // if function is on stack (!) + { + retregs = allregs & ~keepmsk; + s->Sflags &= ~GTregcand; + s->Sflags |= SFLread; + ce = cat(c1,cdrelconst(e1,&retregs)); +#if TARGET_SEGMENTED + if (farfunc) + goto LF1; + else +#endif + goto LF2; + } + else + { int fl; + + fl = FLfunc; + if (!tyfunc(s->ty())) + fl = el_fl(e1); + if (tym1 == TYifunc) + c1 = gen1(c1,0x9C); // PUSHF + ce = CNIL; +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (s != tls_get_addr_sym) + { + //printf("call %s\n", s->Sident); + ce = load_localgot(); + } +#endif + ce = gencs(ce,farfunc ? 0x9A : 0xE8,0,fl,s); // CALL extern + ce->Iflags |= farfunc ? (CFseg | CFoff) : (CFselfrel | CFoff); +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (s == tls_get_addr_sym) + { + if (I32) + { + /* Append a NOP so GNU linker has patch room + */ + ce = gen1(ce, 0x90); // NOP + code_orflag(ce, CFvolatile); // don't schedule it + } + else + { /* Prepend 66 66 48 so GNU linker has patch room + */ + assert(I64); + ce->Irex = REX | REX_W; + ce = cat(gen1(CNIL, 0x66), ce); + ce = cat(gen1(CNIL, 0x66), ce); + } + } +#endif + } + ce = cat(c1,ce); + } + else + { /* Call function via pointer */ + elem *e11; + tym_t e11ty; + +#ifdef DEBUG + if (e1->Eoper != OPind + ) { WRFL((enum FL)el_fl(e1)); WROP(e1->Eoper); } +#endif + c = save87(); // assume 8087 regs are all trashed + assert(e1->Eoper == OPind); + e11 = e1->E1; + e11ty = tybasic(e11->Ety); +#if TARGET_SEGMENTED + assert(!I16 || (e11ty == (farfunc ? TYfptr : TYnptr))); +#else + assert(!I16 || (e11ty == TYnptr)); +#endif + + /* if we can't use loadea() */ + if ((EOP(e11) || e11->Eoper == OPconst) && + (e11->Eoper != OPind || e11->Ecount)) + { + unsigned reg; + + retregs = allregs & ~keepmsk; + cgstate.stackclean++; + ce = scodelem(e11,&retregs,keepmsk,TRUE); + cgstate.stackclean--; + /* Kill registers destroyed by an arbitrary function call */ + ce = cat(ce,getregs((mBP | ALLREGS | mES | XMMREGS) & ~fregsaved)); +#if TARGET_SEGMENTED + if (e11ty == TYfptr) + { unsigned lsreg; + LF1: + reg = findregmsw(retregs); + lsreg = findreglsw(retregs); + floatreg = TRUE; /* use float register */ + reflocal = TRUE; + ce = genc1(ce,0x89, /* MOV floatreg+2,reg */ + modregrm(2,reg,BPRM),FLfltreg,REGSIZE); + genc1(ce,0x89, /* MOV floatreg,lsreg */ + modregrm(2,lsreg,BPRM),FLfltreg,0); + if (tym1 == TYifunc) + gen1(ce,0x9C); // PUSHF + genc1(ce,0xFF, /* CALL [floatreg] */ + modregrm(2,3,BPRM),FLfltreg,0); + } + else +#endif + { + LF2: + reg = findreg(retregs); + ce = gen2(ce,0xFF,modregrmx(3,2,reg)); /* CALL reg */ + if (I64) + code_orrex(ce, REX_W); + } + } + else + { + if (tym1 == TYifunc) + c = gen1(c,0x9C); // PUSHF + // CALL [function] + cs.Iflags = 0; + cgstate.stackclean++; + ce = loadea(e11,&cs,0xFF,farfunc ? 3 : 2,0,keepmsk,(mBP|ALLREGS|mES|XMMREGS) & ~fregsaved); + cgstate.stackclean--; + freenode(e11); + } + s = NULL; + } + c = cat(c,ce); + freenode(e1); + + /* See if we will need the frame pointer. + Calculate it here so we can possibly use BP to fix the stack. + */ +#if 0 + if (!needframe) + { SYMIDX si; + + /* If there is a register available for this basic block */ + if (config.flags4 & CFG4optimized && (ALLREGS & ~regcon.used)) + ; + else + { + for (si = 0; si < globsym.top; si++) + { symbol *s = globsym.tab[si]; + + if (s->Sflags & GTregcand && type_size(s->Stype) != 0) + { + if (config.flags4 & CFG4optimized) + { /* If symbol is live in this basic block and */ + /* isn't already in a register */ + if (s->Srange && vec_testbit(dfoidx,s->Srange) && + s->Sfl != FLreg) + { /* Then symbol must be allocated on stack */ + needframe = TRUE; + break; + } + } + else + { if (mfuncreg == 0) /* if no registers left */ + { needframe = TRUE; + break; + } + } + } + } + } + } +#endif + + retregs = regmask(e->Ety, tym1); + + // If stack needs cleanup + if (OTbinary(e->Eoper) && + !typfunc(tym1) && + !(s && s->Sflags & SFLexit)) + { + if (tym1 == TYhfunc) + { // Hidden parameter is popped off by the callee + c = genadjesp(c, -REGSIZE); + stackpush -= REGSIZE; + if (numpara + numalign > REGSIZE) + c = genstackclean(c, numpara + numalign - REGSIZE, retregs); + } + else + c = genstackclean(c,numpara + numalign,retregs); + } + else + { + c = genadjesp(c,-numpara); + stackpush -= numpara; + if (numalign) + c = genstackclean(c,numalign,retregs); + } + + /* Special handling for functions which return a floating point + value in the top of the 8087 stack. + */ + + if (retregs & mST0) + { + c = genadjfpu(c, 1); + if (*pretregs) // if we want the result + { //assert(stackused == 0); + push87(); // one item on 8087 stack + return cat(c,fixresult87(e,retregs,pretregs)); + } + else + /* Pop unused result off 8087 stack */ + c = gen2(c,0xDD,modregrm(3,3,0)); /* FPOP */ + } + else if (retregs & mST01) + { + c = genadjfpu(c, 2); + if (*pretregs) // if we want the result + { assert(stackused == 0); + push87(); + push87(); // two items on 8087 stack + return cat(c,fixresult_complex87(e,retregs,pretregs)); + } + else + { + // Pop unused result off 8087 stack + c = gen2(c,0xDD,modregrm(3,3,0)); // FPOP + c = gen2(c,0xDD,modregrm(3,3,0)); // FPOP + } + } + + return cat(c,fixresult(e,retregs,pretregs)); +} + +/*************************** + * Determine size of everything that will be pushed. + */ + +targ_size_t paramsize(elem *e,unsigned stackalign) +{ + targ_size_t psize = 0; + targ_size_t szb; + + while (e->Eoper == OPparam) /* if more params */ + { + elem *e2 = e->E2; + psize += paramsize(e->E1,stackalign); // push them backwards + e = e2; + } + tym_t tym = tybasic(e->Ety); + if (tyscalar(tym)) + szb = size(tym); + else if (tym == TYstruct) + szb = type_size(e->ET); + else + { +#ifdef DEBUG + WRTYxx(tym); +#endif + assert(0); + } + psize += align(stackalign,szb); /* align on word stack boundary */ + return psize; +} + +/*************************** + * Generate code to push parameter list. + * stackpush is incremented by stackalign for each PUSH. + */ + +code *params(elem *e,unsigned stackalign) +{ code *c,*ce,cs; + code *cp; + unsigned reg; + targ_size_t szb; // size before alignment + targ_size_t sz; // size after alignment + tym_t tym; + regm_t retregs; + elem *e1; + elem *e2; + symbol *s; + int fl; + + //printf("params(e = %p, stackalign = %d)\n", e, stackalign); + cp = NULL; + stackchanged = 1; + assert(e); + while (e->Eoper == OPparam) /* if more params */ + { + e2 = e->E2; + cp = cat(cp,params(e->E1,stackalign)); // push them backwards + freenode(e); + e = e2; + } + //printf("params()\n"); elem_print(e); + + tym = tybasic(e->Ety); + if (tyfloating(tym)) + obj_fltused(); + + int grex = I64 ? REX_W << 16 : 0; + + /* sz = number of bytes pushed */ + if (tyscalar(tym)) + szb = size(tym); + else if (tym == TYstruct) + szb = type_size(e->ET); + else + { +#ifdef DEBUG + WRTYxx(tym); +#endif + assert(0); + } + sz = align(stackalign,szb); /* align on word stack boundary */ + assert((sz & (stackalign - 1)) == 0); /* ensure that alignment worked */ + assert((sz & (REGSIZE - 1)) == 0); + + c = CNIL; + cs.Iflags = 0; + cs.Irex = 0; + switch (e->Eoper) + { +#if SCPP + case OPstrctor: + { + e1 = e->E1; + c = docommas(&e1); /* skip over any comma expressions */ + + c = genc2(c,0x81,grex | modregrm(3,5,SP),sz); // SUB SP,sizeof(struct) + stackpush += sz; + genadjesp(c,sz); + + // Find OPstrthis and set it to stackpush + exp2_setstrthis(e1,NULL,stackpush,NULL); + + retregs = 0; + ce = codelem(e1,&retregs,TRUE); + goto L2; + } + case OPstrthis: + // This is the parameter for the 'this' pointer corresponding to + // OPstrctor. We push a pointer to an object that was already + // allocated on the stack by OPstrctor. + { unsigned np; + + retregs = allregs; + c = allocreg(&retregs,®,TYoffset); + c = genregs(c,0x89,SP,reg); // MOV reg,SP + if (I64) + code_orrex(c, REX_W); + np = stackpush - e->EV.Vuns; // stack delta to parameter + c = genc2(c,0x81,grex | modregrmx(3,0,reg),np); // ADD reg,np + if (sz > REGSIZE) + { c = gen1(c,0x16); // PUSH SS + stackpush += REGSIZE; + } + c = gen1(c,0x50 + (reg & 7)); // PUSH reg + if (reg & 8) + code_orrex(c, REX_B); + stackpush += REGSIZE; + genadjesp(c,sz); + ce = CNIL; + goto L2; + } +#endif + case OPstrpar: + { code *cc,*c1,*c2,*c3; + unsigned rm; + unsigned seg; // segment override prefix flags + bool doneoff; + unsigned pushsize = REGSIZE; + unsigned op16 = 0; + unsigned npushes; + + e1 = e->E1; + if (sz == 0) + { + ce = docommas(&e1); /* skip over any commas */ + goto L2; + } + if ((sz & 3) == 0 && (sz / REGSIZE) <= 4 && e1->Eoper == OPvar) + { freenode(e); + e = e1; + goto L1; + } + cc = docommas(&e1); /* skip over any commas */ + seg = 0; /* assume no seg override */ + retregs = sz ? IDXREGS : 0; + doneoff = FALSE; + if (!I16 && sz & 2) // if odd number of words to push + { pushsize = 2; + op16 = 1; + } + else if (I16 && config.target_cpu >= TARGET_80386 && (sz & 3) == 0) + { pushsize = 4; // push DWORDs at a time + op16 = 1; + } + npushes = sz / pushsize; + switch (e1->Eoper) + { case OPind: +#if TARGET_SEGMENTED + if (sz) + { switch (tybasic(e1->E1->Ety)) + { + case TYfptr: + case TYhptr: + seg = CFes; + retregs |= mES; + break; + case TYsptr: + if (config.wflags & WFssneds) + seg = CFss; + break; + case TYcptr: + seg = CFcs; + break; + } + } +#endif + c1 = codelem(e1->E1,&retregs,FALSE); + freenode(e1); + break; + case OPvar: + /* Symbol is no longer a candidate for a register */ + e1->EV.sp.Vsym->Sflags &= ~GTregcand; + + if (!e1->Ecount && npushes > 4) + { /* Kludge to point at last word in struct. */ + /* Don't screw up CSEs. */ + e1->EV.sp.Voffset += sz - pushsize; + doneoff = TRUE; + } + //if (LARGEDATA) /* if default isn't DS */ + { static unsigned segtocf[4] = { CFes,CFcs,CFss,0 }; + unsigned s; + int fl; + + fl = el_fl(e1); +#if TARGET_SEGMENTED + if (fl == FLfardata) + { seg = CFes; + retregs |= mES; + } + else +#endif + { + s = segfl[fl]; + assert(s < 4); + seg = segtocf[s]; + if (seg == CFss && !(config.wflags & WFssneds)) + seg = 0; + } + } +#if TARGET_SEGMENTED + if (e1->Ety & mTYfar) + { seg = CFes; + retregs |= mES; + } +#endif + c1 = cdrelconst(e1,&retregs); + /* Reverse the effect of the previous add */ + if (doneoff) + e1->EV.sp.Voffset -= sz - pushsize; + freenode(e1); + break; + case OPstreq: + //case OPcond: + if (!(config.exe & EX_flat)) + { seg = CFes; + retregs |= mES; + } + c1 = codelem(e1,&retregs,FALSE); + break; + default: +#ifdef DEBUG + elem_print(e1); +#endif + assert(0); + } + reg = findreglsw(retregs); + rm = I16 ? regtorm[reg] : regtorm32[reg]; + if (op16) + seg |= CFopsize; // operand size + if (npushes <= 4) + { + assert(!doneoff); + for (c2 = CNIL; npushes > 1; npushes--) + { c2 = genc1(c2,0xFF,buildModregrm(2,6,rm),FLconst,pushsize * (npushes - 1)); // PUSH [reg] + code_orflag(c2,seg); + genadjesp(c2,pushsize); + } + c3 = gen2(CNIL,0xFF,buildModregrm(0,6,rm)); // PUSH [reg] + c3->Iflags |= seg; + genadjesp(c3,pushsize); + ce = cat4(cc,c1,c2,c3); + } + else if (sz) + { int size; + + c2 = getregs_imm(mCX | retregs); + /* MOV CX,sz/2 */ + c2 = movregconst(c2,CX,npushes,0); + if (!doneoff) + { /* This disgusting thing should be done when */ + /* reg is loaded. Too lazy to fix it now. */ + /* ADD reg,sz-2 */ + c2 = genc2(c2,0x81,grex | modregrmx(3,0,reg),sz-pushsize); + } + c3 = getregs(mCX); // the LOOP decrements it + c3 = gen2(c3,0xFF,buildModregrm(0,6,rm)); // PUSH [reg] + c3->Iflags |= seg | CFtarg2; + genc2(c3,0x81,grex | buildModregrm(3,5,reg),pushsize); // SUB reg,2 + size = ((seg & CFSEG) ? -8 : -7) - op16; + if (code_next(c3)->Iop != 0x81) + size++; + //genc2(c3,0xE2,0,size); // LOOP .-7 or .-8 + genjmp(c3,0xE2,FLcode,(block *)c3); // LOOP c3 + regimmed_set(CX,0); + genadjesp(c3,sz); + ce = cat4(cc,c1,c2,c3); + } + else + ce = cat(cc,c1); + stackpush += sz; + goto L2; + } + case OPind: + if (!e->Ecount) /* if *e1 */ + { if (sz <= REGSIZE) + { // Watch out for single byte quantities being up + // against the end of a segment or in memory-mapped I/O + if (!(config.exe & EX_flat) && szb == 1) + break; + goto L1; // can handle it with loadea() + } + + // Avoid PUSH MEM on the Pentium when optimizing for speed + if (config.flags4 & CFG4speed && + (config.target_cpu >= TARGET_80486 && + config.target_cpu <= TARGET_PentiumMMX) && + sz <= 2 * REGSIZE && + !tyfloating(tym)) + break; + + if (tym == TYldouble || tym == TYildouble || tycomplex(tym)) + break; + if (I32) + { + assert(sz == REGSIZE * 2); + ce = loadea(e,&cs,0xFF,6,REGSIZE,0,0); /* PUSH EA+4 */ + ce = genadjesp(ce,REGSIZE); + } + else + { + if (sz == DOUBLESIZE) + { ce = loadea(e,&cs,0xFF,6,DOUBLESIZE - REGSIZE,0,0); /* PUSH EA+6 */ + cs.IEVoffset1 -= REGSIZE; + gen(ce,&cs); /* PUSH EA+4 */ + ce = genadjesp(ce,REGSIZE); + getlvalue_lsw(&cs); + gen(ce,&cs); /* PUSH EA+2 */ + } + else /* TYlong */ + ce = loadea(e,&cs,0xFF,6,REGSIZE,0,0); /* PUSH EA+2 */ + ce = genadjesp(ce,REGSIZE); + } + stackpush += sz; + getlvalue_lsw(&cs); + gen(ce,&cs); /* PUSH EA */ + ce = genadjesp(ce,REGSIZE); + goto L2; + } + break; +#if TARGET_SEGMENTED + case OPnp_fp: + if (!e->Ecount) /* if (far *)e1 */ + { + int segreg; + tym_t tym1; + + e1 = e->E1; + tym1 = tybasic(e1->Ety); + /* BUG: what about pointers to functions? */ + switch (tym1) + { + case TYnptr: segreg = 3<<3; break; + case TYcptr: segreg = 1<<3; break; + default: segreg = 2<<3; break; + } + if (I32 && stackalign == 2) + c = gen1(c,0x66); /* push a word */ + c = gen1(c,0x06 + segreg); /* PUSH SEGREG */ + if (I32 && stackalign == 2) + code_orflag(c,CFopsize); // push a word + c = genadjesp(c,stackalign); + stackpush += stackalign; + ce = params(e1,stackalign); + goto L2; + } + break; +#endif + case OPrelconst: +#if TARGET_SEGMENTED + /* Determine if we can just push the segment register */ + /* Test size of type rather than TYfptr because of (long)(&v) */ + s = e->EV.sp.Vsym; + //if (sytab[s->Sclass] & SCSS && !I32) // if variable is on stack + // needframe = TRUE; // then we need stack frame + if (tysize[tym] == tysize[TYfptr] && + (fl = s->Sfl) != FLfardata && + /* not a function that CS might not be the segment of */ + (!((fl == FLfunc || s->ty() & mTYcs) && + (s->Sclass == SCcomdat || s->Sclass == SCextern || s->Sclass == SCinline || config.wflags & WFthunk)) || + (fl == FLfunc && config.exe == EX_DOSX) + ) + ) + { + stackpush += sz; + c = gen1(c,0x06 + /* PUSH SEGREG */ + (((fl == FLfunc || s->ty() & mTYcs) ? 1 : segfl[fl]) << 3)); + c = genadjesp(c,REGSIZE); + + if (config.target_cpu >= TARGET_80286 && !e->Ecount) + { ce = getoffset(e,STACK); + goto L2; + } + else + { c = cat(c,offsetinreg(e,&retregs)); + unsigned reg = findreg(retregs); + c = genpush(c,reg); // PUSH reg + genadjesp(c,REGSIZE); + } + goto ret; + } + if (config.target_cpu >= TARGET_80286 && !e->Ecount) + { + stackpush += sz; + if (tysize[tym] == tysize[TYfptr]) + { + /* PUSH SEG e */ + code *c1 = gencs(CNIL,0x68,0,FLextern,s); + c1->Iflags = CFseg; + genadjesp(c1,REGSIZE); + c = cat(c,c1); + } + ce = getoffset(e,STACK); + goto L2; + } +#endif + break; /* else must evaluate expression */ + case OPvar: + L1: + if (0 && I32 && sz == 2) + { /* 32 bit code, but pushing 16 bit values anyway */ + ce = loadea(e,&cs,0xFF,6,0,0,0); /* PUSH EA */ + // BUG: 0x66 fails with scheduler + ce = cat(gen1(CNIL,0x66),ce); /* 16 bit override */ + stackpush += sz; + genadjesp(ce,sz); + } + else if (config.flags4 & CFG4speed && + (config.target_cpu >= TARGET_80486 && + config.target_cpu <= TARGET_PentiumMMX) && + sz <= 2 * REGSIZE && + !tyfloating(tym)) + { // Avoid PUSH MEM on the Pentium when optimizing for speed + break; + } + else + { int regsize = REGSIZE; + unsigned flag = 0; + + if (I16 && config.target_cpu >= TARGET_80386 && sz > 2 && + !e->Ecount) + { regsize = 4; + flag |= CFopsize; + } + ce = loadea(e,&cs,0xFF,6,sz - regsize,RMload,0); // PUSH EA+sz-2 + code_orflag(ce,flag); + ce = genadjesp(ce,REGSIZE); + stackpush += sz; + while ((targ_int)(sz -= regsize) > 0) + { ce = cat(ce,loadea(e,&cs,0xFF,6,sz - regsize,RMload,0)); + code_orflag(ce,flag); + ce = genadjesp(ce,REGSIZE); + } + } + L2: + freenode(e); + c = cat(c,ce); + goto ret; + case OPconst: + { + char pushi = 0; + unsigned flag = 0; + int regsize = REGSIZE; + targ_int value; + + if (tycomplex(tym)) + break; + + if (I64 && tyfloating(tym) && sz > 4 && boolres(e)) + // Can't push 64 bit non-zero args directly + break; + + if (I32 && szb == 10) // special case for long double constants + { + assert(sz == 12); + value = ((unsigned short *)&e->EV.Vldouble)[4]; + stackpush += sz; + ce = genadjesp(NULL,sz); + for (int i = 2; i >= 0; i--) + { + if (reghasvalue(allregs, value, ®)) + ce = gen1(ce,0x50 + reg); // PUSH reg + else + ce = genc2(ce,0x68,0,value); // PUSH value + value = ((unsigned *)&e->EV.Vldouble)[i - 1]; + } + goto L2; + } + + assert(I64 || sz <= LNGDBLSIZE); + int i = sz; + if (!I16 && i == 2) + flag = CFopsize; + + if (config.target_cpu >= TARGET_80286) +// && (e->Ecount == 0 || e->Ecount != e->Ecomsub)) + { pushi = 1; + if (I16 && config.target_cpu >= TARGET_80386 && i >= 4) + { regsize = 4; + flag = CFopsize; + } + } + else if (i == REGSIZE) + break; + + stackpush += sz; + ce = genadjesp(NULL,sz); + targ_uns *pi = (targ_uns *) &e->EV.Vdouble; + targ_ushort *ps = (targ_ushort *) pi; + targ_ullong *pl = (targ_ullong *)pi; + i /= regsize; + do + { + if (i) /* be careful not to go negative */ + i--; + targ_size_t value = (regsize == 4) ? pi[i] : ps[i]; + if (regsize == 8) + value = pl[i]; + if (pushi) + { + if (I64 && regsize == 8 && value != (int)value) + { ce = regwithvalue(ce,allregs,value,®,64); + goto Preg; // cannot push imm64 unless it is sign extended 32 bit value + } + if (regsize == REGSIZE && reghasvalue(allregs,value,®)) + goto Preg; + ce = genc2(ce,(szb == 1) ? 0x6A : 0x68,0,value); // PUSH value + } + else + { + ce = regwithvalue(ce,allregs,value,®,0); + Preg: + ce = genpush(ce,reg); // PUSH reg + } + code_orflag(ce,flag); /* operand size */ + } while (i); + goto L2; + } + default: + break; + } + retregs = tybyte(tym) ? BYTEREGS : allregs; + if (tyvector(tym)) + { + retregs = XMMREGS; + c = cat(c,codelem(e,&retregs,FALSE)); + stackpush += sz; + c = genadjesp(c,sz); + c = genc2(c,0x81,grex | modregrm(3,5,SP),sz); // SUB SP,sz + unsigned op = xmmstore(tym); + unsigned r = findreg(retregs); + c = gen2sib(c,op,modregxrm(0,r - XMM0,4),modregrm(0,4,SP)); // MOV [ESP],r + goto ret; + } + else if (tyfloating(tym)) + { if (config.inline8087) + { code *c1,*c2; + unsigned op; + unsigned r; + + retregs = tycomplex(tym) ? mST01 : mST0; + c = cat(c,codelem(e,&retregs,FALSE)); + stackpush += sz; + c = genadjesp(c,sz); + c = genc2(c,0x81,grex | modregrm(3,5,SP),sz); // SUB SP,sz + switch (tym) + { + case TYfloat: + case TYifloat: + case TYcfloat: + op = 0xD9; + r = 3; + break; + + case TYdouble: + case TYidouble: + case TYdouble_alias: + case TYcdouble: + op = 0xDD; + r = 3; + break; + + case TYldouble: + case TYildouble: + case TYcldouble: + op = 0xDB; + r = 7; + break; + + default: + assert(0); + } + if (!I16) + { + c1 = NULL; + c2 = NULL; + if (tycomplex(tym)) + { + // FSTP sz/2[ESP] + c2 = genc1(CNIL,op,(modregrm(0,4,SP) << 8) | modregxrm(2,r,4),FLconst,sz/2); + pop87(); + } + pop87(); + c2 = gen2sib(c2,op,modregrm(0,r,4),modregrm(0,4,SP)); // FSTP [ESP] + } + else + { + retregs = IDXREGS; /* get an index reg */ + c1 = allocreg(&retregs,®,TYoffset); + c1 = genregs(c1,0x89,SP,reg); /* MOV reg,SP */ + pop87(); + c2 = gen2(CNIL,op,modregrm(0,r,regtorm[reg])); // FSTP [reg] + } + if (LARGEDATA) + c2->Iflags |= CFss; /* want to store into stack */ + genfwait(c2); // FWAIT + c = cat3(c,c1,c2); + goto ret; + } + else if (I16 && (tym == TYdouble || tym == TYdouble_alias)) + retregs = mSTACK; + } +#if LONGLONG + else if (I16 && sz == 8) // if long long + retregs = mSTACK; +#endif + c = cat(c,scodelem(e,&retregs,0,TRUE)); + if (retregs != mSTACK) /* if stackpush not already inc'd */ + stackpush += sz; + if (sz <= REGSIZE) + { + c = genpush(c,findreg(retregs)); // PUSH reg + genadjesp(c,REGSIZE); + } + else if (sz == REGSIZE * 2) + { c = genpush(c,findregmsw(retregs)); // PUSH msreg + genpush(c,findreglsw(retregs)); // PUSH lsreg + genadjesp(c,sz); + } +ret: + return cat(cp,c); +} + + +/******************************* + * Get offset portion of e, and store it in an index + * register. Return mask of index register in *pretregs. + */ + +code *offsetinreg( elem *e, regm_t *pretregs) +{ regm_t retregs; + code *c; + unsigned reg; + + retregs = mLSW; /* want only offset */ + if (e->Ecount && e->Ecount != e->Ecomsub) + { unsigned i; + regm_t rm; + + rm = retregs & regcon.cse.mval & ~regcon.cse.mops & ~regcon.mvar; /* possible regs */ + for (i = 0; rm; i++) + { if (mask[i] & rm && regcon.cse.value[i] == e) + { reg = i; + *pretregs = mask[i]; + c = getregs(*pretregs); + goto L3; + } + rm &= ~mask[i]; + } + } + + *pretregs = retregs; + c = allocreg(pretregs,®,TYoffset); + c = cat(c,getoffset(e,reg)); +L3: + cssave(e,*pretregs,FALSE); + freenode(e); + return c; +} + + +/****************************** + * Generate code to load data into registers. + */ + +code *loaddata(elem *e,regm_t *pretregs) +{ unsigned reg,nreg,op,sreg; + tym_t tym; + int sz; + code *c,*ce,cs; + regm_t flags,forregs,regm; + +#ifdef DEBUG + if (debugw) + printf("loaddata(e = %p,*pretregs = %s)\n",e,regm_str(*pretregs)); + //elem_print(e); +#endif + assert(e); + elem_debug(e); + if (*pretregs == 0) + return CNIL; + tym = tybasic(e->Ety); + if (tym == TYstruct) + return cdrelconst(e,pretregs); + if (tyfloating(tym)) + { obj_fltused(); + if (config.inline8087) + { if (*pretregs & mST0) + return load87(e,0,pretregs,NULL,-1); + else if (tycomplex(tym)) + return cload87(e, pretregs); + } + } + sz = tysize[tym]; + cs.Iflags = 0; + cs.Irex = 0; + if (*pretregs == mPSW) + { + regm = allregs; + if (e->Eoper == OPconst) + { /* TRUE: OR SP,SP (SP is never 0) */ + /* FALSE: CMP SP,SP (always equal) */ + c = genregs(CNIL,(boolres(e)) ? 0x09 : 0x39,SP,SP); + if (I64) + code_orrex(c, REX_W); + } + else if (sz <= REGSIZE) + { + if (!I16 && (tym == TYfloat || tym == TYifloat)) + { c = allocreg(®m,®,TYoffset); /* get a register */ + ce = loadea(e,&cs,0x8B,reg,0,0,0); // MOV reg,data + c = cat(c,ce); + ce = gen2(CNIL,0xD1,modregrmx(3,4,reg)); /* SHL reg,1 */ + c = cat(c,ce); + } +#if TARGET_OSX + else if (e->Eoper == OPvar && movOnly(e)) + { c = allocreg(®m,®,TYoffset); /* get a register */ + ce = loadea(e,&cs,0x8B,reg,0,0,0); // MOV reg,data + c = cat(c,ce); + ce = fixresult(e,regm,pretregs); + c = cat(c,ce); + } +#endif + else + { cs.IFL2 = FLconst; + cs.IEV2.Vsize_t = 0; + op = (sz == 1) ? 0x80 : 0x81; + c = loadea(e,&cs,op,7,0,0,0); /* CMP EA,0 */ + + // Convert to TEST instruction if EA is a register + // (to avoid register contention on Pentium) + if ((c->Iop & ~1) == 0x38 && + (c->Irm & modregrm(3,0,0)) == modregrm(3,0,0) + ) + { c->Iop = (c->Iop & 1) | 0x84; + code_newreg(c, c->Irm & 7); + if (c->Irex & REX_B) + //c->Irex = (c->Irex & ~REX_B) | REX_R; + c->Irex |= REX_R; + } + } + } + else if (sz < 8) + { + c = allocreg(®m,®,TYoffset); /* get a register */ + if (I32) // it's a 48 bit pointer + ce = loadea(e,&cs,0x0FB7,reg,REGSIZE,0,0); /* MOVZX reg,data+4 */ + else + { ce = loadea(e,&cs,0x8B,reg,REGSIZE,0,0); /* MOV reg,data+2 */ + if (tym == TYfloat || tym == TYifloat) // dump sign bit + gen2(ce,0xD1,modregrm(3,4,reg)); /* SHL reg,1 */ + } + c = cat(c,ce); + ce = loadea(e,&cs,0x0B,reg,0,regm,0); /* OR reg,data */ + c = cat(c,ce); + } + else if (sz == 8 || (I64 && sz == 2 * REGSIZE && !tyfloating(tym))) + { + c = allocreg(®m,®,TYoffset); /* get a register */ + int i = sz - REGSIZE; + ce = loadea(e,&cs,0x8B,reg,i,0,0); /* MOV reg,data+6 */ + if (tyfloating(tym)) // TYdouble or TYdouble_alias + gen2(ce,0xD1,modregrm(3,4,reg)); // SHL reg,1 + c = cat(c,ce); + + while ((i -= REGSIZE) >= 0) + { + code *c1 = loadea(e,&cs,0x0B,reg,i,regm,0); // OR reg,data+i + if (i == 0) + c1->Iflags |= CFpsw; // need the flags on last OR + c = cat(c,c1); + } + } + else if (sz == tysize[TYldouble]) // TYldouble + return load87(e,0,pretregs,NULL,-1); + else + { +#ifdef DEBUG + elem_print(e); +#endif + assert(0); + } + return c; + } + /* not for flags only */ + flags = *pretregs & mPSW; /* save original */ + forregs = *pretregs & (mBP | ALLREGS | mES | XMMREGS); + if (*pretregs & mSTACK) + forregs |= DOUBLEREGS; + if (e->Eoper == OPconst) + { + targ_size_t value = e->EV.Vint; + if (sz == 8) + value = e->EV.Vullong; + + if (sz == REGSIZE && reghasvalue(forregs,value,®)) + forregs = mask[reg]; + + regm_t save = regcon.immed.mval; + c = allocreg(&forregs,®,tym); /* allocate registers */ + regcon.immed.mval = save; // KLUDGE! + if (sz <= REGSIZE) + { + if (sz == 1) + flags |= 1; + else if (!I16 && sz == SHORTSIZE && + !(mask[reg] & regcon.mvar) && + !(config.flags4 & CFG4speed) + ) + flags |= 2; + if (sz == 8) + flags |= 64; + if (reg >= XMM0) + { /* This comes about because 0, 1, pi, etc., constants don't get stored + * in the data segment, because they are x87 opcodes. + * Not so efficient. We should at least do a PXOR for 0. + */ + unsigned r; + targ_size_t value = e->EV.Vuns; + if (sz == 8) + value = e->EV.Vullong; + ce = regwithvalue(CNIL,ALLREGS,value,&r,flags); + flags = 0; // flags are already set + ce = genfltreg(ce,0x89,r,0); // MOV floatreg,r + if (sz == 8) + code_orrex(ce, REX_W); + assert(sz == 4 || sz == 8); // float or double + unsigned op = xmmload(tym); + ce = genfltreg(ce,op,reg - XMM0,0); // MOVSS/MOVSD XMMreg,floatreg + } + else + { ce = movregconst(CNIL,reg,value,flags); + flags = 0; // flags are already set + } + } + else if (sz < 8) // far pointers, longs for 16 bit targets + { + targ_int msw,lsw; + regm_t mswflags; + + msw = I32 ? e->EV.Vfp.Vseg + : (e->EV.Vulong >> 16); + lsw = e->EV.Vfp.Voff; + mswflags = 0; + if (forregs & mES) + { + ce = movregconst(CNIL,reg,msw,0); // MOV reg,segment + genregs(ce,0x8E,0,reg); // MOV ES,reg + msw = lsw; // MOV reg,offset + } + else + { + sreg = findreglsw(forregs); + ce = movregconst(CNIL,sreg,lsw,0); + reg = findregmsw(forregs); + /* Decide if we need to set flags when we load msw */ + if (flags && (msw && msw|lsw || !(msw|lsw))) + { mswflags = mPSW; + flags = 0; + } + } + ce = movregconst(ce,reg,msw,mswflags); + } + else if (sz == 8) + { + if (I32) + { + targ_long *p = (targ_long *) &e->EV.Vdouble; + if (reg >= XMM0) + { /* This comes about because 0, 1, pi, etc., constants don't get stored + * in the data segment, because they are x87 opcodes. + * Not so efficient. We should at least do a PXOR for 0. + */ + unsigned r; + regm_t rm = ALLREGS; + ce = allocreg(&rm,&r,TYint); // allocate scratch register + ce = movregconst(ce,r,p[0],0); + ce = genfltreg(ce,0x89,r,0); // MOV floatreg,r + ce = movregconst(ce,r,p[1],0); + ce = genfltreg(ce,0x89,r,4); // MOV floatreg+4,r + + unsigned op = xmmload(tym); + ce = genfltreg(ce,op,reg - XMM0,0); // MOVSS/MOVSD XMMreg,floatreg + } + else + { + ce = movregconst(CNIL,findreglsw(forregs),p[0],0); + ce = movregconst(ce,findregmsw(forregs),p[1],0); + } + } + else + { targ_short *p = (targ_short *) &e->EV.Vdouble; + + assert(reg == AX); + ce = movregconst(CNIL,AX,p[3],0); /* MOV AX,p[3] */ + ce = movregconst(ce,DX,p[0],0); + ce = movregconst(ce,CX,p[1],0); + ce = movregconst(ce,BX,p[2],0); + } + } + else if (I64 && sz == 16) + { + ce = movregconst(CNIL,findreglsw(forregs),e->EV.Vcent.lsw,0); + ce = movregconst(ce,findregmsw(forregs),e->EV.Vcent.msw,0); + } + else + assert(0); + c = cat(c,ce); + } + else + { + // See if we can use register that parameter was passed in + if (regcon.params && e->EV.sp.Vsym->Sclass == SCfastpar && + regcon.params & mask[e->EV.sp.Vsym->Spreg] && + !(e->Eoper == OPvar && e->EV.sp.Voffset > 0) && // Must be at the base of that variable + sz <= REGSIZE) // make sure no 'paint' to a larger size happened + { + reg = e->EV.sp.Vsym->Spreg; + forregs = mask[reg]; + mfuncreg &= ~forregs; + regcon.used |= forregs; + return fixresult(e,forregs,pretregs); + } + + c = allocreg(&forregs,®,tym); /* allocate registers */ + + if (sz == 1) + { regm_t nregm; + +#ifdef DEBUG + if (!(forregs & BYTEREGS)) + { elem_print(e); + printf("forregs = x%x\n",forregs); + } +#endif + int op = 0x8A; // byte MOV +#if TARGET_OSX + if (movOnly(e)) + op = 0x8B; +#endif + assert(forregs & BYTEREGS); + if (!I16) + c = cat(c,loadea(e,&cs,op,reg,0,0,0)); // MOV regL,data + else + { nregm = tyuns(tym) ? BYTEREGS : mAX; + if (*pretregs & nregm) + nreg = reg; /* already allocated */ + else + c = cat(c,allocreg(&nregm,&nreg,tym)); + ce = loadea(e,&cs,op,nreg,0,0,0); /* MOV nregL,data */ + c = cat(c,ce); + if (reg != nreg) + { genmovreg(c,reg,nreg); /* MOV reg,nreg */ + cssave(e,mask[nreg],FALSE); + } + } + } + else if (forregs & XMMREGS) + { + // Can't load from registers directly to XMM regs +//printf("test2 %s\n", e->EV.sp.Vsym->Sident); + //e->EV.sp.Vsym->Sflags &= ~GTregcand; + + op = xmmload(tym); + if (e->Eoper == OPvar) + { symbol *s = e->EV.sp.Vsym; + if (s->Sfl == FLreg && !(mask[s->Sreglsw] & XMMREGS)) + { op = LODD; // MOVD/MOVQ + /* getlvalue() will unwind this and unregister s; could use a better solution */ + } + } + ce = loadea(e,&cs,op,reg,0,RMload,0); // MOVSS/MOVSD reg,data + c = cat(c,ce); + } + else if (sz <= REGSIZE) + { + ce = loadea(e,&cs,0x8B,reg,0,RMload,0); // MOV reg,data + c = cat(c,ce); + } + else if (sz <= 2 * REGSIZE && forregs & mES) + { + ce = loadea(e,&cs,0xC4,reg,0,0,mES); /* LES data */ + c = cat(c,ce); + } + else if (sz <= 2 * REGSIZE) + { + if (I32 && sz == 8 && + (*pretregs & (mSTACK | mPSW)) == mSTACK) + { int i; + + assert(0); + /* Note that we allocreg(DOUBLEREGS) needlessly */ + stackchanged = 1; + i = DOUBLESIZE - REGSIZE; + do + { c = cat(c,loadea(e,&cs,0xFF,6,i,0,0)); /* PUSH EA+i */ + c = genadjesp(c,REGSIZE); + stackpush += REGSIZE; + i -= REGSIZE; + } + while (i >= 0); + return c; + } + + reg = findregmsw(forregs); + ce = loadea(e,&cs,0x8B,reg,REGSIZE,forregs,0); /* MOV reg,data+2 */ + if (I32 && sz == REGSIZE + 2) + ce->Iflags |= CFopsize; /* seg is 16 bits */ + c = cat(c,ce); + reg = findreglsw(forregs); + ce = loadea(e,&cs,0x8B,reg,0,forregs,0); + c = cat(c,ce); + } + else if (sz >= 8) + { + code *c1,*c2,*c3; + + assert(!I32); + if ((*pretregs & (mSTACK | mPSW)) == mSTACK) + { int i; + + /* Note that we allocreg(DOUBLEREGS) needlessly */ + stackchanged = 1; + i = sz - REGSIZE; + do + { c = cat(c,loadea(e,&cs,0xFF,6,i,0,0)); /* PUSH EA+i */ + c = genadjesp(c,REGSIZE); + stackpush += REGSIZE; + i -= REGSIZE; + } + while (i >= 0); + return c; + } + else + { + assert(reg == AX); + ce = loadea(e,&cs,0x8B,AX,6,0,0); /* MOV AX,data+6 */ + c1 = loadea(e,&cs,0x8B,BX,4,mAX,0); /* MOV BX,data+4 */ + c2 = loadea(e,&cs,0x8B,CX,2,mAX|mBX,0); /* MOV CX,data+2 */ + c3 = loadea(e,&cs,0x8B,DX,0,mAX|mCX|mCX,0); /* MOV DX,data */ + c = cat6(c,ce,c1,c2,c3,CNIL); + } + } + else + assert(0); + } + /* Flags may already be set */ + *pretregs &= flags | ~mPSW; + c = cat(c,fixresult(e,forregs,pretregs)); + return c; +} + +#endif // SPP diff --git a/backend/cod2.c b/backend/cod2.c new file mode 100644 index 00000000..aebe595c --- /dev/null +++ b/backend/cod2.c @@ -0,0 +1,4997 @@ +// Copyright (C) 1984-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include "cc.h" +#include "oper.h" +#include "el.h" +#include "code.h" +#include "global.h" +#include "type.h" +#if SCPP +#include "exh.h" +#endif + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +int cdcmp_flag; +extern signed char regtorm[8]; + +/******************************************* + * !=0 if cannot use this EA in anything other than a MOV instruction. + */ + +int movOnly(elem *e) +{ +#if TARGET_OSX + if (I64 && config.flags3 & CFG3pic && e->Eoper == OPvar) + { symbol *s = e->EV.sp.Vsym; + // Fixups for these can only be done with a MOV + if (s->Sclass == SCglobal || s->Sclass == SCextern || + s->Sclass == SCcomdat || s->Sclass == SCcomdef) + return 1; + } +#endif + return 0; +} + +/******************************** + * Return mask of index registers used by addressing mode. + * Index is rm of modregrm field. + */ + +regm_t idxregm(code *c) +{ + static const unsigned char idxsib[8] = { mAX,mCX,mDX,mBX,0,mBP,mSI,mDI }; + static const unsigned char idxrm[8] = {mBX|mSI,mBX|mDI,mSI,mDI,mSI,mDI,0,mBX}; + + unsigned rm = c->Irm; + regm_t idxm = 0; + if ((rm & 0xC0) != 0xC0) /* if register is not the destination */ + { + if (I16) + idxm = idxrm[rm & 7]; + else + { + if ((rm & 7) == 4) /* if sib byte */ + { + unsigned sib = c->Isib; + unsigned idxreg = (sib >> 3) & 7; + if (c->Irex & REX_X) + { idxreg |= 8; + idxm = mask[idxreg]; // scaled index reg + } + else + idxm = idxsib[idxreg]; // scaled index reg + if ((sib & 7) == 5 && (rm & 0xC0) == 0) + ; + else + { unsigned base = sib & 7; + if (c->Irex & REX_B) + idxm |= mask[base | 8]; + else + idxm |= idxsib[base]; + } + } + else + { unsigned base = rm & 7; + if (c->Irex & REX_B) + idxm |= mask[base | 8]; + else + idxm |= idxsib[base]; + } + } + } + return idxm; +} + + +#if TARGET_WINDOS +/*************************** + * Gen code for call to floating point routine. + */ + +code *opdouble(elem *e,regm_t *pretregs,unsigned clib) +{ + regm_t retregs1,retregs2; + code *cl, *cr, *c; + + if (config.inline8087) + return orth87(e,pretregs); + + if (tybasic(e->E1->Ety) == TYfloat) + { + clib += CLIBfadd - CLIBdadd; /* convert to float operation */ + retregs1 = FLOATREGS; + retregs2 = FLOATREGS2; + } + else + { + if (I32) + { retregs1 = DOUBLEREGS_32; + retregs2 = DOUBLEREGS2_32; + } + else + { retregs1 = mSTACK; + retregs2 = DOUBLEREGS_16; + } + } + cl = codelem(e->E1, &retregs1,FALSE); + if (retregs1 & mSTACK) + cgstate.stackclean++; + cr = scodelem(e->E2, &retregs2, retregs1 & ~mSTACK, FALSE); + if (retregs1 & mSTACK) + cgstate.stackclean--; + c = callclib(e, clib, pretregs, 0); + return cat3(cl, cr, c); +} +#endif + +/***************************** + * Handle operators which are more or less orthogonal + * ( + - & | ^ ) + */ + +code *cdorth(elem *e,regm_t *pretregs) +{ tym_t ty1; + regm_t retregs,rretregs,posregs; + unsigned reg,rreg,op1,op2,mode; + int rval; + code *c,*cg,*cl; + targ_size_t i; + elem *e1,*e2; + int numwords; /* # of words to be operated on */ + static int nest; + + //printf("cdorth(e = %p, *pretregs = %s)\n",e,regm_str(*pretregs)); + e1 = e->E1; + e2 = e->E2; + if (*pretregs == 0) /* if don't want result */ + { c = codelem(e1,pretregs,FALSE); /* eval left leaf */ + *pretregs = 0; /* in case they got set */ + return cat(c,codelem(e2,pretregs,FALSE)); + } + + ty1 = tybasic(e1->Ety); + if (tyfloating(ty1)) + { + if (*pretregs & XMMREGS || tyvector(ty1)) + return orthxmm(e,pretregs); +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + return orth87(e,pretregs); +#else + return opdouble(e,pretregs,(e->Eoper == OPadd) ? CLIBdadd + : CLIBdsub); +#endif + } + if (tyxmmreg(ty1)) + return orthxmm(e,pretregs); + tym_t ty2 = tybasic(e2->Ety); + int e2oper = e2->Eoper; + tym_t ty = tybasic(e->Ety); + unsigned sz = tysize[ty]; + unsigned byte = (sz == 1); + unsigned char word = (!I16 && sz == SHORTSIZE) ? CFopsize : 0; + unsigned test = FALSE; // assume we destroyed lvalue + code cs; + cs.Iflags = 0; + cs.Irex = 0; + code *cr = CNIL; + + switch (e->Eoper) + { case OPadd: mode = 0; + op1 = 0x03; op2 = 0x13; break; /* ADD, ADC */ + case OPmin: mode = 5; + op1 = 0x2B; op2 = 0x1B; break; /* SUB, SBB */ + case OPor: mode = 1; + op1 = 0x0B; op2 = 0x0B; break; /* OR , OR */ + case OPxor: mode = 6; + op1 = 0x33; op2 = 0x33; break; /* XOR, XOR */ + case OPand: mode = 4; + op1 = 0x23; op2 = 0x23; /* AND, AND */ + if (tyreg(ty1) && + *pretregs == mPSW) /* if flags only */ + { test = TRUE; + op1 = 0x85; /* TEST */ + mode = 0; + } + break; + default: + assert(0); + } + op1 ^= byte; /* if byte operation */ + + /* Compute number of words to operate on. */ + numwords = 1; + if (!I16) + { /* Cannot operate on longs and then do a 'paint' to a far */ + /* pointer, because far pointers are 48 bits and longs are 32. */ + /* Therefore, numwords can never be 2. */ + assert(!(tyfv(ty1) && tyfv(ty2))); + if (sz == 2 * REGSIZE) + { + numwords++; + } + } + else + { /* If ty is a TYfptr, but both operands are long, treat the */ + /* operation as a long. */ +#if TARGET_SEGMENTED + if ((tylong(ty1) || ty1 == TYhptr) && + (tylong(ty2) || ty2 == TYhptr)) + numwords++; +#else + if (tylong(ty1) && tylong(ty2)) + numwords++; +#endif + } + + // Special cases where only flags are set + if (test && tysize[ty1] <= REGSIZE && + (e1->Eoper == OPvar || (e1->Eoper == OPind && !e1->Ecount)) +#if TARGET_OSX + && !movOnly(e1) +#endif + ) + { + // Handle the case of (var & const) + if (e2->Eoper == OPconst && el_signx32(e2)) + { + c = getlvalue(&cs,e1,0); + targ_size_t value = e2->EV.Vpointer; + if (sz == 2) + value &= 0xFFFF; + else if (sz == 4) + value &= 0xFFFFFFFF; + if (reghasvalue(byte ? BYTEREGS : ALLREGS,value,®)) + goto L11; + if (sz == 8 && !I64) + { + assert(value == (int)value); // sign extend imm32 + } + op1 = 0xF7; + cs.IEV2.Vint = value; + cs.IFL2 = FLconst; + goto L10; + } + + // Handle (exp & reg) + if (isregvar(e2,&retregs,®)) + { + c = getlvalue(&cs,e1,0); + L11: + code_newreg(&cs, reg); + if (I64 && byte && reg >= 4) + cs.Irex |= REX; + L10: + cs.Iop = op1 ^ byte; + cs.Iflags |= word | CFpsw; + freenode(e1); + freenode(e2); + return gen(c,&cs); + } + } + + // Look for possible uses of LEA + if (e->Eoper == OPadd && + !(*pretregs & mPSW) && /* flags aren't set by LEA */ + !nest && // could cause infinite recursion if e->Ecount + (sz == REGSIZE || (I64 && sz == 4))) // far pointers aren't handled + { + unsigned rex = (sz == 8) ? REX_W : 0; + + // Handle the case of (e + &var) + int e1oper = e1->Eoper; + if ((e2oper == OPrelconst && (config.target_cpu >= TARGET_Pentium || (!e2->Ecount && stackfl[el_fl(e2)]))) + || // LEA costs too much for simple EAs on older CPUs + (e2oper == OPconst && (e1->Eoper == OPcall || e1->Eoper == OPcallns) && !(*pretregs & mAX)) || + (!I16 && (isscaledindex(e1) || isscaledindex(e2))) || + (!I16 && e1oper == OPvar && e1->EV.sp.Vsym->Sfl == FLreg && (e2oper == OPconst || (e2oper == OPvar && e2->EV.sp.Vsym->Sfl == FLreg))) || + (e2oper == OPconst && e1oper == OPeq && e1->E1->Eoper == OPvar) || + (!I16 && (e2oper == OPrelconst || e2oper == OPconst) && !e1->Ecount && + (e1oper == OPmul || e1oper == OPshl) && + e1->E2->Eoper == OPconst && + ssindex(e1oper,e1->E2->EV.Vuns) + ) || + (!I16 && e1->Ecount) + ) + { + int inc = e->Ecount != 0; + nest += inc; + c = getlvalue(&cs,e,0); + nest -= inc; + unsigned reg; + c = cat(c,allocreg(pretregs,®,ty)); + cs.Iop = 0x8D; + code_newreg(&cs, reg); + c = gen(c,&cs); // LEA reg,EA + if (rex) + code_orrex(c, rex); + return c; + } + + // Handle the case of ((e + c) + e2) + if (!I16 && + e1oper == OPadd && + (e1->E2->Eoper == OPconst && el_signx32(e1->E2) || + e2oper == OPconst && el_signx32(e2)) && + !e1->Ecount + ) + { elem *e11; + elem *ebase; + elem *edisp; + int ss; + int ss2; + unsigned reg1,reg2; + code *c1,*c2,*c3; + + if (e2oper == OPconst && el_signx32(e2)) + { edisp = e2; + ebase = e1->E2; + } + else + { edisp = e1->E2; + ebase = e2; + } + + e11 = e1->E1; + retregs = *pretregs & ALLREGS; + if (!retregs) + retregs = ALLREGS; + ss = 0; + ss2 = 0; + + // Handle the case of (((e * c1) + c2) + e2) + // Handle the case of (((e << c1) + c2) + e2) + if ((e11->Eoper == OPmul || e11->Eoper == OPshl) && + e11->E2->Eoper == OPconst && + !e11->Ecount + ) + { + targ_size_t co1 = el_tolong(e11->E2); + if (e11->Eoper == OPshl) + { + if (co1 > 3) + goto L13; + ss = co1; + } + else + { + ss2 = 1; + switch (co1) + { + case 6: ss = 1; break; + case 12: ss = 1; ss2 = 2; break; + case 24: ss = 1; ss2 = 3; break; + case 10: ss = 2; break; + case 20: ss = 2; ss2 = 2; break; + case 40: ss = 2; ss2 = 3; break; + case 18: ss = 3; break; + case 36: ss = 3; ss2 = 2; break; + case 72: ss = 3; ss2 = 3; break; + default: + ss2 = 0; + goto L13; + } + } + freenode(e11->E2); + freenode(e11); + e11 = e11->E1; + goto L13; + } + else + { + L13: + regm_t regm; + if (e11->Eoper == OPvar && isregvar(e11,®m,®1)) + { + if (tysize[tybasic(e11->Ety)]<= REGSIZE) + retregs = mask[reg1]; // only want the LSW + else + retregs = regm; + c1 = NULL; + freenode(e11); + } + else + c1 = codelem(e11,&retregs,FALSE); + } + rretregs = ALLREGS & ~retregs; + c2 = scodelem(ebase,&rretregs,retregs,TRUE); + { + regm_t sregs = *pretregs & ~rretregs; + if (!sregs) + sregs = ALLREGS & ~rretregs; + c3 = allocreg(&sregs,®,ty); + } + + assert((retregs & (retregs - 1)) == 0); // must be only one register + assert((rretregs & (rretregs - 1)) == 0); // must be only one register + + reg1 = findreg(retregs); + reg2 = findreg(rretregs); + + if (ss2) + { + assert(reg != reg2); + if ((reg1 & 7) == BP) + { static unsigned imm32[4] = {1+1,2+1,4+1,8+1}; + + // IMUL reg,imm32 + c = genc2(CNIL,0x69,modregxrmx(3,reg,reg1),imm32[ss]); + } + else + { // LEA reg,[reg1*ss][reg1] + c = gen2sib(CNIL,0x8D,modregxrm(0,reg,4),modregrm(ss,reg1 & 7,reg1 & 7)); + if (reg1 & 8) + code_orrex(c, REX_X | REX_B); + } + if (rex) + code_orrex(c, rex); + reg1 = reg; + ss = ss2; // use *2 for scale + } + else + c = NULL; + c = cat4(c1,c2,c3,c); + + cs.Iop = 0x8D; // LEA reg,c[reg1*ss][reg2] + cs.Irm = modregrm(2,reg & 7,4); + cs.Isib = modregrm(ss,reg1 & 7,reg2 & 7); + assert(reg2 != BP); + cs.Iflags = CFoff; + cs.Irex = rex; + if (reg & 8) + cs.Irex |= REX_R; + if (reg1 & 8) + cs.Irex |= REX_X; + if (reg2 & 8) + cs.Irex |= REX_B; + cs.IFL1 = FLconst; + cs.IEV1.Vsize_t = edisp->EV.Vuns; + + freenode(edisp); + freenode(e1); + c = gen(c,&cs); + return cat(c,fixresult(e,mask[reg],pretregs)); + } + } + + posregs = (byte) ? BYTEREGS : (mES | ALLREGS | mBP); + retregs = *pretregs & posregs; + if (retregs == 0) /* if no return regs speced */ + /* (like if wanted flags only) */ + retregs = ALLREGS & posregs; // give us some + + if (tysize[ty1] > REGSIZE && numwords == 1) + { /* The only possibilities are (TYfptr + tyword) or (TYfptr - tyword) */ +#if DEBUG + if (tysize[ty2] != REGSIZE) + { printf("e = %p, e->Eoper = ",e); + WROP(e->Eoper); + printf(" e1->Ety = "); + WRTYxx(ty1); + printf(" e2->Ety = "); + WRTYxx(ty2); + printf("\n"); + elem_print(e); + } +#endif + assert(tysize[ty2] == REGSIZE); + +#if TARGET_SEGMENTED + /* Watch out for the case here where you are going to OP reg,EA */ + /* and both the reg and EA use ES! Prevent this by forcing */ + /* reg into the regular registers. */ + if ((e2oper == OPind || + (e2oper == OPvar && el_fl(e2) == FLfardata)) && + !e2->Ecount) + { + retregs = ALLREGS; + } +#endif + + cl = codelem(e1,&retregs,test); + reg = findreglsw(retregs); /* reg is the register with the offset*/ + } +#if TARGET_SEGMENTED + else if (ty1 == TYhptr || ty2 == TYhptr) + { /* Generate code for add/subtract of huge pointers. + No attempt is made to generate very good code. + */ + unsigned mreg,lreg; + unsigned lrreg; + + retregs = (retregs & mLSW) | mDX; + if (ty1 == TYhptr) + { // hptr +- long + rretregs = mLSW & ~(retregs | regcon.mvar); + if (!rretregs) + rretregs = mLSW; + rretregs |= mCX; + cl = codelem(e1,&rretregs,0); + retregs &= ~rretregs; + if (!(retregs & mLSW)) + retregs |= mLSW & ~rretregs; + + cr = scodelem(e2,&retregs,rretregs,TRUE); + } + else + { // long + hptr + cl = codelem(e1,&retregs,0); + rretregs = (mLSW | mCX) & ~retregs; + if (!(rretregs & mLSW)) + rretregs |= mLSW; + cr = scodelem(e2,&rretregs,retregs,TRUE); + } + cg = getregs(rretregs | retregs); + mreg = DX; + lreg = findreglsw(retregs); + c = CNIL; + if (e->Eoper == OPmin) + { // negate retregs + c = gen2(c,0xF7,modregrm(3,3,mreg)); // NEG mreg + gen2(c,0xF7,modregrm(3,3,lreg)); // NEG lreg + code_orflag(c,CFpsw); + genc2(c,0x81,modregrm(3,3,mreg),0); // SBB mreg,0 + } + lrreg = findreglsw(rretregs); + c = genregs(c,0x03,lreg,lrreg); // ADD lreg,lrreg + code_orflag(c,CFpsw); + genmovreg(c,lrreg,CX); // MOV lrreg,CX + genc2(c,0x81,modregrm(3,2,mreg),0); // ADC mreg,0 + genshift(c); // MOV CX,offset __AHSHIFT + gen2(c,0xD3,modregrm(3,4,mreg)); // SHL mreg,CL + genregs(c,0x03,mreg,lrreg); // ADD mreg,MSREG(h) + goto L5; + } +#endif + else + { regm_t regm; + + /* if (tyword + TYfptr) */ + if (tysize[ty1] == REGSIZE && tysize[ty2] > REGSIZE) + { retregs = ~*pretregs & ALLREGS; + + /* if retregs doesn't have any regs in it that aren't reg vars */ + if ((retregs & ~regcon.mvar) == 0) + retregs |= mAX; + } + else if (numwords == 2 && retregs & mES) + retregs = (retregs | mMSW) & ALLREGS; + + // Determine if we should swap operands, because + // mov EAX,x + // add EAX,reg + // is faster than: + // mov EAX,reg + // add EAX,x + else if (e2oper == OPvar && + e1->Eoper == OPvar && + e->Eoper != OPmin && + isregvar(e1,®m,NULL) && + regm != retregs && + tysize[ty1] == tysize[ty2]) + { + elem *es = e1; + e1 = e2; + e2 = es; + } + cl = codelem(e1,&retregs,test); /* eval left leaf */ + reg = findreg(retregs); + } + switch (e2oper) + { + case OPind: /* if addressing mode */ + if (!e2->Ecount) /* if not CSE */ + goto L1; /* try OP reg,EA */ + /* FALL-THROUGH */ + default: /* operator node */ + L2: + rretregs = ALLREGS & ~retregs; + /* Be careful not to do arithmetic on ES */ + if (tysize[ty1] == REGSIZE && tysize[ty2] > REGSIZE && *pretregs != mPSW) + rretregs = *pretregs & (mES | ALLREGS | mBP) & ~retregs; + else if (byte) + rretregs &= BYTEREGS; + + cr = scodelem(e2,&rretregs,retregs,TRUE); /* get rvalue */ + rreg = (tysize[ty2] > REGSIZE) ? findreglsw(rretregs) : findreg(rretregs); + c = CNIL; + if (numwords == 1) /* ADD reg,rreg */ + { + /* reverse operands to avoid moving around the segment value */ + if (tysize[ty2] > REGSIZE) + { c = cat(c,getregs(rretregs)); + c = genregs(c,op1,rreg,reg); + retregs = rretregs; /* reverse operands */ + } + else + { c = genregs(c,op1,reg,rreg); + if (!I16 && *pretregs & mPSW) + c->Iflags |= word; + } + if (I64 && sz == 8) + code_orrex(c, REX_W); + if (I64 && byte && (reg >= 4 || rreg >= 4)) + code_orrex(c, REX); + } + else /* numwords == 2 */ /* ADD lsreg,lsrreg */ + { + reg = findreglsw(retregs); + rreg = findreglsw(rretregs); + c = genregs(c,op1,reg,rreg); + if (e->Eoper == OPadd || e->Eoper == OPmin) + code_orflag(c,CFpsw); + reg = findregmsw(retregs); + rreg = findregmsw(rretregs); + if (!(e2oper == OPu16_32 && // if second operand is 0 + (op2 == 0x0B || op2 == 0x33)) // and OR or XOR + ) + genregs(c,op2,reg,rreg); // ADC msreg,msrreg + } + break; + + case OPrelconst: + if (sz != REGSIZE) + goto L2; + if (segfl[el_fl(e2)] != 3) /* if not in data segment */ + goto L2; + if (evalinregister(e2)) + goto L2; + cs.IEVoffset2 = e2->EV.sp.Voffset; + cs.IEVsym2 = e2->EV.sp.Vsym; + cs.Iflags |= CFoff; + i = 0; /* no INC or DEC opcode */ + rval = 0; + goto L3; + + case OPconst: + if (tyfv(ty2)) + goto L2; + if (numwords == 1) + { + if (!el_signx32(e2)) + goto L2; + i = e2->EV.Vpointer; + if (word) + { + if (!(*pretregs & mPSW) && + config.flags4 & CFG4speed && + (e->Eoper == OPor || e->Eoper == OPxor || test || + (e1->Eoper != OPvar && e1->Eoper != OPind))) + { word = 0; + i &= 0xFFFF; + } + } + rval = reghasvalue(byte ? BYTEREGS : ALLREGS,i,&rreg); + cs.IEV2.Vint = i; + L3: + op1 ^= byte; + cs.Iflags |= word; + if (rval) + { cs.Iop = op1 ^ 2; + mode = rreg; + } + else + cs.Iop = 0x81; + cs.Irm = modregrm(3,mode&7,reg&7); + if (mode & 8) + cs.Irex |= REX_R; + if (reg & 8) + cs.Irex |= REX_B; + if (I64 && sz == 8) + cs.Irex |= REX_W; + if (I64 && byte && (reg >= 4 || (rval && rreg >= 4))) + cs.Irex |= REX; + cs.IFL2 = (e2->Eoper == OPconst) ? FLconst : el_fl(e2); + /* Modify instruction for special cases */ + switch (e->Eoper) + { case OPadd: + { int iop; + + if (i == 1) + iop = 0; /* INC reg */ + else if (i == -1) + iop = 8; /* DEC reg */ + else + break; + cs.Iop = (0x40 | iop | reg) ^ byte; + if ((byte && *pretregs & mPSW) || I64) + { cs.Irm = modregrm(3,0,reg & 7) | iop; + cs.Iop = 0xFF; + } + break; + } + case OPand: + if (test) + cs.Iop = rval ? op1 : 0xF7; // TEST + break; + } + if (*pretregs & mPSW) + cs.Iflags |= CFpsw; + cs.Iop ^= byte; + c = gen(CNIL,&cs); + cs.Iflags &= ~CFpsw; + } + else if (numwords == 2) + { unsigned lsreg; + targ_int msw; + + c = getregs(retregs); + reg = findregmsw(retregs); + lsreg = findreglsw(retregs); + cs.Iop = 0x81; + cs.Irm = modregrm(3,mode,lsreg); + cs.IFL2 = FLconst; + msw = MSREG(e2->EV.Vllong); + cs.IEV2.Vint = e2->EV.Vlong; + switch (e->Eoper) + { case OPadd: + case OPmin: + cs.Iflags |= CFpsw; + break; + } + c = gen(c,&cs); + cs.Iflags &= ~CFpsw; + + cs.Irm = (cs.Irm & modregrm(3,7,0)) | reg; + cs.IEV2.Vint = msw; + if (e->Eoper == OPadd) + cs.Irm |= modregrm(0,2,0); /* ADC */ + c = gen(c,&cs); + } + else + assert(0); + freenode(e2); + break; + + case OPvar: +#if TARGET_OSX + if (movOnly(e2)) + goto L2; +#endif + L1: + if (tyfv(ty2)) + goto L2; + c = loadea(e2,&cs,op1, + ((numwords == 2) ? findreglsw(retregs) : reg), + 0,retregs,retregs); + if (!I16 && word) + { if (*pretregs & mPSW) + code_orflag(c,word); + else + { + code *ce = code_last(c); + ce->Iflags &= ~word; + } + } + else if (numwords == 2) + { + if (e->Eoper == OPadd || e->Eoper == OPmin) + code_orflag(c,CFpsw); + reg = findregmsw(retregs); + if (EOP(e2)) + { getlvalue_msw(&cs); + cs.Iop = op2; + NEWREG(cs.Irm,reg); + c = gen(c,&cs); /* ADC reg,data+2 */ + } + else + c = cat(c,loadea(e2,&cs,op2,reg,REGSIZE,retregs,0)); + } + else if (I64 && sz == 8) + code_orrex(c, REX_W); + freenode(e2); + break; + } + if (sz <= REGSIZE && *pretregs & mPSW) + { + /* If the expression is (_tls_array + ...), then the flags are not set + * since the linker may rewrite these instructions into something else. + */ + if (I64 && e->Eoper == OPadd && e1->Eoper == OPvar) + { + symbol *s = e1->EV.sp.Vsym; + if (s->Sident[0] == '_' && memcmp(s->Sident + 1,"tls_array",10) == 0) + { + goto L7; // don't assume flags are set + } + } + code_orflag(c,CFpsw); + *pretregs &= ~mPSW; /* flags already set */ + L7: ; + } + if (test) + cg = NULL; /* didn't destroy any */ + else + cg = getregs(retregs); /* we will trash these regs */ +L5: + c = cat(c,fixresult(e,retregs,pretregs)); + return cat4(cl,cr,cg,c); +} + + +/***************************** + * Handle multiply, divide, modulo and remquo. + * Note that modulo isn't defined for doubles. + */ + +code *cdmul(elem *e,regm_t *pretregs) +{ unsigned rreg,op,oper,lib,byte; + regm_t resreg,retregs,rretregs; + regm_t keepregs; + tym_t uns; // 1 if unsigned operation, 0 if not + tym_t tyml; + code *c,*cg,*cl,*cr,cs; + elem *e1,*e2; + int sz; + targ_size_t e2factor; + int opunslng; + int pow2; + + if (*pretregs == 0) // if don't want result + { c = codelem(e->E1,pretregs,FALSE); // eval left leaf + *pretregs = 0; // in case they got set + return cat(c,codelem(e->E2,pretregs,FALSE)); + } + + //printf("cdmul(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); + keepregs = 0; + cs.Iflags = 0; + cs.Irex = 0; + c = cg = cr = CNIL; // initialize + e2 = e->E2; + e1 = e->E1; + tyml = tybasic(e1->Ety); + sz = tysize[tyml]; + byte = tybyte(e->Ety) != 0; + uns = tyuns(tyml) || tyuns(e2->Ety); + oper = e->Eoper; + unsigned rex = (I64 && sz == 8) ? REX_W : 0; + unsigned grex = rex << 16; + + if (tyfloating(tyml)) + { + if (*pretregs & XMMREGS && oper != OPmod && tyxmmreg(tyml)) + return orthxmm(e,pretregs); +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + return orth87(e,pretregs); +#else + return opdouble(e,pretregs,(oper == OPmul) ? CLIBdmul : CLIBddiv); +#endif + } + + if (tyxmmreg(tyml)) + return orthxmm(e,pretregs); + + opunslng = I16 ? OPu16_32 : OPu32_64; + switch (oper) + { + case OPmul: + resreg = mAX; + op = 5 - uns; + lib = CLIBlmul; + break; + + case OPdiv: + resreg = mAX; + op = 7 - uns; + lib = uns ? CLIBuldiv : CLIBldiv; + if (I32) + keepregs |= mSI | mDI; + break; + + case OPmod: + resreg = mDX; + op = 7 - uns; + lib = uns ? CLIBulmod : CLIBlmod; + if (I32) + keepregs |= mSI | mDI; + break; + + case OPremquo: + resreg = mDX | mAX; + op = 7 - uns; + lib = uns ? CLIBuldiv : CLIBldiv; + if (I32) + keepregs |= mSI | mDI; + break; + + default: + assert(0); + } + + if (sz <= REGSIZE) // dedicated regs for mul & div + { retregs = mAX; + /* pick some other regs */ + rretregs = byte ? BYTEREGS & ~mAX + : ALLREGS & ~(mAX|mDX); + } + else + { + assert(sz <= 2 * REGSIZE); + retregs = mDX | mAX; + rretregs = mCX | mBX; // second arg + } + + switch (e2->Eoper) + { + case OPu16_32: + case OPs16_32: + case OPu32_64: + case OPs32_64: + if (sz != 2 * REGSIZE || oper != OPmul || e1->Eoper != e2->Eoper || + e1->Ecount || e2->Ecount) + goto L2; + op = (e2->Eoper == opunslng) ? 4 : 5; + retregs = mAX; + cl = codelem(e1->E1,&retregs,FALSE); /* eval left leaf */ + if (e2->E1->Eoper == OPvar || + (e2->E1->Eoper == OPind && !e2->E1->Ecount) + ) + { + cr = loadea(e2->E1,&cs,0xF7,op,0,mAX,mAX | mDX); + } + else + { + rretregs = ALLREGS & ~mAX; + cr = scodelem(e2->E1,&rretregs,retregs,TRUE); // get rvalue + cg = getregs(mAX | mDX); + rreg = findreg(rretregs); + cg = gen2(cg,0xF7,grex | modregrmx(3,op,rreg)); // OP AX,rreg + } + freenode(e->E1); + freenode(e2); + c = fixresult(e,mAX | mDX,pretregs); + break; + + case OPconst: + e2factor = el_tolong(e2); + + if (oper == OPmul && I32 && sz == REGSIZE * 2) + { targ_int msw,lsw; + regm_t scratch; + unsigned reg; + targ_llong e2factor; + + cl = codelem(e1,&retregs,FALSE); // eval left leaf + /* IMUL EDX,EDX,lsw + IMUL reg,EAX,msw + ADD reg,EDX + MOV EDX,lsw + MUL EDX + ADD EDX,reg + + if (msw == 0) + IMUL reg,EDX,lsw + MOV EDX,lsw + MUL EDX + ADD EDX,reg + */ + scratch = allregs & ~(mAX | mDX); + cr = allocreg(&scratch,®,TYint); + cg = getregs(mDX | mAX); + + e2factor = el_tolong(e2); + lsw = e2factor & ((1LL << (REGSIZE * 8)) - 1); + msw = e2factor >> (REGSIZE * 8); + + if (msw) + { cg = genmulimm(cg,DX,DX,lsw); + cg = genmulimm(cg,reg,AX,msw); + cg = gen2(cg,0x03,modregrm(3,reg,DX)); + } + else + cg = genmulimm(cg,reg,DX,lsw); + + cg = movregconst(cg,DX,lsw,0); // MOV EDX,lsw + cg = cat(cg,getregs(mDX)); + cg = gen2(cg,0xF7,modregrm(3,4,DX)); // MUL EDX + gen2(cg,0x03,modregrm(3,DX,reg)); // ADD EDX,reg + + resreg = mDX | mAX; + freenode(e2); + goto L3; + } + + if (oper != OPmul && e2factor == 10 && + (!I16 && sz == 4) && + config.flags4 & CFG4speed && !uns) + { + /* R1 / 10 + * + * MOV EAX,0x66666667 + * IMUL R1 + * MOV EAX,R1 + * SAR EAX,31 + * SAR EDX,2 + * SUB EDX,EAX + * IMUL EAX,EDX,10 + * SUB R1,EAX + * + * EDX = quotient + * R1 = remainder + */ + regm_t regm; + unsigned reg; + + regm = allregs & ~(mAX | mDX); + cl = codelem(e1,®m,FALSE); // eval left leaf + reg = findreg(regm); + cg = getregs(regm | mDX | mAX); + + cg = movregconst(cg, AX, 0x66666667, 0); // MOV EAX,0x66666667 + cg = gen2(cg,0xF7,modregrmx(3,5,reg)); // IMUL R1 + genmovreg(cg, AX, reg); // MOV EAX,R1 + genc2(cg,0xC1,modregrm(3,7,AX),31); // SAR EAX,31 + genc2(cg,0xC1,modregrm(3,7,DX),2); // SAR EDX,2 + gen2(cg,0x2B,modregrm(3,DX,AX)); // SUB EDX,EAX + + switch (oper) + { case OPdiv: + resreg = mDX; + break; + + case OPmod: + genmulimm(cg,AX,DX,10); // IMUL EAX,EDX,10 + gen2(cg,0x2B,modregxrm(3,reg,AX)); // SUB R1,EAX + resreg = regm; + break; + + case OPremquo: + genmulimm(cg,AX,DX,10); // IMUL EAX,EDX,10 + gen2(cg,0x2B,modregxrm(3,reg,AX)); // SUB R1,EAX + genmovreg(cg, AX, DX); // MOV EAX,EDX + genmovreg(cg, DX, reg); // MOV EDX,R1 + resreg = mDX | mAX; + break; + + default: + assert(0); + } + freenode(e2); + goto L3; + } + + if (sz > REGSIZE || !el_signx32(e2)) + goto L2; + + if (oper == OPmul && config.target_cpu >= TARGET_80286) + { unsigned reg; + int ss; + + freenode(e2); + retregs = byte ? BYTEREGS : ALLREGS; + resreg = *pretregs & (ALLREGS | mBP); + if (!resreg) + resreg = retregs; + + if (!I16) + { // See if we can use an LEA instruction + int ss2 = 0; + int shift; + + switch (e2factor) + { + case 12: ss = 1; ss2 = 2; goto L4; + case 24: ss = 1; ss2 = 3; goto L4; + + case 6: + case 3: ss = 1; goto L4; + + case 20: ss = 2; ss2 = 2; goto L4; + case 40: ss = 2; ss2 = 3; goto L4; + + case 10: + case 5: ss = 2; goto L4; + + case 36: ss = 3; ss2 = 2; goto L4; + case 72: ss = 3; ss2 = 3; goto L4; + + case 18: + case 9: ss = 3; goto L4; + + L4: + { +#if 1 + regm_t regm = byte ? BYTEREGS : ALLREGS; + regm &= ~(mBP | mR13); // don't use EBP + cl = codelem(e->E1,®m,TRUE); + unsigned r = findreg(regm); + + if (ss2) + { // Don't use EBP + resreg &= ~(mBP | mR13); + if (!resreg) + resreg = retregs; + } + cg = allocreg(&resreg,®,tyml); + + c = gen2sib(CNIL,0x8D,grex | modregxrm(0,reg,4), + modregxrmx(ss,r,r)); + assert((r & 7) != BP); + if (ss2) + { + gen2sib(c,0x8D,grex | modregxrm(0,reg,4), + modregxrm(ss2,reg,5)); + code_last(c)->IFL1 = FLconst; + code_last(c)->IEV1.Vint = 0; + } + else if (!(e2factor & 1)) // if even factor + { genregs(c,0x03,reg,reg); // ADD reg,reg + code_orrex(c,rex); + } + cg = cat(cg,c); + goto L3; +#else + + // Don't use EBP + resreg &= ~mBP; + if (!resreg) + resreg = retregs; + + cl = codelem(e->E1,&resreg,FALSE); + reg = findreg(resreg); + cg = getregs(resreg); + c = gen2sib(CNIL,0x8D,modregrm(0,reg,4), + modregrm(ss,reg,reg)); + if (ss2) + { + gen2sib(c,0x8D,modregrm(0,reg,4), + modregrm(ss2,reg,5)); + code_last(c)->IFL1 = FLconst; + code_last(c)->IEV1.Vint = 0; + } + else if (!(e2factor & 1)) // if even factor + genregs(c,0x03,reg,reg); // ADD reg,reg + cg = cat(cg,c); + goto L3; +#endif + } + case 37: + case 74: shift = 2; + goto L5; + case 13: + case 26: shift = 0; + goto L5; + L5: + { + // Don't use EBP + resreg &= ~(mBP | mR13); + if (!resreg) + resreg = retregs; + cl = allocreg(&resreg,®,TYint); + + regm_t sregm = (ALLREGS & ~mR13) & ~resreg; + cl = cat(cl,codelem(e->E1,&sregm,FALSE)); + unsigned sreg = findreg(sregm); + cg = getregs(resreg | sregm); + // LEA reg,[sreg * 4][sreg] + // SHL sreg,shift + // LEA reg,[sreg * 8][reg] + assert((sreg & 7) != BP); + assert((reg & 7) != BP); + c = gen2sib(CNIL,0x8D,grex | modregxrm(0,reg,4), + modregxrmx(2,sreg,sreg)); + if (shift) + genc2(c,0xC1,grex | modregrmx(3,4,sreg),shift); + gen2sib(c,0x8D,grex | modregxrm(0,reg,4), + modregxrmx(3,sreg,reg)); + if (!(e2factor & 1)) // if even factor + { genregs(c,0x03,reg,reg); // ADD reg,reg + code_orrex(c,rex); + } + cg = cat(cg,c); + goto L3; + } + } + } + + cl = scodelem(e->E1,&retregs,0,TRUE); // eval left leaf + reg = findreg(retregs); + cg = allocreg(&resreg,&rreg,e->Ety); + + /* IMUL reg,imm16 */ + cg = genc2(cg,0x69,grex | modregxrmx(3,rreg,reg),e2factor); + goto L3; + } + + // Special code for signed divide or modulo by power of 2 + if ((sz == REGSIZE || (I64 && sz == 4)) && + (oper == OPdiv || oper == OPmod) && !uns && + (pow2 = ispow2(e2factor)) != -1 && + !(config.target_cpu < TARGET_80286 && pow2 != 1 && oper == OPdiv) + ) + { + if (pow2 == 1 && oper == OPdiv && config.target_cpu > TARGET_80386) + { + // test eax,eax + // jns L1 + // add eax,1 + // L1: sar eax,1 + + code *cnop; + + retregs = allregs; + cl = codelem(e->E1,&retregs,FALSE); // eval left leaf + unsigned reg = findreg(retregs); + freenode(e2); + cg = getregs(retregs); + cg = gentstreg(cg,reg); // TEST reg,reg + code_orrex(cg, rex); + cnop = gennop(CNIL); + genjmp(cg,JNS,FLcode,(block *)cnop); // JNS cnop + if (I64) + { + gen2(cg,0xFF,modregrmx(3,0,reg)); // INC reg + code_orrex(cg,rex); + } + else + gen1(cg,0x40 + reg); // INC reg + cg = cat(cg,cnop); + gen2(cg,0xD1,grex | modregrmx(3,7,reg)); // SAR reg,1 + resreg = retregs; + goto L3; + } + cl = codelem(e->E1,&retregs,FALSE); // eval left leaf + freenode(e2); + cg = getregs(mAX | mDX); // trash these regs + cg = gen1(cg,0x99); // CWD + code_orrex(cg, rex); + if (pow2 == 1) + { + if (oper == OPdiv) + { gen2(cg,0x2B,grex | modregrm(3,AX,DX)); // SUB AX,DX + gen2(cg,0xD1,grex | modregrm(3,7,AX)); // SAR AX,1 + } + else // OPmod + { gen2(cg,0x33,grex | modregrm(3,AX,DX)); // XOR AX,DX + genc2(cg,0x81,grex | modregrm(3,4,AX),1); // AND AX,1 + gen2(cg,0x03,grex | modregrm(3,DX,AX)); // ADD DX,AX + } + } + else + { targ_ulong m; + + m = (1 << pow2) - 1; + if (oper == OPdiv) + { genc2(cg,0x81,grex | modregrm(3,4,DX),m); // AND DX,m + gen2(cg,0x03,grex | modregrm(3,AX,DX)); // ADD AX,DX + // Be careful not to generate this for 8088 + assert(config.target_cpu >= TARGET_80286); + genc2(cg,0xC1,grex | modregrm(3,7,AX),pow2); // SAR AX,pow2 + } + else // OPmod + { gen2(cg,0x33,grex | modregrm(3,AX,DX)); // XOR AX,DX + gen2(cg,0x2B,grex | modregrm(3,AX,DX)); // SUB AX,DX + genc2(cg,0x81,grex | modregrm(3,4,AX),m); // AND AX,mask + gen2(cg,0x33,grex | modregrm(3,AX,DX)); // XOR AX,DX + gen2(cg,0x2B,grex | modregrm(3,AX,DX)); // SUB AX,DX + resreg = mAX; + } + } + goto L3; + } + goto L2; + case OPind: + if (!e2->Ecount) /* if not CSE */ + goto L1; /* try OP reg,EA */ + goto L2; + default: /* OPconst and operators */ + L2: + //printf("test2 %p, retregs = %s rretregs = %s resreg = %s\n", e, regm_str(retregs), regm_str(rretregs), regm_str(resreg)); + cl = codelem(e1,&retregs,FALSE); /* eval left leaf */ + cr = scodelem(e2,&rretregs,retregs,TRUE); /* get rvalue */ + if (sz <= REGSIZE) + { cg = getregs(mAX | mDX); /* trash these regs */ + if (op == 7) /* signed divide */ + { cg = gen1(cg,0x99); // CWD + code_orrex(cg,rex); + } + else if (op == 6) /* unsigned divide */ + { + cg = movregconst(cg,DX,0,(sz == 8) ? 64 : 0); // MOV DX,0 + cg = cat(cg,getregs(mDX)); + } + rreg = findreg(rretregs); + cg = gen2(cg,0xF7 ^ byte,grex | modregrmx(3,op,rreg)); // OP AX,rreg + if (I64 && byte && rreg >= 4) + code_orrex(cg, REX); + L3: + c = fixresult(e,resreg,pretregs); + } + else if (sz == 2 * REGSIZE) + { + if (config.target_cpu >= TARGET_PentiumPro && oper == OPmul) + { + /* IMUL ECX,EAX + IMUL EDX,EBX + ADD ECX,EDX + MUL EBX + ADD EDX,ECX + */ + cg = getregs(mAX|mDX|mCX); + cg = gen2(cg,0x0FAF,modregrm(3,CX,AX)); + gen2(cg,0x0FAF,modregrm(3,DX,BX)); + gen2(cg,0x03,modregrm(3,CX,DX)); + gen2(cg,0xF7,modregrm(3,4,BX)); + gen2(cg,0x03,modregrm(3,DX,CX)); + c = fixresult(e,mDX|mAX,pretregs); + } + else + c = callclib(e,lib,pretregs,keepregs); + } + else + assert(0); + break; + case OPvar: + L1: + if (!I16 && sz <= REGSIZE) + { + if (oper == OPmul && sz > 1) /* no byte version */ + { + /* Generate IMUL r32,r/m32 */ + retregs = *pretregs & (ALLREGS | mBP); + if (!retregs) + retregs = ALLREGS; + cl = codelem(e1,&retregs,FALSE); /* eval left leaf */ + resreg = retregs; + cr = loadea(e2,&cs,0x0FAF,findreg(resreg),0,retregs,retregs); + freenode(e2); + goto L3; + } + } + else + { + if (sz == 2 * REGSIZE) + { int reg; + + if (oper != OPmul || e->E1->Eoper != opunslng || + e1->Ecount) + goto L2; // have to handle it with codelem() + + retregs = ALLREGS & ~(mAX | mDX); + cl = codelem(e1->E1,&retregs,FALSE); // eval left leaf + reg = findreg(retregs); + cl = cat(cl,getregs(mAX)); + cl = genmovreg(cl,AX,reg); // MOV AX,reg + cr = loadea(e2,&cs,0xF7,4,REGSIZE,mAX | mDX | mskl(reg),mAX | mDX); // MUL EA+2 + cg = getregs(retregs); + cg = gen1(cg,0x90 + reg); // XCHG AX,reg + cg = cat(cg,getregs(mAX | mDX)); + if ((cs.Irm & 0xC0) == 0xC0) // if EA is a register + cg = cat(cg,loadea(e2,&cs,0xF7,4,0,mAX | mskl(reg),mAX | mDX)); // MUL EA + else + { getlvalue_lsw(&cs); + gen(cg,&cs); // MUL EA + } + gen2(cg,0x03,modregrm(3,DX,reg)); // ADD DX,reg + + freenode(e1); + c = fixresult(e,mAX | mDX,pretregs); + break; + } + assert(sz <= REGSIZE); + } + + /* loadea() handles CWD or CLR DX for divides */ + cl = codelem(e->E1,&retregs,FALSE); /* eval left leaf */ + cr = loadea(e2,&cs,0xF7 ^ byte,op,0, + (oper == OPmul) ? mAX : mAX | mDX, + mAX | mDX); + freenode(e2); + goto L3; + } + return cat4(cl,cr,cg,c); +} + + +/*************************** + * Handle OPnot and OPbool. + * Generate: + * c: [evaluate e1] + * cfalse: [save reg code] + * clr reg + * jmp cnop + * ctrue: [save reg code] + * clr reg + * inc reg + * cnop: nop + */ + +code *cdnot(elem *e,regm_t *pretregs) +{ unsigned reg; + tym_t forflags; + code *c1,*c,*cfalse,*ctrue,*cnop; + unsigned sz; + regm_t retregs; + elem *e1; + int op; + + e1 = e->E1; + if (*pretregs == 0) + goto L1; + if (*pretregs == mPSW) + { /*assert(e->Eoper != OPnot && e->Eoper != OPbool);*/ /* should've been optimized */ + L1: + return codelem(e1,pretregs,FALSE); /* evaluate e1 for cc */ + } + + op = e->Eoper; + sz = tysize(e1->Ety); + unsigned rex = (I64 && sz == 8) ? REX_W : 0; + unsigned grex = rex << 16; + if (!tyfloating(e1->Ety)) + { + if (sz <= REGSIZE && e1->Eoper == OPvar) + { code cs; + + c = getlvalue(&cs,e1,0); + freenode(e1); + if (!I16 && sz == 2) + cs.Iflags |= CFopsize; + + retregs = *pretregs & (ALLREGS | mBP); + if (config.target_cpu >= TARGET_80486 && + tysize(e->Ety) == 1) + { + if (reghasvalue((sz == 1) ? BYTEREGS : ALLREGS,0,®)) + cs.Iop = 0x39; + else + { cs.Iop = 0x81; + reg = 7; + cs.IFL2 = FLconst; + cs.IEV2.Vint = 0; + } + cs.Iop ^= (sz == 1); + code_newreg(&cs,reg); + c = gen(c,&cs); // CMP e1,0 + + retregs &= BYTEREGS; + if (!retregs) + retregs = BYTEREGS; + c1 = allocreg(&retregs,®,TYint); + + int iop; + if (op == OPbool) + { + iop = 0x0F95; // SETNZ rm8 + } + else + { + iop = 0x0F94; // SETZ rm8 + } + c1 = gen2(c1,iop,grex | modregrmx(3,0,reg)); + if (reg >= 4) + code_orrex(c1, REX); + if (op == OPbool) + *pretregs &= ~mPSW; + goto L4; + } + + if (reghasvalue((sz == 1) ? BYTEREGS : ALLREGS,1,®)) + cs.Iop = 0x39; + else + { cs.Iop = 0x81; + reg = 7; + cs.IFL2 = FLconst; + cs.IEV2.Vint = 1; + } + cs.Iop ^= (sz == 1); + code_newreg(&cs,reg); + c = gen(c,&cs); // CMP e1,1 + + c1 = allocreg(&retregs,®,TYint); + op ^= (OPbool ^ OPnot); // switch operators + goto L2; + } + else if (sz <= REGSIZE && + // NEG bytereg is too expensive + (sz != 1 || config.target_cpu < TARGET_PentiumPro)) + { + retregs = *pretregs & (ALLREGS | mBP); + if (sz == 1 && !(retregs &= BYTEREGS)) + retregs = BYTEREGS; + c = codelem(e->E1,&retregs,FALSE); + reg = findreg(retregs); + c1 = getregs(retregs); + c1 = gen2(c1,0xF7 ^ (sz == 1),grex | modregrmx(3,3,reg)); // NEG reg + code_orflag(c1,CFpsw); + if (!I16 && sz == SHORTSIZE) + code_orflag(c1,CFopsize); + L2: + c1 = genregs(c1,0x19,reg,reg); // SBB reg,reg + code_orrex(c1, rex); + // At this point, reg==0 if e1==0, reg==-1 if e1!=0 + if (op == OPnot) + { + if (I64) + gen2(c1,0xFF,grex | modregrmx(3,0,reg)); // INC reg + else + gen1(c1,0x40 + reg); // INC reg + } + else + gen2(c1,0xF7,grex | modregrmx(3,3,reg)); // NEG reg + if (*pretregs & mPSW) + { code_orflag(c1,CFpsw); + *pretregs &= ~mPSW; // flags are always set anyway + } + L4: + return cat3(c,c1,fixresult(e,retregs,pretregs)); + } + } + cnop = gennop(CNIL); + ctrue = gennop(CNIL); + c = logexp(e->E1,(op == OPnot) ? FALSE : TRUE,FLcode,ctrue); + forflags = *pretregs & mPSW; + if (I64 && sz == 8) + forflags |= 64; + assert(tysize(e->Ety) <= REGSIZE); // result better be int + cfalse = allocreg(pretregs,®,e->Ety); // allocate reg for result + for (c1 = cfalse; c1; c1 = code_next(c1)) + gen(ctrue,c1); // duplicate reg save code + cfalse = movregconst(cfalse,reg,0,forflags); // mov 0 into reg + regcon.immed.mval &= ~mask[reg]; // mark reg as unavail + ctrue = movregconst(ctrue,reg,1,forflags); // mov 1 into reg + regcon.immed.mval &= ~mask[reg]; // mark reg as unavail + genjmp(cfalse,JMP,FLcode,(block *) cnop); // skip over ctrue + c = cat4(c,cfalse,ctrue,cnop); + return c; +} + + +/************************ + * Complement operator + */ + +code *cdcom(elem *e,regm_t *pretregs) +{ unsigned reg,op; + regm_t retregs,possregs; + code *c,*c1,*cg; + tym_t tym; + int sz; + + if (*pretregs == 0) + return codelem(e->E1,pretregs,FALSE); + tym = tybasic(e->Ety); + sz = tysize[tym]; + unsigned rex = (I64 && sz == 8) ? REX_W : 0; + possregs = (sz == 1) ? BYTEREGS : allregs; + retregs = *pretregs & possregs; + if (retregs == 0) + retregs = possregs; + c1 = codelem(e->E1,&retregs,FALSE); + cg = getregs(retregs); /* retregs will be destroyed */ +#if 0 + if (sz == 4 * REGSIZE) + { + c = gen2(CNIL,0xF7,modregrm(3,2,AX)); // NOT AX + gen2(c,0xF7,modregrm(3,2,BX)); // NOT BX + gen2(c,0xF7,modregrm(3,2,CX)); // NOT CX + gen2(c,0xF7,modregrm(3,2,DX)); // NOT DX + } + else +#endif + { + reg = (sz <= REGSIZE) ? findreg(retregs) : findregmsw(retregs); + op = (sz == 1) ? 0xF6 : 0xF7; + c = genregs(CNIL,op,2,reg); // NOT reg + code_orrex(c, rex); + if (I64 && sz == 1 && reg >= 4) + code_orrex(c, REX); + if (sz == 2 * REGSIZE) + { reg = findreglsw(retregs); + genregs(c,op,2,reg); // NOT reg+1 + } + } + return cat4(c1,cg,c,fixresult(e,retregs,pretregs)); +} + +/************************ + * Bswap operator + */ + +code *cdbswap(elem *e,regm_t *pretregs) +{ unsigned reg,op; + regm_t retregs; + code *c,*c1,*cg; + tym_t tym; + int sz; + + if (*pretregs == 0) + return codelem(e->E1,pretregs,FALSE); + + tym = tybasic(e->Ety); + assert(tysize[tym] == 4); + retregs = *pretregs & allregs; + if (retregs == 0) + retregs = allregs; + c1 = codelem(e->E1,&retregs,FALSE); + cg = getregs(retregs); // retregs will be destroyed + reg = findreg(retregs); + c = gen2(CNIL,0x0FC8 + (reg & 7),0); // BSWAP reg + if (reg & 8) + code_orrex(c, REX_B); + return cat4(c1,cg,c,fixresult(e,retregs,pretregs)); +} + +/************************* + * ?: operator + */ + +code *cdcond(elem *e,regm_t *pretregs) +{ regm_t psw; + code *cc,*c,*c1,*cnop1,*c2,*cnop2; + con_t regconold,regconsave; + unsigned stackpushold,stackpushsave; + int ehindexold,ehindexsave; + unsigned jop; + unsigned op1; + unsigned sz1; + unsigned sz2; + elem *e1; + elem *e2; + elem *e21; + elem *e22; + + /* vars to save state of 8087 */ + int stackusedold,stackusedsave; + NDP _8087old[arraysize(_8087elems)]; + NDP _8087save[arraysize(_8087elems)]; + + _chkstack(); + + //printf("cdcond(e = %p, *pretregs = %s)\n",e,regm_str(*pretregs)); + e1 = e->E1; + e2 = e->E2; + e21 = e2->E1; + e22 = e2->E2; + cc = docommas(&e1); + cgstate.stackclean++; + psw = *pretregs & mPSW; /* save PSW bit */ + op1 = e1->Eoper; + sz1 = tysize(e1->Ety); + unsigned rex = (I64 && sz1 == 8) ? REX_W : 0; + unsigned grex = rex << 16; + jop = jmpopcode(e1); + + if (!OTrel(op1) && e1 == e21 && + sz1 <= REGSIZE && !tyfloating(e1->Ety)) + { // Recognize (e ? e : f) + regm_t retregs; + + cnop1 = gennop(CNIL); + retregs = *pretregs | mPSW; + c = codelem(e1,&retregs,FALSE); + + c = cat(c,cse_flush(1)); // flush CSEs to memory + c = genjmp(c,jop,FLcode,(block *)cnop1); + freenode(e21); + + regconsave = regcon; + stackpushsave = stackpush; + + retregs |= psw; + if (retregs & (mBP | ALLREGS)) + regimmed_set(findreg(retregs),0); + c2 = codelem(e22,&retregs,FALSE); + + andregcon(®consave); + assert(stackpushsave == stackpush); + + *pretregs = retregs; + freenode(e2); + c = cat4(cc,c,c2,cnop1); + goto Lret; + } + + if (OTrel(op1) && sz1 <= REGSIZE && tysize(e2->Ety) <= REGSIZE && + !e1->Ecount && + (jop == JC || jop == JNC) && + (sz2 = tysize(e2->Ety)) <= REGSIZE && + e21->Eoper == OPconst && + e22->Eoper == OPconst + ) + { regm_t retregs; + unsigned reg; + targ_size_t v1,v2; + int opcode; + + retregs = *pretregs & (ALLREGS | mBP); + if (!retregs) + retregs = ALLREGS; + cdcmp_flag = 1; + c = codelem(e1,&retregs,FALSE); + reg = findreg(retregs); + v1 = e21->EV.Vllong; + v2 = e22->EV.Vllong; + if (jop == JNC) + { v1 = v2; + v2 = e21->EV.Vlong; + } + + opcode = 0x81; + switch (sz2) + { case 1: opcode--; + v1 = (signed char) v1; + v2 = (signed char) v2; + break; + case 2: v1 = (short) v1; + v2 = (short) v2; + break; + case 4: v1 = (int) v1; + v2 = (int) v2; + break; + } + + if (v1 == 0 && v2 == ~(targ_size_t)0) + c = gen2(c,0xF6 + (opcode & 1),grex | modregrmx(3,2,reg)); // NOT reg + else + { + v1 -= v2; + c = genc2(c,opcode,grex | modregrmx(3,4,reg),v1); // AND reg,v1-v2 + if (v2 == 1 && !I64) + gen1(c,0x40 + reg); // INC reg + else if (v2 == -1L && !I64) + gen1(c,0x48 + reg); // DEC reg + else + genc2(c,opcode,grex | modregrmx(3,0,reg),v2); // ADD reg,v2 + } + + freenode(e21); + freenode(e22); + freenode(e2); + + c = cat(c,fixresult(e,retregs,pretregs)); + goto Lret; + } + + if (op1 != OPcond && op1 != OPandand && op1 != OPoror && + op1 != OPnot && op1 != OPbool && + e21->Eoper == OPconst && + sz1 <= REGSIZE && + *pretregs & (mBP | ALLREGS) && + tysize(e21->Ety) <= REGSIZE && !tyfloating(e21->Ety)) + { // Recognize (e ? c : f) + unsigned reg; + regm_t retregs; + + cnop1 = gennop(CNIL); + retregs = mPSW; + jop = jmpopcode(e1); // get jmp condition + c = codelem(e1,&retregs,FALSE); + + // Set the register with e21 without affecting the flags + retregs = *pretregs & (ALLREGS | mBP); + if (retregs & ~regcon.mvar) + retregs &= ~regcon.mvar; // don't disturb register variables + // NOTE: see my email (sign extension bug? possible fix, some questions + c = regwithvalue(c,retregs,e21->EV.Vllong,®,tysize(e21->Ety) == 8 ? 64|8 : 8); + retregs = mask[reg]; + + c = cat(c,cse_flush(1)); // flush CSE's to memory + c = genjmp(c,jop,FLcode,(block *)cnop1); + freenode(e21); + + regconsave = regcon; + stackpushsave = stackpush; + + c2 = codelem(e22,&retregs,FALSE); + + andregcon(®consave); + assert(stackpushsave == stackpush); + + freenode(e2); + c = cat6(cc,c,c2,cnop1,fixresult(e,retregs,pretregs),NULL); + goto Lret; + } + + cnop1 = gennop(CNIL); + cnop2 = gennop(CNIL); /* dummy target addresses */ + c = logexp(e1,FALSE,FLcode,cnop1); /* evaluate condition */ + regconold = regcon; + stackusedold = stackused; + stackpushold = stackpush; + memcpy(_8087old,_8087elems,sizeof(_8087elems)); + c1 = codelem(e21,pretregs,FALSE); + +#if SCPP + if (CPP && e2->Eoper == OPcolon2) + { code cs; + + // This is necessary so that any cleanup code on one branch + // is redone on the other branch. + cs.Iop = ESCAPE | ESCmark2; + cs.Iflags = 0; + cs.Irex = 0; + c1 = cat(gen(CNIL,&cs),c1); + cs.Iop = ESCAPE | ESCrelease2; + c1 = gen(c1,&cs); + } +#endif + + regconsave = regcon; + regcon = regconold; + + stackpushsave = stackpush; + stackpush = stackpushold; + + stackusedsave = stackused; + stackused = stackusedold; + + memcpy(_8087save,_8087elems,sizeof(_8087elems)); + memcpy(_8087elems,_8087old,sizeof(_8087elems)); + + *pretregs |= psw; /* PSW bit may have been trashed */ + c2 = codelem(e22,pretregs,FALSE); /* use same regs as E1 */ + andregcon(®conold); + andregcon(®consave); + assert(stackused == stackusedsave); + assert(stackpush == stackpushsave); + memcpy(_8087elems,_8087save,sizeof(_8087elems)); + freenode(e2); + c = cat(cc,c); + c = cat6(c,c1,genjmp(CNIL,JMP,FLcode,(block *) cnop2),cnop1,c2,cnop2); + if (*pretregs & mST0) + note87(e,0,0); +Lret: + cgstate.stackclean--; + return c; +} + +/********************* + * Comma operator + */ + +code *cdcomma(elem *e,regm_t *pretregs) +{ regm_t retregs; + code *cl,*cr; + + retregs = 0; + cl = codelem(e->E1,&retregs,FALSE); /* ignore value from left leaf */ + cr = codelem(e->E2,pretregs,FALSE); /* do right leaf */ + return cat(cl,cr); +} + + +/********************************* + * Do && and || operators. + * Generate: + * (evaluate e1 and e2, if TRUE goto cnop1) + * cnop3: NOP + * cg: [save reg code] ;if we must preserve reg + * CLR reg ;FALSE result (set Z also) + * JMP cnop2 + * + * cnop1: NOP ;if e1 evaluates to TRUE + * [save reg code] ;preserve reg + * + * MOV reg,1 ;TRUE result + * or + * CLR reg ;if return result in flags + * INC reg + * + * cnop2: NOP ;mark end of code + */ + +code *cdloglog(elem *e,regm_t *pretregs) +{ regm_t retregs; + unsigned reg; + code *c; + code *cl,*cr,*cg,*cnop1,*cnop2,*cnop3; + code *c1; + con_t regconsave; + unsigned stackpushsave; + elem *e2; + unsigned sz = tysize(e->Ety); + + /* We can trip the assert with the following: */ + /* if ( (b<=a) ? (c=a ) */ + /* We'll generate ugly code for it, but it's too obscure a case */ + /* to expend much effort on it. */ + /*assert(*pretregs != mPSW);*/ + + cgstate.stackclean++; + cnop1 = gennop(CNIL); + cnop3 = gennop(CNIL); + e2 = e->E2; + cl = (e->Eoper == OPoror) + ? logexp(e->E1,1,FLcode,cnop1) + : logexp(e->E1,0,FLcode,cnop3); + regconsave = regcon; + stackpushsave = stackpush; + if (*pretregs == 0) /* if don't want result */ + { int noreturn = el_noreturn(e2); + + cr = codelem(e2,pretregs,FALSE); + if (noreturn) + { + regconsave.used |= regcon.used; + regcon = regconsave; + } + else + andregcon(®consave); + assert(stackpush == stackpushsave); + c = cat4(cl,cr,cnop3,cnop1); // eval code, throw away result + goto Lret; + } + cnop2 = gennop(CNIL); + if (tybasic(e2->Ety) == TYbool && + sz == tysize(e2->Ety) && + !(*pretregs & mPSW) && + e2->Eoper == OPcall) + { + cr = codelem(e2,pretregs,FALSE); + + andregcon(®consave); + + // stack depth should not change when evaluating E2 + assert(stackpush == stackpushsave); + + assert(sz <= 4); // result better be int + retregs = *pretregs & allregs; + cnop1 = cat(cnop1,allocreg(&retregs,®,TYint)); // allocate reg for result + cg = genjmp(NULL,JMP,FLcode,(block *) cnop2); // JMP cnop2 + cnop1 = movregconst(cnop1,reg,e->Eoper == OPoror,0); // reg = 1 + regcon.immed.mval &= ~mask[reg]; // mark reg as unavail + *pretregs = retregs; + if (e->Eoper == OPoror) + c = cat6(cl,cr,cnop3,cg,cnop1,cnop2); + else + c = cat6(cl,cr,cg,cnop3,cnop1,cnop2); + + goto Lret; + } + cr = logexp(e2,1,FLcode,cnop1); + andregcon(®consave); + + /* stack depth should not change when evaluating E2 */ + assert(stackpush == stackpushsave); + + assert(sz <= 4); // result better be int + retregs = *pretregs & (ALLREGS | mBP); + if (!retregs) retregs = ALLREGS; // if mPSW only + cg = allocreg(&retregs,®,TYint); // allocate reg for result + for (c1 = cg; c1; c1 = code_next(c1)) // for each instruction + gen(cnop1,c1); // duplicate it + cg = movregconst(cg,reg,0,*pretregs & mPSW); // MOV reg,0 + regcon.immed.mval &= ~mask[reg]; // mark reg as unavail + genjmp(cg,JMP,FLcode,(block *) cnop2); // JMP cnop2 + cnop1 = movregconst(cnop1,reg,1,*pretregs & mPSW); // reg = 1 + regcon.immed.mval &= ~mask[reg]; // mark reg as unavail + *pretregs = retregs; + c = cat6(cl,cr,cnop3,cg,cnop1,cnop2); +Lret: + cgstate.stackclean--; + return c; +} + + +/********************* + * Generate code for shift left or shift right (OPshl,OPshr,OPashr,OProl,OPror). + */ + +code *cdshift(elem *e,regm_t *pretregs) +{ unsigned resreg,shiftcnt,byte; + unsigned s1,s2,oper; + tym_t tyml; + int sz; + regm_t retregs,rretregs; + code *cg,*cl,*cr; + code *c; + elem *e1; + elem *e2; + regm_t forccs,forregs; + bool e2isconst; + + e1 = e->E1; + if (*pretregs == 0) // if don't want result + { c = codelem(e1,pretregs,FALSE); // eval left leaf + *pretregs = 0; // in case they got set + return cat(c,codelem(e->E2,pretregs,FALSE)); + } + + tyml = tybasic(e1->Ety); + sz = tysize[tyml]; + assert(!tyfloating(tyml)); + oper = e->Eoper; + unsigned rex = (I64 && sz == 8) ? REX_W : 0; + unsigned grex = rex << 16; + +#if SCPP + // Do this until the rest of the compiler does OPshr/OPashr correctly + if (oper == OPshr) + oper = (tyuns(tyml)) ? OPshr : OPashr; +#endif + + switch (oper) + { case OPshl: + s1 = 4; // SHL + s2 = 2; // RCL + break; + case OPshr: + s1 = 5; // SHR + s2 = 3; // RCR + break; + case OPashr: + s1 = 7; // SAR + s2 = 3; // RCR + break; + case OProl: + s1 = 0; // ROL + break; + case OPror: + s1 = 1; // ROR + break; + default: + assert(0); + } + + unsigned sreg = ~0; // guard against using value without assigning to sreg + c = cg = cr = CNIL; /* initialize */ + e2 = e->E2; + forccs = *pretregs & mPSW; /* if return result in CCs */ + forregs = *pretregs & (ALLREGS | mBP); // mask of possible return regs + e2isconst = FALSE; /* assume for the moment */ + byte = (sz == 1); + switch (e2->Eoper) + { + case OPconst: + e2isconst = TRUE; /* e2 is a constant */ + shiftcnt = e2->EV.Vint; /* get shift count */ + if ((!I16 && sz <= REGSIZE) || + shiftcnt <= 4 || /* if sequence of shifts */ + (sz == 2 && + (shiftcnt == 8 || config.target_cpu >= TARGET_80286)) || + (sz == 2 * REGSIZE && shiftcnt == 8 * REGSIZE) + ) + { retregs = (forregs) ? forregs + : ALLREGS; + if (byte) + { retregs &= BYTEREGS; + if (!retregs) + retregs = BYTEREGS; + } + else if (sz > REGSIZE && sz <= 2 * REGSIZE && + !(retregs & mMSW)) + retregs |= mMSW & ALLREGS; + if (s1 == 7) /* if arithmetic right shift */ + { + if (shiftcnt == 8) + retregs = mAX; + else if (sz == 2 * REGSIZE && shiftcnt == 8 * REGSIZE) + retregs = mDX|mAX; + } + + if (sz == 2 * REGSIZE && shiftcnt == 8 * REGSIZE && + oper == OPshl && + !e1->Ecount && + (e1->Eoper == OPs16_32 || e1->Eoper == OPu16_32 || + e1->Eoper == OPs32_64 || e1->Eoper == OPu32_64) + ) + { // Handle (shtlng)s << 16 + regm_t r; + + r = retregs & mMSW; + cl = codelem(e1->E1,&r,FALSE); // eval left leaf + cl = regwithvalue(cl,retregs & mLSW,0,&resreg,0); + cg = getregs(r); + retregs = r | mask[resreg]; + if (forccs) + { sreg = findreg(r); + cg = gentstreg(cg,sreg); + *pretregs &= ~mPSW; // already set + } + freenode(e1); + freenode(e2); + break; + } + + // See if we should use LEA reg,xxx instead of shift + if (!I16 && shiftcnt >= 1 && shiftcnt <= 3 && + (sz == REGSIZE || (I64 && sz == 4)) && + oper == OPshl && + e1->Eoper == OPvar && + !(*pretregs & mPSW) && + config.flags4 & CFG4speed + ) + { + unsigned reg; + regm_t regm; + + if (isregvar(e1,®m,®) && !(regm & retregs)) + { code cs; + cl = allocreg(&retregs,&resreg,e->Ety); + buildEA(&cs,-1,reg,1 << shiftcnt,0); + cs.Iop = 0x8D; + code_newreg(&cs,resreg); + cs.Iflags = 0; + if (I64 && sz == 8) + cs.Irex |= REX_W; + cg = gen(NULL,&cs); // LEA resreg,[reg * ss] + freenode(e1); + freenode(e2); + break; + } + } + + cl = codelem(e1,&retregs,FALSE); // eval left leaf + //assert((retregs & regcon.mvar) == 0); + cg = getregs(retregs); // trash these regs + + { + if (sz == 2 * REGSIZE) + { resreg = findregmsw(retregs); + sreg = findreglsw(retregs); + } + else + { resreg = findreg(retregs); + sreg = ~0; // an invalid value + } + if (config.target_cpu >= TARGET_80286 && + sz <= REGSIZE) + { + /* SHL resreg,shiftcnt */ + assert(!(sz == 1 && (mask[resreg] & ~BYTEREGS))); + c = genc2(CNIL,0xC1 ^ byte,grex | modregxrmx(3,s1,resreg),shiftcnt); + if (shiftcnt == 1) + c->Iop += 0x10; /* short form of shift */ + if (I64 && sz == 1 && resreg >= 4) + c->Irex |= REX; + // See if we need operand size prefix + if (!I16 && oper != OPshl && sz == 2) + c->Iflags |= CFopsize; + if (forccs) + c->Iflags |= CFpsw; // need flags result + } + else if (shiftcnt == 8) + { if (!(retregs & BYTEREGS) || resreg >= 4) + { + cl = cat(cl,cg); + goto L1; + } + + if (pass != PASSfinal && (!forregs || forregs & (mSI | mDI))) + { + // e1 might get into SI or DI in a later pass, + // so don't put CX into a register + cg = cat(cg, getregs(mCX)); + } + + assert(sz == 2); + switch (oper) + { + case OPshl: + /* MOV regH,regL XOR regL,regL */ + assert(resreg < 4 && !rex); + c = genregs(CNIL,0x8A,resreg+4,resreg); + genregs(c,0x32,resreg,resreg); + break; + + case OPshr: + case OPashr: + /* MOV regL,regH */ + c = genregs(CNIL,0x8A,resreg,resreg+4); + if (oper == OPashr) + gen1(c,0x98); /* CBW */ + else + genregs(c,0x32,resreg+4,resreg+4); /* CLR regH */ + break; + + case OPror: + case OProl: + // XCHG regL,regH + c = genregs(CNIL,0x86,resreg+4,resreg); + break; + + default: + assert(0); + } + if (forccs) + gentstreg(c,resreg); + } + else if (shiftcnt == REGSIZE * 8) // it's an lword + { + if (oper == OPshl) + swap((int *) &resreg,(int *) &sreg); + c = genmovreg(CNIL,sreg,resreg); // MOV sreg,resreg + if (oper == OPashr) + gen1(c,0x99); // CWD + else + movregconst(c,resreg,0,0); // MOV resreg,0 + if (forccs) + { gentstreg(c,sreg); + *pretregs &= mBP | ALLREGS | mES; + } + } + else + { c = CNIL; + if (oper == OPshl && sz == 2 * REGSIZE) + swap((int *) &resreg,(int *) &sreg); + while (shiftcnt--) + { c = gen2(c,0xD1 ^ byte,modregrm(3,s1,resreg)); + if (sz == 2 * REGSIZE) + gen2(c,0xD1,modregrm(3,s2,sreg)); + } + if (forccs) + code_orflag(c,CFpsw); + } + if (sz <= REGSIZE) + *pretregs &= mBP | ALLREGS; // flags already set + } + freenode(e2); + break; + } + /* FALL-THROUGH */ + default: + retregs = forregs & ~mCX; /* CX will be shift count */ + if (sz <= REGSIZE) + { + if (forregs & ~regcon.mvar && !(retregs & ~regcon.mvar)) + retregs = ALLREGS & ~mCX; /* need something */ + else if (!retregs) + retregs = ALLREGS & ~mCX; /* need something */ + if (sz == 1) + { retregs &= mAX|mBX|mDX; + if (!retregs) + retregs = mAX|mBX|mDX; + } + } + else + { + if (!(retregs & mMSW)) + retregs = ALLREGS & ~mCX; + } + cl = codelem(e->E1,&retregs,FALSE); /* eval left leaf */ + + if (sz <= REGSIZE) + resreg = findreg(retregs); + else + { + resreg = findregmsw(retregs); + sreg = findreglsw(retregs); + } + L1: + rretregs = mCX; /* CX is shift count */ + if (sz <= REGSIZE) + { + cr = scodelem(e2,&rretregs,retregs,FALSE); /* get rvalue */ + cg = getregs(retregs); /* trash these regs */ + c = gen2(CNIL,0xD3 ^ byte,grex | modregrmx(3,s1,resreg)); /* Sxx resreg,CX */ + + if (!I16 && sz == 2 && (oper == OProl || oper == OPror)) + c->Iflags |= CFopsize; + + // Note that a shift by CL does not set the flags if + // CL == 0. If e2 is a constant, we know it isn't 0 + // (it would have been optimized out). + if (e2isconst) + *pretregs &= mBP | ALLREGS; // flags already set with result + } + else if (sz == 2 * REGSIZE && + config.target_cpu >= TARGET_80386) + { + unsigned hreg = resreg; + unsigned lreg = sreg; + unsigned rex = I64 ? (REX_W << 16) : 0; + if (e2isconst) + { + cr = NULL; + cg = getregs(retregs); + if (shiftcnt & (REGSIZE * 8)) + { + if (oper == OPshr) + { // SHR hreg,shiftcnt + // MOV lreg,hreg + // XOR hreg,hreg + c = genc2(NULL,0xC1,rex | modregrm(3,s1,hreg),shiftcnt - (REGSIZE * 8)); + c = genmovreg(c,lreg,hreg); + c = movregconst(c,hreg,0,0); + } + else if (oper == OPashr) + { // MOV lreg,hreg + // SAR hreg,31 + // SHRD lreg,hreg,shiftcnt + c = genmovreg(NULL,lreg,hreg); + c = genc2(c,0xC1,rex | modregrm(3,s1,hreg),(REGSIZE * 8) - 1); + c = genc2(c,0x0FAC,rex | modregrm(3,hreg,lreg),shiftcnt - (REGSIZE * 8)); + } + else + { // SHL lreg,shiftcnt + // MOV hreg,lreg + // XOR lreg,lreg + c = genc2(NULL,0xC1,rex | modregrm(3,s1,lreg),shiftcnt - (REGSIZE * 8)); + c = genmovreg(c,hreg,lreg); + c = movregconst(c,lreg,0,0); + } + } + else + { + if (oper == OPshr || oper == OPashr) + { // SHRD lreg,hreg,shiftcnt + // SHR/SAR hreg,shiftcnt + c = genc2(NULL,0x0FAC,rex | modregrm(3,hreg,lreg),shiftcnt); + c = genc2(c,0xC1,rex | modregrm(3,s1,hreg),shiftcnt); + } + else + { // SHLD hreg,lreg,shiftcnt + // SHL lreg,shiftcnt + c = genc2(NULL,0x0FA4,rex | modregrm(3,lreg,hreg),shiftcnt); + c = genc2(c,0xC1,rex | modregrm(3,s1,lreg),shiftcnt); + } + } + freenode(e2); + } + else if (config.target_cpu >= TARGET_80486 && REGSIZE == 2) + { + cr = scodelem(e2,&rretregs,retregs,FALSE); // get rvalue in CX + cg = getregs(retregs); // modify these regs + if (oper == OPshl) + { + /* + SHLD hreg,lreg,CL + SHL lreg,CL + */ + + c = gen2(NULL,0x0FA5,modregrm(3,lreg,hreg)); + gen2(c,0xD3,modregrm(3,4,lreg)); + } + else + { + /* + SHRD lreg,hreg,CL + SAR hreg,CL + + -- or -- + + SHRD lreg,hreg,CL + SHR hreg,CL + */ + c = gen2(NULL,0x0FAD,modregrm(3,hreg,lreg)); + gen2(c,0xD3,modregrm(3,s1,hreg)); + } + } + else + { code *cl1,*cl2; + + cr = scodelem(e2,&rretregs,retregs,FALSE); // get rvalue in CX + cg = getregs(retregs | mCX); // modify these regs + // TEST CL,0x20 + c = genc2(NULL,0xF6,modregrm(3,0,CX),REGSIZE * 8); + if (oper == OPshl) + { + /* TEST CL,20H + JNE L1 + SHLD hreg,lreg,CL + SHL lreg,CL + JMP L2 + L1: AND CL,20H-1 + SHL lreg,CL + MOV hreg,lreg + XOR lreg,lreg + L2: NOP + */ + + cl1 = NULL; + if (REGSIZE == 2) + cl1 = genc2(NULL,0x80,modregrm(3,4,CX),REGSIZE * 8 - 1); + cl1 = gen2(cl1,0xD3,modregrm(3,4,lreg)); + genmovreg(cl1,hreg,lreg); + genregs(cl1,0x31,lreg,lreg); + + genjmp(c,JNE,FLcode,(block *)cl1); + gen2(c,0x0FA5,modregrm(3,lreg,hreg)); + gen2(c,0xD3,modregrm(3,4,lreg)); + } + else + { if (oper == OPashr) + { + /* TEST CL,20H + JNE L1 + SHRD lreg,hreg,CL + SAR hreg,CL + JMP L2 + L1: AND CL,15 + MOV lreg,hreg + SAR hreg,31 + SHRD lreg,hreg,CL + L2: NOP + */ + + cl1 = NULL; + if (REGSIZE == 2) + cl1 = genc2(NULL,0x80,modregrm(3,4,CX),REGSIZE * 8 - 1); + cl1 = genmovreg(cl1,lreg,hreg); + genc2(cl1,0xC1,modregrm(3,s1,hreg),31); + gen2(cl1,0x0FAD,modregrm(3,hreg,lreg)); + } + else + { + /* TEST CL,20H + JNE L1 + SHRD lreg,hreg,CL + SHR hreg,CL + JMP L2 + L1: AND CL,15 + SHR hreg,CL + MOV lreg,hreg + XOR hreg,hreg + L2: NOP + */ + + cl1 = NULL; + if (REGSIZE == 2) + cl1 = genc2(NULL,0x80,modregrm(3,4,CX),REGSIZE * 8 - 1); + cl1 = gen2(cl1,0xD3,modregrm(3,5,hreg)); + genmovreg(cl1,lreg,hreg); + genregs(cl1,0x31,hreg,hreg); + } + genjmp(c,JNE,FLcode,(block *)cl1); + gen2(c,0x0FAD,modregrm(3,hreg,lreg)); + gen2(c,0xD3,modregrm(3,s1,hreg)); + } + cl2 = gennop(NULL); + genjmp(c,JMPS,FLcode,(block *)cl2); + c = cat3(c,cl1,cl2); + } + break; + } + else if (sz == 2 * REGSIZE) + { + c = CNIL; + if (!e2isconst) // if not sure shift count != 0 + c = genc2(c,0xE3,0,6); // JCXZ .+6 + cr = scodelem(e2,&rretregs,retregs,FALSE); + cg = getregs(retregs | mCX); + if (oper == OPshl) + swap((int *) &resreg,(int *) &sreg); + c = gen2(c,0xD1,modregrm(3,s1,resreg)); + code_orflag(c,CFtarg2); + gen2(c,0xD1,modregrm(3,s2,sreg)); + genc2(c,0xE2,0,(targ_uns)-6); // LOOP .-6 + regimmed_set(CX,0); // note that now CX == 0 + } + else + assert(0); + break; + } + c = cat(c,fixresult(e,retregs,pretregs)); + return cat4(cl,cr,cg,c); +} + + +/*************************** + * Perform a 'star' reference (indirection). + */ + +code *cdind(elem *e,regm_t *pretregs) +{ code *c,*ce,cs; + tym_t tym; + regm_t idxregs,retregs; + unsigned reg,nreg,byte; + elem *e1; + unsigned sz; + + //printf("cdind(e = %p, *pretregs = %s)\n",e,regm_str(*pretregs)); + tym = tybasic(e->Ety); + if (tyfloating(tym)) + { + if (config.inline8087) + { + if (*pretregs & mST0) + return cdind87(e, pretregs); + if (tycomplex(tym)) + return cload87(e, pretregs); + if (*pretregs & mPSW) + return cdind87(e, pretregs); + } + } + + e1 = e->E1; + assert(e1); + switch (tym) + { case TYstruct: + case TYarray: + // This case should never happen, why is it here? + tym = TYnptr; // don't confuse allocreg() +#if TARGET_SEGMENTED + if (*pretregs & (mES | mCX) || e->Ety & mTYfar) + tym = TYfptr; +#endif + +#if 0 + c = getlvalue(&cs,e,RMload); // get addressing mode + if (*pretregs == 0) + return c; + idxregs = idxregm(&cs); // mask of index regs used + c = cat(c,fixresult(e,idxregs,pretregs)); + return c; +#endif + break; + } + sz = tysize[tym]; + byte = tybyte(tym) != 0; + + c = getlvalue(&cs,e,RMload); // get addressing mode + //printf("Irex = %02x, Irm = x%02x, Isib = x%02x\n", cs.Irex, cs.Irm, cs.Isib); + /*fprintf(stderr,"cd2 :\n"); WRcodlst(c);*/ + if (*pretregs == 0) + return c; + + idxregs = idxregm(&cs); // mask of index regs used + + if (*pretregs == mPSW) + { + if (!I16 && tym == TYfloat) + { retregs = ALLREGS & ~idxregs; + c = cat(c,allocreg(&retregs,®,TYfloat)); + cs.Iop = 0x8B; + code_newreg(&cs,reg); + ce = gen(CNIL,&cs); // MOV reg,lsw + gen2(ce,0xD1,modregrmx(3,4,reg)); // SHL reg,1 + } + else if (sz <= REGSIZE) + { + cs.Iop = 0x81 ^ byte; + cs.Irm |= modregrm(0,7,0); + cs.IFL2 = FLconst; + cs.IEV2.Vint = 0; + ce = gen(CNIL,&cs); /* CMP [idx],0 */ + } + else if (!I16 && sz == REGSIZE + 2) // if far pointer + { retregs = ALLREGS & ~idxregs; + c = cat(c,allocreg(&retregs,®,TYint)); + cs.Iop = 0x0FB7; + cs.Irm |= modregrm(0,reg,0); + getlvalue_msw(&cs); + ce = gen(CNIL,&cs); /* MOVZX reg,msw */ + goto L4; + } + else if (sz <= 2 * REGSIZE) + { retregs = ALLREGS & ~idxregs; + c = cat(c,allocreg(&retregs,®,TYint)); + cs.Iop = 0x8B; + code_newreg(&cs,reg); + getlvalue_msw(&cs); + ce = gen(CNIL,&cs); /* MOV reg,msw */ + if (I32) + { if (tym == TYdouble || tym == TYdouble_alias) + gen2(ce,0xD1,modregrm(3,4,reg)); // SHL reg,1 + } + else if (tym == TYfloat) + gen2(ce,0xD1,modregrm(3,4,reg)); /* SHL reg,1 */ + L4: cs.Iop = 0x0B; + getlvalue_lsw(&cs); + gen(ce,&cs); /* OR reg,lsw */ + } + else if (!I32 && sz == 8) + { *pretregs |= DOUBLEREGS_16; /* fake it for now */ + goto L1; + } + else + { + debugx(WRTYxx(tym)); + assert(0); + } + c = cat(c,ce); + } + else /* else return result in reg */ + { + L1: retregs = *pretregs; + if (sz == 8 && + (retregs & (mPSW | mSTACK | ALLREGS | mBP)) == mSTACK) + { int i; + + /* Optimizer should not CSE these, as the result is worse code! */ + assert(!e->Ecount); + + cs.Iop = 0xFF; + cs.Irm |= modregrm(0,6,0); + cs.IEVoffset1 += 8 - REGSIZE; + stackchanged = 1; + i = 8 - REGSIZE; + do + { + c = gen(c,&cs); /* PUSH EA+i */ + c = genadjesp(c,REGSIZE); + cs.IEVoffset1 -= REGSIZE; + stackpush += REGSIZE; + i -= REGSIZE; + } + while (i >= 0); + goto L3; + } + if (I16 && sz == 8) + retregs = DOUBLEREGS_16; + + /* Watch out for loading an lptr from an lptr! We must have */ + /* the offset loaded into a different register. */ + /*if (retregs & mES && (cs.Iflags & CFSEG) == CFes) + retregs = ALLREGS;*/ + + { + assert(!byte || retregs & BYTEREGS); + c = cat(c,allocreg(&retregs,®,tym)); /* alloc registers */ + } + if (retregs & XMMREGS) + { + assert(sz == 4 || sz == 8 || sz == 16); // float, double or vector + cs.Iop = xmmload(tym); + reg -= XMM0; + goto L2; + } + else if (sz <= REGSIZE) + { + cs.Iop = 0x8B ^ byte; + L2: code_newreg(&cs,reg); + ce = gen(CNIL,&cs); /* MOV reg,[idx] */ + if (byte && reg >= 4) + code_orrex(ce, REX); + } +#if TARGET_SEGMENTED + else if ((tym == TYfptr || tym == TYhptr) && retregs & mES) + { + cs.Iop = 0xC4; /* LES reg,[idx] */ + goto L2; + } +#endif + else if (sz <= 2 * REGSIZE) + { unsigned lsreg; + + cs.Iop = 0x8B; + /* Be careful not to interfere with index registers */ + if (!I16) + { + /* Can't handle if both result registers are used in */ + /* the addressing mode. */ + if ((retregs & idxregs) == retregs) + { + retregs = mMSW & allregs & ~idxregs; + if (!retregs) + retregs |= mCX; + retregs |= mLSW & ~idxregs; + + // We can run out of registers, so if that's possible, + // give us *one* of the idxregs + if ((retregs & ~regcon.mvar & mLSW) == 0) + { + regm_t x = idxregs & mLSW; + if (x) + retregs |= mask[findreg(x)]; // give us one idxreg + } + else if ((retregs & ~regcon.mvar & mMSW) == 0) + { + regm_t x = idxregs & mMSW; + if (x) + retregs |= mask[findreg(x)]; // give us one idxreg + } + + c = cat(c,allocreg(&retregs,®,tym)); /* alloc registers */ + assert((retregs & idxregs) != retregs); + } + + lsreg = findreglsw(retregs); + if (mask[reg] & idxregs) /* reg is in addr mode */ + { + code_newreg(&cs,lsreg); + ce = gen(CNIL,&cs); /* MOV lsreg,lsw */ + if (sz == REGSIZE + 2) + cs.Iflags |= CFopsize; + lsreg = reg; + getlvalue_msw(&cs); // MOV reg,msw + } + else + { + code_newreg(&cs,reg); + getlvalue_msw(&cs); + ce = gen(CNIL,&cs); // MOV reg,msw + if (sz == REGSIZE + 2) + ce->Iflags |= CFopsize; + getlvalue_lsw(&cs); // MOV lsreg,lsw + } + NEWREG(cs.Irm,lsreg); + gen(ce,&cs); + } + else + { + /* Index registers are always the lsw! */ + cs.Irm |= modregrm(0,reg,0); + getlvalue_msw(&cs); + ce = gen(CNIL,&cs); /* MOV reg,msw */ + lsreg = findreglsw(retregs); + NEWREG(cs.Irm,lsreg); + getlvalue_lsw(&cs); /* MOV lsreg,lsw */ + gen(ce,&cs); + } + } + else if (I16 && sz == 8) + { + assert(reg == AX); + cs.Iop = 0x8B; + cs.IEVoffset1 += 6; + ce = gen(CNIL,&cs); /* MOV AX,EA+6 */ + cs.Irm |= modregrm(0,CX,0); + cs.IEVoffset1 -= 4; + gen(ce,&cs); /* MOV CX,EA+2 */ + NEWREG(cs.Irm,DX); + cs.IEVoffset1 -= 2; + gen(ce,&cs); /* MOV DX,EA */ + cs.IEVoffset1 += 4; + NEWREG(cs.Irm,BX); + gen(ce,&cs); /* MOV BX,EA+4 */ + } + else + assert(0); + c = cat(c,ce); + L3: + c = cat(c,fixresult(e,retregs,pretregs)); + } + /*fprintf(stderr,"cdafter :\n"); WRcodlst(c);*/ + return c; +} + + + +#if !TARGET_SEGMENTED +#define cod2_setES(ty) NULL +#else +/******************************** + * Generate code to load ES with the right segment value, + * do nothing if e is a far pointer. + */ + +STATIC code *cod2_setES(tym_t ty) +{ code *c2; + int push; + + c2 = CNIL; + switch (tybasic(ty)) + { + case TYnptr: + if (!(config.flags3 & CFG3eseqds)) + { push = 0x1E; /* PUSH DS */ + goto L1; + } + break; + case TYcptr: + push = 0x0E; /* PUSH CS */ + goto L1; + case TYsptr: + if ((config.wflags & WFssneds) || !(config.flags3 & CFG3eseqds)) + { push = 0x16; /* PUSH SS */ + L1: + /* Must load ES */ + c2 = getregs(mES); + c2 = gen1(c2,push); + gen1(c2,0x07); /* POP ES */ + } + break; + } + return c2; +} +#endif + +/******************************** + * Generate code for intrinsic strlen(). + */ + +code *cdstrlen( elem *e, regm_t *pretregs) +{ code *c1,*c2,*c3,*c4; + + /* Generate strlen in CX: + LES DI,e1 + CLR AX ;scan for 0 + MOV CX,-1 ;largest possible string + REPNE SCASB + NOT CX + DEC CX + */ + + regm_t retregs = mDI; + tym_t ty1 = e->E1->Ety; + if (!tyreg(ty1)) + retregs |= mES; + c1 = codelem(e->E1,&retregs,FALSE); + + /* Make sure ES contains proper segment value */ + c2 = cod2_setES(ty1); + + unsigned char rex = I64 ? REX_W : 0; + + c3 = getregs_imm(mAX | mCX); + c3 = movregconst(c3,AX,0,1); /* MOV AL,0 */ + c3 = movregconst(c3,CX,-1LL,I64 ? 64 : 0); // MOV CX,-1 + c3 = cat(c3,getregs(mDI|mCX)); + c3 = gen1(c3,0xF2); /* REPNE */ + gen1(c3,0xAE); /* SCASB */ + genregs(c3,0xF7,2,CX); /* NOT CX */ + code_orrex(c3,rex); + if (I64) + c4 = gen2(CNIL,0xFF,(rex << 16) | modregrm(3,1,CX)); // DEC reg + else + c4 = gen1(CNIL,0x48 + CX); // DEC CX + + if (*pretregs & mPSW) + { + c4->Iflags |= CFpsw; + *pretregs &= ~mPSW; + } + return cat6(c1,c2,c3,c4,fixresult(e,mCX,pretregs),CNIL); +} + + +/********************************* + * Generate code for strcmp(s1,s2) intrinsic. + */ + +code *cdstrcmp( elem *e, regm_t *pretregs) +{ code *c1,*c1a,*c2,*c3,*c4; + char need_DS; + int segreg; + + /* + MOV SI,s1 ;get destination pointer (s1) + MOV CX,s1+2 + LES DI,s2 ;get source pointer (s2) + PUSH DS + MOV DS,CX + CLR AX ;scan for 0 + MOV CX,-1 ;largest possible string + REPNE SCASB + NOT CX ;CX = string length of s2 + SUB DI,CX ;point DI back to beginning + REPE CMPSB ;compare string + POP DS + JE L1 ;strings are equal + SBB AX,AX + SBB AX,-1 + L1: + */ + + regm_t retregs1 = mSI; + tym_t ty1 = e->E1->Ety; + if (!tyreg(ty1)) + retregs1 |= mCX; + c1 = codelem(e->E1,&retregs1,FALSE); + + regm_t retregs = mDI; + tym_t ty2 = e->E2->Ety; + if (!tyreg(ty2)) + retregs |= mES; + c1 = cat(c1,scodelem(e->E2,&retregs,retregs1,FALSE)); + + /* Make sure ES contains proper segment value */ + c2 = cod2_setES(ty2); + c3 = getregs_imm(mAX | mCX); + + unsigned char rex = I64 ? REX_W : 0; + + /* Load DS with right value */ + switch (tybasic(ty1)) + { + case TYnptr: + need_DS = FALSE; + break; +#if TARGET_SEGMENTED + case TYsptr: + if (config.wflags & WFssneds) /* if sptr can't use DS segment */ + segreg = SEG_SS; + else + segreg = SEG_DS; + goto L1; + case TYcptr: + segreg = SEG_CS; + L1: + c3 = gen1(c3,0x1E); /* PUSH DS */ + gen1(c3,0x06 + (segreg << 3)); /* PUSH segreg */ + gen1(c3,0x1F); /* POP DS */ + need_DS = TRUE; + break; + case TYfptr: + case TYvptr: + case TYhptr: + c3 = gen1(c3,0x1E); /* PUSH DS */ + gen2(c3,0x8E,modregrm(3,SEG_DS,CX)); /* MOV DS,CX */ + need_DS = TRUE; + break; +#endif + default: + assert(0); + } + + c3 = movregconst(c3,AX,0,0); /* MOV AX,0 */ + c3 = movregconst(c3,CX,-1LL,I64 ? 64 : 0); // MOV CX,-1 + c3 = cat(c3,getregs(mSI|mDI|mCX)); + c3 = gen1(c3,0xF2); /* REPNE */ + gen1(c3,0xAE); /* SCASB */ + genregs(c3,0xF7,2,CX); /* NOT CX */ + code_orrex(c3,rex); + genregs(c3,0x2B,DI,CX); /* SUB DI,CX */ + code_orrex(c3,rex); + gen1(c3,0xF3); /* REPE */ + gen1(c3,0xA6); /* CMPSB */ + if (need_DS) + gen1(c3,0x1F); /* POP DS */ + c4 = gennop(CNIL); + if (*pretregs != mPSW) /* if not flags only */ + { + genjmp(c3,JE,FLcode,(block *) c4); /* JE L1 */ + c3 = cat(c3,getregs(mAX)); + genregs(c3,0x1B,AX,AX); /* SBB AX,AX */ + code_orrex(c3,rex); + genc2(c3,0x81,(rex << 16) | modregrm(3,3,AX),(targ_uns)-1); // SBB AX,-1 + } + + *pretregs &= ~mPSW; + return cat6(c1,c2,c3,c4,fixresult(e,mAX,pretregs),CNIL); +} + +/********************************* + * Generate code for memcmp(s1,s2,n) intrinsic. + */ + +code *cdmemcmp(elem *e,regm_t *pretregs) +{ code *c1,*c2,*c3,*c4; + char need_DS; + int segreg; + + /* + MOV SI,s1 ;get destination pointer (s1) + MOV DX,s1+2 + LES DI,s2 ;get source pointer (s2) + MOV CX,n ;get number of bytes to compare + PUSH DS + MOV DS,DX + XOR AX,AX + REPE CMPSB ;compare string + POP DS + JE L1 ;strings are equal + SBB AX,AX + SBB AX,-1 + L1: + */ + + elem *e1 = e->E1; + assert(e1->Eoper == OPparam); + + // Get s1 into DX:SI + regm_t retregs1 = mSI; + tym_t ty1 = e1->E1->Ety; + if (!tyreg(ty1)) + retregs1 |= mDX; + c1 = codelem(e1->E1,&retregs1,FALSE); + + // Get s2 into ES:DI + regm_t retregs = mDI; + tym_t ty2 = e1->E2->Ety; + if (!tyreg(ty2)) + retregs |= mES; + c1 = cat(c1,scodelem(e1->E2,&retregs,retregs1,FALSE)); + freenode(e1); + + // Get nbytes into CX + regm_t retregs3 = mCX; + c1 = cat(c1,scodelem(e->E2,&retregs3,retregs | retregs1,FALSE)); + + /* Make sure ES contains proper segment value */ + c2 = cod2_setES(ty2); + + /* Load DS with right value */ + c3 = NULL; + switch (tybasic(ty1)) + { + case TYnptr: + need_DS = FALSE; + break; +#if TARGET_SEGMENTED + case TYsptr: + if (config.wflags & WFssneds) /* if sptr can't use DS segment */ + segreg = SEG_SS; + else + segreg = SEG_DS; + goto L1; + case TYcptr: + segreg = SEG_CS; + L1: + c3 = gen1(c3,0x1E); /* PUSH DS */ + gen1(c3,0x06 + (segreg << 3)); /* PUSH segreg */ + gen1(c3,0x1F); /* POP DS */ + need_DS = TRUE; + break; + case TYfptr: + case TYvptr: + case TYhptr: + c3 = gen1(c3,0x1E); /* PUSH DS */ + gen2(c3,0x8E,modregrm(3,SEG_DS,DX)); /* MOV DS,DX */ + need_DS = TRUE; + break; +#endif + default: + assert(0); + } + +#if 1 + c3 = cat(c3,getregs(mAX)); + c3 = gen2(c3,0x33,modregrm(3,AX,AX)); // XOR AX,AX +#else + if (*pretregs != mPSW) // if not flags only + c3 = regwithvalue(c3,mAX,0,NULL,0); // put 0 in AX +#endif + + c3 = cat(c3,getregs(mCX | mSI | mDI)); + c3 = gen1(c3,0xF3); /* REPE */ + gen1(c3,0xA6); /* CMPSB */ + if (need_DS) + gen1(c3,0x1F); /* POP DS */ + if (*pretregs != mPSW) /* if not flags only */ + { + c4 = gennop(CNIL); + genjmp(c3,JE,FLcode,(block *) c4); /* JE L1 */ + c3 = cat(c3,getregs(mAX)); + genregs(c3,0x1B,AX,AX); /* SBB AX,AX */ + genc2(c3,0x81,modregrm(3,3,AX),(targ_uns)-1); /* SBB AX,-1 */ + c3 = cat(c3,c4); + } + + *pretregs &= ~mPSW; + return cat4(c1,c2,c3,fixresult(e,mAX,pretregs)); +} + +/********************************* + * Generate code for strcpy(s1,s2) intrinsic. + */ + +code *cdstrcpy(elem *e,regm_t *pretregs) +{ code *c1,*c2,*c3,*c4; + regm_t retregs; + tym_t ty1,ty2; + char need_DS; + int segreg; + + /* + LES DI,s2 ;ES:DI = s2 + CLR AX ;scan for 0 + MOV CX,-1 ;largest possible string + REPNE SCASB ;find end of s2 + NOT CX ;CX = strlen(s2) + 1 (for EOS) + SUB DI,CX + MOV SI,DI + PUSH DS + PUSH ES + LES DI,s1 + POP DS + MOV AX,DI ;return value is s1 + REP MOVSB + POP DS + */ + + stackchanged = 1; + retregs = mDI; + ty2 = tybasic(e->E2->Ety); + if (!tyreg(ty2)) + retregs |= mES; + unsigned char rex = I64 ? REX_W : 0; + c1 = codelem(e->E2,&retregs,FALSE); + + /* Make sure ES contains proper segment value */ + c2 = cod2_setES(ty2); + c3 = getregs_imm(mAX | mCX); + c3 = movregconst(c3,AX,0,1); /* MOV AL,0 */ + c3 = movregconst(c3,CX,-1,I64?64:0); // MOV CX,-1 + c3 = cat(c3,getregs(mAX|mCX|mSI|mDI)); + c3 = gen1(c3,0xF2); /* REPNE */ + gen1(c3,0xAE); /* SCASB */ + genregs(c3,0xF7,2,CX); /* NOT CX */ + code_orrex(c3,rex); + genregs(c3,0x2B,DI,CX); /* SUB DI,CX */ + code_orrex(c3,rex); + genmovreg(c3,SI,DI); /* MOV SI,DI */ + code_orrex(c3,rex); + + /* Load DS with right value */ + switch (ty2) + { + case TYnptr: + need_DS = FALSE; + break; +#if TARGET_SEGMENTED + case TYsptr: + if (config.wflags & WFssneds) /* if sptr can't use DS segment */ + segreg = SEG_SS; + else + segreg = SEG_DS; + goto L1; + case TYcptr: + segreg = SEG_CS; + L1: + c3 = gen1(c3,0x1E); /* PUSH DS */ + gen1(c3,0x06 + (segreg << 3)); /* PUSH segreg */ + genadjesp(c3,REGSIZE * 2); + need_DS = TRUE; + break; + case TYfptr: + case TYvptr: + case TYhptr: + segreg = SEG_ES; + goto L1; + break; +#endif + default: + assert(0); + } + + retregs = mDI; + ty1 = tybasic(e->E1->Ety); + if (!tyreg(ty1)) + retregs |= mES; + c3 = cat(c3,scodelem(e->E1,&retregs,mCX|mSI,FALSE)); + c3 = cat(c3,getregs(mAX|mCX|mSI|mDI)); + + /* Make sure ES contains proper segment value */ + if (ty2 != TYnptr || ty1 != ty2) + c4 = cod2_setES(ty1); + else + c4 = CNIL; /* ES is already same as DS */ + + if (need_DS) + c4 = gen1(c4,0x1F); /* POP DS */ + if (*pretregs) + { c4 = genmovreg(c4,AX,DI); /* MOV AX,DI */ + code_orrex(c4,rex); + } + c4 = gen1(c4,0xF3); /* REP */ + gen1(c4,0xA4); /* MOVSB */ + + if (need_DS) + { gen1(c4,0x1F); /* POP DS */ + genadjesp(c4,-(REGSIZE * 2)); + } + return cat6(c1,c2,c3,c4,fixresult(e,mAX | mES,pretregs),CNIL); +} + +/********************************* + * Generate code for memcpy(s1,s2,n) intrinsic. + * OPmemcpy + * / \ + * s1 OPparam + * / \ + * s2 n + */ + +code *cdmemcpy(elem *e,regm_t *pretregs) +{ code *c1,*c2,*c3,*c4; + regm_t retregs1; + regm_t retregs2; + regm_t retregs3; + tym_t ty1,ty2; + char need_DS; + int segreg; + elem *e2; + + /* + MOV SI,s2 + MOV DX,s2+2 + MOV CX,n + LES DI,s1 + PUSH DS + MOV DS,DX + MOV AX,DI ;return value is s1 + REP MOVSB + POP DS + */ + + e2 = e->E2; + assert(e2->Eoper == OPparam); + + // Get s2 into DX:SI + retregs2 = mSI; + ty2 = e2->E1->Ety; + if (!tyreg(ty2)) + retregs2 |= mDX; + c1 = codelem(e2->E1,&retregs2,FALSE); + + // Get nbytes into CX + retregs3 = mCX; + c1 = cat(c1,scodelem(e2->E2,&retregs3,retregs2,FALSE)); + freenode(e2); + + // Get s1 into ES:DI + retregs1 = mDI; + ty1 = e->E1->Ety; + if (!tyreg(ty1)) + retregs1 |= mES; + c1 = cat(c1,scodelem(e->E1,&retregs1,retregs2 | retregs3,FALSE)); + + unsigned char rex = I64 ? REX_W : 0; + + /* Make sure ES contains proper segment value */ + c2 = cod2_setES(ty1); + + /* Load DS with right value */ + c3 = NULL; + switch (tybasic(ty2)) + { + case TYnptr: + need_DS = FALSE; + break; +#if TARGET_SEGMENTED + case TYsptr: + if (config.wflags & WFssneds) /* if sptr can't use DS segment */ + segreg = SEG_SS; + else + segreg = SEG_DS; + goto L1; + case TYcptr: + segreg = SEG_CS; + L1: + c3 = gen1(c3,0x1E); /* PUSH DS */ + gen1(c3,0x06 + (segreg << 3)); /* PUSH segreg */ + gen1(c3,0x1F); /* POP DS */ + need_DS = TRUE; + break; + case TYfptr: + case TYvptr: + case TYhptr: + c3 = gen1(c3,0x1E); /* PUSH DS */ + gen2(c3,0x8E,modregrm(3,SEG_DS,DX)); /* MOV DS,DX */ + need_DS = TRUE; + break; +#endif + default: + assert(0); + } + + if (*pretregs) // if need return value + { c3 = cat(c3,getregs(mAX)); + c3 = genmovreg(c3,AX,DI); + code_orrex(c3, rex); + } + + if (0 && I32 && config.flags4 & CFG4speed) + { + /* This is only faster if the memory is dword aligned, if not + * it is significantly slower than just a rep movsb. + */ + /* mov EDX,ECX + * shr ECX,2 + * jz L1 + * repe movsd + * L1: and EDX,3 + * jz L2 + * mov ECX,EDX + * repe movsb + * L2: nop + */ + c3 = cat(c3,getregs(mSI | mDI | mCX | mDX)); + c3 = genmovreg(c3,DX,CX); // MOV EDX,ECX + c3 = genc2(c3,0xC1,modregrm(3,5,CX),2); // SHR ECX,2 + code *cx = genc2(CNIL, 0x81, modregrm(3,4,DX),3); // AND EDX,3 + genjmp(c3, JE, FLcode, (block *)cx); // JZ L1 + gen1(c3,0xF3); // REPE + gen1(c3,0xA5); // MOVSW + c3 = cat(c3,cx); + + code *cnop = gennop(CNIL); + genjmp(c3, JE, FLcode, (block *)cnop); // JZ L2 + genmovreg(c3,CX,DX); // MOV ECX,EDX + gen1(c3,0xF3); // REPE + gen1(c3,0xA4); // MOVSB + c3 = cat(c3, cnop); + } + else + { + c3 = cat(c3,getregs(mSI | mDI | mCX)); + if (!I32 && config.flags4 & CFG4speed) // if speed optimization + { c3 = gen2(c3,0xD1,(rex << 16) | modregrm(3,5,CX)); // SHR CX,1 + gen1(c3,0xF3); // REPE + gen1(c3,0xA5); // MOVSW + gen2(c3,0x11,(rex << 16) | modregrm(3,CX,CX)); // ADC CX,CX + } + c3 = gen1(c3,0xF3); // REPE + gen1(c3,0xA4); // MOVSB + if (need_DS) + gen1(c3,0x1F); // POP DS + } + return cat4(c1,c2,c3,fixresult(e,mES|mAX,pretregs)); +} + + +/********************************* + * Generate code for memset(s,val,n) intrinsic. + * (s OPmemset (n OPparam val)) + */ + +#if 1 +code *cdmemset(elem *e,regm_t *pretregs) +{ code *c1,*c2,*c3 = NULL,*c4; + regm_t retregs1; + regm_t retregs2; + regm_t retregs3; + unsigned reg,vreg; + tym_t ty1; + int segreg; + unsigned remainder; + targ_uns numbytes,numwords; + int op; + targ_size_t value; + unsigned m; + + //printf("cdmemset(*pretregs = %s)\n", regm_str(*pretregs)); + elem *e2 = e->E2; + assert(e2->Eoper == OPparam); + + unsigned char rex = I64 ? REX_W : 0; + + if (e2->E2->Eoper == OPconst) + { + value = el_tolong(e2->E2); + value &= 0xFF; + value |= value << 8; + value |= value << 16; + value |= value << 32; + } + else + value = 0xDEADBEEF; // stop annoying false positives that value is not inited + + if (e2->E1->Eoper == OPconst) + { + numbytes = el_tolong(e2->E1); + if (numbytes <= REP_THRESHOLD && + !I16 && // doesn't work for 16 bits + e2->E2->Eoper == OPconst) + { + targ_uns offset = 0; + retregs1 = *pretregs; + if (!retregs1) + retregs1 = ALLREGS; + c1 = codelem(e->E1,&retregs1,FALSE); + reg = findreg(retregs1); + if (e2->E2->Eoper == OPconst) + { + unsigned m = buildModregrm(0,0,reg); + switch (numbytes) + { + case 4: // MOV [reg],imm32 + c3 = genc2(CNIL,0xC7,m,value); + goto fixres; + case 2: // MOV [reg],imm16 + c3 = genc2(CNIL,0xC7,m,value); + c3->Iflags = CFopsize; + goto fixres; + case 1: // MOV [reg],imm8 + c3 = genc2(CNIL,0xC6,m,value); + goto fixres; + } + } + + c1 = regwithvalue(c1, BYTEREGS & ~retregs1, value, &vreg, I64 ? 64 : 0); + freenode(e2->E2); + freenode(e2); + + m = (rex << 16) | buildModregrm(2,vreg,reg); + while (numbytes >= REGSIZE) + { // MOV dword ptr offset[reg],vreg + c2 = gen2(CNIL,0x89,m); + c2->IEVoffset1 = offset; + c2->IFL1 = FLconst; + numbytes -= REGSIZE; + offset += REGSIZE; + c3 = cat(c3,c2); + } + m &= ~(rex << 16); + if (numbytes & 4) + { // MOV dword ptr offset[reg],vreg + c2 = gen2(CNIL,0x89,m); + c2->IEVoffset1 = offset; + c2->IFL1 = FLconst; + offset += 4; + c3 = cat(c3,c2); + } + if (numbytes & 2) + { // MOV word ptr offset[reg],vreg + c2 = gen2(CNIL,0x89,m); + c2->IEVoffset1 = offset; + c2->IFL1 = FLconst; + c2->Iflags = CFopsize; + offset += 2; + c3 = cat(c3,c2); + } + if (numbytes & 1) + { // MOV byte ptr offset[reg],vreg + c2 = gen2(CNIL,0x88,m); + c2->IEVoffset1 = offset; + c2->IFL1 = FLconst; + if (I64 && vreg >= 4) + c2->Irex |= REX; + c3 = cat(c3,c2); + } +fixres: + return cat3(c1,c3,fixresult(e,retregs1,pretregs)); + } + } + + // Get nbytes into CX + retregs2 = mCX; + if (!I16 && e2->E1->Eoper == OPconst && e2->E2->Eoper == OPconst) + { + remainder = numbytes & (4 - 1); + numwords = numbytes / 4; // number of words + op = 0xAB; // moving by words + c1 = getregs(mCX); + c1 = movregconst(c1,CX,numwords,I64?64:0); // # of bytes/words + } + else + { + remainder = 0; + op = 0xAA; // must move by bytes + c1 = codelem(e2->E1,&retregs2,FALSE); + } + + // Get val into AX + + retregs3 = mAX; + if (!I16 && e2->E2->Eoper == OPconst) + { + c1 = regwithvalue(c1, mAX, value, NULL, I64?64:0); + freenode(e2->E2); + } + else + { + c1 = cat(c1,scodelem(e2->E2,&retregs3,retregs2,FALSE)); +#if 0 + if (I32) + { + c1 = gen2(c1,0x8A,modregrm(3,AH,AL)); // MOV AH,AL + c1 = genc2(c1,0xC1,modregrm(3,4,AX),8); // SHL EAX,8 + c1 = gen2(c1,0x8A,modregrm(3,AL,AH)); // MOV AL,AH + c1 = genc2(c1,0xC1,modregrm(3,4,AX),8); // SHL EAX,8 + c1 = gen2(c1,0x8A,modregrm(3,AL,AH)); // MOV AL,AH + } +#endif + } + freenode(e2); + + // Get s into ES:DI + retregs1 = mDI; + ty1 = e->E1->Ety; + if (!tyreg(ty1)) + retregs1 |= mES; + c1 = cat(c1,scodelem(e->E1,&retregs1,retregs2 | retregs3,FALSE)); + reg = DI; //findreg(retregs1); + + // Make sure ES contains proper segment value + c2 = cod2_setES(ty1); + + c3 = NULL; + if (*pretregs) // if need return value + { c3 = getregs(mBX); + c3 = genmovreg(c3,BX,DI); + code_orrex(c3,rex); + } + + c3 = cat(c3,getregs(mDI | mCX)); + if (I16 && config.flags4 & CFG4speed) // if speed optimization + { + c3 = cat(c3,getregs(mAX)); + c3 = gen2(c3,0x8A,modregrm(3,AH,AL)); // MOV AH,AL + gen2(c3,0xD1,modregrm(3,5,CX)); // SHR CX,1 + gen1(c3,0xF3); // REP + gen1(c3,0xAB); // STOSW + gen2(c3,0x11,modregrm(3,CX,CX)); // ADC CX,CX + op = 0xAA; + } + + c3 = gen1(c3,0xF3); // REP + gen1(c3,op); // STOSD + m = buildModregrm(2,AX,reg); + if (remainder & 4) + { + code *ctmp; + ctmp = gen2(CNIL,0x89,m); + ctmp->IFL1 = FLconst; + c3 = cat(c3,ctmp); + } + if (remainder & 2) + { + code *ctmp; + ctmp = gen2(CNIL,0x89,m); + ctmp->Iflags = CFopsize; + ctmp->IEVoffset1 = remainder & 4; + ctmp->IFL1 = FLconst; + c3 = cat(c3,ctmp); + } + if (remainder & 1) + { + code *ctmp; + ctmp = gen2(CNIL,0x88,m); + ctmp->IEVoffset1 = remainder & ~1; + ctmp->IFL1 = FLconst; + c3 = cat(c3,ctmp); + } + regimmed_set(CX,0); + return cat4(c1,c2,c3,fixresult(e,mES|mBX,pretregs)); +} +#else +// BUG: Pat made many improvements in the linux version, I need +// to verify they work for 16 bits and fold them in. -Walter + +code *cdmemset(elem *e,regm_t *pretregs) +{ code *c1,*c2,*c3 = NULL,*c4; + regm_t retregs1; + regm_t retregs2; + regm_t retregs3; + tym_t ty1; + elem *e2; + targ_size_t value; + + /* + les DI,s + mov BX,DI ;Return value. + mov CX,n + mov AL,val + mov AH,AL ;Set up a 16 bit pattern. + shr CX,1 + rep stosw + adc CX,CX + rep stosb + */ + + e2 = e->E2; + assert(e2->Eoper == OPparam); + + // Get nbytes into CX + retregs2 = mCX; + c1 = codelem(e2->E1,&retregs2,FALSE); + + // Get val into AX + retregs3 = mAX; + c1 = cat(c1,scodelem(e2->E2,&retregs3,retregs2,FALSE)); + freenode(e2); + + // Get s into ES:DI + retregs1 = mDI; + ty1 = e->E1->Ety; + if (!tyreg(ty1)) + retregs1 |= mES; + c1 = cat(c1,scodelem(e->E1,&retregs1,retregs2 | retregs3,FALSE)); + + /* Make sure ES contains proper segment value */ + c2 = cod2_setES(ty1); + + c3 = NULL; + if (*pretregs) // if need return value + { c3 = getregs(mBX); + c3 = genmovreg(c3,BX,DI); + } + + c3 = cat(c3,getregs(mDI | mCX)); + if (!I32 && config.flags4 & CFG4speed) // if speed optimization + { + c3 = cat(c3,getregs(mAX)); + c3 = gen2(c3,0x8A,modregrm(3,AH,AL)); // MOV AH,AL + gen2(c3,0xD1,modregrm(3,5,CX)); // SHR CX,1 + gen1(c3,0xF3); // REP + gen1(c3,0xAB); // STOSW + gen2(c3,0x11,modregrm(3,CX,CX)); // ADC CX,CX + } + c3 = gen1(c3,0xF3); // REP + gen1(c3,0xAA); // STOSB + regimmed_set(CX,0); + return cat4(c1,c2,c3,fixresult(e,mES|mBX,pretregs)); +} +#endif + +/********************** + * Do structure assignments. + * This should be fixed so that (s1 = s2) is rewritten to (&s1 = &s2). + * Mebbe call cdstreq() for double assignments??? + */ + +code *cdstreq(elem *e,regm_t *pretregs) +{ code *c1,*c2,*c3; + code *c1a; + regm_t srcregs,dstregs; /* source & destination reg masks */ + char need_DS = FALSE; + elem *e1 = e->E1,*e2 = e->E2; + int segreg; + + unsigned numbytes = type_size(e->ET); // # of bytes in structure/union + unsigned char rex = I64 ? REX_W : 0; + + //printf("cdstreq(e = %p, *pretregs = x%x)\n", e, *pretregs); + + /* First, load pointer to rvalue into SI */ + srcregs = mSI; /* source is DS:SI */ + c1 = docommas(&e2); + if (e2->Eoper == OPind) /* if (.. = *p) */ + { elem *e21 = e2->E1; + + segreg = SEG_DS; +#if TARGET_SEGMENTED + switch (tybasic(e21->Ety)) + { + case TYsptr: + if (config.wflags & WFssneds) /* if sptr can't use DS segment */ + segreg = SEG_SS; + break; + case TYcptr: + if (!(config.exe & EX_flat)) + segreg = SEG_CS; + break; + case TYfptr: + case TYvptr: + case TYhptr: + srcregs |= mCX; /* get segment also */ + need_DS = TRUE; + break; + } +#endif + c1a = codelem(e21,&srcregs,FALSE); + freenode(e2); + if (segreg != SEG_DS) /* if not DS */ + { c1a = cat(c1a,getregs(mCX)); + c1a = gen2(c1a,0x8C,modregrm(3,segreg,CX)); /* MOV CX,segreg */ + need_DS = TRUE; + } + } + else if (e2->Eoper == OPvar) + { +#if TARGET_SEGMENTED + if (e2->EV.sp.Vsym->ty() & mTYfar) // if e2 is in a far segment + { srcregs |= mCX; /* get segment also */ + need_DS = TRUE; + c1a = cdrelconst(e2,&srcregs); + } + else +#endif + { + c1a = cdrelconst(e2,&srcregs); + segreg = segfl[el_fl(e2)]; + if ((config.wflags & WFssneds) && segreg == SEG_SS || /* if source is on stack */ + segreg == SEG_CS) /* if source is in CS */ + { code *c; + + need_DS = TRUE; /* we need to reload DS */ + // Load CX with segment + srcregs |= mCX; + c = getregs(mCX); + c = gen2(c,0x8C, /* MOV CX,[SS|CS] */ + modregrm(3,segreg,CX)); + c1a = cat(c,c1a); + } + } + freenode(e2); + } + else + { + if (!(config.exe & EX_flat)) + { need_DS = TRUE; + srcregs |= mCX; + } + c1a = codelem(e2,&srcregs,FALSE); + } + c1 = cat(c1,c1a); + + /* now get pointer to lvalue (destination) in ES:DI */ + dstregs = (config.exe & EX_flat) ? mDI : mES|mDI; + if (e1->Eoper == OPind) /* if (*p = ..) */ + { + if (tyreg(e1->E1->Ety)) + dstregs = mDI; + c2 = cod2_setES(e1->E1->Ety); + c2 = cat(c2,scodelem(e1->E1,&dstregs,srcregs,FALSE)); + } + else + c2 = cdrelconst(e1,&dstregs); + freenode(e1); + + c3 = getregs((srcregs | dstregs) & (mLSW | mDI)); + if (need_DS) + { assert(!(config.exe & EX_flat)); + c3 = gen1(c3,0x1E); /* PUSH DS */ + gen2(c3,0x8E,modregrm(3,SEG_DS,CX)); /* MOV DS,CX */ + } + if (numbytes <= REGSIZE * (6 + (REGSIZE == 4))) + { while (numbytes >= REGSIZE) + { + c3 = gen1(c3,0xA5); /* MOVSW */ + code_orrex(c3, rex); + numbytes -= REGSIZE; + } + //if (numbytes) + // printf("cdstreq numbytes %d\n",numbytes); + while (numbytes--) + c3 = gen1(c3,0xA4); /* MOVSB */ + } + else + { +#if 1 + unsigned remainder; + + remainder = numbytes & (REGSIZE - 1); + numbytes /= REGSIZE; // number of words + c3 = cat(c3,getregs_imm(mCX)); + c3 = movregconst(c3,CX,numbytes,0); // # of bytes/words + gen1(c3,0xF3); // REP + if (REGSIZE == 8) + gen1(c3,REX | REX_W); + gen1(c3,0xA5); // REP MOVSD + regimmed_set(CX,0); // note that CX == 0 + for (; remainder; remainder--) + { + gen1(c3, 0xA4); // MOVSB + } +#else + unsigned movs; + + if (numbytes & (REGSIZE - 1)) /* if odd */ + movs = 0xA4; /* MOVSB */ + else + { movs = 0xA5; /* MOVSW */ + numbytes /= REGSIZE; /* # of words */ + } + c3 = cat(c3,getregs_imm(mCX)); + c3 = movregconst(c3,CX,numbytes,0); /* # of bytes/words */ + gen1(c3,0xF3); /* REP */ + gen1(c3,movs); + regimmed_set(CX,0); /* note that CX == 0 */ +#endif + } + if (need_DS) + gen1(c3,0x1F); // POP DS + assert(!(*pretregs & mPSW)); + if (*pretregs) + { /* ES:DI points past what we want */ + regm_t retregs; + + genc2(c3,0x81,(rex << 16) | modregrm(3,5,DI), type_size(e->ET)); // SUB DI,numbytes + retregs = mDI; + if (*pretregs & mMSW && !(config.exe & EX_flat)) + retregs |= mES; + c3 = cat(c3,fixresult(e,retregs,pretregs)); + } + return cat3(c1,c2,c3); +} + + +/********************** + * Get the address of. + * Is also called by cdstreq() to set up pointer to a structure. + */ + +code *cdrelconst(elem *e,regm_t *pretregs) +{ code *c,*c1; + enum SC sclass; + unsigned mreg, /* segment of the address (TYfptrs only) */ + lreg; /* offset of the address */ + tym_t tym; + + //printf("cdrelconst(e = %p)\n", e); + + c = CNIL; + + /* The following should not happen, but cgelem.c is a little stupid. */ + /* Assertion can be tripped by func("string" == 0); and similar */ + /* things. Need to add goals to optelem() to fix this completely. */ + /*assert((*pretregs & mPSW) == 0);*/ + if (*pretregs & mPSW) + { *pretregs &= ~mPSW; + c = gentstreg(c,SP); // SP is never 0 + if (I64) + code_orrex(c, REX_W); + } + if (!*pretregs) + return c; + + assert(e); + tym = tybasic(e->Ety); + switch (tym) + { case TYstruct: + case TYarray: + case TYldouble: + case TYildouble: + case TYcldouble: + tym = TYnptr; // don't confuse allocreg() +#if TARGET_SEGMENTED + if (*pretregs & (mES | mCX) || e->Ety & mTYfar) + { + tym = TYfptr; + } +#endif + break; + case TYifunc: +#if TARGET_SEGMENTED + tym = TYfptr; +#else + assert(0); // what's the right thing to do here? TYptr? +#endif + break; + default: + if (tyfunc(tym)) + tym = +#if TARGET_SEGMENTED + tyfarfunc(tym) ? TYfptr : +#endif + TYnptr; + break; + } + /*assert(tym & typtr);*/ /* don't fail on (int)&a */ + + c = cat(c,allocreg(pretregs,&lreg,tym)); + if (tysize[tym] > REGSIZE) /* fptr could've been cast to long */ + { tym_t ety; + symbol *s; + + //elem_print(e); + assert(TARGET_SEGMENTED); + + if (*pretregs & mES) + { regm_t scratch = (mAX|mBX|mDX|mDI) & ~mask[lreg]; + /* Do not allocate CX or SI here, as cdstreq() needs */ + /* them preserved. cdstreq() should use scodelem()... */ + + c = cat(c,allocreg(&scratch,&mreg,TYint)); + } + else + { mreg = lreg; + lreg = findreglsw(*pretregs); + } + + /* if (get segment of function that isn't necessarily in the */ + /* current segment (i.e. CS doesn't have the right value in it) */ + s = e->EV.sp.Vsym; + if (s->Sfl == FLdatseg) + { assert(0); + goto loadreg; + } + sclass = (enum SC) s->Sclass; + ety = tybasic(s->ty()); + if ((tyfarfunc(ety) || ety == TYifunc) && + (sclass == SCextern || ClassInline(sclass) || config.wflags & WFthunk) +#if TARGET_SEGMENTED + || s->Sfl == FLfardata + || (s->ty() & mTYcs && s->Sseg != cseg && (LARGECODE || s->Sclass == SCcomdat)) +#endif + ) + { /* MOV mreg,seg of symbol */ + c1 = gencs(CNIL,0xB8 + mreg,0,FLextern,s); + c1->Iflags = CFseg; + c = cat(c,c1); + assert(TARGET_SEGMENTED); + } + else + { int fl; + + loadreg: + fl = s->Sfl; +#if TARGET_SEGMENTED + if (s->ty() & mTYcs) + fl = FLcsdata; +#endif + c = gen2(c,0x8C, /* MOV mreg,SEG REGISTER */ + modregrm(3,segfl[fl],mreg)); + } + if (*pretregs & mES) + gen2(c,0x8E,modregrm(3,0,mreg)); /* MOV ES,mreg */ + } + return cat(c,getoffset(e,lreg)); +} + +/********************************* + * Load the offset portion of the address represented by e into + * reg. + */ + +code *getoffset(elem *e,unsigned reg) +{ code cs; + code *c; + + //printf("getoffset(e = %p, reg = %d)\n", e, reg); + cs.Iflags = 0; + unsigned char rex = 0; + cs.Irex = rex; + assert(e->Eoper == OPvar || e->Eoper == OPrelconst); + enum FL fl = el_fl(e); + switch (fl) + { + case FLdatseg: + cs.IEV2._EP.Vpointer = e->EV.Vpointer; + goto L3; + +#if TARGET_SEGMENTED + case FLfardata: + goto L4; +#endif + + case FLtlsdata: +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + { + L5: + if (I64 && config.flags3 & CFG3pic) + { + /* Generate: + * LEA DI,s@TLSGD[RIP] + */ + assert(reg == DI); + code css; + css.Irex = REX | REX_W; + css.Iop = 0x8D; // LEA + css.Irm = modregrm(0,DI,5); + css.Iflags = CFopsize; + css.IFL1 = fl; + css.IEVsym1 = e->EV.sp.Vsym; + css.IEVoffset1 = e->EV.sp.Voffset; + c = gen(NULL, &css); + return c; + } + /* Generate: + * MOV reg,GS:[00000000] + * ADD reg, offset s@TLS_LE + * for locals, and for globals: + * MOV reg,GS:[00000000] + * ADD reg, s@TLS_IE + * note different fixup + */ + int stack = 0; + c = NULL; + if (reg == STACK) + { regm_t retregs = ALLREGS; + + c = allocreg(&retregs,®,TYoffset); + reg = findreg(retregs); + stack = 1; + } + + code css; + css.Irex = rex; + css.Iop = 0x8B; + css.Irm = modregrm(0, 0, BPRM); + code_newreg(&css, reg); + css.Iflags = CFgs; + css.IFL1 = FLconst; + css.IEV1.Vuns = 0; + c = gen(c, &css); // MOV reg,GS:[00000000] + + if (e->EV.sp.Vsym->Sclass == SCstatic || e->EV.sp.Vsym->Sclass == SClocstat) + { // ADD reg, offset s + cs.Irex = rex; + cs.Iop = 0x81; + cs.Irm = modregrm(3,0,reg & 7); + if (reg & 8) + cs.Irex |= REX_B; + cs.Iflags = CFoff; + cs.IFL2 = fl; + cs.IEVsym2 = e->EV.sp.Vsym; + cs.IEVoffset2 = e->EV.sp.Voffset; + } + else + { // ADD reg, s + cs.Irex = rex; + cs.Iop = 0x03; + cs.Irm = modregrm(0,0,BPRM); + code_newreg(&cs, reg); + cs.Iflags = CFoff; + cs.IFL1 = fl; + cs.IEVsym1 = e->EV.sp.Vsym; + cs.IEVoffset1 = e->EV.sp.Voffset; + } + c = gen(c, &cs); // ADD reg, xxxx + + if (stack) + { + c = gen1(c,0x50 + (reg & 7)); // PUSH reg + if (reg & 8) + code_orrex(c, REX_B); + c = genadjesp(c,REGSIZE); + stackchanged = 1; + } + break; + } +#else + goto L4; +#endif + + case FLfunc: + fl = FLextern; /* don't want PC relative addresses */ + goto L4; + + case FLextern: +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (e->EV.sp.Vsym->ty() & mTYthread) + goto L5; +#endif + case FLdata: + case FLudata: +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + case FLgot: + case FLgotoff: +#endif +#if TARGET_SEGMENTED + case FLcsdata: +#endif + L4: + cs.IEVsym2 = e->EV.sp.Vsym; + cs.IEVoffset2 = e->EV.sp.Voffset; + L3: + if (reg == STACK) + { stackchanged = 1; + cs.Iop = 0x68; /* PUSH immed16 */ + c = genadjesp(NULL,REGSIZE); + } + else + { cs.Iop = 0xB8 + (reg & 7); // MOV reg,immed16 + if (reg & 8) + cs.Irex |= REX_B; + if (I64) + { cs.Irex |= REX_W; + if (config.flags3 & CFG3pic) + { // LEA reg,immed32[RIP] + cs.Iop = 0x8D; +#if TARGET_OSX + symbol *s = e->EV.sp.Vsym; +// if (fl == FLextern) +// cs.Iop = 0x8B; // MOV reg,[00][RIP] +#endif + cs.Irm = modregrm(0,reg & 7,5); + if (reg & 8) + cs.Irex = (cs.Irex & ~REX_B) | REX_R; + cs.IFL1 = fl; + cs.IEVsym1 = cs.IEVsym2; + cs.IEVoffset1 = cs.IEVoffset2; + } + } + c = NULL; + } + cs.Iflags = CFoff; /* want offset only */ + cs.IFL2 = fl; + c = gen(c,&cs); + break; + +#if 0 && TARGET_LINUX + case FLgot: + case FLgotoff: + { + gotref = 1; + symbol *s = e->EV.sp.Vsym; + // When using 8B (MOV), indicating that rm is used + // rm operands are always placed in IEV1 not IEV2 + cs.IEVsym1 = s; + cs.IEVoffset1 = e->EV.sp.Voffset; + cs.Irm = modregrm(2,reg,BX); // reg,disp32[EBX] + cs.IFL1 = fl; + cs.Iop = (fl == FLgotoff) + ? 0x8D // LEA reg, s[EBX] + : 0x8B; // MOV reg, s[EBX] + cs.Iflags = CFoff; // want offset only + c = gen(NULL,&cs); + break; + } +#endif + + case FLreg: + /* Allow this since the tree optimizer puts & in front of */ + /* register doubles. */ + goto L2; + case FLauto: + case FLtmp: + case FLbprel: + case FLfltreg: + reflocal = TRUE; + goto L2; + case FLpara: + refparam = TRUE; + L2: + if (reg == STACK) + { regm_t retregs = ALLREGS; + + c = allocreg(&retregs,®,TYoffset); + reg = findreg(retregs); + c = cat(c,loadea(e,&cs,0x8D,reg,0,0,0)); /* LEA reg,EA */ + if (I64) + code_orrex(c, REX_W); + c = gen1(c,0x50 + (reg & 7)); // PUSH reg + if (reg & 8) + code_orrex(c, REX_B); + c = genadjesp(c,REGSIZE); + stackchanged = 1; + } + else + { c = loadea(e,&cs,0x8D,reg,0,0,0); /* LEA reg,EA */ + if (I64) + code_orrex(c, REX_W); + } + break; + default: +#ifdef DEBUG + elem_print(e); + debugx(WRFL(fl)); +#endif + assert(0); + } + return c; +} + + +/****************** + * Negate, sqrt operator + */ + +code *cdneg(elem *e,regm_t *pretregs) +{ unsigned byte; + regm_t retregs,possregs; + int reg; + int sz; + tym_t tyml; + code *c,*c1,*cg; + + //printf("cdneg()\n"); + //elem_print(e); + if (*pretregs == 0) + return codelem(e->E1,pretregs,FALSE); + tyml = tybasic(e->E1->Ety); + sz = tysize[tyml]; + if (tyfloating(tyml)) + { if (tycomplex(tyml)) + return neg_complex87(e, pretregs); + if (tyxmmreg(tyml) && e->Eoper == OPneg && *pretregs & XMMREGS) + return xmmneg(e,pretregs); + if (config.inline8087 && + ((*pretregs & (ALLREGS | mBP)) == 0 || e->Eoper == OPsqrt || I64)) + return neg87(e,pretregs); + retregs = (I16 && sz == 8) ? DOUBLEREGS_16 : ALLREGS; + c1 = codelem(e->E1,&retregs,FALSE); + c1 = cat(c1,getregs(retregs)); + if (I32) + { reg = (sz == 8) ? findregmsw(retregs) : findreg(retregs); + c1 = genc2(c1,0x81,modregrm(3,6,reg),0x80000000); /* XOR EDX,sign bit */ + } + else + { reg = (sz == 8) ? AX : findregmsw(retregs); + c1 = genc2(c1,0x81,modregrm(3,6,reg),0x8000); /* XOR AX,0x8000 */ + } + return cat(c1,fixresult(e,retregs,pretregs)); + } + + byte = sz == 1; + possregs = (byte) ? BYTEREGS : allregs; + retregs = *pretregs & possregs; + if (retregs == 0) + retregs = possregs; + c1 = codelem(e->E1,&retregs,FALSE); + cg = getregs(retregs); /* retregs will be destroyed */ + if (sz <= REGSIZE) + { + unsigned reg = findreg(retregs); + unsigned rex = (I64 && sz == 8) ? REX_W : 0; + if (I64 && sz == 1 && reg >= 4) + rex |= REX; + c = gen2(CNIL,0xF7 ^ byte,(rex << 16) | modregrmx(3,3,reg)); // NEG reg + if (!I16 && tysize[tyml] == SHORTSIZE && *pretregs & mPSW) + c->Iflags |= CFopsize | CFpsw; + *pretregs &= mBP | ALLREGS; // flags already set + } + else if (sz == 2 * REGSIZE) + { unsigned msreg,lsreg; + + msreg = findregmsw(retregs); + c = gen2(CNIL,0xF7,modregrm(3,3,msreg)); /* NEG msreg */ + lsreg = findreglsw(retregs); + gen2(c,0xF7,modregrm(3,3,lsreg)); /* NEG lsreg */ + genc2(c,0x81,modregrm(3,3,msreg),0); /* SBB msreg,0 */ + } + else + assert(0); + return cat4(c1,cg,c,fixresult(e,retregs,pretregs)); +} + + +/****************** + * Absolute value operator + */ + +code *cdabs( elem *e, regm_t *pretregs) +{ unsigned byte; + regm_t retregs,possregs; + int reg; + tym_t tyml; + code *c,*c1,*cg; + + if (*pretregs == 0) + return codelem(e->E1,pretregs,FALSE); + tyml = tybasic(e->E1->Ety); + int sz = tysize[tyml]; + unsigned rex = (I64 && sz == 8) ? REX_W : 0; + if (tyfloating(tyml)) + { if (config.inline8087 && ((*pretregs & (ALLREGS | mBP)) == 0 || I64)) + return neg87(e,pretregs); + retregs = (!I32 && sz == 8) ? DOUBLEREGS_16 : ALLREGS; + c1 = codelem(e->E1,&retregs,FALSE); + /*cg = callclib(e,CLIBdneg,pretregs,0);*/ + c1 = cat(c1,getregs(retregs)); + if (I32) + { reg = (sz == 8) ? findregmsw(retregs) : findreg(retregs); + c1 = genc2(c1,0x81,modregrm(3,4,reg),0x7FFFFFFF); /* AND EDX,~sign bit */ + } + else + { reg = (sz == 8) ? AX : findregmsw(retregs); + c1 = genc2(c1,0x81,modregrm(3,4,reg),0x7FFF); /* AND AX,0x7FFF */ + } + return cat(c1,fixresult(e,retregs,pretregs)); + } + + byte = sz == 1; + assert(byte == 0); + byte = 0; + possregs = (sz <= REGSIZE) ? mAX : allregs; + if (!I16 && sz == REGSIZE) + possregs = allregs; + retregs = *pretregs & possregs; + if (retregs == 0) + retregs = possregs; + c1 = codelem(e->E1,&retregs,FALSE); + cg = getregs(retregs); /* retregs will be destroyed */ + if (sz <= REGSIZE) + { + /* CWD + XOR AX,DX + SUB AX,DX + or: + MOV r,reg + SAR r,63 + XOR reg,r + SUB reg,r + */ + unsigned reg; + unsigned r; + + if (!I16 && sz == REGSIZE) + { regm_t scratch = allregs & ~retregs; + reg = findreg(retregs); + cg = allocreg(&scratch,&r,TYint); + cg = cat(cg,getregs(retregs)); + cg = genmovreg(cg,r,reg); // MOV r,reg + cg = genc2(cg,0xC1,modregrmx(3,7,r),REGSIZE * 8 - 1); // SAR r,31/63 + code_orrex(cg, rex); + } + else + { + reg = AX; + r = DX; + cg = cat(cg,getregs(mDX)); + if (!I16 && sz == SHORTSIZE) + cg = gen1(cg,0x98); // CWDE + cg = gen1(cg,0x99); // CWD + code_orrex(cg, rex); + } + gen2(cg,0x33 ^ byte,(rex << 16) | modregxrmx(3,reg,r)); // XOR reg,r + c = gen2(CNIL,0x2B ^ byte,(rex << 16) | modregxrmx(3,reg,r)); // SUB reg,r + if (!I16 && sz == SHORTSIZE && *pretregs & mPSW) + c->Iflags |= CFopsize | CFpsw; + if (*pretregs & mPSW) + c->Iflags |= CFpsw; + *pretregs &= ~mPSW; // flags already set + } + else if (sz == 2 * REGSIZE) + { unsigned msreg,lsreg; + code *cnop; + + /* tst DX + jns L2 + neg DX + neg AX + sbb DX,0 + L2: + */ + + cnop = gennop(CNIL); + msreg = findregmsw(retregs); + lsreg = findreglsw(retregs); + c = genorreg(CNIL,msreg,msreg); + c = genjmp(c,JNS,FLcode,(block *)cnop); + c = gen2(c,0xF7,modregrm(3,3,msreg)); // NEG msreg + gen2(c,0xF7,modregrm(3,3,lsreg)); // NEG lsreg+1 + genc2(c,0x81,modregrm(3,3,msreg),0); // SBB msreg,0 + c = cat(c,cnop); + } + else + assert(0); + return cat4(c1,cg,c,fixresult(e,retregs,pretregs)); +} + +/************************** + * Post increment and post decrement. + */ + +code *cdpost(elem *e,regm_t *pretregs) +{ code cs,*c1,*c2,*c3,*c5,*c6; + unsigned reg,op,byte; + tym_t tyml; + regm_t retregs,possregs,idxregs; + targ_int n; + elem *e2; + int sz; + int stackpushsave; + + //printf("cdpost(pretregs = %s)\n", regm_str(*pretregs)); + retregs = *pretregs; + op = e->Eoper; /* OPxxxx */ + if (retregs == 0) /* if nothing to return */ + return cdaddass(e,pretregs); + c5 = CNIL; + tyml = tybasic(e->E1->Ety); + sz = tysize[tyml]; + e2 = e->E2; + unsigned rex = (I64 && sz == 8) ? REX_W : 0; + + if (tyfloating(tyml)) + { +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + return post87(e,pretregs); +#else + if (config.inline8087) + return post87(e,pretregs); + assert(sz <= 8); + c1 = getlvalue(&cs,e->E1,DOUBLEREGS); + freenode(e->E1); + idxregs = idxregm(&cs); // mask of index regs used + cs.Iop = 0x8B; /* MOV DOUBLEREGS,EA */ + c2 = fltregs(&cs,tyml); + stackchanged = 1; + stackpushsave = stackpush; + if (sz == 8) + { + if (I32) + { + gen1(c2,0x50 + DX); /* PUSH DOUBLEREGS */ + gen1(c2,0x50 + AX); + stackpush += DOUBLESIZE; + retregs = DOUBLEREGS2_32; + } + else + { + gen1(c2,0x50 + AX); + gen1(c2,0x50 + BX); + gen1(c2,0x50 + CX); + gen1(c2,0x50 + DX); /* PUSH DOUBLEREGS */ + stackpush += DOUBLESIZE + DOUBLESIZE; + + gen1(c2,0x50 + AX); + gen1(c2,0x50 + BX); + gen1(c2,0x50 + CX); + gen1(c2,0x50 + DX); /* PUSH DOUBLEREGS */ + retregs = DOUBLEREGS_16; + } + } + else + { + stackpush += FLOATSIZE; /* so we know something is on */ + if (!I32) + gen1(c2,0x50 + DX); + gen1(c2,0x50 + AX); + retregs = FLOATREGS2; + } + c2 = genadjesp(c2,stackpush - stackpushsave); + + cgstate.stackclean++; + c3 = scodelem(e2,&retregs,idxregs,FALSE); + cgstate.stackclean--; + + code *c4; + if (tyml == TYdouble || tyml == TYdouble_alias) + { + retregs = DOUBLEREGS; + c4 = callclib(e,(op == OPpostinc) ? CLIBdadd : CLIBdsub, + &retregs,idxregs); + } + else /* tyml == TYfloat */ + { + retregs = FLOATREGS; + c4 = callclib(e,(op == OPpostinc) ? CLIBfadd : CLIBfsub, + &retregs,idxregs); + } + cs.Iop = 0x89; /* MOV EA,DOUBLEREGS */ + c5 = fltregs(&cs,tyml); + stackpushsave = stackpush; + if (tyml == TYdouble || tyml == TYdouble_alias) + { if (*pretregs == mSTACK) + retregs = mSTACK; /* leave result on stack */ + else + { + if (I32) + { gen1(c5,0x58 + AX); + gen1(c5,0x58 + DX); + } + else + { gen1(c5,0x58 + DX); + gen1(c5,0x58 + CX); + gen1(c5,0x58 + BX); + gen1(c5,0x58 + AX); + } + stackpush -= DOUBLESIZE; + retregs = DOUBLEREGS; + } + } + else + { gen1(c5,0x58 + AX); + if (!I32) + gen1(c5,0x58 + DX); + stackpush -= FLOATSIZE; + retregs = FLOATREGS; + } + c5 = genadjesp(c5,stackpush - stackpushsave); + c6 = fixresult(e,retregs,pretregs); + return cat6(c1,c2,c3,c4,c5,c6); +#endif + } + + assert(e2->Eoper == OPconst); + byte = (sz == 1); + possregs = byte ? BYTEREGS : allregs; + c1 = getlvalue(&cs,e->E1,0); + freenode(e->E1); + idxregs = idxregm(&cs); // mask of index regs used + if (sz <= REGSIZE && *pretregs == mPSW && (cs.Irm & 0xC0) == 0xC0 && + (!I16 || (idxregs & (mBX | mSI | mDI | mBP)))) + { // Generate: + // TEST reg,reg + // LEA reg,n[reg] // don't affect flags + int rm; + + reg = cs.Irm & 7; + if (cs.Irex & REX_B) + reg |= 8; + cs.Iop = 0x85 ^ byte; + code_newreg(&cs, reg); + cs.Iflags |= CFpsw; + c2 = gen(NULL,&cs); // TEST reg,reg + + // If lvalue is a register variable, we must mark it as modified + c3 = modEA(&cs); + + n = e2->EV.Vint; + if (op == OPpostdec) + n = -n; + rm = reg; + if (I16) + rm = regtorm[reg]; + code *c4 = genc1(NULL,0x8D,(rex << 16) | buildModregrm(2,reg,rm),FLconst,n); // LEA reg,n[reg] + return cat4(c1,c2,c3,c4); + } + else if (sz <= REGSIZE || tyfv(tyml)) + { code cs2; + + cs.Iop = 0x8B ^ byte; + retregs = possregs & ~idxregs & *pretregs; + if (!tyfv(tyml)) + { if (retregs == 0) + retregs = possregs & ~idxregs; + } + else /* tyfv(tyml) */ + { if ((retregs &= mLSW) == 0) + retregs = mLSW & ~idxregs; + /* Can't use LES if the EA uses ES as a seg override */ + if (*pretregs & mES && (cs.Iflags & CFSEG) != CFes) + { cs.Iop = 0xC4; /* LES */ + c1 = cat(c1,getregs(mES)); /* allocate ES */ + } + } + c2 = allocreg(&retregs,®,TYint); + code_newreg(&cs, reg); + if (sz == 1 && I64 && reg >= 4) + cs.Irex |= REX; + c3 = gen(CNIL,&cs); /* MOV reg,EA */ + cs2 = cs; + + /* If lvalue is a register variable, we must mark it as modified */ + c3 = cat(c3,modEA(&cs)); + + cs.Iop = 0x81 ^ byte; + cs.Irm &= ~modregrm(0,7,0); /* reg field = 0 */ + cs.Irex &= ~REX_R; + if (op == OPpostdec) + cs.Irm |= modregrm(0,5,0); /* SUB */ + cs.IFL2 = FLconst; + n = e2->EV.Vint; + cs.IEV2.Vint = n; + if (n == 1) /* can use INC or DEC */ + { cs.Iop |= 0xFE; /* xFE is dec byte, xFF is word */ + if (op == OPpostdec) + NEWREG(cs.Irm,1); // DEC EA + else + NEWREG(cs.Irm,0); // INC EA + } + else if (n == -1) // can use INC or DEC + { cs.Iop |= 0xFE; // xFE is dec byte, xFF is word + if (op == OPpostinc) + NEWREG(cs.Irm,1); // DEC EA + else + NEWREG(cs.Irm,0); // INC EA + } + + // For scheduling purposes, we wish to replace: + // MOV reg,EA + // OP EA + // with: + // MOV reg,EA + // OP reg + // MOV EA,reg + // ~OP reg + if (sz <= REGSIZE && (cs.Irm & 0xC0) != 0xC0 && + config.target_cpu >= TARGET_Pentium && + config.flags4 & CFG4speed) + { + // Replace EA in cs with reg + cs.Irm = (cs.Irm & ~modregrm(3,0,7)) | modregrm(3,0,reg & 7); + if (reg & 8) + { cs.Irex &= ~REX_R; + cs.Irex |= REX_B; + } + else + cs.Irex &= ~REX_B; + if (I64 && sz == 1 && reg >= 4) + cs.Irex |= REX; + gen(c3,&cs); // ADD/SUB reg,const + + // Reverse MOV direction + cs2.Iop ^= 2; + gen(c3,&cs2); // MOV EA,reg + + // Toggle INC <-> DEC, ADD <-> SUB + cs.Irm ^= (n == 1 || n == -1) ? modregrm(0,1,0) : modregrm(0,5,0); + gen(c3,&cs); + + if (*pretregs & mPSW) + { *pretregs &= ~mPSW; // flags already set + code_orflag(c3,CFpsw); + } + } + else + gen(c3,&cs); // ADD/SUB EA,const + + freenode(e2); + if (tyfv(tyml)) + { unsigned preg; + + getlvalue_msw(&cs); + if (*pretregs & mES) + { preg = ES; + /* ES is already loaded if CFes is 0 */ + cs.Iop = ((cs.Iflags & CFSEG) == CFes) ? 0x8E : NOP; + NEWREG(cs.Irm,0); /* MOV ES,EA+2 */ + } + else + { + retregs = *pretregs & mMSW; + if (!retregs) + retregs = mMSW; + c3 = cat(c3,allocreg(&retregs,&preg,TYint)); + cs.Iop = 0x8B; + if (I32) + cs.Iflags |= CFopsize; + NEWREG(cs.Irm,preg); /* MOV preg,EA+2 */ + } + c3 = cat(c3,getregs(mask[preg])); + gen(c3,&cs); + retregs = mask[reg] | mask[preg]; + } + return cat4(c1,c2,c3,fixresult(e,retregs,pretregs)); + } +#if TARGET_SEGMENTED + else if (tyml == TYhptr) + { + unsigned long rvalue; + unsigned lreg; + unsigned rtmp; + regm_t mtmp; + + rvalue = e2->EV.Vlong; + freenode(e2); + + // If h--, convert to h++ + if (e->Eoper == OPpostdec) + rvalue = -rvalue; + + retregs = mLSW & ~idxregs & *pretregs; + if (!retregs) + retregs = mLSW & ~idxregs; + c1 = cat(c1,allocreg(&retregs,&lreg,TYint)); + + // Can't use LES if the EA uses ES as a seg override + if (*pretregs & mES && (cs.Iflags & CFSEG) != CFes) + { cs.Iop = 0xC4; + retregs |= mES; + c1 = cat(c1,getregs(mES|mCX)); // allocate ES + cs.Irm |= modregrm(0,lreg,0); + c2 = gen(CNIL,&cs); // LES lreg,EA + } + else + { cs.Iop = 0x8B; + retregs |= mDX; + c1 = cat(c1,getregs(mDX|mCX)); + cs.Irm |= modregrm(0,lreg,0); + c2 = gen(CNIL,&cs); // MOV lreg,EA + NEWREG(cs.Irm,DX); + getlvalue_msw(&cs); + gen(c2,&cs); // MOV DX,EA+2 + getlvalue_lsw(&cs); + } + + // Allocate temporary register, rtmp + mtmp = ALLREGS & ~mCX & ~idxregs & ~retregs; + c2 = cat(c2,allocreg(&mtmp,&rtmp,TYint)); + + movregconst(c2,rtmp,rvalue >> 16,0); // MOV rtmp,e2+2 + c3 = getregs(mtmp); + cs.Iop = 0x81; + NEWREG(cs.Irm,0); + cs.IFL2 = FLconst; + cs.IEV2.Vint = rvalue; + c3 = gen(c3,&cs); // ADD EA,e2 + code_orflag(c3,CFpsw); + genc2(c3,0x81,modregrm(3,2,rtmp),0); // ADC rtmp,0 + genshift(c3); // MOV CX,offset __AHSHIFT + gen2(c3,0xD3,modregrm(3,4,rtmp)); // SHL rtmp,CL + cs.Iop = 0x01; + NEWREG(cs.Irm,rtmp); // ADD EA+2,rtmp + getlvalue_msw(&cs); + gen(c3,&cs); + return cat4(c1,c2,c3,fixresult(e,retregs,pretregs)); + } +#endif + else if (sz == 2 * REGSIZE) + { unsigned sreg; + + retregs = allregs & ~idxregs & *pretregs; + if ((retregs & mLSW) == 0) + retregs |= mLSW & ~idxregs; + if ((retregs & mMSW) == 0) + retregs |= ALLREGS & mMSW; + assert(retregs & mMSW && retregs & mLSW); + c2 = allocreg(&retregs,®,tyml); + sreg = findreglsw(retregs); + cs.Iop = 0x8B; + cs.Irm |= modregrm(0,sreg,0); + c3 = gen(CNIL,&cs); /* MOV sreg,EA */ + NEWREG(cs.Irm,reg); + getlvalue_msw(&cs); + gen(c3,&cs); /* MOV reg,EA+2 */ + cs.Iop = 0x81; + cs.Irm &= ~modregrm(0,7,0); /* reg field = 0 for ADD */ + if (op == OPpostdec) + cs.Irm |= modregrm(0,5,0); /* SUB */ + getlvalue_lsw(&cs); + cs.IFL2 = FLconst; + cs.IEV2.Vlong = e2->EV.Vlong; + gen(c3,&cs); /* ADD/SUB EA,const */ + code_orflag(c3,CFpsw); + getlvalue_msw(&cs); + cs.IEV2.Vlong = 0; + if (op == OPpostinc) + cs.Irm ^= modregrm(0,2,0); /* ADC */ + else + cs.Irm ^= modregrm(0,6,0); /* SBB */ + cs.IEV2.Vlong = e2->EV.Vullong >> (REGSIZE * 8); + gen(c3,&cs); /* ADC/SBB EA,0 */ + freenode(e2); + return cat4(c1,c2,c3,fixresult(e,retregs,pretregs)); + } + else + { assert(0); + /* NOTREACHED */ + return 0; + } +} + + +code *cderr(elem *e,regm_t *pretregs) +{ +#if DEBUG + elem_print(e); +#endif +//printf("op = %d, %d\n", e->Eoper, OPstring); +//printf("string = %p, len = %d\n", e->EV.ss.Vstring, e->EV.ss.Vstrlen); +//printf("string = '%.*s'\n", e->EV.ss.Vstrlen, e->EV.ss.Vstring); + assert(0); + return 0; +} + +code *cdinfo(elem *e,regm_t *pretregs) +{ + code cs; + code *c; + regm_t retregs; + + switch (e->E1->Eoper) + { +#if MARS + case OPdctor: + c = codelem(e->E2,pretregs,FALSE); + retregs = 0; + c = cat(c,codelem(e->E1,&retregs,FALSE)); + break; +#endif +#if SCPP + case OPdtor: + c = cdcomma(e,pretregs); + break; + case OPctor: + c = codelem(e->E2,pretregs,FALSE); + retregs = 0; + c = cat(c,codelem(e->E1,&retregs,FALSE)); + break; + case OPmark: + if (0 && config.exe == EX_NT) + { unsigned idx; + + idx = except_index_get(); + except_mark(); + c = codelem(e->E2,pretregs,FALSE); + if (config.exe == EX_NT && idx != except_index_get()) + { usednteh |= NTEHcleanup; + c = cat(c,nteh_gensindex(idx - 1)); + } + except_release(); + assert(idx == except_index_get()); + } + else + { +#if 0 + usednteh |= EHcleanup; + if (config.exe == EX_NT) + usednteh |= NTEHcleanup; +#endif + cs.Iop = ESCAPE | ESCmark; + cs.Iflags = 0; + cs.Irex = 0; + c = gen(CNIL,&cs); + c = cat(c,codelem(e->E2,pretregs,FALSE)); + cs.Iop = ESCAPE | ESCrelease; + gen(c,&cs); + } + freenode(e->E1); + break; +#endif + default: + assert(0); + } + return c; +} + +/******************************************* + * D constructor. + */ + +code *cddctor(elem *e,regm_t *pretregs) +{ +#if MARS + /* Generate: + ESCAPE | ESCdctor + MOV sindex[BP],index + */ + usednteh |= EHcleanup; + if (config.exe == EX_NT) + { usednteh |= NTEHcleanup | NTEH_try; + nteh_usevars(); + } + assert(*pretregs == 0); + code cs; + cs.Iop = ESCAPE | ESCdctor; + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL1 = FLctor; + cs.IEV1.Vtor = e; + code *c = gen(CNIL,&cs); + c = cat(c, nteh_gensindex(0)); // the actual index will be patched in later + // by except_fillInEHTable() + return c; +#else + return NULL; +#endif +} + +/******************************************* + * D destructor. + */ + +code *cdddtor(elem *e,regm_t *pretregs) +{ +#if MARS + /* Generate: + ESCAPE | ESCddtor + MOV sindex[BP],index + CALL dtor + JMP L1 + Ldtor: + ... e->E1 ... + RET + L1: NOP + */ + usednteh |= EHcleanup; + if (config.exe == EX_NT) + { usednteh |= NTEHcleanup | NTEH_try; + nteh_usevars(); + } + + code cs; + cs.Iop = ESCAPE | ESCddtor; + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL1 = FLdtor; + cs.IEV1.Vtor = e; + code *cd = gen(CNIL,&cs); + + cd = cat(cd, nteh_gensindex(0)); // the actual index will be patched in later + // by except_fillInEHTable() + + assert(*pretregs == 0); + code *c = codelem(e->E1,pretregs,FALSE); + gen1(c,0xC3); // RET + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (config.flags3 & CFG3pic) + { + int nalign = 0; + if (STACKALIGN == 16) + { nalign = STACKALIGN - REGSIZE; + cd = genc2(cd,0x81,modregrm(3,5,SP),nalign); // SUB ESP,nalign + if (I64) + code_orrex(cd, REX_W); + } + calledafunc = 1; + genjmp(cd,0xE8,FLcode,(block *)c); // CALL Ldtor + if (nalign) + { cd = genc2(cd,0x81,modregrm(3,0,SP),nalign); // ADD ESP,nalign + if (I64) + code_orrex(cd, REX_W); + } + } + else +#endif + genjmp(cd,0xE8,FLcode,(block *)c); // CALL Ldtor + + code *cnop = gennop(CNIL); + + genjmp(cd,JMP,FLcode,(block *)cnop); + + return cat4(cd, c, cnop, NULL); +#else + return NULL; +#endif +} + + +/******************************************* + * C++ constructor. + */ + +code *cdctor(elem *e,regm_t *pretregs) +{ +#if SCPP + code cs; + code *c; + +#if 0 + if (config.exe == EX_NT) + { usednteh |= NTEHcleanup; + except_push(NULL,e,NULL); + return nteh_gensindex(except_index_get() - 1); + } +#else + usednteh |= EHcleanup; + if (config.exe == EX_NT) + usednteh |= NTEHcleanup; +#endif + assert(*pretregs == 0); + cs.Iop = ESCAPE | ESCctor; + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL1 = FLctor; + cs.IEV1.Vtor = e; + c = gen(CNIL,&cs); + //except_push(c,e,NULL); + return c; +#else + return NULL; +#endif +} + +code *cddtor(elem *e,regm_t *pretregs) +{ +#if SCPP + code cs; + code *c; + +#if 0 + if (config.exe == EX_NT) + { usednteh |= NTEHcleanup; + except_pop(NULL,e,NULL); + return nteh_gensindex(except_index_get() - 1); + } +#else + usednteh |= EHcleanup; + if (config.exe == EX_NT) + usednteh |= NTEHcleanup; +#endif + assert(*pretregs == 0); + cs.Iop = ESCAPE | ESCdtor; + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL1 = FLdtor; + cs.IEV1.Vtor = e; + c = gen(CNIL,&cs); + //except_pop(c,e,NULL); + return c; +#else + return NULL; +#endif +} + +code *cdmark(elem *e,regm_t *pretregs) +{ + return NULL; +} + +#if !NTEXCEPTIONS +code *cdsetjmp(elem *e,regm_t *pretregs) +{ + assert(0); + return NULL; +} +#endif + +/***************************************** + */ + +code *cdvoid(elem *e,regm_t *pretregs) +{ + assert(*pretregs == 0); + return codelem(e->E1,pretregs,FALSE); +} + +/***************************************** + */ + +code *cdhalt(elem *e,regm_t *pretregs) +{ + assert(*pretregs == 0); + return gen1(NULL, 0xF4); // HLT +} + +/**************************************** + * Check to see if pointer is NULL. + */ + +code *cdnullcheck(elem *e,regm_t *pretregs) +{ regm_t retregs; + regm_t scratch; + unsigned reg; + code *c; + code *cs; + + assert(!I16); + retregs = *pretregs; + if ((retregs & allregs) == 0) + retregs |= allregs; + c = codelem(e->E1,&retregs,FALSE); + scratch = allregs & ~retregs; + cs = allocreg(&scratch,®,TYint); + unsigned rex = I64 ? REX_W : 0; + cs = genc1(cs,0x8B,(rex << 16) | buildModregrm(2,reg,findreg(retregs)),FLconst,0); // MOV reg,0[e] + return cat3(c,cs,fixresult(e,retregs,pretregs)); +} + +#endif // !SPP diff --git a/backend/cod3.c b/backend/cod3.c new file mode 100644 index 00000000..7848ade4 --- /dev/null +++ b/backend/cod3.c @@ -0,0 +1,6458 @@ +// Copyright (C) 1984-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include +#include "cc.h" +#include "el.h" +#include "code.h" +#include "oper.h" +#include "global.h" +#include "type.h" +#include "tinfo.h" +#if SCPP +#include "exh.h" +#endif + +#if HYDRATE +#include "parser.h" +#endif + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +extern targ_size_t retsize; +STATIC void pinholeopt_unittest(); +STATIC void do8bit (enum FL,union evc *); +STATIC void do16bit (enum FL,union evc *,int); +STATIC void do32bit (enum FL,union evc *,int,targ_size_t = 0); +STATIC void do64bit (enum FL,union evc *,int); + +static int hasframe; /* !=0 if this function has a stack frame */ +static targ_size_t Foff; // BP offset of floating register +static targ_size_t CSoff; // offset of common sub expressions +static targ_size_t NDPoff; // offset of saved 8087 registers +int BPoff; // offset from BP +static int EBPtoESP; // add to EBP offset to get ESP offset +static int AAoff; // offset of alloca temporary + +#if ELFOBJ || MACHOBJ +#define JMPSEG CDATA +#define JMPOFF CDoffset +#else +#define JMPSEG DATA +#define JMPOFF Doffset +#endif + +/************* + * Size in bytes of each instruction. + * 0 means illegal instruction. + * bit M: if there is a modregrm field (EV1 is reserved for modregrm) + * bit T: if there is a second operand (EV2) + * bit E: if second operand is only 8 bits + * bit A: a short version exists for the AX reg + * bit R: a short version exists for regs + * bits 2..0: size of instruction (excluding optional bytes) + */ + +#define M 0x80 +#define T 0x40 +#define E 0x20 +#define A 0x10 +#define R 0x08 +#define W 0 + +static unsigned char inssize[256] = +{ M|2,M|2,M|2,M|2, T|E|2,T|3,1,1, /* 00 */ + M|2,M|2,M|2,M|2, T|E|2,T|3,1,1, /* 08 */ + M|2,M|2,M|2,M|2, T|E|2,T|3,1,1, /* 10 */ + M|2,M|2,M|2,M|2, T|E|2,T|3,1,1, /* 18 */ + M|2,M|2,M|2,M|2, T|E|2,T|3,1,1, /* 20 */ + M|2,M|2,M|2,M|2, T|E|2,T|3,1,1, /* 28 */ + M|2,M|2,M|2,M|2, T|E|2,T|3,1,1, /* 30 */ + M|2,M|2,M|2,M|2, T|E|2,T|3,1,1, /* 38 */ + 1,1,1,1, 1,1,1,1, /* 40 */ + 1,1,1,1, 1,1,1,1, /* 48 */ + 1,1,1,1, 1,1,1,1, /* 50 */ + 1,1,1,1, 1,1,1,1, /* 58 */ + 1,1,M|2,M|2, 1,1,1,1, /* 60 */ + T|3,M|T|4,T|E|2,M|T|E|3, 1,1,1,1, /* 68 */ + T|E|2,T|E|2,T|E|2,T|E|2, T|E|2,T|E|2,T|E|2,T|E|2, /* 70 */ + T|E|2,T|E|2,T|E|2,T|E|2, T|E|2,T|E|2,T|E|2,T|E|2, /* 78 */ + M|T|E|A|3,M|T|A|4,M|T|E|3,M|T|E|3, M|2,M|2,M|2,M|A|R|2, /* 80 */ + M|A|2,M|A|2,M|A|2,M|A|2, M|2,M|2,M|2,M|R|2, /* 88 */ + 1,1,1,1, 1,1,1,1, /* 90 */ + 1,1,T|5,1, 1,1,1,1, /* 98 */ +#if 0 /* cod3_set32() patches this */ + T|5,T|5,T|5,T|5, 1,1,1,1, /* A0 */ +#else + T|3,T|3,T|3,T|3, 1,1,1,1, /* A0 */ +#endif + T|E|2,T|3,1,1, 1,1,1,1, /* A8 */ + T|E|2,T|E|2,T|E|2,T|E|2, T|E|2,T|E|2,T|E|2,T|E|2, /* B0 */ + T|3,T|3,T|3,T|3, T|3,T|3,T|3,T|3, /* B8 */ + M|T|E|3,M|T|E|3,T|3,1, M|2,M|2,M|T|E|R|3,M|T|R|4, /* C0 */ + T|E|4,1,T|3,1, 1,T|E|2,1,1, /* C8 */ + M|2,M|2,M|2,M|2, T|E|2,T|E|2,0,1, /* D0 */ + /* For the floating instructions, allow room for the FWAIT */ + M|2,M|2,M|2,M|2, M|2,M|2,M|2,M|2, /* D8 */ + T|E|2,T|E|2,T|E|2,T|E|2, T|E|2,T|E|2,T|E|2,T|E|2, /* E0 */ + T|3,T|3,T|5,T|E|2, 1,1,1,1, /* E8 */ + 1,0,1,1, 1,1,M|A|2,M|A|2, /* F0 */ + 1,1,1,1, 1,1,M|2,M|R|2 /* F8 */ +}; + +static const unsigned char inssize32[256] = +{ 2,2,2,2, 2,5,1,1, /* 00 */ + 2,2,2,2, 2,5,1,1, /* 08 */ + 2,2,2,2, 2,5,1,1, /* 10 */ + 2,2,2,2, 2,5,1,1, /* 18 */ + 2,2,2,2, 2,5,1,1, /* 20 */ + 2,2,2,2, 2,5,1,1, /* 28 */ + 2,2,2,2, 2,5,1,1, /* 30 */ + 2,2,2,2, 2,5,1,1, /* 38 */ + 1,1,1,1, 1,1,1,1, /* 40 */ + 1,1,1,1, 1,1,1,1, /* 48 */ + 1,1,1,1, 1,1,1,1, /* 50 */ + 1,1,1,1, 1,1,1,1, /* 58 */ + 1,1,2,2, 1,1,1,1, /* 60 */ + 5,6,2,3, 1,1,1,1, /* 68 */ + 2,2,2,2, 2,2,2,2, /* 70 */ + 2,2,2,2, 2,2,2,2, /* 78 */ + 3,6,3,3, 2,2,2,2, /* 80 */ + 2,2,2,2, 2,2,2,2, /* 88 */ + 1,1,1,1, 1,1,1,1, /* 90 */ + 1,1,7,1, 1,1,1,1, /* 98 */ + 5,5,5,5, 1,1,1,1, /* A0 */ + 2,5,1,1, 1,1,1,1, /* A8 */ + 2,2,2,2, 2,2,2,2, /* B0 */ + 5,5,5,5, 5,5,5,5, /* B8 */ + 3,3,3,1, 2,2,3,6, /* C0 */ + 4,1,3,1, 1,2,1,1, /* C8 */ + 2,2,2,2, 2,2,0,1, /* D0 */ + /* For the floating instructions, don't need room for the FWAIT */ + 2,2,2,2, 2,2,2,2, /* D8 */ + + 2,2,2,2, 2,2,2,2, /* E0 */ + 5,5,7,2, 1,1,1,1, /* E8 */ + 1,0,1,1, 1,1,2,2, /* F0 */ + 1,1,1,1, 1,1,2,2 /* F8 */ +}; + +/* For 2 byte opcodes starting with 0x0F */ +static unsigned char inssize2[256] = +{ M|3,M|3,M|3,M|3, 2,2,2,2, // 00 + 2,2,M|3,2, 2,2,2,M|T|E|4, // 08 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // 10 + M|3,2,2,2, 2,2,2,2, // 18 + M|3,M|3,M|3,M|3, M|3,2,M|3,2, // 20 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // 28 + 2,2,2,2, 2,2,2,2, // 30 + M|4,2,M|T|E|5,2, 2,2,2,2, // 38 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // 40 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // 48 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // 50 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // 58 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // 60 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // 68 + M|T|E|4,M|T|E|4,M|T|E|4,M|T|E|4, M|3,M|3,M|3,2, // 70 + 2,2,2,2, M|3,M|3,M|3,M|3, // 78 + W|T|4,W|T|4,W|T|4,W|T|4, W|T|4,W|T|4,W|T|4,W|T|4, // 80 + W|T|4,W|T|4,W|T|4,W|T|4, W|T|4,W|T|4,W|T|4,W|T|4, // 88 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // 90 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // 98 + 2,2,2,M|3, M|T|E|4,M|3,2,2, // A0 + 2,2,2,M|3, M|T|E|4,M|3,M|3,M|3, // A8 + M|E|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // B0 + M|3,2,M|T|E|4,M|3, M|3,M|3,M|3,M|3, // B8 + M|3,M|3,M|T|E|4,M|3, M|T|E|4,M|T|E|4,M|T|E|4,M|3, // C0 + 2,2,2,2, 2,2,2,2, // C8 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // D0 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // D8 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // E0 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // E8 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,M|3, // F0 + M|3,M|3,M|3,M|3, M|3,M|3,M|3,2 // F8 +}; + +/************************************************* + * Allocate register temporaries + */ + +code *REGSAVE::save(code *c, int reg, unsigned *pidx) +{ + unsigned i; + if (reg >= XMM0) + { + alignment = 16; + idx = (idx + 15) & ~15; + i = idx; + idx += 16; + // MOVD idx[RBP],xmm + c = genc1(c,0xF20F11,modregxrm(2, reg - XMM0, BPRM),FLregsave,(targ_uns) i); + } + else + { + if (!alignment) + alignment = REGSIZE; + i = idx; + idx += REGSIZE; + // MOV idx[RBP],reg + c = genc1(c,0x89,modregxrm(2, reg, BPRM),FLregsave,(targ_uns) i); + if (I64) + code_orrex(c, REX_W); + } + reflocal = TRUE; + if (idx > top) + top = idx; // keep high water mark + *pidx = i; + return c; +} + +code *REGSAVE::restore(code *c, int reg, unsigned idx) +{ + if (reg >= XMM0) + { + assert(alignment == 16); + // MOVD xmm,idx[RBP] + c = genc1(c,0xF20F10,modregxrm(2, reg - XMM0, BPRM),FLregsave,(targ_uns) idx); + } + else + { // MOV reg,idx[RBP] + c = genc1(c,0x8B,modregxrm(2, reg, BPRM),FLregsave,(targ_uns) idx); + if (I64) + code_orrex(c, REX_W); + } + return c; +} + +/************************************ + * Size for vex encoded instruction. + */ + +unsigned char vex_inssize(code *c) +{ + assert(c->Iflags & CFvex); + unsigned char ins; + if (c->Iflags & CFvex3) + { + switch (c->Ivex.mmmm) + { + case 0: // no prefix + case 1: // 0F + ins = inssize2[c->Ivex.op] + 2; + break; + case 2: // 0F 38 + ins = inssize2[0x38] + 1; + break; + case 3: // 0F 3A + ins = inssize2[0x3A] + 1; + break; + default: + assert(0); + } + } + else + { + ins = inssize2[c->Ivex.op] + 1; + } + return ins; +} + +/************************************ + * Determine if there is a modregrm byte for code. + */ + +int cod3_EA(code *c) +{ unsigned ins; + + unsigned op1 = c->Iop & 0xFF; + if (op1 == ESCAPE) + ins = 0; + else if ((c->Iop & 0xFFFD00) == 0x0F3800) + ins = inssize2[(c->Iop >> 8) & 0xFF]; + else if ((c->Iop & 0xFF00) == 0x0F00) + ins = inssize2[op1]; + else + ins = inssize[op1]; + return ins & M; +} + +/******************************** + * Fix global variables for 386. + */ + +void cod3_set32() +{ + inssize[0xA0] = T|5; + inssize[0xA1] = T|5; + inssize[0xA2] = T|5; + inssize[0xA3] = T|5; + BPRM = 5; /* [EBP] addressing mode */ + fregsaved = mBP | mBX | mSI | mDI; // saved across function calls + FLOATREGS = FLOATREGS_32; + FLOATREGS2 = FLOATREGS2_32; + DOUBLEREGS = DOUBLEREGS_32; + if (config.flags3 & CFG3eseqds) + fregsaved |= mES; + + for (unsigned i = 0x80; i < 0x90; i++) + inssize2[i] = W|T|6; +} + +/******************************** + * Fix global variables for I64. + */ + +void cod3_set64() +{ + inssize[0xA0] = T|5; // MOV AL,mem + inssize[0xA1] = T|5; // MOV RAX,mem + inssize[0xA2] = T|5; // MOV mem,AL + inssize[0xA3] = T|5; // MOV mem,RAX + BPRM = 5; // [RBP] addressing mode + + fregsaved = mBP | mBX | mR12 | mR13 | mR14 | mR15 | mES; // saved across function calls + FLOATREGS = FLOATREGS_64; + FLOATREGS2 = FLOATREGS2_64; + DOUBLEREGS = DOUBLEREGS_64; + STACKALIGN = 16; + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + ALLREGS = mAX|mBX|mCX|mDX|mSI|mDI| mR8|mR9|mR10|mR11|mR12|mR13|mR14|mR15; + BYTEREGS = ALLREGS; +#endif + + for (unsigned i = 0x80; i < 0x90; i++) + inssize2[i] = W|T|6; +} + +/********************************* + * Word or dword align start of function. + */ + +void cod3_align() +{ + static unsigned char nops[7] = { 0x90,0x90,0x90,0x90,0x90,0x90,0x90 }; + unsigned nbytes; +#if OMFOBJ + if (config.flags4 & CFG4speed) // if optimized for speed + { + // Pick alignment based on CPU target + if (config.target_cpu == TARGET_80486 || + config.target_cpu >= TARGET_PentiumPro) + { // 486 does reads on 16 byte boundaries, so if we are near + // such a boundary, align us to it + + nbytes = -Coffset & 15; + if (nbytes < 8) + { + Coffset += obj_bytes(cseg,Coffset,nbytes,nops); // XCHG AX,AX + } + } + } +#else + nbytes = -Coffset & 3; + //dbg_printf("cod3_align Coffset %x nbytes %d\n",Coffset,nbytes); + obj_bytes(cseg,Coffset,nbytes,nops); +#endif +} + +/***************************** + * Given a type, return a mask of + * registers to hold that type. + * Input: + * tyf function type + */ + +regm_t regmask(tym_t tym, tym_t tyf) +{ + switch (tybasic(tym)) + { + case TYvoid: + case TYstruct: + return 0; + case TYbool: + case TYwchar_t: + case TYchar16: + case TYchar: + case TYschar: + case TYuchar: + case TYshort: + case TYushort: + case TYint: + case TYuint: +#if JHANDLE + case TYjhandle: +#endif + case TYnullptr: + case TYnptr: +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: +#endif + return mAX; + + case TYfloat: + case TYifloat: + if (I64) + return mXMM0; + if (config.exe & EX_flat) + return mST0; + case TYlong: + case TYulong: + case TYdchar: + if (!I16) + return mAX; +#if TARGET_SEGMENTED + case TYfptr: + case TYhptr: +#endif + return mDX | mAX; + + case TYcent: + case TYucent: + assert(I64); + return mDX | mAX; + +#if TARGET_SEGMENTED + case TYvptr: + return mDX | mBX; +#endif + + case TYdouble: + case TYdouble_alias: + case TYidouble: + if (I64) + return mXMM0; + if (config.exe & EX_flat) + return mST0; + return DOUBLEREGS; + + case TYllong: + case TYullong: + return I64 ? mAX : (I32 ? mDX | mAX : DOUBLEREGS); + + case TYldouble: + case TYildouble: + return mST0; + + case TYcfloat: +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (I32 && tybasic(tyf) == TYnfunc) + return mDX | mAX; +#endif + case TYcdouble: + if (I64) + return mXMM0 | mXMM1; + case TYcldouble: + return mST01; + + // SIMD vector types + case TYfloat4: + case TYdouble2: + case TYschar16: + case TYuchar16: + case TYshort8: + case TYushort8: + case TYlong4: + case TYulong4: + case TYllong2: + case TYullong2: + if (!config.fpxmmregs) + { printf("SIMD operations not supported on this platform\n"); + exit(1); + } + return mXMM0; + + default: +#if DEBUG + WRTYxx(tym); +#endif + assert(0); + return 0; + } +} + +/******************************* + * Generate block exit code + */ +void outblkexitcode(block *bl, code*& c, int& anyspill, const char* sflsave, symbol** retsym, const regm_t mfuncregsave) +{ + elem *e = bl->Belem; + block *nextb; + block *bs1,*bs2; + regm_t retregs = 0; + bool jcond; + + switch (bl->BC) /* block exit condition */ + { + case BCiftrue: + jcond = TRUE; + bs1 = list_block(bl->Bsucc); + bs2 = list_block(list_next(bl->Bsucc)); + if (bs1 == bl->Bnext) + { // Swap bs1 and bs2 + block *btmp; + + jcond ^= 1; + btmp = bs1; + bs1 = bs2; + bs2 = btmp; + } + c = cat(c,logexp(e,jcond,FLblock,(code *) bs1)); + nextb = bs2; + bl->Bcode = NULL; + L2: + if (nextb != bl->Bnext) + { if (configv.addlinenumbers && bl->Bsrcpos.Slinnum && + !(funcsym_p->ty() & mTYnaked)) + cgen_linnum(&c,bl->Bsrcpos); + assert(!(bl->Bflags & BFLepilog)); + c = cat(c,genjmp(CNIL,JMP,FLblock,nextb)); + } + bl->Bcode = cat(bl->Bcode,c); + break; + case BCjmptab: + case BCifthen: + case BCswitch: + assert(!(bl->Bflags & BFLepilog)); + doswitch(bl); /* hide messy details */ + bl->Bcode = cat(c,bl->Bcode); + break; +#if MARS + case BCjcatch: + // Mark all registers as destroyed. This will prevent + // register assignments to variables used in catch blocks. + c = cat(c,getregs((I32 | I64) ? allregs : (ALLREGS | mES))); +#if 0 && TARGET_LINUX + if (config.flags3 & CFG3pic && !(allregs & mBX)) + { + c = cat(c, cod3_load_got()); + } +#endif + goto case_goto; +#endif +#if SCPP + case BCcatch: + // Mark all registers as destroyed. This will prevent + // register assignments to variables used in catch blocks. + c = cat(c,getregs(allregs | mES)); +#if 0 && TARGET_LINUX + if (config.flags3 & CFG3pic && !(allregs & mBX)) + { + c = cat(c, cod3_load_got()); + } +#endif + goto case_goto; + + case BCtry: + usednteh |= EHtry; + if (config.flags2 & CFG2seh) + usednteh |= NTEHtry; + goto case_goto; +#endif + case BCgoto: + nextb = list_block(bl->Bsucc); + if ((funcsym_p->Sfunc->Fflags3 & Fnteh || + (MARS /*&& config.flags2 & CFG2seh*/)) && + bl->Btry != nextb->Btry && + nextb->BC != BC_finally) + { int toindex; + int fromindex; + + bl->Bcode = NULL; + c = gencodelem(c,e,&retregs,TRUE); + toindex = nextb->Btry ? nextb->Btry->Bscope_index : -1; + assert(bl->Btry); + fromindex = bl->Btry->Bscope_index; +#if MARS + if (toindex + 1 == fromindex) + { // Simply call __finally + if (bl->Btry && + list_block(list_next(bl->Btry->Bsucc))->BC == BCjcatch) + { + goto L2; + } + } +#endif + if (config.flags2 & CFG2seh) + c = cat(c,nteh_unwind(0,toindex)); +#if MARS && (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) + else if (toindex + 1 <= fromindex) + { + //c = cat(c, linux_unwind(0, toindex)); + block *bt; + + //printf("B%d: fromindex = %d, toindex = %d\n", bl->Bdfoidx, fromindex, toindex); + bt = bl; + while ((bt = bt->Btry) != NULL && bt->Bscope_index != toindex) + { block *bf; + + //printf("\tbt->Bscope_index = %d, bt->Blast_index = %d\n", bt->Bscope_index, bt->Blast_index); + bf = list_block(list_next(bt->Bsucc)); + // Only look at try-finally blocks + if (bf->BC == BCjcatch) + continue; + + if (bf == nextb) + continue; + //printf("\tbf = B%d, nextb = B%d\n", bf->Bdfoidx, nextb->Bdfoidx); + if (nextb->BC == BCgoto && + !nextb->Belem && + bf == list_block(nextb->Bsucc)) + continue; + + // call __finally + code *cs; + code *cr; + int nalign = 0; + + gensaverestore(retregs,&cs,&cr); + if (STACKALIGN == 16) + { int npush = (numbitsset(retregs) + 1) * REGSIZE; + if (npush & (STACKALIGN - 1)) + { nalign = STACKALIGN - (npush & (STACKALIGN - 1)); + cs = genc2(cs,0x81,modregrm(3,5,SP),nalign); // SUB ESP,nalign + if (I64) + code_orrex(cs, REX_W); + } + } + cs = genc(cs,0xE8,0,0,0,FLblock,(long)list_block(bf->Bsucc)); + if (nalign) + { cs = genc2(cs,0x81,modregrm(3,0,SP),nalign); // ADD ESP,nalign + if (I64) + code_orrex(cs, REX_W); + } + c = cat3(c,cs,cr); + } + } +#endif + goto L2; + } + case_goto: + c = gencodelem(c,e,&retregs,TRUE); + if (anyspill) + { // Add in the epilog code + code *cstore = NULL; + code *cload = NULL; + + for (int i = 0; i < anyspill; i++) + { symbol *s = globsym.tab[i]; + + if (s->Sflags & SFLspill && + vec_testbit(dfoidx,s->Srange)) + { + s->Sfl = sflsave[i]; // undo block register assignments + cgreg_spillreg_epilog(bl,s,&cstore,&cload); + } + } + c = cat3(c,cstore,cload); + } + + L3: + bl->Bcode = NULL; + nextb = list_block(bl->Bsucc); + goto L2; + + case BC_try: + if (config.flags2 & CFG2seh) + { usednteh |= NTEH_try; + nteh_usevars(); + } + else + usednteh |= EHtry; + goto case_goto; + + case BC_finally: + // Mark all registers as destroyed. This will prevent + // register assignments to variables used in finally blocks. + assert(!getregs(allregs)); + assert(!e); + assert(!bl->Bcode); +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (config.flags3 & CFG3pic) + { + int nalign = 0; + if (STACKALIGN == 16) + { nalign = STACKALIGN - REGSIZE; + c = genc2(c,0x81,modregrm(3,5,SP),nalign); // SUB ESP,nalign + if (I64) + code_orrex(c, REX_W); + } + // CALL bl->Bsucc + c = genc(c,0xE8,0,0,0,FLblock,(long)list_block(bl->Bsucc)); + if (nalign) + { c = genc2(c,0x81,modregrm(3,0,SP),nalign); // ADD ESP,nalign + if (I64) + code_orrex(c, REX_W); + } + // JMP list_next(bl->Bsucc) + nextb = list_block(list_next(bl->Bsucc)); + goto L2; + } + else +#endif + { + // Generate a PUSH of the address of the successor to the + // corresponding BC_ret + //assert(list_block(list_next(bl->Bsucc))->BC == BC_ret); + // PUSH &succ + c = genc(c,0x68,0,0,0,FLblock,(long)list_block(list_next(bl->Bsucc))); + nextb = list_block(bl->Bsucc); + goto L2; + } + + case BC_ret: + c = gencodelem(c,e,&retregs,TRUE); + bl->Bcode = gen1(c,0xC3); // RET + break; + +#if NTEXCEPTIONS + case BC_except: + assert(!e); + usednteh |= NTEH_except; + c = cat(c,nteh_setsp(0x8B)); + getregs(allregs); + goto L3; + + case BC_filter: + c = cat(c,nteh_filter(bl)); + // Mark all registers as destroyed. This will prevent + // register assignments to variables used in filter blocks. + getregs(allregs); + retregs = regmask(e->Ety, TYnfunc); + c = gencodelem(c,e,&retregs,TRUE); + bl->Bcode = gen1(c,0xC3); // RET + break; +#endif + + case BCretexp: + retregs = regmask(e->Ety, funcsym_p->ty()); + + // For the final load into the return regs, don't set regcon.used, + // so that the optimizer can potentially use retregs for register + // variable assignments. + + if (config.flags4 & CFG4optimized) + { regm_t usedsave; + + c = cat(c,docommas(&e)); + usedsave = regcon.used; + if (EOP(e)) + c = gencodelem(c,e,&retregs,TRUE); + else + { + if (e->Eoper == OPconst) + regcon.mvar = 0; + c = gencodelem(c,e,&retregs,TRUE); + regcon.used = usedsave; + if (e->Eoper == OPvar) + { symbol *s = e->EV.sp.Vsym; + + if (s->Sfl == FLreg && s->Sregm != mAX) + *retsym = s; + } + } + } + else + { + case BCret: + case BCexit: + c = gencodelem(c,e,&retregs,TRUE); + } + bl->Bcode = c; + if (retregs == mST0) + { assert(stackused == 1); + pop87(); // account for return value + } + else if (retregs == mST01) + { assert(stackused == 2); + pop87(); + pop87(); // account for return value + } + if (bl->BC == BCexit && config.flags4 & CFG4optimized) + mfuncreg = mfuncregsave; + if (MARS || usednteh & NTEH_try) + { block *bt; + + bt = bl; + while ((bt = bt->Btry) != NULL) + { block *bf; + + bf = list_block(list_next(bt->Bsucc)); +#if MARS + // Only look at try-finally blocks + if (bf->BC == BCjcatch) + { + continue; + } +#endif + if (config.flags2 & CFG2seh) + { + if (bt->Bscope_index == 0) + { + // call __finally + code *cs; + code *cr; + + c = cat(c,nteh_gensindex(-1)); + gensaverestore(retregs,&cs,&cr); + cs = genc(cs,0xE8,0,0,0,FLblock,(long)list_block(bf->Bsucc)); + bl->Bcode = cat3(c,cs,cr); + } + else + bl->Bcode = cat(c,nteh_unwind(retregs,~0)); + break; + } + else + { + // call __finally + code *cs; + code *cr; + int nalign = 0; + + gensaverestore(retregs,&cs,&cr); + if (STACKALIGN == 16) + { int npush = (numbitsset(retregs) + 1) * REGSIZE; + if (npush & (STACKALIGN - 1)) + { nalign = STACKALIGN - (npush & (STACKALIGN - 1)); + cs = genc2(cs,0x81,modregrm(3,5,SP),nalign); // SUB ESP,nalign + if (I64) + code_orrex(cs, REX_W); + } + } + // CALL bf->Bsucc + cs = genc(cs,0xE8,0,0,0,FLblock,(long)list_block(bf->Bsucc)); + if (nalign) + { cs = genc2(cs,0x81,modregrm(3,0,SP),nalign); // ADD ESP,nalign + if (I64) + code_orrex(cs, REX_W); + } + bl->Bcode = c = cat3(c,cs,cr); + } + } + } + break; + +#if SCPP || MARS + case BCasm: + assert(!e); + // Mark destroyed registers + assert(!c); + c = cat(c,getregs(iasm_regs(bl))); + if (bl->Bsucc) + { nextb = list_block(bl->Bsucc); + if (!bl->Bnext) + goto L2; + if (nextb != bl->Bnext && + bl->Bnext && + !(bl->Bnext->BC == BCgoto && + !bl->Bnext->Belem && + nextb == list_block(bl->Bnext->Bsucc))) + { code *cl; + + // See if already have JMP at end of block + cl = code_last(bl->Bcode); + if (!cl || cl->Iop != JMP) + goto L2; // add JMP at end of block + } + } + break; +#endif + default: +#ifdef DEBUG + printf("bl->BC = %d\n",bl->BC); +#endif + assert(0); + } +} + +/******************************* + * Generate code for blocks ending in a switch statement. + * Take BCswitch and decide on + * BCifthen use if - then code + * BCjmptab index into jump table + * BCswitch search table for match + */ + +void doswitch(block *b) +{ code *cc,*c,*ce; + regm_t retregs; + unsigned ncases,n,reg,reg2,rm; + targ_llong vmax,vmin,val; + targ_llong *p; + list_t bl; + elem *e; + + tym_t tys; + int sz; + unsigned char dword; + unsigned char mswsame; +#if LONGLONG + targ_ulong msw; +#else + unsigned msw; +#endif + + e = b->Belem; + elem_debug(e); + cc = docommas(&e); + cgstate.stackclean++; + tys = tybasic(e->Ety); + sz = tysize[tys]; + dword = (sz == 2 * REGSIZE); + mswsame = 1; // assume all msw's are the same + p = b->BS.Bswitch; /* pointer to case data */ + assert(p); + ncases = *p++; /* number of cases */ + + vmax = MINLL; // smallest possible llong + vmin = MAXLL; // largest possible llong + for (n = 0; n < ncases; n++) // find max and min case values + { val = *p++; + if (val > vmax) vmax = val; + if (val < vmin) vmin = val; + if (REGSIZE == 2) + { + unsigned short ms = (val >> 16) & 0xFFFF; + if (n == 0) + msw = ms; + else if (msw != ms) + mswsame = 0; + } + else // REGSIZE == 4 + { + targ_ulong ms = (val >> 32) & 0xFFFFFFFF; + if (n == 0) + msw = ms; + else if (msw != ms) + mswsame = 0; + } + } + p -= ncases; + //dbg_printf("vmax = x%lx, vmin = x%lx, vmax-vmin = x%lx\n",vmax,vmin,vmax - vmin); + + if (I64) + { // For now, just generate basic if-then sequence to get us running + retregs = ALLREGS; + b->BC = BCifthen; + c = scodelem(e,&retregs,0,TRUE); + assert(!dword); // 128 bit switches not supported + reg = findreg(retregs); // reg that result is in + bl = b->Bsucc; + for (n = 0; n < ncases; n++) + { code *cx; + val = *p; + if (sz == 4) + cx = genc2(CNIL,0x81,modregrmx(3,7,reg),val); // CMP reg,val + else if (sz == 8) + { + if (val == (int)val) // if val is a 64 bit value sign-extended from 32 bits + { + cx = genc2(CNIL,0x81,modregrmx(3,7,reg),val); // CMP reg,value32 + cx->Irex |= REX_W; // 64 bit operand + } + else + { unsigned sreg; + // MOV sreg,value64 + cx = regwithvalue(CNIL, ALLREGS & ~mask[reg], val, &sreg, 64); + cx = genregs(cx,0x3B,reg,sreg); // CMP reg,sreg + code_orrex(cx, REX_W); + } + } + else + assert(0); + bl = list_next(bl); + genjmp(cx,JE,FLblock,list_block(bl)); // JE caseaddr + c = cat(c,cx); + p++; + } + if (list_block(b->Bsucc) != b->Bnext) /* if default is not next block */ + c = cat(c,genjmp(CNIL,JMP,FLblock,list_block(b->Bsucc))); + ce = NULL; + } + // Need to do research on MACHOBJ to see about better methods + else if (MACHOBJ || ncases <= 3) + { // generate if-then sequence + retregs = ALLREGS; + L1: + b->BC = BCifthen; + c = scodelem(e,&retregs,0,TRUE); + if (dword) + { reg = findreglsw(retregs); + reg2 = findregmsw(retregs); + } + else + reg = findreg(retregs); /* reg that result is in */ + bl = b->Bsucc; + if (dword && mswsame) + { /* CMP reg2,MSW */ + c = genc2(c,0x81,modregrm(3,7,reg2),msw); + genjmp(c,JNE,FLblock,list_block(b->Bsucc)); /* JNE default */ + } + for (n = 0; n < ncases; n++) + { code *cnext = CNIL; + /* CMP reg,casevalue */ + c = cat(c,ce = genc2(CNIL,0x81,modregrm(3,7,reg),(targ_int)*p)); + if (dword && !mswsame) + { + cnext = gennop(CNIL); + genjmp(ce,JNE,FLcode,(block *) cnext); + genc2(ce,0x81,modregrm(3,7,reg2),MSREG(*p)); + } + bl = list_next(bl); + /* JE caseaddr */ + genjmp(ce,JE,FLblock,list_block(bl)); + c = cat(c,cnext); + p++; + } + if (list_block(b->Bsucc) != b->Bnext) /* if default is not next block */ + c = cat(c,genjmp(CNIL,JMP,FLblock,list_block(b->Bsucc))); + ce = NULL; + } +#if TARGET_WINDOS // try and find relocation to support this + else if ((targ_ullong)(vmax - vmin) <= ncases * 2) // then use jump table + { int modify; + + b->BC = BCjmptab; + retregs = IDXREGS; + if (dword) + retregs |= mMSW; + modify = (vmin || !I32); + c = scodelem(e,&retregs,0,!modify); + reg = findreg(retregs & IDXREGS); /* reg that result is in */ + if (dword) + reg2 = findregmsw(retregs); + if (modify) + { + assert(!(retregs & regcon.mvar)); + c = cat(c,getregs(retregs)); + } + if (vmin) /* if there is a minimum */ + { + c = genc2(c,0x81,modregrm(3,5,reg),vmin); /* SUB reg,vmin */ + if (dword) + { genc2(c,0x81,modregrm(3,3,reg2),MSREG(vmin)); // SBB reg2,vmin + genjmp(c,JNE,FLblock,list_block(b->Bsucc)); /* JNE default */ + } + } + else if (dword) + { c = gentstreg(c,reg2); // TEST reg2,reg2 + genjmp(c,JNE,FLblock,list_block(b->Bsucc)); /* JNE default */ + } + if (vmax - vmin != REGMASK) /* if there is a maximum */ + { /* CMP reg,vmax-vmin */ + c = genc2(c,0x81,modregrm(3,7,reg),vmax-vmin); + genjmp(c,JA,FLblock,list_block(b->Bsucc)); /* JA default */ + } + if (!I32) + c = gen2(c,0xD1,modregrm(3,4,reg)); /* SHL reg,1 */ + if (I32) + { + ce = genc1(CNIL,0xFF,modregrm(0,4,4),FLswitch,0); /* JMP [CS:]disp[idxreg*4] */ + ce->Isib = modregrm(2,reg,5); + } + else + { rm = getaddrmode(retregs) | modregrm(0,4,0); + ce = genc1(CNIL,0xFF,rm,FLswitch,0); /* JMP [CS:]disp[idxreg] */ + } + int flags = (config.flags & CFGromable) ? CFcs : 0; // table is in code seg + ce->Iflags |= flags; // segment override + ce->IEV1.Vswitch = b; + b->Btablesize = (int) (vmax - vmin + 1) * tysize[TYnptr]; + } +#endif + else /* else use switch table (BCswitch) */ + { targ_size_t disp; + int mod; + code *esw; + code *ct; + + retregs = mAX; /* SCASW requires AX */ + if (dword) + retregs |= mDX; + else if (ncases <= 6 || config.flags4 & CFG4speed) + goto L1; + c = scodelem(e,&retregs,0,TRUE); + if (dword && mswsame) + { /* CMP DX,MSW */ + c = genc2(c,0x81,modregrm(3,7,DX),msw); + genjmp(c,JNE,FLblock,list_block(b->Bsucc)); /* JNE default */ + } + ce = getregs(mCX|mDI); +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (config.flags3 & CFG3pic) + { // Add in GOT + code *cx; + code *cgot; + + ce = cat(ce, getregs(mDX)); + cx = genc2(NULL,CALL,0,0); // CALL L1 + gen1(cx, 0x58 + DI); // L1: POP EDI + + // ADD EDI,_GLOBAL_OFFSET_TABLE_+3 + symbol *gotsym = elfobj_getGOTsym(); + cgot = gencs(CNIL,0x81,modregrm(3,0,DI),FLextern,gotsym); + cgot->Iflags = CFoff; + cgot->IEVoffset2 = 3; + + makeitextern(gotsym); + + genmovreg(cgot, DX, DI); // MOV EDX, EDI + // ADD EDI,offset of switch table + esw = gencs(CNIL,0x81,modregrm(3,0,DI),FLswitch,NULL); + esw->IEV2.Vswitch = b; + esw = cat3(cx, cgot, esw); + } + else +#endif + { + // MOV DI,offset of switch table + esw = gencs(CNIL,0xC7,modregrm(3,0,DI),FLswitch,NULL); + esw->IEV2.Vswitch = b; + } + ce = cat(ce,esw); + movregconst(ce,CX,ncases,0); /* MOV CX,ncases */ + + /* The switch table will be accessed through ES:DI. + * Therefore, load ES with proper segment value. + */ + if (config.flags3 & CFG3eseqds) + { assert(!(config.flags & CFGromable)); + ce = cat(ce,getregs(mCX)); // allocate CX + } + else + { + ce = cat(ce,getregs(mES|mCX)); // allocate ES and CX + gen1(ce,(config.flags & CFGromable) ? 0x0E : 0x1E); // PUSH CS/DS + gen1(ce,0x07); // POP ES + } + + disp = (ncases - 1) * intsize; /* displacement to jump table */ + if (dword && !mswsame) + { code *cloop; + + /* Build the following: + L1: SCASW + JNE L2 + CMP DX,[CS:]disp[DI] + L2: LOOPNE L1 + */ + + mod = (disp > 127) ? 2 : 1; /* displacement size */ + cloop = genc2(CNIL,0xE0,0,-7 - mod - + ((config.flags & CFGromable) ? 1 : 0)); /* LOOPNE scasw */ + ce = gen1(ce,0xAF); /* SCASW */ + code_orflag(ce,CFtarg2); // target of jump + genjmp(ce,JNE,FLcode,(block *) cloop); /* JNE loop */ + /* CMP DX,[CS:]disp[DI] */ + ct = genc1(CNIL,0x39,modregrm(mod,DX,5),FLconst,disp); + int flags = (config.flags & CFGromable) ? CFcs : 0; // table is in code seg + ct->Iflags |= flags; // possible seg override + ce = cat3(ce,ct,cloop); + disp += ncases * intsize; /* skip over msw table */ + } + else + { + ce = gen1(ce,0xF2); /* REPNE */ + gen1(ce,0xAF); /* SCASW */ + } + genjmp(ce,JNE,FLblock,list_block(b->Bsucc)); /* JNE default */ + mod = (disp > 127) ? 2 : 1; /* 1 or 2 byte displacement */ + if (config.flags & CFGromable) + gen1(ce,SEGCS); /* table is in code segment */ +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (config.flags3 & CFG3pic) + { // ADD EDX,(ncases-1)*2[EDI] + ct = genc1(CNIL,0x03,modregrm(mod,DX,7),FLconst,disp); + // JMP EDX + gen2(ct,0xFF,modregrm(3,4,DX)); + } + else +#endif + { // JMP (ncases-1)*2[DI] + ct = genc1(CNIL,0xFF,modregrm(mod,4,(I32 ? 7 : 5)),FLconst,disp); + int flags = (config.flags & CFGromable) ? CFcs : 0; // table is in code seg + ct->Iflags |= flags; + } + ce = cat(ce,ct); + b->Btablesize = disp + intsize + ncases * tysize[TYnptr]; + } + b->Bcode = cat3(cc,c,ce); + //assert(b->Bcode); + cgstate.stackclean--; +} + +/****************************** + * Output data block for a jump table (BCjmptab). + * The 'holes' in the table get filled with the + * default label. + */ + +void outjmptab(block *b) +{ + unsigned ncases,n; + targ_llong u,vmin,vmax,val,*p; + targ_size_t alignbytes,def,targ,*poffset; + int jmpseg; + + poffset = (config.flags & CFGromable) ? &Coffset : &JMPOFF; + p = b->BS.Bswitch; /* pointer to case data */ + ncases = *p++; /* number of cases */ + vmax = MINLL; // smallest possible llong + vmin = MAXLL; // largest possible llong + for (n = 0; n < ncases; n++) /* find min case value */ + { val = p[n]; + if (val > vmax) vmax = val; + if (val < vmin) vmin = val; + } + jmpseg = (config.flags & CFGromable) ? cseg : JMPSEG; + + /* Any alignment bytes necessary */ + alignbytes = align(0,*poffset) - *poffset; + obj_lidata(jmpseg,*poffset,alignbytes); + + def = list_block(b->Bsucc)->Boffset; /* default address */ + assert(vmin <= vmax); + for (u = vmin; ; u++) + { targ = def; /* default */ + for (n = 0; n < ncases; n++) + { if (p[n] == u) + { targ = list_block(list_nth(b->Bsucc,n + 1))->Boffset; + break; + } + } + reftocodseg(jmpseg,*poffset,targ); + *poffset += tysize[TYnptr]; + if (u == vmax) /* for case that (vmax == ~0) */ + break; + } +} + +/****************************** + * Output data block for a switch table. + * Two consecutive tables, the first is the case value table, the + * second is the address table. + */ + +void outswitab(block *b) +{ unsigned ncases,n; + targ_llong *p; + targ_size_t val; + targ_size_t alignbytes,*poffset; + int seg; /* target segment for table */ + list_t bl; + unsigned sz; + targ_size_t offset; + + //printf("outswitab()\n"); + p = b->BS.Bswitch; /* pointer to case data */ + ncases = *p++; /* number of cases */ + + if (config.flags & CFGromable) + { poffset = &Coffset; + assert(cseg == CODE); + seg = cseg; + } + else + { + poffset = &JMPOFF; + seg = JMPSEG; + } + offset = *poffset; + alignbytes = align(0,*poffset) - *poffset; + obj_lidata(seg,*poffset,alignbytes); /* any alignment bytes necessary */ + assert(*poffset == offset + alignbytes); + + sz = intsize; + for (n = 0; n < ncases; n++) /* send out value table */ + { + //printf("\tcase %d, offset = x%x\n", n, *poffset); +#if OMFOBJ + *poffset += +#endif + obj_bytes(seg,*poffset,sz,p); + p++; + } + offset += alignbytes + sz * ncases; + assert(*poffset == offset); + + if (b->Btablesize == ncases * (REGSIZE * 2 + tysize[TYnptr])) + { + /* Send out MSW table */ + p -= ncases; + for (n = 0; n < ncases; n++) + { val = MSREG(*p); + p++; +#if OMFOBJ + *poffset += +#endif + obj_bytes(seg,*poffset,REGSIZE,&val); + } + offset += REGSIZE * ncases; + assert(*poffset == offset); + } + + bl = b->Bsucc; + for (n = 0; n < ncases; n++) /* send out address table */ + { bl = list_next(bl); + reftocodseg(seg,*poffset,list_block(bl)->Boffset); + *poffset += tysize[TYnptr]; + } + assert(*poffset == offset + ncases * tysize[TYnptr]); +} + +/***************************** + * Return a jump opcode relevant to the elem for a JMP TRUE. + */ + +int jmpopcode(elem *e) +{ tym_t tym; + int zero,i,jp,op; + static const char jops[][2][6] = + { /* <= > < >= == != <=0 >0 <0 >=0 ==0 !=0 */ + { {JLE,JG ,JL ,JGE,JE ,JNE},{JLE,JG ,JS ,JNS,JE ,JNE} }, /* signed */ + { {JBE,JA ,JB ,JAE,JE ,JNE},{JE ,JNE,JB ,JAE,JE ,JNE} }, /* unsigned */ +#if 0 + { {JLE,JG ,JL ,JGE,JE ,JNE},{JLE,JG ,JL ,JGE,JE ,JNE} }, /* real */ + { {JBE,JA ,JB ,JAE,JE ,JNE},{JBE,JA ,JB ,JAE,JE ,JNE} }, /* 8087 */ + { {JA ,JBE,JAE,JB ,JE ,JNE},{JBE,JA ,JB ,JAE,JE ,JNE} }, /* 8087 R */ +#endif + }; + +#define XP (JP << 8) +#define XNP (JNP << 8) + static const unsigned jfops[1][26] = + /* le gt lt ge eqeq ne unord lg leg ule ul uge */ + { + { XNP|JBE,JA,XNP|JB,JAE,XNP|JE, XP|JNE,JP, JNE,JNP, JBE,JC,XP|JAE, + + /* ug ue ngt nge nlt nle ord nlg nleg nule nul nuge nug nue */ + XP|JA,JE,JBE,JB, XP|JAE,XP|JA, JNP,JE, JP, JA, JNC,XNP|JB, XNP|JBE,JNE }, /* 8087 */ + }; + + assert(e); + while (e->Eoper == OPcomma || + /* The !EOP(e->E1) is to line up with the case in cdeq() where */ + /* we decide if mPSW is passed on when evaluating E2 or not. */ + (e->Eoper == OPeq && !EOP(e->E1))) + e = e->E2; /* right operand determines it */ + + op = e->Eoper; + if (e->Ecount != e->Ecomsub) // comsubs just get Z bit set + return JNE; + if (!OTrel(op)) // not relational operator + { + tym_t tymx = tybasic(e->Ety); + if (tyfloating(tymx) && config.inline8087 && + (tymx == TYldouble || tymx == TYildouble || tymx == TYcldouble || + tymx == TYcdouble || tymx == TYcfloat || + op == OPind)) + { + return XP|JNE; + } + return (op >= OPbt && op <= OPbts) ? JC : JNE; + } + + if (e->E2->Eoper == OPconst) + zero = !boolres(e->E2); + else + zero = 0; + + tym = e->E1->Ety; + if (tyfloating(tym)) +#if 1 + { i = 0; + if (config.inline8087) + { i = 1; + +#if 1 +#define NOSAHF (I64 || config.fpxmmregs) + if (rel_exception(op) || config.flags4 & CFG4fastfloat) + { + if (zero) + { + if (NOSAHF) + op = swaprel(op); + } + else if (NOSAHF) + op = swaprel(op); + else if (cmporder87(e->E2)) + op = swaprel(op); + else + ; + } + else + { + if (zero && config.target_cpu < TARGET_80386) + ; + else + op = swaprel(op); + } +#else + if (zero && !rel_exception(op) && config.target_cpu >= TARGET_80386) + op = swaprel(op); + else if (!zero && + (cmporder87(e->E2) || !(rel_exception(op) || config.flags4 & CFG4fastfloat))) + /* compare is reversed */ + op = swaprel(op); +#endif + } + jp = jfops[0][op - OPle]; + goto L1; + } +#else + i = (config.inline8087) ? (3 + cmporder87(e->E2)) : 2; +#endif + else if (tyuns(tym) || tyuns(e->E2->Ety)) + i = 1; + else if (tyintegral(tym) || typtr(tym)) + i = 0; + else + { +#if DEBUG + elem_print(e); + WRTYxx(tym); +#endif + assert(0); + } + + jp = jops[i][zero][op - OPle]; /* table starts with OPle */ +L1: +#if DEBUG + if ((jp & 0xF0) != 0x70) + WROP(op), + printf("i %d zero %d op x%x jp x%x\n",i,zero,op,jp); +#endif + assert((jp & 0xF0) == 0x70); + return jp; +} + +/********************************** + * Append code to *pc which validates pointer described by + * addressing mode in *pcs. Modify addressing mode in *pcs. + * Input: + * keepmsk mask of registers we must not destroy or use + * if (keepmsk & RMstore), this will be only a store operation + * into the lvalue + */ + +void cod3_ptrchk(code **pc,code *pcs,regm_t keepmsk) +{ code *c; + code *cs2; + unsigned char rm,sib; + unsigned reg; + unsigned flagsave; + unsigned opsave; + regm_t idxregs; + regm_t tosave; + regm_t used; + int i; + + assert(!I64); + if (!I16 && pcs->Iflags & (CFes | CFss | CFcs | CFds | CFfs | CFgs)) + return; // not designed to deal with 48 bit far pointers + + c = *pc; + + rm = pcs->Irm; + assert(!(rm & 0x40)); // no disp8 or reg addressing modes + + // If the addressing mode is already a register + reg = rm & 7; + if (I16) + { static const unsigned char imode[8] = { BP,BP,BP,BP,SI,DI,BP,BX }; + + reg = imode[reg]; // convert [SI] to SI, etc. + } + idxregs = mask[reg]; + if ((rm & 0x80 && (pcs->IFL1 != FLoffset || pcs->IEV1.Vuns)) || + !(idxregs & ALLREGS) + ) + { + // Load the offset into a register, so we can push the address + idxregs = (I16 ? IDXREGS : ALLREGS) & ~keepmsk; // only these can be index regs + assert(idxregs); + c = cat(c,allocreg(&idxregs,®,TYoffset)); + + opsave = pcs->Iop; + flagsave = pcs->Iflags; + pcs->Iop = 0x8D; + pcs->Irm |= modregrm(0,reg,0); + pcs->Iflags &= ~(CFopsize | CFss | CFes | CFcs); // no prefix bytes needed + c = gen(c,pcs); // LEA reg,EA + + pcs->Iflags = flagsave; + pcs->Iop = opsave; + } + + // registers destroyed by the function call + //used = (mBP | ALLREGS | mES) & ~fregsaved; + used = 0; // much less code generated this way + + cs2 = CNIL; + tosave = used & (keepmsk | idxregs); + for (i = 0; tosave; i++) + { regm_t mi = mask[i]; + + assert(i < REGMAX); + if (mi & tosave) /* i = register to save */ + { + int push,pop; + + stackchanged = 1; + if (i == ES) + { push = 0x06; + pop = 0x07; + } + else + { push = 0x50 + i; + pop = push | 8; + } + c = gen1(c,push); // PUSH i + cs2 = cat(gen1(CNIL,pop),cs2); // POP i + tosave &= ~mi; + } + } + + // For 16 bit models, push a far pointer + if (I16) + { int segreg; + + switch (pcs->Iflags & (CFes | CFss | CFcs | CFds | CFfs | CFgs)) + { case CFes: segreg = 0x06; break; + case CFss: segreg = 0x16; break; + case CFcs: segreg = 0x0E; break; + case 0: segreg = 0x1E; break; // DS + default: + assert(0); + } + + // See if we should default to SS: + // (Happens when BP is part of the addressing mode) + if (segreg == 0x1E && (rm & 0xC0) != 0xC0 && + rm & 2 && (rm & 7) != 7) + { segreg = 0x16; + if (config.wflags & WFssneds) + pcs->Iflags |= CFss; // because BP won't be there anymore + } + c = gen1(c,segreg); // PUSH segreg + } + + c = gen1(c,0x50 + reg); // PUSH reg + + // Rewrite the addressing mode in *pcs so it is just 0[reg] + setaddrmode(pcs, idxregs); + pcs->IFL1 = FLoffset; + pcs->IEV1.Vuns = 0; + + // Call the validation function + { + makeitextern(rtlsym[RTLSYM_PTRCHK]); + + used &= ~(keepmsk | idxregs); // regs destroyed by this exercise + c = cat(c,getregs(used)); + // CALL __ptrchk + gencs(c,(LARGECODE) ? 0x9A : CALL,0,FLfunc,rtlsym[RTLSYM_PTRCHK]); + } + + *pc = cat(c,cs2); +} + + + +/*********************************** + * Determine if BP can be used as a general purpose register. + * Note parallels between this routine and prolog(). + * Returns: + * 0 can't be used, needed for frame + * mBP can be used + */ + +regm_t cod3_useBP() +{ + tym_t tym; + tym_t tyf; + + // Note that DOSX memory model cannot use EBP as a general purpose + // register, as SS != DS. + if (!(config.exe & EX_flat) || config.flags & (CFGalwaysframe | CFGnoebp)) + goto Lcant; + + if (anyiasm) + goto Lcant; + + tyf = funcsym_p->ty(); + if (tyf & mTYnaked) // if no prolog/epilog for function + goto Lcant; + + if (funcsym_p->Sfunc->Fflags3 & Ffakeeh) + { + goto Lcant; // need consistent stack frame + } + + tym = tybasic(tyf); + if (tym == TYifunc) + goto Lcant; + + stackoffsets(0); + localsize = Aoffset; // an estimate only +// if (localsize) + { + if (!(config.flags4 & CFG4speed) || + config.target_cpu < TARGET_Pentium || + tyfarfunc(tym) || + config.flags & CFGstack || + localsize >= 0x100 || // arbitrary value < 0x1000 + (usednteh & ~NTEHjmonitor) || + usedalloca + ) + goto Lcant; + } +Lcan: + return mBP; + +Lcant: + return 0; +} + +/*************************************** + * Gen code for OPframeptr + */ + +code *cdframeptr(elem *e, regm_t *pretregs) +{ + unsigned reg; + code cs; + + regm_t retregs = *pretregs & allregs; + if (!retregs) + retregs = allregs; + code *cg = allocreg(&retregs, ®, TYint); + + cs.Iop = ESCAPE | ESCframeptr; + cs.Iflags = 0; + cs.Irex = 0; + cs.Irm = reg; + cg = gen(cg,&cs); + + return cat(cg,fixresult(e,retregs,pretregs)); +} + +/*************************************** + * Gen code for load of _GLOBAL_OFFSET_TABLE_. + * This value gets cached in the local variable 'localgot'. + */ + +code *cdgot(elem *e, regm_t *pretregs) +{ +#if TARGET_OSX + regm_t retregs; + unsigned reg; + code *c; + + retregs = *pretregs & allregs; + if (!retregs) + retregs = allregs; + c = allocreg(&retregs, ®, TYnptr); + + c = genc(c,CALL,0,0,0,FLgot,0); // CALL L1 + gen1(c, 0x58 + reg); // L1: POP reg + + return cat(c,fixresult(e,retregs,pretregs)); +#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + regm_t retregs; + unsigned reg; + code *c; + code *cgot; + + retregs = *pretregs & allregs; + if (!retregs) + retregs = allregs; + c = allocreg(&retregs, ®, TYnptr); + + c = genc2(c,CALL,0,0); // CALL L1 + gen1(c, 0x58 + reg); // L1: POP reg + + // ADD reg,_GLOBAL_OFFSET_TABLE_+3 + symbol *gotsym = elfobj_getGOTsym(); + cgot = gencs(CNIL,0x81,modregrm(3,0,reg),FLextern,gotsym); + /* Because the 2:3 offset from L1: is hardcoded, + * this sequence of instructions must not + * have any instructions in between, + * so set CFvolatile to prevent the scheduler from rearranging it. + */ + cgot->Iflags = CFoff | CFvolatile; + cgot->IEVoffset2 = (reg == AX) ? 2 : 3; + + makeitextern(gotsym); + return cat3(c,cgot,fixresult(e,retregs,pretregs)); +#else + assert(0); + return NULL; +#endif +} + +/************************************************** + * Load contents of localgot into EBX. + */ + +code *load_localgot() +{ +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (config.flags3 & CFG3pic && I32) + { + if (localgot) + { + localgot->Sflags &= ~GTregcand; // because this hack doesn't work with reg allocator + elem *e = el_var(localgot); + regm_t retregs = mBX; + code *c = codelem(e,&retregs,FALSE); + el_free(e); + return c; + } + else + { + elem *e = el_long(TYnptr, 0); + e->Eoper = OPgot; + regm_t retregs = mBX; + code *c = codelem(e,&retregs,FALSE); + el_free(e); + return c; + } + } +#endif + return NULL; +} + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +/***************************** + * Returns: + * # of bytes stored + */ + +#define ONS_OHD 4 // max # of extra bytes added by obj_namestring() + +STATIC int obj_namestring(char *p,const char *name) +{ unsigned len; + + len = strlen(name); + if (len > 255) + { + short *ps = (short *)p; + p[0] = 0xFF; + p[1] = 0; + ps[1] = len; + memcpy(p + 4,name,len); + len += ONS_OHD; + } + else + { p[0] = len; + memcpy(p + 1,name,len); + len++; + } + return len; +} +#endif + +code *genregs(code *c,unsigned op,unsigned dstreg,unsigned srcreg) +{ return gen2(c,op,modregxrmx(3,dstreg,srcreg)); } + +code *gentstreg(code *c,unsigned t) +{ + c = gen2(c,0x85,modregxrmx(3,t,t)); // TEST t,t + code_orflag(c,CFpsw); + return c; +} + +code *genpush(code *c, unsigned reg) +{ + c = gen1(c, 0x50 + (reg & 7)); + if (reg & 8) + code_orrex(c, REX_B); + return c; +} + +code *genpop(code *c, unsigned reg) +{ + c = gen1(c, 0x58 + (reg & 7)); + if (reg & 8) + code_orrex(c, REX_B); + return c; +} + +/************************** + * Generate a MOV to save a register to a stack slot + */ +code *gensavereg(unsigned& reg, targ_uns slot) +{ + // MOV i[BP],reg + unsigned op = 0x89; // normal mov + if (reg == ES) + { reg = 0; // the real reg number + op = 0x8C; // segment reg mov + } + code *c = genc1(NULL,op,modregxrm(2, reg, BPRM),FLcs,slot); + if (I64) + code_orrex(c, REX_W); + + return c; +} + +/************************** + * Generate a MOV to,from register instruction. + * Smart enough to dump redundant register moves, and segment + * register moves. + */ + +code *genmovreg(code *c,unsigned to,unsigned from) +{ +#if DEBUG + if (to > ES || from > ES) + printf("genmovreg(c = %p, to = %d, from = %d)\n",c,to,from); +#endif + assert(to <= ES && from <= ES); + if (to != from) + { + if (to == ES) + c = genregs(c,0x8E,0,from); + else if (from == ES) + c = genregs(c,0x8C,0,to); + else + c = genregs(c,0x89,from,to); + if (I64) + code_orrex(c, REX_W); + } + return c; +} + +/*************************************** + * Generate immediate multiply instruction for r1=r2*imm. + * Optimize it into LEA's if we can. + */ + +code *genmulimm(code *c,unsigned r1,unsigned r2,targ_int imm) +{ code cs; + + // These optimizations should probably be put into pinholeopt() + switch (imm) + { case 1: + c = genmovreg(c,r1,r2); + break; + case 5: + cs.Iop = LEA; + cs.Iflags = 0; + cs.Irex = 0; + buildEA(&cs,r2,r2,4,0); + cs.orReg(r1); + c = gen(c,&cs); + break; + default: + c = genc2(c,0x69,modregxrmx(3,r1,r2),imm); // IMUL r1,r2,imm + break; + } + return c; +} + +/****************************** + * Load CX with the value of _AHSHIFT. + */ + +code *genshift(code *c) +{ +#if SCPP && TX86 + code *c1; + + // Set up ahshift to trick ourselves into giving the right fixup, + // which must be seg-relative, external frame, external target. + c1 = gencs(CNIL,0xC7,modregrm(3,0,CX),FLfunc,rtlsym[RTLSYM_AHSHIFT]); + c1->Iflags |= CFoff; + return cat(c,c1); +#else + assert(0); + return 0; +#endif +} + +/****************************** + * Move constant value into reg. + * Take advantage of existing values in registers. + * If flags & mPSW + * set flags based on result + * Else if flags & 8 + * do not disturb flags + * Else + * don't care about flags + * If flags & 1 then byte move + * If flags & 2 then short move (for I32 and I64) + * If flags & 4 then don't disturb unused portion of register + * If flags & 16 then reg is a byte register AL..BH + * If flags & 64 (0x40) then 64 bit move (I64 only) + * Returns: + * code (if any) generated + */ + +code *movregconst(code *c,unsigned reg,targ_size_t value,regm_t flags) +{ unsigned r; + regm_t mreg; + + //printf("movregconst(reg=%s, value= %lld (%llx), flags=%x)\n", regm_str(mask[reg]), value, value, flags); +#define genclrreg(a,r) genregs(a,0x31,r,r) + + regm_t regm = regcon.immed.mval & mask[reg]; + targ_size_t regv = regcon.immed.value[reg]; + + if (flags & 1) // 8 bits + { + value &= 0xFF; + regm &= BYTEREGS; + + // If we already have the right value in the right register + if (regm && (regv & 0xFF) == value) + goto L2; + + if (flags & 16 && reg & 4 && // if an H byte register + regcon.immed.mval & mask[reg & 3] && + (((regv = regcon.immed.value[reg & 3]) >> 8) & 0xFF) == value) + goto L2; + + /* Avoid byte register loads on Pentium Pro and Pentium II + * to avoid dependency stalls. + */ + if (config.flags4 & CFG4speed && + config.target_cpu >= TARGET_PentiumPro && !(flags & 4)) + goto L3; + + // See if another register has the right value + r = 0; + for (mreg = (regcon.immed.mval & BYTEREGS); mreg; mreg >>= 1) + { + if (mreg & 1) + { + if ((regcon.immed.value[r] & 0xFF) == value) + { c = genregs(c,0x8A,reg,r); // MOV regL,rL + if (I64 && reg >= 4 || r >= 4) + code_orrex(c, REX); + goto L2; + } + if (!(I64 && reg >= 4) && + r < 4 && ((regcon.immed.value[r] >> 8) & 0xFF) == value) + { c = genregs(c,0x8A,reg,r | 4); // MOV regL,rH + goto L2; + } + } + r++; + } + + if (value == 0 && !(flags & 8)) + { + if (!(flags & 4) && // if we can set the whole register + !(flags & 16 && reg & 4)) // and reg is not an H register + { c = genregs(c,0x31,reg,reg); // XOR reg,reg + regimmed_set(reg,value); + regv = 0; + } + else + c = genregs(c,0x30,reg,reg); // XOR regL,regL + flags &= ~mPSW; // flags already set by XOR + } + else + { c = genc2(c,0xC6,modregrmx(3,0,reg),value); /* MOV regL,value */ + if (reg >= 4 && I64) + { + code_orrex(c, REX); + } + } + L2: + if (flags & mPSW) + genregs(c,0x84,reg,reg); // TEST regL,regL + + if (regm) + // Set just the 'L' part of the register value + regimmed_set(reg,(regv & ~(targ_size_t)0xFF) | value); + else if (flags & 16 && reg & 4 && regcon.immed.mval & mask[reg & 3]) + // Set just the 'H' part of the register value + regimmed_set((reg & 3),(regv & ~(targ_size_t)0xFF00) | (value << 8)); + return c; + } +L3: + if (I16) + value = (targ_short) value; /* sign-extend MSW */ + else if (I32) + value = (targ_int) value; + + if (!I16 && flags & 2) // load 16 bit value + { + value &= 0xFFFF; + if (value == 0) + goto L1; + else + { + if (flags & mPSW) + goto L1; + code *c1 = genc2(CNIL,0xC7,modregrmx(3,0,reg),value); // MOV reg,value + c1->Iflags |= CFopsize; // yes, even for I64 + c = cat(c,c1); + if (regm) + // High bits of register are not affected by 16 bit load + regimmed_set(reg,(regv & ~(targ_size_t)0xFFFF) | value); + } + return c; + } +L1: + + /* If we already have the right value in the right register */ + if (regm && (regv & 0xFFFFFFFF) == (value & 0xFFFFFFFF) && !(flags & 64)) + { if (flags & mPSW) + c = gentstreg(c,reg); + } + else if (flags & 64 && regm && regv == value) + { // Look at the full 64 bits + if (flags & mPSW) + { + c = gentstreg(c,reg); + code_orrex(c, REX_W); + } + } + else + { + if (flags & mPSW) + { + switch (value) + { case 0: + c = genclrreg(c,reg); + if (flags & 64) + code_orrex(c, REX_W); + break; + case 1: + if (I64) + goto L4; + c = genclrreg(c,reg); + goto inc; + case -1: + if (I64) + goto L4; + c = genclrreg(c,reg); + goto dec; + default: + L4: + if (flags & 64) + { + c = genc2(c,0xC7,(REX_W << 16) | modregrmx(3,0,reg),value); // MOV reg,value64 + gentstreg(c,reg); + code_orrex(c, REX_W); + } + else + { c = genc2(c,0xC7,modregrmx(3,0,reg),value); /* MOV reg,value */ + gentstreg(c,reg); + } + break; + } + } + else + { + /* Look for single byte conversion */ + if (regcon.immed.mval & mAX) + { + if (I32) + { if (reg == AX && value == (targ_short) regv) + { c = gen1(c,0x98); /* CWDE */ + goto done; + } + if (reg == DX && + value == (regcon.immed.value[AX] & 0x80000000 ? 0xFFFFFFFF : 0) && + !(config.flags4 & CFG4speed && config.target_cpu >= TARGET_Pentium) + ) + { c = gen1(c,0x99); /* CDQ */ + goto done; + } + } + else if (I16) + { + if (reg == AX && + (targ_short) value == (signed char) regv) + { c = gen1(c,0x98); /* CBW */ + goto done; + } + + if (reg == DX && + (targ_short) value == (regcon.immed.value[AX] & 0x8000 ? (targ_short) 0xFFFF : (targ_short) 0) && + !(config.flags4 & CFG4speed && config.target_cpu >= TARGET_Pentium) + ) + { c = gen1(c,0x99); /* CWD */ + goto done; + } + } + } + if (value == 0 && !(flags & 8) && config.target_cpu >= TARGET_80486) + { c = genclrreg(c,reg); // CLR reg + if (flags & 64) + code_orrex(c, REX_W); + goto done; + } + + if (!I64 && regm && !(flags & 8)) + { if (regv + 1 == value || + /* Catch case of (0xFFFF+1 == 0) for 16 bit compiles */ + (I16 && (targ_short)(regv + 1) == (targ_short)value)) + { + inc: + c = gen1(c,0x40 + reg); /* INC reg */ + goto done; + } + if (regv - 1 == value) + { + dec: + c = gen1(c,0x48 + reg); /* DEC reg */ + goto done; + } + } + + /* See if another register has the right value */ + r = 0; + for (mreg = regcon.immed.mval; mreg; mreg >>= 1) + { +#ifdef DEBUG + assert(!I16 || regcon.immed.value[r] == (targ_short)regcon.immed.value[r]); +#endif + if (mreg & 1 && regcon.immed.value[r] == value) + { c = genmovreg(c,reg,r); + if (flags & 64) + code_orrex(c, REX_W); + goto done; + } + r++; + } + + if (value == 0 && !(flags & 8)) + { c = genclrreg(c,reg); // CLR reg + if (flags & 64) + code_orrex(c, REX_W); + } + else + { /* See if we can just load a byte */ + if (regm & BYTEREGS && + !(config.flags4 & CFG4speed && config.target_cpu >= TARGET_PentiumPro) + ) + { + if ((regv & ~(targ_size_t)0xFF) == (value & ~(targ_size_t)0xFF)) + { c = movregconst(c,reg,value,(flags & 8) |4|1); // load regL + return c; + } + if (regm & (mAX|mBX|mCX|mDX) && + (regv & ~(targ_size_t)0xFF00) == (value & ~(targ_size_t)0xFF00) && + !I64) + { c = movregconst(c,4|reg,value >> 8,(flags & 8) |4|1|16); // load regH + return c; + } + } + if (flags & 64) + c = genc2(c,0xC7,(REX_W << 16) | modregrmx(3,0,reg),value); // MOV reg,value64 + else + c = genc2(c,0xC7,modregrmx(3,0,reg),value); // MOV reg,value + } + } + done: + regimmed_set(reg,value); + } + return c; +} + +/************************** + * Generate a jump instruction. + */ + +code *genjmp(code *c,unsigned op,unsigned fltarg,block *targ) +{ code cs; + code *cj; + code *cnop; + + cs.Iop = op & 0xFF; + cs.Iflags = 0; + cs.Irex = 0; + if (op != JMP && op != 0xE8) // if not already long branch + cs.Iflags = CFjmp16; /* assume long branch for op = 0x7x */ + cs.IFL2 = fltarg; /* FLblock (or FLcode) */ + cs.IEV2.Vblock = targ; /* target block (or code) */ + if (fltarg == FLcode) + ((code *)targ)->Iflags |= CFtarg; + + if (config.flags4 & CFG4fastfloat) // if fast floating point + return gen(c,&cs); + + cj = gen(CNIL,&cs); + switch (op & 0xFF00) /* look at second jump opcode */ + { + /* The JP and JNP come from floating point comparisons */ + case JP << 8: + cs.Iop = JP; + gen(cj,&cs); + break; + case JNP << 8: + /* Do a JP around the jump instruction */ + cnop = gennop(CNIL); + c = genjmp(c,JP,FLcode,(block *) cnop); + cat(cj,cnop); + break; + case 1 << 8: /* toggled no jump */ + case 0 << 8: + break; + default: +#ifdef DEBUG + printf("jop = x%x\n",op); +#endif + assert(0); + } + return cat(c,cj); +} + +/******************************* + * Generate code for a function start. + * Input: + * Coffset address of start of code + * Output: + * Coffset adjusted for size of code generated + * EBPtoESP + * hasframe + * BPoff + */ + +code *prolog() +{ + SYMIDX si; + unsigned reg; + char enter; + unsigned Foffset; + unsigned xlocalsize; // amount to subtract from ESP to make room for locals + unsigned pushallocreg; + char guessneedframe; + regm_t namedargs = 0; + + //printf("cod3.prolog(), needframe = %d, Aalign = %d\n", needframe, Aalign); + debugx(debugw && printf("funcstart()\n")); + regcon.immed.mval = 0; /* no values in registers yet */ + EBPtoESP = -REGSIZE; + hasframe = 0; + char pushds = 0; + BPoff = 0; + code *c = CNIL; + int pushalloc = 0; + tym_t tyf = funcsym_p->ty(); + tym_t tym = tybasic(tyf); + unsigned farfunc = tyfarfunc(tym); + pushallocreg = (tyf == TYmfunc) ? CX : AX; + if (config.flags & CFGalwaysframe || funcsym_p->Sfunc->Fflags3 & Ffakeeh) + needframe = 1; + +Lagain: + guessneedframe = needframe; +// if (needframe && config.exe & (EX_LINUX | EX_FREEBSD | EX_SOLARIS) && !(usednteh & ~NTEHjmonitor)) +// usednteh |= NTEHpassthru; + + /* Compute BP offsets for variables on stack. + * The organization is: + * Poff parameters + * seg of return addr (if far function) + * IP of return addr + * BP-> caller's BP + * DS (if Windows prolog/epilog) + * exception handling context symbol + * Aoff autos and regs + * regsave.off any saved registers + * Foff floating register + * AAoff alloca temporary + * CSoff common subs + * NDPoff any 8087 saved registers + * Toff temporaries + * monitor context record + * any saved registers + */ + + if (tym == TYifunc) + Poff = 26; + else if (I64) + Poff = 16; + else if (I32) + Poff = farfunc ? 12 : 8; + else + Poff = farfunc ? 6 : 4; + + Aoff = 0; +#if NTEXCEPTIONS == 2 + Aoff -= nteh_contextsym_size(); +#if MARS + if (funcsym_p->Sfunc->Fflags3 & Ffakeeh && nteh_contextsym_size() == 0) + Aoff -= 5 * 4; +#endif +#endif + Aoff = -align(0,-Aoff + Aoffset); + + regsave.off = Aoff - align(0,regsave.top); + Foffset = floatreg ? (config.fpxmmregs ? 16 : DOUBLESIZE) : 0; + Foff = regsave.off - align(0,Foffset); + assert(usedalloca != 1); + AAoff = usedalloca ? (Foff - REGSIZE) : Foff; + CSoff = AAoff - align(0,cstop * REGSIZE); + NDPoff = CSoff - align(0,NDP::savetop * NDPSAVESIZE); + Toff = NDPoff - align(0,Toffset); + + if (Foffset > Aalign) + Aalign = Foffset; + if (Aalign > REGSIZE) + { + // Adjust Aoff so that it is Aalign byte aligned, assuming that + // before function parameters were pushed the stack was + // Aalign byte aligned + targ_size_t psize = (Poffset + (REGSIZE - 1)) & ~(REGSIZE - 1); + int sz = psize + -Aoff + Poff + (needframe ? 0 : REGSIZE); + if (sz & (Aalign - 1)) + { int adj = Aalign - (sz & (Aalign - 1)); + Aoff -= adj; + regsave.off -= adj; + Foff -= adj; + AAoff -= adj; + CSoff -= adj; + NDPoff -= adj; + Toff -= adj; + } + } + + localsize = -Toff; + + regm_t topush = fregsaved & ~mfuncreg; // mask of registers that need saving + int npush = 0; // number of registers that need saving + for (regm_t x = topush; x; x >>= 1) + npush += x & 1; + + // Keep the stack aligned by 8 for any subsequent function calls + if (!I16 && calledafunc && + (STACKALIGN == 16 || config.flags4 & CFG4stackalign)) + { + //printf("npush = %d Poff = x%x needframe = %d localsize = x%x\n", npush, Poff, needframe, localsize); + + int sz = Poff + (needframe ? 0 : -REGSIZE) + localsize + npush * REGSIZE; + if (STACKALIGN == 16) + { + if (sz & (8|4)) + localsize += STACKALIGN - (sz & (8|4)); + } + else if (sz & 4) + localsize += 4; + } + + //printf("Foff x%02x Aoff x%02x Toff x%02x NDPoff x%02x CSoff x%02x Poff x%02x localsize x%02x\n", + //(int)Foff,(int)Aoff,(int)Toff,(int)NDPoff,(int)CSoff,(int)Poff,(int)localsize); + + xlocalsize = localsize; + + if (tyf & mTYnaked) // if no prolog/epilog for function + { + hasframe = 1; + return NULL; + } + + if (tym == TYifunc) + { static unsigned char ops2[] = { 0x60,0x1E,0x06,0 }; + static unsigned char ops0[] = { 0x50,0x51,0x52,0x53, + 0x54,0x55,0x56,0x57, + 0x1E,0x06,0 }; + + unsigned char *p; + + p = (config.target_cpu >= TARGET_80286) ? ops2 : ops0; + do + c = gen1(c,*p); + while (*++p); + c = genregs(c,0x8B,BP,SP); // MOV BP,SP + if (localsize) + c = genc2(c,0x81,modregrm(3,5,SP),localsize); // SUB SP,localsize + tyf |= mTYloadds; + hasframe = 1; + goto Lcont; + } + + /* Determine if we need BP set up */ + if (config.flags & CFGalwaysframe) + needframe = 1; + else + { + if (localsize) + { + if (I16 || + !(config.flags4 & CFG4speed) || + config.target_cpu < TARGET_Pentium || + farfunc || + config.flags & CFGstack || + xlocalsize >= 0x1000 || + (usednteh & ~NTEHjmonitor) || + anyiasm || + usedalloca + ) + needframe = 1; + } + if (refparam && (anyiasm || I16)) + needframe = 1; + } + + if (needframe) + { assert(mfuncreg & mBP); // shouldn't have used mBP + + if (!guessneedframe) // if guessed wrong + goto Lagain; + } + + if (I16 && config.wflags & WFwindows && farfunc) + { int wflags; + int segreg; + +#if SCPP + // alloca() can't be because the 'special' parameter won't be at + // a known offset from BP. + if (usedalloca == 1) + synerr(EM_alloca_win); // alloca() can't be in Windows functions +#endif + + wflags = config.wflags; + if (wflags & WFreduced && !(tyf & mTYexport)) + { // reduced prolog/epilog for non-exported functions + wflags &= ~(WFdgroup | WFds | WFss); + } + + c = getregs(mAX); + assert(!c); /* should not have any value in AX */ + + switch (wflags & (WFdgroup | WFds | WFss)) + { case WFdgroup: // MOV AX,DGROUP + if (wflags & WFreduced) + tyf &= ~mTYloadds; // remove redundancy + c = genc(c,0xC7,modregrm(3,0,AX),0,0,FLdatseg,(targ_uns) 0); + c->Iflags ^= CFseg | CFoff; // turn off CFoff, on CFseg + break; + case WFss: + segreg = 2; // SS + goto Lmovax; + case WFds: + segreg = 3; // DS + Lmovax: + c = gen2(c,0x8C,modregrm(3,segreg,AX)); // MOV AX,segreg + if (wflags & WFds) + gen1(c,0x90); // NOP + break; + case 0: + break; + default: +#ifdef DEBUG + printf("config.wflags = x%x\n",config.wflags); +#endif + assert(0); + } + if (wflags & WFincbp) + c = gen1(c,0x40 + BP); // INC BP + c = gen1(c,0x50 + BP); // PUSH BP + genregs(c,0x8B,BP,SP); // MOV BP,SP + if (wflags & (WFsaveds | WFds | WFss | WFdgroup)) + { gen1(c,0x1E); // PUSH DS + pushds = TRUE; + BPoff = -REGSIZE; + } + if (wflags & (WFds | WFss | WFdgroup)) + gen2(c,0x8E,modregrm(3,3,AX)); // MOV DS,AX + + enter = FALSE; /* don't use ENTER instruction */ + hasframe = 1; /* we have a stack frame */ + } + else + if (needframe) // if variables or parameters + { + if (config.wflags & WFincbp && farfunc) + c = gen1(c,0x40 + BP); /* INC BP */ + if (config.target_cpu < TARGET_80286 || + config.exe & (EX_LINUX | EX_LINUX64 | EX_OSX | EX_OSX64 | EX_FREEBSD | EX_FREEBSD64 | EX_SOLARIS | EX_SOLARIS64) || + !localsize || + config.flags & CFGstack || + (xlocalsize >= 0x1000 && config.exe & EX_flat) || + localsize >= 0x10000 || +#if NTEXCEPTIONS == 2 + (usednteh & ~NTEHjmonitor && (config.flags2 & CFG2seh)) || +#endif + (config.target_cpu >= TARGET_80386 && + config.flags4 & CFG4speed) + ) + { + c = gen1(c,0x50 + BP); // PUSH BP + genregs(c,0x8B,BP,SP); // MOV BP,SP + if (I64) + code_orrex(c, REX_W); // MOV RBP,RSP +#if ELFOBJ || MACHOBJ + if (config.fulltypes) + // Don't reorder instructions, as dwarf CFA relies on it + code_orflag(c, CFvolatile); +#endif + enter = FALSE; /* do not use ENTER instruction */ +#if NTEXCEPTIONS == 2 + if (usednteh & ~NTEHjmonitor && (config.flags2 & CFG2seh)) + { + code *ce = nteh_prolog(); + c = cat(c,ce); + int sz = nteh_contextsym_size(); + assert(sz != 0); // should be 5*4, not 0 + xlocalsize -= sz; // sz is already subtracted from ESP + // by nteh_prolog() + } +#endif +#if ELFOBJ || MACHOBJ + if (config.fulltypes) + { int off = I64 ? 16 : 8; + dwarf_CFA_set_loc(1); // address after PUSH EBP + dwarf_CFA_set_reg_offset(SP, off); // CFA is now 8[ESP] + dwarf_CFA_offset(BP, -off); // EBP is at 0[ESP] + dwarf_CFA_set_loc(3); // address after MOV EBP,ESP + // Yes, I know the parameter is 8 when we mean 0! + // But this gets the cfa register set to EBP correctly + dwarf_CFA_set_reg_offset(BP, off); // CFA is now 0[EBP] + } +#endif + } + else + enter = TRUE; + hasframe = 1; + } + + if (config.flags & CFGstack) /* if stack overflow check */ + goto Ladjstack; + + if (needframe) /* if variables or parameters */ + { + if (xlocalsize) /* if any stack offset */ + { + Ladjstack: +#if !TARGET_LINUX // seems that Linux doesn't need to fault in stack pages + if ((config.flags & CFGstack && !(I32 && xlocalsize < 0x1000)) // if stack overflow check +#if TARGET_WINDOS + || (xlocalsize >= 0x1000 && config.exe & EX_flat) +#endif + ) + { + if (I16) + { + // BUG: Won't work if parameter is passed in AX + c = movregconst(c,AX,xlocalsize,FALSE); // MOV AX,localsize + makeitextern(rtlsym[RTLSYM_CHKSTK]); + // CALL _chkstk + gencs(c,(LARGECODE) ? 0x9A : CALL,0,FLfunc,rtlsym[RTLSYM_CHKSTK]); + useregs((ALLREGS | mBP | mES) & ~rtlsym[RTLSYM_CHKSTK]->Sregsaved); + } + else + { + /* Watch out for 64 bit code where EDX is passed as a register parameter + */ + int reg = I64 ? R11 : DX; // scratch register + + /* MOV EDX, xlocalsize/0x1000 + * L1: SUB ESP, 0x1000 + * TEST [ESP],ESP + * DEC EDX + * JNE L1 + * SUB ESP, xlocalsize % 0x1000 + */ + c = movregconst(c, reg, xlocalsize / 0x1000, FALSE); + code *csub = genc2(NULL,0x81,modregrm(3,5,SP),0x1000); + if (I64) + code_orrex(csub, REX_W); + code_orflag(csub, CFtarg2); + gen2sib(csub, 0x85, modregrm(0,SP,4),modregrm(0,4,SP)); + if (I64) + { gen2(csub, 0xFF, (REX_W << 16) | modregrmx(3,0,R11)); // DEC R11 + genc2(csub,JNE,0,(targ_uns)-14); + } + else + { gen1(csub, 0x48 + DX); // DEC EDX + genc2(csub,JNE,0,(targ_uns)-12); + } + regimmed_set(reg,0); // reg is now 0 + genc2(csub,0x81,modregrm(3,5,SP),xlocalsize & 0xFFF); + if (I64) + code_orrex(csub, REX_W); + c = cat(c,csub); + useregs(mask[reg]); + } + } + else +#endif + { + if (enter) + { // ENTER xlocalsize,0 + c = genc(c,0xC8,0,FLconst,xlocalsize,FLconst,(targ_uns) 0); +#if ELFOBJ || MACHOBJ + assert(!config.fulltypes); // didn't emit Dwarf data +#endif + } + else if (xlocalsize == REGSIZE && config.flags4 & CFG4optimized) + { c = gen1(c,0x50 + pushallocreg); // PUSH AX + // Do this to prevent an -x[EBP] to be moved in + // front of the push. + code_orflag(c,CFvolatile); + pushalloc = 1; + } + else + { // SUB SP,xlocalsize + c = genc2(c,0x81,modregrm(3,5,SP),xlocalsize); + if (I64) + code_orrex(c, REX_W); + } + } + + if (usedalloca) + { + // Set up magic parameter for alloca() + // MOV -REGSIZE[BP],localsize - BPoff + //c = genc(c,0xC7,modregrm(2,0,BPRM),FLconst,-REGSIZE,FLconst,localsize - BPoff); + c = genc(c,0xC7,modregrm(2,0,BPRM), + FLconst,AAoff + BPoff, + FLconst,localsize - BPoff); + if (I64) + code_orrex(c, REX_W); + } + } + else + assert(usedalloca == 0); + } + else if (xlocalsize) + { + assert(I32); + + if (xlocalsize == REGSIZE) + { c = gen1(c,0x50 + pushallocreg); // PUSH AX + pushalloc = 1; + } + else if (xlocalsize == 2 * REGSIZE) + { c = gen1(c,0x50 + pushallocreg); // PUSH AX + gen1(c,0x50 + pushallocreg); // PUSH AX + pushalloc = 1; + } + else + { // SUB ESP,xlocalsize + c = genc2(c,0x81,modregrm(3,5,SP),xlocalsize); + if (I64) + code_orrex(c, REX_W); + } + BPoff += REGSIZE; + } + else + assert((localsize | usedalloca) == 0 || (usednteh & NTEHjmonitor)); + EBPtoESP += xlocalsize; + + /* The idea is to generate trace for all functions if -Nc is not thrown. + * If -Nc is thrown, generate trace only for global COMDATs, because those + * are relevant to the FUNCTIONS statement in the linker .DEF file. + * This same logic should be in epilog(). + */ + if (config.flags & CFGtrace && + (!(config.flags4 & CFG4allcomdat) || + funcsym_p->Sclass == SCcomdat || + funcsym_p->Sclass == SCglobal || + (config.flags2 & CFG2comdat && SymInline(funcsym_p)) + ) + ) + { + if (STACKALIGN == 16 && npush) + { /* This could be avoided by moving the function call to after the + * registers are saved. But I don't remember why the call is here + * and not there. + */ + c = genc2(c,0x81,modregrm(3,5,SP),npush * REGSIZE); // SUB ESP,npush * REGSIZE + if (I64) + code_orrex(c, REX_W); + } + + symbol *s = rtlsym[farfunc ? RTLSYM_TRACE_PRO_F : RTLSYM_TRACE_PRO_N]; + makeitextern(s); + c = gencs(c,I16 ? 0x9A : CALL,0,FLfunc,s); // CALL _trace + if (!I16) + code_orflag(c,CFoff | CFselfrel); + /* Embedding the function name inline after the call works, but it + * makes disassembling the code annoying. + */ +#if ELFOBJ || MACHOBJ + size_t len = strlen(funcsym_p->Sident); + char *buffer = (char *)malloc(len + 4); + assert(buffer); + if (len <= 254) + { buffer[0] = len; + memcpy(buffer + 1, funcsym_p->Sident, len); + len++; + } + else + { buffer[0] = 0xFF; + buffer[1] = 0; + buffer[2] = len & 0xFF; + buffer[3] = len >> 8; + memcpy(buffer + 4, funcsym_p->Sident, len); + len += 4; + } + genasm(c, buffer, len); // append func name + free(buffer); +#else + char name[IDMAX+IDOHD+1]; + size_t len = obj_mangle(funcsym_p,name); + assert(len < sizeof(name)); + genasm(c,name,len); // append func name +#endif + if (STACKALIGN == 16 && npush) + { + c = genc2(c,0x81,modregrm(3,0,SP),npush * REGSIZE); // ADD ESP,npush * REGSIZE + if (I64) + code_orrex(c, REX_W); + } + useregs((ALLREGS | mBP | mES) & ~s->Sregsaved); + } + +#if MARS + if (usednteh & NTEHjmonitor) + { Symbol *sthis; + + for (si = 0; 1; si++) + { assert(si < globsym.top); + sthis = globsym.tab[si]; + if (strcmp(sthis->Sident,"this") == 0) + break; + } + c = cat(c,nteh_monitor_prolog(sthis)); + EBPtoESP += 3 * 4; + } +#endif + + while (topush) /* while registers to push */ + { reg = findreg(topush); + topush &= ~mask[reg]; + c = gen1(c,0x50 + (reg & 7)); + if (reg & 8) + code_orrex(c, REX_B); + EBPtoESP += REGSIZE; +#if ELFOBJ || MACHOBJ + if (config.fulltypes) + { // Emit debug_frame data giving location of saved register + // relative to 0[EBP] + pinholeopt(c, NULL); + dwarf_CFA_set_loc(calcblksize(c)); // address after PUSH reg + dwarf_CFA_offset(reg, -EBPtoESP - REGSIZE); + } +#endif + } + +Lcont: + + /* Determine if we need to reload DS */ + if (tyf & mTYloadds) + { code *c1; + + if (!pushds) // if not already pushed + c = gen1(c,0x1E); // PUSH DS + c1 = genc(CNIL,0xC7,modregrm(3,0,AX),0,0,FLdatseg,(targ_uns) 0); /* MOV AX,DGROUP */ + c1->Iflags ^= CFseg | CFoff; /* turn off CFoff, on CFseg */ + c = cat(c,c1); + gen2(c,0x8E,modregrm(3,3,AX)); /* MOV DS,AX */ + useregs(mAX); + } + + if (tym == TYifunc) + c = gen1(c,0xFC); // CLD + +#if NTEXCEPTIONS == 2 + if (usednteh & NTEH_except) + c = cat(c,nteh_setsp(0x89)); // MOV __context[EBP].esp,ESP +#endif + + // Load register parameters off of the stack. Do not use + // assignaddr(), as it will replace the stack reference with + // the register! + for (si = 0; si < globsym.top; si++) + { symbol *s = globsym.tab[si]; + code *c2; + unsigned sz = type_size(s->Stype); + + if ((s->Sclass == SCregpar || s->Sclass == SCparameter) && + s->Sfl == FLreg && + (refparam +#if MARS + // This variable has been reference by a nested function + || s->Stype->Tty & mTYvolatile +#endif + )) + { + /* MOV reg,param[BP] */ + //assert(refparam); + if (mask[s->Sreglsw] & XMMREGS) + { + unsigned op = xmmload(s->Stype->Tty); // MOVSS/D xreg,mem + unsigned xreg = s->Sreglsw - XMM0; + code *c2 = genc1(CNIL,op,modregxrm(2,xreg,BPRM),FLconst,Poff + s->Soffset); + if (!hasframe) + { // Convert to ESP relative address rather than EBP + c2->Irm = modregxrm(2,xreg,4); + c2->Isib = modregrm(0,4,SP); + c2->IEVpointer1 += EBPtoESP; + } + c = cat(c,c2); + } + else + { + code *c2 = genc1(CNIL,0x8B ^ (sz == 1), + modregxrm(2,s->Sreglsw,BPRM),FLconst,Poff + s->Soffset); + if (!I16 && sz == SHORTSIZE) + c2->Iflags |= CFopsize; // operand size + if (I64 && sz >= REGSIZE) + c2->Irex |= REX_W; + if (!hasframe) + { /* Convert to ESP relative address rather than EBP */ + assert(!I16); + c2->Irm = modregxrm(2,s->Sreglsw,4); + c2->Isib = modregrm(0,4,SP); + c2->IEVpointer1 += EBPtoESP; + } + if (sz > REGSIZE) + { + code *c3 = genc1(CNIL,0x8B, + modregxrm(2,s->Sregmsw,BPRM),FLconst,Poff + s->Soffset + REGSIZE); + if (I64) + c3->Irex |= REX_W; + if (!hasframe) + { /* Convert to ESP relative address rather than EBP */ + assert(!I16); + c3->Irm = modregxrm(2,s->Sregmsw,4); + c3->Isib = modregrm(0,4,SP); + c3->IEVpointer1 += EBPtoESP; + } + c2 = cat(c2,c3); + } + c = cat(c,c2); + } + } + else if (s->Sclass == SCfastpar) + { // Argument is passed in a register + unsigned preg = s->Spreg; + + namedargs |= mask[preg]; + + if (s->Sfl == FLreg) + { // MOV reg,preg + if (mask[preg] & XMMREGS) + { + unsigned op = xmmload(s->Stype->Tty); // MOVSS/D xreg,preg + unsigned xreg = s->Sreglsw - XMM0; + c = gen2(c,op,modregxrmx(3,xreg,preg - XMM0)); + } + else + { + c = genmovreg(c,s->Sreglsw,preg); + if (I64 && sz == 8) + code_orrex(c, REX_W); + } + } + else if (s->Sflags & SFLdead || + (!anyiasm && !(s->Sflags & SFLread) && s->Sflags & SFLunambig && +#if MARS + // This variable has been reference by a nested function + !(s->Stype->Tty & mTYvolatile) && +#endif + (config.flags4 & CFG4optimized || !config.fulltypes))) + { + // Ignore it, as it is never referenced + ; + } + else + { + targ_size_t offset = Aoff + BPoff + s->Soffset; + int op = 0x89; // MOV x[EBP],preg + if (preg >= XMM0 && preg <= XMM15) + { + op = xmmstore(s->Stype->Tty); + } + if (hasframe) + { + if (!(pushalloc && preg == pushallocreg)) + { + // MOV x[EBP],preg + c2 = genc1(CNIL,op, + modregxrm(2,preg,BPRM),FLconst, offset); + if (preg >= XMM0 && preg <= XMM15) + { + } + else + { +//printf("%s Aoff = %d, BPoff = %d, Soffset = %d, sz = %d\n", s->Sident, (int)Aoff, (int)BPoff, (int)s->Soffset, (int)sz); +// if (offset & 2) +// c2->Iflags |= CFopsize; + if (I64 && sz == 8) + code_orrex(c2, REX_W); + } + c = cat(c, c2); + } + } + else + { + offset += EBPtoESP; + if (!(pushalloc && preg == pushallocreg)) + { + // MOV offset[ESP],preg + // BUG: byte size? + c2 = genc1(CNIL,op, + (modregrm(0,4,SP) << 8) | + modregxrm(2,preg,4),FLconst,offset); + if (preg >= XMM0 && preg <= XMM15) + { + } + else + { + if (I64 && sz == 8) + c2->Irex |= REX_W; +// if (offset & 2) +// c2->Iflags |= CFopsize; + } + c = cat(c,c2); + } + } + } + } + } + + /* Load arguments passed in registers into the varargs save area + * so they can be accessed by va_arg(). + */ + if (I64 && variadic(funcsym_p->Stype)) + { + /* Look for __va_argsave + */ + symbol *sv = NULL; + for (SYMIDX si = 0; si < globsym.top; si++) + { symbol *s = globsym.tab[si]; + if (s->Sident[0] == '_' && strcmp(s->Sident, "__va_argsave") == 0) + { sv = s; + break; + } + } + + if (sv && !(sv->Sflags & SFLdead)) + { + /* Generate code to move any arguments passed in registers into + * the stack variable __va_argsave, + * so we can reference it via pointers through va_arg(). + * struct __va_argsave_t { + * size_t[6] regs; + * real[8] fpregs; + * uint offset_regs; + * uint offset_fpregs; + * void* stack_args; + * void* reg_args; + * } + * The MOVAPS instructions seg fault if data is not aligned on + * 16 bytes, so this gives us a nice check to ensure no mistakes. + MOV voff+0*8[RBP],EDI + MOV voff+1*8[RBP],ESI + MOV voff+2*8[RBP],RDX + MOV voff+3*8[RBP],RCX + MOV voff+4*8[RBP],R8 + MOV voff+5*8[RBP],R9 + MOVZX EAX,AL // AL = 0..8, # of XMM registers used + SHL EAX,2 // 4 bytes for each MOVAPS + LEA RDX,offset L2[RIP] + SUB RDX,RAX + LEA RAX,voff+6*8+0x7F[RBP] + JMP EDX + MOVAPS -0x0F[RAX],XMM7 // only save XMM registers if actually used + MOVAPS -0x1F[RAX],XMM6 + MOVAPS -0x2F[RAX],XMM5 + MOVAPS -0x3F[RAX],XMM4 + MOVAPS -0x4F[RAX],XMM3 + MOVAPS -0x5F[RAX],XMM2 + MOVAPS -0x6F[RAX],XMM1 + MOVAPS -0x7F[RAX],XMM0 + L2: + MOV 1[RAX],offset_regs // set __va_argsave.offset_regs + MOV 5[RAX],offset_fpregs // set __va_argsave.offset_fpregs + LEA RDX, Poff+Poffset[RBP] + MOV 9[RAX],RDX // set __va_argsave.stack_args + SUB RAX,6*8+0x7F // point to start of __va_argsave + MOV 6*8+8*16+4+4+8[RAX],RAX // set __va_argsave.reg_args + */ + targ_size_t voff = Aoff + BPoff + sv->Soffset; // EBP offset of start of sv + const int vregnum = 6; + const unsigned vsize = vregnum * 8 + 8 * 16; + code *cv = CNIL; + + static unsigned char regs[vregnum] = { DI,SI,DX,CX,R8,R9 }; + + if (!hasframe) + voff += EBPtoESP; + for (int i = 0; i < vregnum; i++) + { + unsigned r = regs[i]; + if (!(mask[r] & namedargs)) // named args are already dealt with + { unsigned ea = (REX_W << 16) | modregxrm(2,r,BPRM); + if (!hasframe) + ea = (REX_W << 16) | (modregrm(0,4,SP) << 8) | modregxrm(2,r,4); + cv = genc1(cv,0x89,ea,FLconst,voff + i*8); + } + } + + cv = genregs(cv,0x0FB6,AX,AX); // MOVZX EAX,AL + genc2(cv,0xC1,modregrm(3,4,AX),2); // SHL EAX,2 + int raxoff = voff+6*8+0x7F; + unsigned L2offset = (raxoff < -0x7F) ? 0x2C : 0x29; + if (!hasframe) + L2offset += 1; // +1 for sib byte + // LEA RDX,offset L2[RIP] + genc1(cv,0x8D,(REX_W << 16) | modregrm(0,DX,5),FLconst,L2offset); + genregs(cv,0x29,AX,DX); // SUB RDX,RAX + code_orrex(cv, REX_W); + // LEA RAX,voff+vsize-6*8-16+0x7F[RBP] + unsigned ea = (REX_W << 16) | modregrm(2,AX,BPRM); + if (!hasframe) + // add sib byte for [RSP] addressing + ea = (REX_W << 16) | (modregrm(0,4,SP) << 8) | modregxrm(2,AX,4); + genc1(cv,0x8D,ea,FLconst,raxoff); + gen2(cv,0xFF,modregrm(3,4,DX)); // JMP EDX + for (int i = 0; i < 8; i++) + { + // MOVAPS -15-16*i[RAX],XMM7-i + genc1(cv,0x0F29,modregrm(0,XMM7-i,0),FLconst,-15-16*i); + } + + /* Compute offset_regs and offset_fpregs + */ + unsigned offset_regs = 0; + unsigned offset_fpregs = vregnum * 8; + for (int i = AX; i <= XMM7; i++) + { regm_t m = mask[i]; + if (m & namedargs) + { + if (m & (mDI|mSI|mDX|mCX|mR8|mR9)) + offset_regs += 8; + else if (m & XMMREGS) + offset_fpregs += 16; + namedargs &= ~m; + if (!namedargs) + break; + } + } + // MOV 1[RAX],offset_regs + genc(cv,0xC7,modregrm(2,0,AX),FLconst,1,FLconst,offset_regs); + + // MOV 5[RAX],offset_fpregs + genc(cv,0xC7,modregrm(2,0,AX),FLconst,5,FLconst,offset_fpregs); + + // LEA RDX, Poff+Poffset[RBP] + ea = modregrm(2,DX,BPRM); + if (!hasframe) + ea = (modregrm(0,4,SP) << 8) | modregrm(2,DX,4); + Poffset = (Poffset + (REGSIZE - 1)) & ~(REGSIZE - 1); + genc1(cv,0x8D,(REX_W << 16) | ea,FLconst,Poff + Poffset); + + // MOV 9[RAX],RDX + genc1(cv,0x89,(REX_W << 16) | modregrm(2,DX,AX),FLconst,9); + + // SUB RAX,6*8+0x7F // point to start of __va_argsave + genc2(cv,0x2D,0,6*8+0x7F); + code_orrex(cv, REX_W); + + // MOV 6*8+8*16+4+4+8[RAX],RAX // set __va_argsave.reg_args + genc1(cv,0x89,(REX_W << 16) | modregrm(2,AX,AX),FLconst,6*8+8*16+4+4+8); + + pinholeopt(cv, NULL); + useregs(mDX|mAX); + c = cat(c,cv); + } + } + +#if 0 && TARGET_LINUX + if (gotref) + { // position independent reference + c = cat(c, cod3_load_got()); + } +#endif + + return c; +} + +/******************************* + * Generate and return function epilog. + * Output: + * retsize Size of function epilog + */ + +static targ_size_t spoff; + +void epilog(block *b) +{ code *c; + code *cr; + code *ce; + code *cpopds; + unsigned reg; + unsigned regx; // register that's not a return reg + regm_t topop,regm; + tym_t tyf,tym; + int op; + char farfunc; + targ_size_t xlocalsize = localsize; + + c = CNIL; + ce = b->Bcode; + tyf = funcsym_p->ty(); + tym = tybasic(tyf); + farfunc = tyfarfunc(tym); + if (!(b->Bflags & BFLepilog)) // if no epilog code + goto Lret; // just generate RET + regx = (b->BC == BCret) ? AX : CX; + + spoff = 0; + retsize = 0; + + if (tyf & mTYnaked) // if no prolog/epilog + return; + + if (tym == TYifunc) + { static unsigned char ops2[] = { 0x07,0x1F,0x61,0xCF,0 }; + static unsigned char ops0[] = { 0x07,0x1F,0x5F,0x5E, + 0x5D,0x5B,0x5B,0x5A, + 0x59,0x58,0xCF,0 }; + unsigned char *p; + + c = genregs(c,0x8B,SP,BP); // MOV SP,BP + p = (config.target_cpu >= TARGET_80286) ? ops2 : ops0; + do + gen1(c,*p); + while (*++p); + goto Lopt; + } + + if (config.flags & CFGtrace && + (!(config.flags4 & CFG4allcomdat) || + funcsym_p->Sclass == SCcomdat || + funcsym_p->Sclass == SCglobal || + (config.flags2 & CFG2comdat && SymInline(funcsym_p)) + ) + ) + { + symbol *s = rtlsym[farfunc ? RTLSYM_TRACE_EPI_F : RTLSYM_TRACE_EPI_N]; + makeitextern(s); + c = gencs(c,I16 ? 0x9A : CALL,0,FLfunc,s); // CALLF _trace + if (!I16) + code_orflag(c,CFoff | CFselfrel); + useregs((ALLREGS | mBP | mES) & ~s->Sregsaved); + } + + if (usednteh & ~NTEHjmonitor && (config.exe == EX_NT || MARS)) + c = cat(c,nteh_epilog()); + + cpopds = CNIL; + if (tyf & mTYloadds) + { cpopds = gen1(cpopds,0x1F); // POP DS + c = cat(c,cpopds); + spoff += intsize; + } + + /* Pop all the general purpose registers saved on the stack + * by the prolog code. Remember to do them in the reverse + * order they were pushed. + */ + reg = I64 ? R15 : DI; + regm = 1 << reg; + topop = fregsaved & ~mfuncreg; +#ifdef DEBUG + if (topop & ~0xFFFF) + printf("fregsaved = x%x, mfuncreg = x%x\n",fregsaved,mfuncreg); +#endif + assert(!(topop & ~0xFFFF)); + while (topop) + { if (topop & regm) + { c = gen1(c,0x58 + (reg & 7)); // POP reg + if (reg & 8) + code_orrex(c, REX_B); + topop &= ~regm; + spoff += REGSIZE; + } + regm >>= 1; + reg--; + } + +#if MARS + if (usednteh & NTEHjmonitor) + { + regm_t retregs = 0; + if (b->BC == BCretexp) + retregs = regmask(b->Belem->Ety, tym); + code *cn = nteh_monitor_epilog(retregs); + c = cat(c,cn); + xlocalsize += 8; + } +#endif + + if (config.wflags & WFwindows && farfunc) + { + int wflags = config.wflags; + if (wflags & WFreduced && !(tyf & mTYexport)) + { // reduced prolog/epilog for non-exported functions + wflags &= ~(WFdgroup | WFds | WFss); + if (!(wflags & WFsaveds)) + goto L4; + } + + if (localsize | usedalloca) + { + c = genc1(c,0x8D,modregrm(1,SP,6),FLconst,(targ_uns)-2); /* LEA SP,-2[BP] */ + } + if (wflags & (WFsaveds | WFds | WFss | WFdgroup)) + { if (cpopds) + cpopds->Iop = NOP; // don't need previous one + c = gen1(c,0x1F); // POP DS + } + c = gen1(c,0x58 + BP); // POP BP + if (config.wflags & WFincbp) + gen1(c,0x48 + BP); // DEC BP + assert(hasframe); + } + else + { + if (needframe || (xlocalsize && hasframe)) + { + L4: + assert(hasframe); + if (xlocalsize | usedalloca) + { if (config.target_cpu >= TARGET_80286 && + !(config.target_cpu >= TARGET_80386 && + config.flags4 & CFG4speed) + ) + c = gen1(c,0xC9); // LEAVE + else if (0 && xlocalsize == REGSIZE && !usedalloca && I32) + { // This doesn't work - I should figure out why + mfuncreg &= ~mask[regx]; + c = gen1(c,0x58 + regx); // POP regx + c = gen1(c,0x58 + BP); // POP BP + } + else + { c = genregs(c,0x8B,SP,BP); // MOV SP,BP + if (I64) + code_orrex(c, REX_W); // MOV RSP,RBP + c = gen1(c,0x58 + BP); // POP BP + } + } + else + c = gen1(c,0x58 + BP); // POP BP + if (config.wflags & WFincbp && farfunc) + gen1(c,0x48 + BP); // DEC BP + } + else if (xlocalsize == REGSIZE && (!I16 || b->BC == BCret)) + { mfuncreg &= ~mask[regx]; + c = gen1(c,0x58 + regx); // POP regx + } + else if (xlocalsize) + { + c = genc2(c,0x81,modregrm(3,0,SP),xlocalsize); // ADD SP,xlocalsize + if (I64) + code_orrex(c, REX_W); + } + } + if (b->BC == BCret || b->BC == BCretexp) + { +Lret: + op = tyfarfunc(tym) ? 0xCA : 0xC2; + if (tym == TYhfunc) + { + c = genc2(c,0xC2,0,4); // RET 4 + } + else if (!typfunc(tym) || // if caller cleans the stack + Poffset == 0) // or nothing pushed on the stack anyway + { op++; // to a regular RET + c = gen1(c,op); + } + else + { // Stack is always aligned on register size boundary + Poffset = (Poffset + (REGSIZE - 1)) & ~(REGSIZE - 1); + c = genc2(c,op,0,Poffset); // RET Poffset + } + } + +Lopt: + // If last instruction in ce is ADD SP,imm, and first instruction + // in c sets SP, we can dump the ADD. + cr = code_last(ce); + if (cr && c && !I64) + { + if (cr->Iop == 0x81 && cr->Irm == modregrm(3,0,SP)) // if ADD SP,imm + { + if ( + c->Iop == 0xC9 || // LEAVE + (c->Iop == 0x8B && c->Irm == modregrm(3,SP,BP)) || // MOV SP,BP + (c->Iop == 0x8D && c->Irm == modregrm(1,SP,6)) // LEA SP,-imm[BP] + ) + cr->Iop = NOP; + else if (c->Iop == 0x58 + BP) // if POP BP + { cr->Iop = 0x8B; + cr->Irm = modregrm(3,SP,BP); // MOV SP,BP + } + } +#if 0 // These optimizations don't work if the called function + // cleans off the stack. + else if (c->Iop == 0xC3 && cr->Iop == CALL) // CALL near + { cr->Iop = 0xE9; // JMP near + c->Iop = NOP; + } + else if (c->Iop == 0xCB && cr->Iop == 0x9A) // CALL far + { cr->Iop = 0xEA; // JMP far + c->Iop = NOP; + } +#endif + } + + retsize += calcblksize(c); // compute size of function epilog + b->Bcode = cat(ce,c); +} + +/******************************* + * Return offset of SP from BP. + */ + +targ_size_t cod3_spoff() +{ + return spoff + localsize; +} + +/********************************** + * Load value of _GLOBAL_OFFSET_TABLE_ into EBX + */ + +code *cod3_load_got() +{ +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + code *c; + code *cgot; + + c = genc2(NULL,CALL,0,0); // CALL L1 + gen1(c, 0x58 + BX); // L1: POP EBX + + // ADD EBX,_GLOBAL_OFFSET_TABLE_+3 + symbol *gotsym = elfobj_getGOTsym(); + cgot = gencs(CNIL,0x81,0xC3,FLextern,gotsym); + cgot->Iflags = CFoff; + cgot->IEVoffset2 = 3; + + makeitextern(gotsym); + return cat(c,cgot); +#else + assert(0); + return NULL; +#endif +} + +code* gen_spill_reg(Symbol* s, bool toreg) +{ + code *c; + code cs; + regm_t keepmsk = toreg ? RMload : RMstore; + int sz = type_size(s->Stype); + + elem* e = el_var(s); // so we can trick getlvalue() into working for us + + if (mask[s->Sreglsw] & XMMREGS) + { // Convert to save/restore of XMM register + if (toreg) + cs.Iop = xmmload(s->Stype->Tty); // MOVSS/D xreg,mem + else + cs.Iop = xmmstore(s->Stype->Tty); // MOVSS/D mem,xreg + c = getlvalue(&cs,e,keepmsk); + cs.orReg(s->Sreglsw - XMM0); + c = gen(c,&cs); + } + else + { + cs.Iop = toreg ? 0x8B : 0x89; // MOV reg,mem[ESP] : MOV mem[ESP],reg + cs.Iop ^= (sz == 1); + c = getlvalue(&cs,e,keepmsk); + cs.orReg(s->Sreglsw); + if (I64 && sz == 1 && s->Sreglsw >= 4) + cs.Irex |= REX; + c = gen(c,&cs); + if (sz > REGSIZE) + { + cs.setReg(s->Sregmsw); + getlvalue_msw(&cs); + c = gen(c,&cs); + } + } + + el_free(e); + + return c; +} + +/**************************** + * Generate code for, and output a thunk. + * Input: + * thisty Type of this pointer + * p ESP parameter offset to this pointer + * d offset to add to 'this' pointer + * d2 offset from 'this' to vptr + * i offset into vtbl[] + */ + +void cod3_thunk(symbol *sthunk,symbol *sfunc,unsigned p,tym_t thisty, + targ_size_t d,int i,targ_size_t d2) +{ code *c,*c1; + targ_size_t thunkoffset; + tym_t thunkty; + + cod3_align(); + + /* Skip over return address */ + thunkty = tybasic(sthunk->ty()); +#if TARGET_SEGMENTED + if (tyfarfunc(thunkty)) + p += I32 ? 8 : tysize[TYfptr]; /* far function */ + else +#endif + p += tysize[TYnptr]; + + if (!I16) + { + /* + Generate: + ADD p[ESP],d + For direct call: + JMP sfunc + For virtual call: + MOV EAX, p[ESP] EAX = this + MOV EAX, d2[EAX] EAX = this->vptr + JMP i[EAX] jump to virtual function + */ + unsigned reg = 0; + if ((targ_ptrdiff_t)d < 0) + { + d = -d; + reg = 5; // switch from ADD to SUB + } + if (thunkty == TYmfunc) + { // ADD ECX,d + c = CNIL; + if (d) + c = genc2(c,0x81,modregrm(3,reg,CX),d); + } + else if (thunkty == TYjfunc || (I64 && thunkty == TYnfunc)) + { // ADD EAX,d + c = CNIL; + if (d) + c = genc2(c,0x81,modregrm(3,reg,I64 ? DI : AX),d); + } + else + { + c = genc(CNIL,0x81,modregrm(2,reg,4), + FLconst,p, // to this + FLconst,d); // ADD p[ESP],d + c->Isib = modregrm(0,4,SP); + } + if (I64 && c) + c->Irex |= REX_W; + } + else + { + /* + Generate: + MOV BX,SP + ADD [SS:] p[BX],d + For direct call: + JMP sfunc + For virtual call: + MOV BX, p[BX] BX = this + MOV BX, d2[BX] BX = this->vptr + JMP i[BX] jump to virtual function + */ + + + c = genregs(CNIL,0x89,SP,BX); /* MOV BX,SP */ + c1 = genc(CNIL,0x81,modregrm(2,0,7), + FLconst,p, /* to this */ + FLconst,d); /* ADD p[BX],d */ + if (config.wflags & WFssneds || + // If DS needs reloading from SS, + // then assume SS != DS on thunk entry + (config.wflags & WFss && LARGEDATA)) + c1->Iflags |= CFss; /* SS: */ + c = cat(c,c1); + } + + if ((i & 0xFFFF) != 0xFFFF) /* if virtual call */ + { code *c2,*c3; + +#define FARTHIS (tysize(thisty) > REGSIZE) +#define FARVPTR FARTHIS + +#if TARGET_SEGMENTED + assert(thisty != TYvptr); /* can't handle this case */ +#endif + + if (!I16) + { + assert(!FARTHIS && !LARGECODE); + if (thunkty == TYmfunc) // if 'this' is in ECX + { c1 = CNIL; + + // MOV EAX,d2[ECX] + c2 = genc1(CNIL,0x8B,modregrm(2,AX,CX),FLconst,d2); + } + else if (thunkty == TYjfunc) // if 'this' is in EAX + { c1 = CNIL; + + // MOV EAX,d2[EAX] + c2 = genc1(CNIL,0x8B,modregrm(2,AX,AX),FLconst,d2); + } + else + { + // MOV EAX,p[ESP] + c1 = genc1(CNIL,0x8B,(modregrm(0,4,SP) << 8) | modregrm(2,AX,4),FLconst,(targ_uns) p); + if (I64) + c1->Irex |= REX_W; + + // MOV EAX,d2[EAX] + c2 = genc1(CNIL,0x8B,modregrm(2,AX,AX),FLconst,d2); + } + if (I64) + code_orrex(c2, REX_W); + /* JMP i[EAX] */ + c3 = genc1(CNIL,0xFF,modregrm(2,4,0),FLconst,(targ_uns) i); + } + else + { + /* MOV/LES BX,[SS:] p[BX] */ + c1 = genc1(CNIL,(FARTHIS ? 0xC4 : 0x8B),modregrm(2,BX,7),FLconst,(targ_uns) p); + if (config.wflags & WFssneds || + // If DS needs reloading from SS, + // then assume SS != DS on thunk entry + (config.wflags & WFss && LARGEDATA)) + c1->Iflags |= CFss; /* SS: */ + + /* MOV/LES BX,[ES:]d2[BX] */ + c2 = genc1(CNIL,(FARVPTR ? 0xC4 : 0x8B),modregrm(2,BX,7),FLconst,d2); + if (FARTHIS) + c2->Iflags |= CFes; /* ES: */ + + /* JMP i[BX] */ + c3 = genc1(CNIL,0xFF,modregrm(2,(LARGECODE ? 5 : 4),7),FLconst,(targ_uns) i); + if (FARVPTR) + c3->Iflags |= CFes; /* ES: */ + } + c = cat4(c,c1,c2,c3); + } + else + { + c1 = gencs(CNIL,(LARGECODE ? 0xEA : 0xE9),0,FLfunc,sfunc); /* JMP sfunc */ + c1->Iflags |= LARGECODE ? (CFseg | CFoff) : (CFselfrel | CFoff); + c = cat(c,c1); + } + + thunkoffset = Coffset; + pinholeopt(c,NULL); + codout(c); + code_free(c); + + sthunk->Soffset = thunkoffset; + sthunk->Ssize = Coffset - thunkoffset; /* size of thunk */ + sthunk->Sseg = cseg; +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + objpubdef(cseg,sthunk,sthunk->Soffset); +#endif + searchfixlist(sthunk); /* resolve forward refs */ +} + +/***************************** + * Assume symbol s is extern. + */ + +void makeitextern(symbol *s) +{ + if (s->Sxtrnnum == 0) + { s->Sclass = SCextern; /* external */ + /*printf("makeitextern(x%x)\n",s);*/ + objextern(s); + } +} + + +/******************************* + * Replace JMPs in Bgotocode with JMP SHORTs whereever possible. + * This routine depends on FLcode jumps to only be forward + * referenced. + * BFLjmpoptdone is set to TRUE if nothing more can be done + * with this block. + * Input: + * flag !=0 means don't have correct Boffsets yet + * Returns: + * number of bytes saved + */ + +int branch(block *bl,int flag) +{ int bytesaved; + code *c,*cn,*ct; + targ_size_t offset,disp; + targ_size_t csize; + + if (!flag) + bl->Bflags |= BFLjmpoptdone; // assume this will be all + c = bl->Bcode; + if (!c) + return 0; + bytesaved = 0; + offset = bl->Boffset; /* offset of start of block */ + while (1) + { unsigned char op; + + csize = calccodsize(c); + cn = code_next(c); + op = c->Iop; + if ((op & ~0x0F) == 0x70 && c->Iflags & CFjmp16 || + op == JMP) + { + L1: + switch (c->IFL2) + { + case FLblock: + if (flag) // no offsets yet, don't optimize + goto L3; + disp = c->IEV2.Vblock->Boffset - offset - csize; + + /* If this is a forward branch, and there is an aligned + * block intervening, it is possible that shrinking + * the jump instruction will cause it to be out of + * range of the target. This happens if the alignment + * prevents the target block from moving correspondingly + * closer. + */ + if (disp >= 0x7F-4 && c->IEV2.Vblock->Boffset > offset) + { /* Look for intervening alignment + */ + for (block *b = bl->Bnext; b; b = b->Bnext) + { + if (b->Balign) + { + bl->Bflags &= ~BFLjmpoptdone; // some JMPs left + goto L3; + } + if (b == c->IEV2.Vblock) + break; + } + } + + break; + + case FLcode: + { code *cr; + + disp = 0; + + ct = c->IEV2.Vcode; /* target of branch */ + assert(ct->Iflags & (CFtarg | CFtarg2)); + for (cr = cn; cr; cr = code_next(cr)) + { + if (cr == ct) + break; + disp += calccodsize(cr); + } + + if (!cr) + { // Didn't find it in forward search. Try backwards jump + int s = 0; + disp = 0; + for (cr = bl->Bcode; cr != cn; cr = code_next(cr)) + { + assert(cr != NULL); // must have found it + if (cr == ct) + s = 1; + if (s) + disp += calccodsize(cr); + } + } + + if (config.flags4 & CFG4optimized && !flag) + { + /* Propagate branch forward past junk */ + while (1) + { if (ct->Iop == NOP || + ct->Iop == (ESCAPE | ESClinnum)) + { ct = code_next(ct); + if (!ct) + goto L2; + } + else + { c->IEV2.Vcode = ct; + ct->Iflags |= CFtarg; + break; + } + } + + /* And eliminate jmps to jmps */ + if ((op == ct->Iop || ct->Iop == JMP) && + (op == JMP || c->Iflags & CFjmp16)) + { c->IFL2 = ct->IFL2; + c->IEV2.Vcode = ct->IEV2.Vcode; + /*printf("eliminating branch\n");*/ + goto L1; + } + L2: ; + } + } + break; + + default: + goto L3; + } + + if (disp == 0) // bra to next instruction + { bytesaved += csize; + c->Iop = NOP; // del branch instruction + c->IEV2.Vcode = NULL; + c = cn; + if (!c) + break; + continue; + } + else if ((targ_size_t)(targ_schar)(disp - 2) == (disp - 2) && + (targ_size_t)(targ_schar)disp == disp) + { + if (op == JMP) + { c->Iop = JMPS; // JMP SHORT + bytesaved += I16 ? 1 : 3; + } + else // else Jcond + { c->Iflags &= ~CFjmp16; // a branch is ok + bytesaved += I16 ? 3 : 4; + + // Replace a cond jump around a call to a function that + // never returns with a cond jump to that function. + if (config.flags4 & CFG4optimized && + config.target_cpu >= TARGET_80386 && + disp == (I16 ? 3 : 5) && + cn && + cn->Iop == CALL && + cn->IFL2 == FLfunc && + cn->IEVsym2->Sflags & SFLexit && + !(cn->Iflags & (CFtarg | CFtarg2)) + ) + { + cn->Iop = 0x0F00 | ((c->Iop & 0x0F) ^ 0x81); + c->Iop = NOP; + c->IEV2.Vcode = NULL; + bytesaved++; + + // If nobody else points to ct, we can remove the CFtarg + if (flag && ct) + { code *cx; + + for (cx = bl->Bcode; 1; cx = code_next(cx)) + { + if (!cx) + { ct->Iflags &= ~CFtarg; + break; + } + if (cx->IEV2.Vcode == ct) + break; + } + } + } + } + csize = calccodsize(c); + } + else + bl->Bflags &= ~BFLjmpoptdone; // some JMPs left + } +L3: + if (cn) + { offset += csize; + c = cn; + } + else + break; + } + //printf("bytesaved = x%x\n",bytesaved); + return bytesaved; +} + +/************************************************ + * Adjust all Soffset's of stack variables so they + * are all relative to the frame pointer. + */ + +#if MARS + +void cod3_adjSymOffsets() +{ SYMIDX si; + + //printf("cod3_adjSymOffsets()\n"); + for (si = 0; si < globsym.top; si++) + { //printf("globsym.tab[%d] = %p\n",si,globsym.tab[si]); + symbol *s = globsym.tab[si]; + + switch (s->Sclass) + { + case SCparameter: + case SCregpar: +//printf("s = '%s', Soffset = x%x, Poff = x%x, EBPtoESP = x%x\n", s->Sident, s->Soffset, Poff, EBPtoESP); + s->Soffset += Poff; +if (0 && !(funcsym_p->Sfunc->Fflags3 & Fmember)) +{ + if (!hasframe) + s->Soffset += EBPtoESP; + if (funcsym_p->Sfunc->Fflags3 & Fnested) + s->Soffset += REGSIZE; +} + break; + case SCauto: + case SCfastpar: + case SCregister: + case_auto: +//printf("s = '%s', Soffset = x%x, Aoff = x%x, BPoff = x%x EBPtoESP = x%x\n", s->Sident, s->Soffset, Aoff, BPoff, EBPtoESP); +// if (!(funcsym_p->Sfunc->Fflags3 & Fnested)) + s->Soffset += Aoff + BPoff; + break; + case SCbprel: + break; + default: + continue; + } +#if 0 + if (!hasframe) + s->Soffset += EBPtoESP; +#endif + } +} + +#endif + +/******************************* + * Take symbol info in union ev and replace it with a real address + * in Vpointer. + */ + +void assignaddr(block *bl) +{ + int EBPtoESPsave = EBPtoESP; + int hasframesave = hasframe; + + if (bl->Bflags & BFLoutsideprolog) + { EBPtoESP = -REGSIZE; + hasframe = 0; + } + assignaddrc(bl->Bcode); + hasframe = hasframesave; + EBPtoESP = EBPtoESPsave; +} + +void assignaddrc(code *c) +{ + int sn; + symbol *s; + unsigned char ins,rm; + targ_size_t soff; + targ_size_t base; + + base = EBPtoESP; + for (; c; c = code_next(c)) + { +#ifdef DEBUG + if (0) + { printf("assignaddrc()\n"); + c->print(); + } + if (code_next(c) && code_next(code_next(c)) == c) + assert(0); +#endif + if (c->Iflags & CFvex) + ins = vex_inssize(c); + else if ((c->Iop & 0xFFFD00) == 0x0F3800) + ins = inssize2[(c->Iop >> 8) & 0xFF]; + else if ((c->Iop & 0xFF00) == 0x0F00) + ins = inssize2[c->Iop & 0xFF]; + else if ((c->Iop & 0xFF) == ESCAPE) + { + if (c->Iop == (ESCAPE | ESCadjesp)) + { + //printf("adjusting EBPtoESP (%d) by %ld\n",EBPtoESP,c->IEV2.Vint); + EBPtoESP += c->IEV1.Vint; + c->Iop = NOP; + } + if (c->Iop == (ESCAPE | ESCframeptr)) + { // Convert to load of frame pointer + // c->Irm is the register to use + if (hasframe) + { // MOV reg,EBP + c->Iop = 0x89; + if (c->Irm & 8) + c->Irex |= REX_B; + c->Irm = modregrm(3,BP,c->Irm & 7); + } + else + { // LEA reg,EBPtoESP[ESP] + c->Iop = 0x8D; + if (c->Irm & 8) + c->Irex |= REX_R; + c->Irm = modregrm(2,c->Irm & 7,4); + c->Isib = modregrm(0,4,SP); + c->Iflags = CFoff; + c->IFL1 = FLconst; + c->IEV1.Vuns = EBPtoESP; + } + } + if (I64) + c->Irex |= REX_W; + continue; + } + else + ins = inssize[c->Iop & 0xFF]; + if (!(ins & M) || + ((rm = c->Irm) & 0xC0) == 0xC0) + goto do2; /* if no first operand */ + if (is32bitaddr(I32,c->Iflags)) + { + + if ( + ((rm & 0xC0) == 0 && !((rm & 7) == 4 && (c->Isib & 7) == 5 || (rm & 7) == 5)) + ) + goto do2; /* if no first operand */ + } + else + { + if ( + ((rm & 0xC0) == 0 && !((rm & 7) == 6)) + ) + goto do2; /* if no first operand */ + } + s = c->IEVsym1; + switch (c->IFL1) + { +#if OMFOBJ + case FLdata: + if (s->Sclass == SCcomdat) + { c->IFL1 = FLextern; + goto do2; + } +#if MARS + c->IEVseg1 = s->Sseg; +#else + c->IEVseg1 = DATA; +#endif + c->IEVpointer1 += s->Soffset; + c->IFL1 = FLdatseg; + goto do2; + case FLudata: +#if MARS + c->IEVseg1 = s->Sseg; +#else + c->IEVseg1 = UDATA; +#endif + c->IEVpointer1 += s->Soffset; + c->IFL1 = FLdatseg; + goto do2; +#else // don't loose symbol information + case FLdata: + case FLudata: + case FLtlsdata: + c->IFL1 = FLextern; + goto do2; +#endif + case FLdatseg: + c->IEVseg1 = DATA; + goto do2; + +#if TARGET_SEGMENTED + case FLfardata: + case FLcsdata: +#endif + case FLpseudo: + goto do2; + + case FLstack: + //printf("Soffset = %d, EBPtoESP = %d, base = %d, pointer = %d\n", + //s->Soffset,EBPtoESP,base,c->IEVpointer1); + c->IEVpointer1 += s->Soffset + EBPtoESP - base - EEoffset; + break; + + case FLreg: + case FLauto: + soff = Aoff; + L1: + if (s->Sflags & SFLunambig && !(s->Sflags & SFLread) && // if never loaded + !anyiasm && + // if not optimized, leave it in for debuggability + (config.flags4 & CFG4optimized || !config.fulltypes)) + { c->Iop = NOP; // remove references to it + continue; + } + if (s->Sfl == FLreg && c->IEVpointer1 < 2) + { int reg = s->Sreglsw; + + assert(!(s->Sregm & ~mask[reg])); + if (c->IEVpointer1 == 1) + { assert(reg < 4); /* must be a BYTEREGS */ + reg |= 4; /* convert to high byte reg */ + } + if (reg & 8) + { assert(I64); + c->Irex |= REX_B; + reg &= 7; + } + c->Irm = (c->Irm & modregrm(0,7,0)) + | modregrm(3,0,reg); + assert(c->Iop != LES && c->Iop != LEA); + goto do2; + } + else + { c->IEVpointer1 += s->Soffset + soff + BPoff; + if (s->Sflags & SFLunambig) + c->Iflags |= CFunambig; + L2: + if (!hasframe) + { /* Convert to ESP relative address instead of EBP */ + unsigned char rm; + + assert(!I16); + c->IEVpointer1 += EBPtoESP; + rm = c->Irm; + if ((rm & 7) == 4) // if SIB byte + { + assert((c->Isib & 7) == BP); + assert((rm & 0xC0) != 0); + c->Isib = (c->Isib & ~7) | modregrm(0,0,SP); + } + else + { + assert((rm & 7) == 5); + c->Irm = (rm & modregrm(0,7,0)) + | modregrm(2,0,4); + c->Isib = modregrm(0,4,SP); + } + } + } + break; + case FLpara: + soff = Poff - BPoff; // cancel out add of BPoff + goto L1; + case FLtmp: + soff = Toff; + goto L1; + case FLfltreg: + c->IEVpointer1 += Foff + BPoff; + c->Iflags |= CFunambig; + goto L2; + case FLallocatmp: + c->IEVpointer1 += AAoff + BPoff; + goto L2; + case FLbprel: + c->IEVpointer1 += s->Soffset; + break; + case FLcs: + sn = c->IEV1.Vuns; + if (!CSE_loaded(sn)) // if never loaded + { c->Iop = NOP; + continue; + } + c->IEVpointer1 = sn * REGSIZE + CSoff + BPoff; + c->Iflags |= CFunambig; + goto L2; + case FLregsave: + sn = c->IEV1.Vuns; + c->IEVpointer1 = sn + regsave.off + BPoff; + c->Iflags |= CFunambig; + goto L2; + case FLndp: +#if MARS + assert(c->IEV1.Vuns < NDP::savetop); +#endif + c->IEVpointer1 = c->IEV1.Vuns * NDPSAVESIZE + NDPoff + BPoff; + c->Iflags |= CFunambig; + goto L2; + case FLoffset: + break; + case FLlocalsize: + c->IEVpointer1 += localsize; + break; + case FLconst: + default: + goto do2; + } + c->IFL1 = FLconst; + do2: + /* Ignore TEST (F6 and F7) opcodes */ + if (!(ins & T)) goto done; /* if no second operand */ + s = c->IEVsym2; + switch (c->IFL2) + { +#if ELFOBJ || MACHOBJ + case FLdata: + case FLudata: + case FLtlsdata: + c->IFL2 = FLextern; + goto do2; +#else + case FLdata: + if (s->Sclass == SCcomdat) + { c->IFL2 = FLextern; + goto do2; + } +#if MARS + c->IEVseg2 = s->Sseg; +#else + c->IEVseg2 = DATA; +#endif + c->IEVpointer2 += s->Soffset; + c->IFL2 = FLdatseg; + goto done; + case FLudata: +#if MARS + c->IEVseg2 = s->Sseg; +#else + c->IEVseg2 = UDATA; +#endif + c->IEVpointer2 += s->Soffset; + c->IFL2 = FLdatseg; + goto done; +#endif + case FLdatseg: + c->IEVseg2 = DATA; + goto done; +#if TARGET_SEGMENTED + case FLcsdata: + case FLfardata: + goto done; +#endif + case FLreg: + case FLpseudo: + assert(0); + /* NOTREACHED */ + case FLauto: + c->IEVpointer2 += s->Soffset + Aoff + BPoff; + break; + case FLpara: + c->IEVpointer2 += s->Soffset + Poff; + break; + case FLtmp: + c->IEVpointer2 += s->Soffset + Toff + BPoff; + break; + case FLfltreg: + c->IEVpointer2 += Foff + BPoff; + break; + case FLallocatmp: + c->IEVpointer2 += AAoff + BPoff; + break; + case FLbprel: + c->IEVpointer2 += s->Soffset; + break; + + case FLstack: + c->IEVpointer2 += s->Soffset + EBPtoESP - base; + break; + + case FLcs: + case FLndp: + case FLregsave: + assert(0); + /* NOTREACHED */ + + case FLconst: + break; + + case FLlocalsize: + c->IEVpointer2 += localsize; + break; + + default: + goto done; + } + c->IFL2 = FLconst; + done: + ; + } +} + +/******************************* + * Return offset from BP of symbol s. + */ + +targ_size_t cod3_bpoffset(symbol *s) +{ targ_size_t offset; + + symbol_debug(s); + offset = s->Soffset; + switch (s->Sfl) + { + case FLpara: + offset += Poff; + break; + case FLauto: + offset += Aoff + BPoff; + break; + case FLtmp: + offset += Toff + BPoff; + break; + default: +#ifdef DEBUG + WRFL((enum FL)s->Sfl); + symbol_print(s); +#endif + assert(0); + } + assert(hasframe); + return offset; +} + + +/******************************* + * Find shorter versions of the same instructions. + * Does these optimizations: + * replaces jmps to the next instruction with NOPs + * sign extension of modregrm displacement + * sign extension of immediate data (can't do it for OR, AND, XOR + * as the opcodes are not defined) + * short versions for AX EA + * short versions for reg EA + * Input: + * b -> block for code (or NULL) + */ + +void pinholeopt(code *c,block *b) +{ targ_size_t a; + unsigned op,mod; + unsigned char ins; + int usespace; + int useopsize; + int space; + block *bn; + +#ifdef DEBUG + static int tested; if (!tested) { tested++; pinholeopt_unittest(); } +#endif + +#if 0 + code *cstart = c; + if (debugc) + { + printf("+pinholeopt(%p)\n",c); + } +#endif + + if (b) + { bn = b->Bnext; + usespace = (config.flags4 & CFG4space && b->BC != BCasm); + useopsize = (I16 || (config.flags4 & CFG4space && b->BC != BCasm)); + } + else + { bn = NULL; + usespace = (config.flags4 & CFG4space); + useopsize = (I16 || config.flags4 & CFG4space); + } + for (; c; c = code_next(c)) + { + L1: + op = c->Iop; + if (c->Iflags & CFvex) + ins = vex_inssize(c); + else if ((op & 0xFFFD00) == 0x0F3800) + ins = inssize2[(op >> 8) & 0xFF]; + else if ((op & 0xFF00) == 0x0F00) + ins = inssize2[op & 0xFF]; + else + ins = inssize[op & 0xFF]; + if (ins & M) // if modregrm byte + { int shortop = (c->Iflags & CFopsize) ? !I16 : I16; + int local_BPRM = BPRM; + + if (c->Iflags & CFaddrsize) + local_BPRM ^= 5 ^ 6; // toggle between 5 and 6 + + unsigned rm = c->Irm; + unsigned reg = rm & modregrm(0,7,0); // isolate reg field + unsigned ereg = rm & 7; + //printf("c = %p, op = %02x rm = %02x\n", c, op, rm); + + /* If immediate second operand */ + if ((ins & T || + ((op == 0xF6 || op == 0xF7) && (reg < modregrm(0,2,0) || reg > modregrm(0,3,0))) + ) && + c->IFL2 == FLconst) + { + int flags = c->Iflags & CFpsw; /* if want result in flags */ + targ_long u = c->IEV2.Vuns; + if (ins & E) + u = (signed char) u; + else if (shortop) + u = (short) u; + + // Replace CMP reg,0 with TEST reg,reg + if ((op & 0xFE) == 0x80 && // 80 is CMP R8,imm8; 81 is CMP reg,imm + rm >= modregrm(3,7,AX) && + u == 0) + { c->Iop = (op & 1) | 0x84; + c->Irm = modregrm(3,ereg,ereg); + if (c->Irex & REX_B) + c->Irex |= REX_R; + goto L1; + } + + /* Optimize ANDs with an immediate constant */ + if ((op == 0x81 || op == 0x80) && reg == modregrm(0,4,0)) + { + if (rm >= modregrm(3,4,AX)) // AND reg,imm + { + if (u == 0) + { /* Replace with XOR reg,reg */ + c->Iop = 0x30 | (op & 1); + c->Irm = modregrm(3,ereg,ereg); + if (c->Irex & REX_B) + c->Irex |= REX_R; + goto L1; + } + if (u == 0xFFFFFFFF && !flags) + { c->Iop = NOP; + goto L1; + } + } + if (op == 0x81 && !flags) + { // If we can do the operation in one byte + + // If EA is not SI or DI + if ((rm < modregrm(3,4,SP) || I64) && + (config.flags4 & CFG4space || + config.target_cpu < TARGET_PentiumPro) + ) + { + if ((u & 0xFFFFFF00) == 0xFFFFFF00) + goto L2; + else if (rm < modregrm(3,0,0) || (!c->Irex && ereg < 4)) + { if (!shortop) + { if ((u & 0xFFFF00FF) == 0xFFFF00FF) + goto L3; + } + else + { + if ((u & 0xFF) == 0xFF) + goto L3; + } + } + } + if (!shortop && useopsize) + { + if ((u & 0xFFFF0000) == 0xFFFF0000) + { c->Iflags ^= CFopsize; + goto L1; + } + if ((u & 0xFFFF) == 0xFFFF && rm < modregrm(3,4,AX)) + { c->IEVoffset1 += 2; /* address MSW */ + c->IEV2.Vuns >>= 16; + c->Iflags ^= CFopsize; + goto L1; + } + if (rm >= modregrm(3,4,AX)) + { + if (u == 0xFF && (rm <= modregrm(3,4,BX) || I64)) + { c->Iop = 0x0FB6; // MOVZX + c->Irm = modregrm(3,ereg,ereg); + if (c->Irex & REX_B) + c->Irex |= REX_R; + goto L1; + } + if (u == 0xFFFF) + { c->Iop = 0x0FB7; // MOVZX + c->Irm = modregrm(3,ereg,ereg); + if (c->Irex & REX_B) + c->Irex |= REX_R; + goto L1; + } + } + } + } + } + + /* Look for ADD,OR,SUB,XOR with u that we can eliminate */ + if (!flags && + (op == 0x81 || op == 0x80) && + (reg == modregrm(0,0,0) || reg == modregrm(0,1,0) || // ADD,OR + reg == modregrm(0,5,0) || reg == modregrm(0,6,0)) // SUB, XOR + ) + { + if (u == 0) + { + c->Iop = NOP; + goto L1; + } + if (u == ~0 && reg == modregrm(0,6,0)) /* XOR */ + { + c->Iop = 0xF6 | (op & 1); /* NOT */ + c->Irm ^= modregrm(0,6^2,0); + goto L1; + } + if (!shortop && + useopsize && + op == 0x81 && + (u & 0xFFFF0000) == 0 && + (reg == modregrm(0,6,0) || reg == modregrm(0,1,0))) + { c->Iflags ^= CFopsize; + goto L1; + } + } + + /* Look for TEST or OR or XOR with an immediate constant */ + /* that we can replace with a byte operation */ + if (op == 0xF7 && reg == modregrm(0,0,0) || + op == 0x81 && reg == modregrm(0,6,0) && !flags || + op == 0x81 && reg == modregrm(0,1,0)) + { + // See if we can replace a dword with a word + // (avoid for 32 bit instructions, because CFopsize + // is too slow) + if (!shortop && useopsize) + { if ((u & 0xFFFF0000) == 0) + { c->Iflags ^= CFopsize; + goto L1; + } + /* If memory (not register) addressing mode */ + if ((u & 0xFFFF) == 0 && rm < modregrm(3,0,AX)) + { c->IEVoffset1 += 2; /* address MSW */ + c->IEV2.Vuns >>= 16; + c->Iflags ^= CFopsize; + goto L1; + } + } + + // If EA is not SI or DI + if (rm < (modregrm(3,0,SP) | reg) && + (usespace || + config.target_cpu < TARGET_PentiumPro) + ) + { + if ((u & 0xFFFFFF00) == 0) + { + L2: c->Iop--; /* to byte instruction */ + c->Iflags &= ~CFopsize; + goto L1; + } + if (((u & 0xFFFF00FF) == 0 || + (shortop && (u & 0xFF) == 0)) && + (rm < modregrm(3,0,0) || (!c->Irex && ereg < 4))) + { + L3: + c->IEV2.Vuns >>= 8; + if (rm >= (modregrm(3,0,AX) | reg)) + c->Irm |= 4; /* AX->AH, BX->BH, etc. */ + else + c->IEVoffset1 += 1; + goto L2; + } + } +#if 0 + // BUG: which is right? + else if ((u & 0xFFFF0000) == 0) +#else + else if (0 && op == 0xF7 && + rm >= modregrm(3,0,SP) && + (u & 0xFFFF0000) == 0) +#endif + c->Iflags &= ~CFopsize; + } + + // Try to replace TEST reg,-1 with TEST reg,reg + if (op == 0xF6 && rm >= modregrm(3,0,AX) && rm <= modregrm(3,0,7)) // TEST regL,immed8 + { if ((u & 0xFF) == 0xFF) + { + L4: c->Iop = 0x84; // TEST regL,regL + c->Irm = modregrm(3,ereg,ereg); + if (c->Irex & REX_B) + c->Irex |= REX_R; + c->Iflags &= ~CFopsize; + goto L1; + } + } + if (op == 0xF7 && rm >= modregrm(3,0,AX) && rm <= modregrm(3,0,7) && (I64 || ereg < 4)) + { if (u == 0xFF) + goto L4; + if ((u & 0xFFFF) == 0xFF00 && shortop && !c->Irex && ereg < 4) + { ereg |= 4; /* to regH */ + goto L4; + } + } + + /* Look for sign extended immediate data */ + if ((signed char) u == u) + { + if (op == 0x81) + { if (reg != 0x08 && reg != 0x20 && reg != 0x30) + c->Iop = op = 0x83; /* 8 bit sgn ext */ + } + else if (op == 0x69) /* IMUL rw,ew,dw */ + c->Iop = op = 0x6B; /* IMUL rw,ew,db */ + } + + // Look for SHIFT EA,imm8 we can replace with short form + if (u == 1 && ((op & 0xFE) == 0xC0)) + c->Iop |= 0xD0; + + } /* if immediate second operand */ + + /* Look for AX short form */ + if (ins & A) + { if (rm == modregrm(0,AX,local_BPRM) && + !(c->Irex & REX_R) && // and it's AX, not R8 + (op & ~3) == 0x88 && + !I64) + { op = ((op & 3) + 0xA0) ^ 2; + /* 8A-> A0 */ + /* 8B-> A1 */ + /* 88-> A2 */ + /* 89-> A3 */ + c->Iop = op; + c->IFL2 = c->IFL1; + c->IEV2 = c->IEV1; + } + + /* Replace MOV REG1,REG2 with MOV EREG1,EREG2 */ + else if (!I16 && + (op == 0x89 || op == 0x8B) && + (rm & 0xC0) == 0xC0 && + (!b || b->BC != BCasm) + ) + c->Iflags &= ~CFopsize; + + // If rm is AX + else if ((rm & modregrm(3,0,7)) == modregrm(3,0,AX) && !(c->Irex & (REX_R | REX_B))) + { switch (op) + { case 0x80: op = reg | 4; break; + case 0x81: op = reg | 5; break; + case 0x87: op = 0x90 + (reg>>3); break; // XCHG + case 0xF6: + if (reg == 0) + op = 0xA8; /* TEST AL,immed8 */ + break; + case 0xF7: + if (reg == 0) + op = 0xA9; /* TEST AX,immed16 */ + break; + } + c->Iop = op; + } + } + + /* Look for reg short form */ + if ((ins & R) && (rm & 0xC0) == 0xC0) + { switch (op) + { case 0xC6: op = 0xB0 + ereg; break; + case 0xC7: op = 0xB8 + ereg; break; + case 0xFF: + switch (reg) + { case 6<<3: op = 0x50+ereg; break;/* PUSH*/ + case 0<<3: if (!I64) op = 0x40+ereg; break; /* INC*/ + case 1<<3: if (!I64) op = 0x48+ereg; break; /* DEC*/ + } + break; + case 0x8F: op = 0x58 + ereg; break; + case 0x87: + if (reg == 0) op = 0x90 + ereg; + break; + } + c->Iop = op; + } + + // Look to replace SHL reg,1 with ADD reg,reg + if ((op & ~1) == 0xD0 && + (rm & modregrm(3,7,0)) == modregrm(3,4,0) && + config.target_cpu >= TARGET_80486) + { + c->Iop &= 1; + c->Irm = (rm & modregrm(3,0,7)) | (ereg << 3); + if (c->Irex & REX_B) + c->Irex |= REX_R; + if (!(c->Iflags & CFpsw) && !I16) + c->Iflags &= ~CFopsize; + goto L1; + } + + /* Look for sign extended modregrm displacement, or 0 + * displacement. + */ + + if (((rm & 0xC0) == 0x80) && // it's a 16/32 bit disp + c->IFL1 == FLconst) // and it's a constant + { + a = c->IEVpointer1; + if (a == 0 && (rm & 7) != local_BPRM && // if 0[disp] + !(local_BPRM == 5 && (rm & 7) == 4 && (c->Isib & 7) == BP) + ) + c->Irm &= 0x3F; + else if (!I16) + { + if ((targ_size_t)(targ_schar)a == a) + c->Irm ^= 0xC0; /* do 8 sx */ + } + else if (((targ_size_t)(targ_schar)a & 0xFFFF) == (a & 0xFFFF)) + c->Irm ^= 0xC0; /* do 8 sx */ + } + + /* Look for LEA reg,[ireg], replace with MOV reg,ireg */ + else if (op == 0x8D) + { rm = c->Irm & 7; + mod = c->Irm & modregrm(3,0,0); + if (mod == 0) + { + if (!I16) + { + switch (rm) + { + case 4: + case 5: + break; + default: + c->Irm |= modregrm(3,0,0); + c->Iop = 0x8B; + break; + } + } + else + { + switch (rm) + { + case 4: rm = modregrm(3,0,SI); goto L6; + case 5: rm = modregrm(3,0,DI); goto L6; + case 7: rm = modregrm(3,0,BX); goto L6; + L6: c->Irm = rm + reg; + c->Iop = 0x8B; + break; + } + } + } + + /* replace LEA reg,0[BP] with MOV reg,BP */ + else if (mod == modregrm(1,0,0) && rm == local_BPRM && + c->IFL1 == FLconst && c->IEVpointer1 == 0) + { c->Iop = 0x8B; /* MOV reg,BP */ + c->Irm = modregrm(3,0,BP) + reg; + } + } + + // Replace [R13] with 0[R13] + if (c->Irex & REX_B && (c->Irm & modregrm(3,0,5)) == modregrm(0,0,5)) + { + c->Irm |= modregrm(1,0,0); + c->IFL1 = FLconst; + c->IEVpointer1 = 0; + } + } + else if (!(c->Iflags & CFvex)) + { + switch (op) + { + default: + if ((op & ~0x0F) != 0x70) + break; + case JMP: + switch (c->IFL2) + { case FLcode: + if (c->IEV2.Vcode == code_next(c)) + { c->Iop = NOP; + continue; + } + break; + case FLblock: + if (!code_next(c) && c->IEV2.Vblock == bn) + { c->Iop = NOP; + continue; + } + break; + case FLconst: + case FLfunc: + case FLextern: + break; + default: +#ifdef DEBUG + WRFL((enum FL)c->IFL2); +#endif + assert(0); + } + break; + + case 0x68: // PUSH immed16 + if (c->IFL2 == FLconst) + { + targ_long u = c->IEV2.Vuns; + if (I64 || + ((c->Iflags & CFopsize) ? I16 : I32)) + { // PUSH 32/64 bit operand + if (u == (signed char) u) + c->Iop = 0x6A; // PUSH immed8 + } + else // PUSH 16 bit operand + { if ((short)u == (signed char) u) + c->Iop = 0x6A; // PUSH immed8 + } + } + break; + } + } + } +#if 0 + if (1 || debugc) { + printf("-pinholeopt(%p)\n",cstart); + for (c = cstart; c; c = code_next(c)) + c->print(); + } +#endif +} + +#ifdef DEBUG +STATIC void pinholeopt_unittest() +{ + //printf("pinholeopt_unittest()\n"); + struct CS { unsigned model,op,ea,ev1,ev2,flags; } tests[][2] = + { + // XOR reg,immed NOT regL + {{ 16,0x81,modregrm(3,6,BX),0,0xFF,0 }, { 0,0xF6,modregrm(3,2,BX),0,0xFF }}, + + // MOV 0[BX],3 MOV [BX],3 + {{ 16,0xC7,modregrm(2,0,7),0,3}, { 0,0xC7,modregrm(0,0,7),0,3 }}, + +#if 0 // only if config.flags4 & CFG4space + // TEST regL,immed8 + {{ 0,0xF6,modregrm(3,0,BX),0,0xFF,0 }, { 0,0x84,modregrm(3,BX,BX),0,0xFF }}, + {{ 0,0xF7,modregrm(3,0,BX),0,0xFF,0 }, { 0,0x84,modregrm(3,BX,BX),0,0xFF }}, + {{ 64,0xF6,modregrmx(3,0,R8),0,0xFF,0 }, { 0,0x84,modregxrmx(3,R8,R8),0,0xFF }}, + {{ 64,0xF7,modregrmx(3,0,R8),0,0xFF,0 }, { 0,0x84,modregxrmx(3,R8,R8),0,0xFF }}, +#endif + + // PUSH immed => PUSH immed8 + {{ 0,0x68,0,0,0 }, { 0,0x6A,0,0,0 }}, + {{ 0,0x68,0,0,0x7F }, { 0,0x6A,0,0,0x7F }}, + {{ 0,0x68,0,0,0x80 }, { 0,0x68,0,0,0x80 }}, + {{ 16,0x68,0,0,0,CFopsize }, { 0,0x6A,0,0,0,CFopsize }}, + {{ 16,0x68,0,0,0x7F,CFopsize }, { 0,0x6A,0,0,0x7F,CFopsize }}, + {{ 16,0x68,0,0,0x80,CFopsize }, { 0,0x68,0,0,0x80,CFopsize }}, + {{ 16,0x68,0,0,0x10000,0 }, { 0,0x6A,0,0,0x10000,0 }}, + {{ 16,0x68,0,0,0x10000,CFopsize }, { 0,0x68,0,0,0x10000,CFopsize }}, + {{ 32,0x68,0,0,0,CFopsize }, { 0,0x6A,0,0,0,CFopsize }}, + {{ 32,0x68,0,0,0x7F,CFopsize }, { 0,0x6A,0,0,0x7F,CFopsize }}, + {{ 32,0x68,0,0,0x80,CFopsize }, { 0,0x68,0,0,0x80,CFopsize }}, + {{ 32,0x68,0,0,0x10000,CFopsize }, { 0,0x6A,0,0,0x10000,CFopsize }}, + {{ 32,0x68,0,0,0x8000,CFopsize }, { 0,0x68,0,0,0x8000,CFopsize }}, + }; + + //config.flags4 |= CFG4space; + for (int i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) + { CS *pin = &tests[i][0]; + CS *pout = &tests[i][1]; + code cs; + memset(&cs, 0, sizeof(cs)); + if (pin->model) + { + if (I16 && pin->model != 16) + continue; + if (I32 && pin->model != 32) + continue; + if (I64 && pin->model != 64) + continue; + } + //printf("[%d]\n", i); + cs.Iop = pin->op; + cs.Iea = pin->ea; + cs.IFL1 = FLconst; + cs.IFL2 = FLconst; + cs.IEV1.Vuns = pin->ev1; + cs.IEV2.Vuns = pin->ev2; + cs.Iflags = pin->flags; + pinholeopt(&cs, NULL); + if (cs.Iop != pout->op) + { printf("[%d] Iop = x%02x, pout = x%02x\n", i, cs.Iop, pout->op); + assert(0); + } + assert(cs.Iea == pout->ea); + assert(cs.IEV1.Vuns == pout->ev1); + assert(cs.IEV2.Vuns == pout->ev2); + assert(cs.Iflags == pout->flags); + } +} +#endif + +/************************** + * Compute jump addresses for FLcode. + * Note: only works for forward referenced code. + * only direct jumps and branches are detected. + * LOOP instructions only work for backward refs. + */ + +void jmpaddr(code *c) +{ code *ci,*cn,*ctarg,*cstart; + targ_size_t ad; + unsigned op; + + //printf("jmpaddr()\n"); + cstart = c; /* remember start of code */ + while (c) + { + op = c->Iop; + if (op <= 0xEB && + inssize[op] & T && // if second operand + c->IFL2 == FLcode && + ((op & ~0x0F) == 0x70 || op == JMP || op == JMPS || op == JCXZ || op == CALL)) + { ci = code_next(c); + ctarg = c->IEV2.Vcode; /* target code */ + ad = 0; /* IP displacement */ + while (ci && ci != ctarg) + { + ad += calccodsize(ci); + ci = code_next(ci); + } + if (!ci) + goto Lbackjmp; // couldn't find it + if (!I16 || op == JMP || op == JMPS || op == JCXZ || op == CALL) + c->IEVpointer2 = ad; + else /* else conditional */ + { if (!(c->Iflags & CFjmp16)) /* if branch */ + c->IEVpointer2 = ad; + else /* branch around a long jump */ + { cn = code_next(c); + code_next(c) = code_calloc(); + code_next(code_next(c)) = cn; + c->Iop = op ^ 1; /* converse jmp */ + c->Iflags &= ~CFjmp16; + c->IEVpointer2 = I16 ? 3 : 5; + cn = code_next(c); + cn->Iop = JMP; /* long jump */ + cn->IFL2 = FLconst; + cn->IEVpointer2 = ad; + } + } + c->IFL2 = FLconst; + } + if (op == LOOP && c->IFL2 == FLcode) /* backwards refs */ + { + Lbackjmp: + ctarg = c->IEV2.Vcode; + for (ci = cstart; ci != ctarg; ci = code_next(ci)) + if (!ci || ci == c) + assert(0); + ad = 2; /* - IP displacement */ + while (ci != c) + { assert(ci); + ad += calccodsize(ci); + ci = code_next(ci); + } + c->IEVpointer2 = (-ad) & 0xFF; + c->IFL2 = FLconst; + } + c = code_next(c); + } +} + +/******************************* + * Calculate bl->Bsize. + */ + +unsigned calcblksize(code *c) +{ unsigned size; + + for (size = 0; c; c = code_next(c)) + { + unsigned sz = calccodsize(c); + //printf("off=%02x, sz = %d, code %p: op=%02x\n", size, sz, c, c->Iop); + size += sz; + } +//printf("calcblksize(c = x%x) = %d\n", c, size); + return size; +} + +/***************************** + * Calculate and return code size of a code. + * Note that NOPs are sometimes used as markers, but are + * never output. LINNUMs are never output. + * Note: This routine must be fast. Profiling shows it is significant. + */ + +unsigned calccodsize(code *c) +{ unsigned size; + unsigned op; + unsigned char rm,mod,ins; + unsigned iflags; + unsigned i32 = I32 || I64; + unsigned a32 = i32; + +#ifdef DEBUG + assert((a32 & ~1) == 0); +#endif + iflags = c->Iflags; + op = c->Iop; + if (iflags & CFvex) + { + ins = vex_inssize(c); + size = ins & 7; + goto Lmodrm; + } + else if ((op & 0xFF00) == 0x0F00 || (op & 0xFFFD00) == 0x0F3800) + op = 0x0F; + else + op &= 0xFF; + switch (op) + { + case 0x0F: + if ((c->Iop & 0xFFFD00) == 0x0F3800) + { // 3 byte op ( 0F38-- or 0F3A-- ) + ins = inssize2[(c->Iop >> 8) & 0xFF]; + size = ins & 7; + if (c->Iop & 0xFF000000) + size++; + } + else + { // 2 byte op ( 0F-- ) + ins = inssize2[c->Iop & 0xFF]; + size = ins & 7; + if (c->Iop & 0xFF0000) + size++; + } + break; + + case NOP: + case ESCAPE: + size = 0; // since these won't be output + goto Lret2; + + case ASM: + if (c->Iflags == CFaddrsize) // kludge for DA inline asm + size = NPTRSIZE; + else + size = c->IEV1.as.len; + goto Lret2; + + case 0xA1: + case 0xA3: + if (c->Irex) + { + size = 9; // 64 bit immediate value for MOV to/from RAX + goto Lret; + } + goto Ldefault; + + case 0xF6: /* TEST mem8,immed8 */ + ins = inssize[op]; + size = ins & 7; + if (i32) + size = inssize32[op]; + if ((c->Irm & (7<<3)) == 0) + size++; /* size of immed8 */ + break; + + case 0xF7: + ins = inssize[op]; + size = ins & 7; + if (i32) + size = inssize32[op]; + if ((c->Irm & (7<<3)) == 0) + size += (i32 ^ ((iflags & CFopsize) !=0)) ? 4 : 2; + break; + + default: + Ldefault: + ins = inssize[op]; + size = ins & 7; + if (i32) + size = inssize32[op]; + } + + if (iflags & (CFwait | CFopsize | CFaddrsize | CFSEG)) + { + if (iflags & CFwait) // if add FWAIT prefix + size++; + if (iflags & CFSEG) // if segment override + size++; + + // If the instruction has a second operand that is not an 8 bit, + // and the operand size prefix is present, then fix the size computation + // because the operand size will be different. + // Walter, I had problems with this bit at the end. There can still be + // an ADDRSIZE prefix for these and it does indeed change the operand size. + + if (iflags & (CFopsize | CFaddrsize)) + { + if ((ins & (T|E)) == T) + { + if ((op & 0xAC) == 0xA0) + { + if (iflags & CFaddrsize && !I64) + { if (I32) + size -= 2; + else + size += 2; + } + } + else if (iflags & CFopsize) + { if (I16) + size += 2; + else + size -= 2; + } + } + if (iflags & CFaddrsize) + { if (!I64) + a32 ^= 1; + size++; + } + if (iflags & CFopsize) + size++; /* +1 for OPSIZE prefix */ + } + } + +Lmodrm: + if ((op & ~0x0F) == 0x70) + { if (iflags & CFjmp16) // if long branch + size += I16 ? 3 : 4; // + 3(4) bytes for JMP + } + else if (ins & M) // if modregrm byte + { + rm = c->Irm; + mod = rm & 0xC0; + if (a32 || I64) + { // 32 bit addressing + if (issib(rm)) + size++; + switch (mod) + { case 0: + if (issib(rm) && (c->Isib & 7) == 5 || + (rm & 7) == 5) + size += 4; /* disp32 */ + if (c->Irex & REX_B && (rm & 7) == 5) + /* Instead of selecting R13, this mode is an [RIP] relative + * address. Although valid, it's redundant, and should not + * be generated. Instead, generate 0[R13] instead of [R13]. + */ + assert(0); + break; + case 0x40: + size++; /* disp8 */ + break; + case 0x80: + size += 4; /* disp32 */ + break; + } + } + else + { // 16 bit addressing + if (mod == 0x40) /* 01: 8 bit displacement */ + size++; + else if (mod == 0x80 || (mod == 0 && (rm & 7) == 6)) + size += 2; + } + } + +Lret: + if (!(iflags & CFvex) && c->Irex) + { size++; + if (c->Irex & REX_W && (op & ~7) == 0xB8) + size += 4; + } +Lret2: + //printf("op = x%02x, size = %d\n",op,size); + return size; +} + +/******************************** + * Return !=0 if codes match. + */ + +#if 0 + +int code_match(code *c1,code *c2) +{ code cs1,cs2; + unsigned char ins; + + if (c1 == c2) + goto match; + cs1 = *c1; + cs2 = *c2; + if (cs1.Iop != cs2.Iop) + goto nomatch; + switch (cs1.Iop) + { + case ESCAPE | ESCctor: + case ESCAPE | ESCdtor: + goto nomatch; + + case NOP: + goto match; + + case ASM: + if (cs1.IEV1.as.len == cs2.IEV1.as.len && + memcmp(cs1.IEV1.as.bytes,cs2.IEV1.as.bytes,cs1.EV1.as.len) == 0) + goto match; + else + goto nomatch; + + default: + if ((cs1.Iop & 0xFF) == ESCAPE) + goto match; + break; + } + if (cs1.Iflags != cs2.Iflags) + goto nomatch; + + ins = inssize[cs1.Iop & 0xFF]; + if ((cs1.Iop & 0xFFFD00) == 0x0F3800) + { + ins = inssize2[(cs1.Iop >> 8) & 0xFF]; + } + else if ((cs1.Iop & 0xFF00) == 0x0F00) + { + ins = inssize2[cs1.Iop & 0xFF]; + } + + if (ins & M) // if modregrm byte + { + if (cs1.Irm != cs2.Irm) + goto nomatch; + if ((cs1.Irm & 0xC0) == 0xC0) + goto do2; + if (is32bitaddr(I32,cs1.Iflags)) + { + if (issib(cs1.Irm) && cs1.Isib != cs2.Isib) + goto nomatch; + if ( + ((rm & 0xC0) == 0 && !((rm & 7) == 4 && (c->Isib & 7) == 5 || (rm & 7) == 5)) + ) + goto do2; /* if no first operand */ + } + else + { + if ( + ((rm & 0xC0) == 0 && !((rm & 7) == 6)) + ) + goto do2; /* if no first operand */ + } + if (cs1.IFL1 != cs2.IFL1) + goto nomatch; + if (flinsymtab[cs1.IFL1] && cs1.IEVsym1 != cs2.IEVsym1) + goto nomatch; + if (cs1.IEVoffset1 != cs2.IEVoffset1) + goto nomatch; + } + +do2: + if (!(ins & T)) // if no second operand + goto match; + if (cs1.IFL2 != cs2.IFL2) + goto nomatch; + if (flinsymtab[cs1.IFL2] && cs1.IEVsym2 != cs2.IEVsym2) + goto nomatch; + if (cs1.IEVoffset2 != cs2.IEVoffset2) + goto nomatch; + +match: + return 1; + +nomatch: + return 0; +} + +#endif + +/************************** + * Write code to intermediate file. + * Code starts at offset. + * Returns: + * addr of end of code + */ + +static targ_size_t offset; /* to save code use a global */ +static char bytes[100]; +static char *pgen; + +#define GEN(c) (*pgen++ = (c)) +#define GENP(n,p) (memcpy(pgen,(p),(n)), pgen += (n)) +#if ELFOBJ || MACHOBJ +#define FLUSH() if (pgen-bytes) cod3_flush() +#else +#define FLUSH() ((pgen - bytes) && cod3_flush()) +#endif +#define OFFSET() (offset + (pgen - bytes)) + +STATIC void cod3_flush() +{ + // Emit accumulated bytes to code segment +#ifdef DEBUG + assert(pgen - bytes < sizeof(bytes)); +#endif + offset += obj_bytes(cseg,offset,pgen - bytes,bytes); + pgen = bytes; +} + +unsigned codout(code *c) +{ unsigned op; + unsigned char rm,mod; + unsigned char ins; + code *cn; + unsigned flags; + symbol *s; + +#ifdef DEBUG + if (debugc) printf("codout(%p), Coffset = x%llx\n",c,(unsigned long long)Coffset); +#endif + + pgen = bytes; + offset = Coffset; + for (; c; c = code_next(c)) + { +#ifdef DEBUG + if (debugc) { printf("off=%02lx, sz=%ld, ",(long)OFFSET(),(long)calccodsize(c)); c->print(); } + unsigned startoffset = OFFSET(); +#endif + op = c->Iop; + ins = inssize[op & 0xFF]; + switch (op & 0xFF) + { case ESCAPE: + /* Check for SSE4 opcode v/pmaxuw xmm1,xmm2/m128 */ + if(op == 0x660F383E || c->Iflags & CFvex) break; + + switch (op & 0xFFFF00) + { case ESClinnum: + /* put out line number stuff */ + objlinnum(c->IEV1.Vsrcpos,OFFSET()); + break; +#if SCPP +#if 1 + case ESCctor: + case ESCdtor: + case ESCoffset: + if (config.exe != EX_NT) + except_pair_setoffset(c,OFFSET() - funcoffset); + break; + case ESCmark: + case ESCrelease: + case ESCmark2: + case ESCrelease2: + break; +#else + case ESCctor: + except_push(OFFSET() - funcoffset,c->IEV1.Vtor,NULL); + break; + case ESCdtor: + except_pop(OFFSET() - funcoffset,c->IEV1.Vtor,NULL); + break; + case ESCmark: + except_mark(); + break; + case ESCrelease: + except_release(); + break; +#endif +#endif + } +#ifdef DEBUG + assert(calccodsize(c) == 0); +#endif + continue; + case NOP: /* don't send them out */ + if (op != NOP) + break; +#ifdef DEBUG + assert(calccodsize(c) == 0); +#endif + continue; + case ASM: + if (op != ASM) + break; + FLUSH(); + if (c->Iflags == CFaddrsize) // kludge for DA inline asm + { + do32bit(FLblockoff,&c->IEV1,0); + } + else + { + offset += obj_bytes(cseg,offset,c->IEV1.as.len,c->IEV1.as.bytes); + } +#ifdef DEBUG + assert(calccodsize(c) == c->IEV1.as.len); +#endif + continue; + } + flags = c->Iflags; + + // See if we need to flush (don't have room for largest code sequence) + if (pgen - bytes > sizeof(bytes) - (1+4+4+8+8)) + FLUSH(); + + // see if we need to put out prefix bytes + if (flags & (CFwait | CFPREFIX | CFjmp16)) + { int override; + + if (flags & CFwait) + GEN(0x9B); // FWAIT + /* ? SEGES : SEGSS */ + switch (flags & CFSEG) + { case CFes: override = SEGES; goto segover; + case CFss: override = SEGSS; goto segover; + case CFcs: override = SEGCS; goto segover; + case CFds: override = SEGDS; goto segover; + case CFfs: override = SEGFS; goto segover; + case CFgs: override = SEGGS; goto segover; + segover: GEN(override); + break; + } + + if (flags & CFaddrsize) + GEN(0x67); + + // Do this last because of instructions like ADDPD + if (flags & CFopsize) + GEN(0x66); /* operand size */ + + if ((op & ~0x0F) == 0x70 && flags & CFjmp16) /* long condit jmp */ + { + if (!I16) + { // Put out 16 bit conditional jump + c->Iop = op = 0x0F00 | (0x80 | (op & 0x0F)); + } + else + { + cn = code_calloc(); + /*cxcalloc++;*/ + code_next(cn) = code_next(c); + code_next(c) = cn; // link into code + cn->Iop = JMP; // JMP block + cn->IFL2 = c->IFL2; + cn->IEV2.Vblock = c->IEV2.Vblock; + c->Iop = op ^= 1; // toggle condition + c->IFL2 = FLconst; + c->IEVpointer2 = I16 ? 3 : 5; // skip over JMP block + c->Iflags &= ~CFjmp16; + } + } + } + + if (flags & CFvex) + { + if (flags & CFvex3) + { + GEN(0xC4); + GEN(VEX3_B1(c->Ivex)); + GEN(VEX3_B2(c->Ivex)); + GEN(c->Ivex.op); + } + else + { + GEN(0xC5); + GEN(VEX2_B1(c->Ivex)); + GEN(c->Ivex.op); + } + ins = vex_inssize(c); + goto Lmodrm; + } + + if (op > 0xFF) + { + if ((op & 0xFFFD00) == 0x0F3800) + ins = inssize2[(op >> 8) & 0xFF]; + else if ((op & 0xFF00) == 0x0F00) + ins = inssize2[op & 0xFF]; + + if (op & 0xFF000000) + { + unsigned char op1 = op >> 24; + if (op1 == 0xF2 || op1 == 0xF3 || op1 == 0x66) + { + GEN(op1); + if (c->Irex) + GEN(c->Irex | REX); + } + else + { + if (c->Irex) + GEN(c->Irex | REX); + GEN(op1); + } + GEN((op >> 16) & 0xFF); + GEN((op >> 8) & 0xFF); + GEN(op & 0xFF); + } + else if (op & 0xFF0000) + { + unsigned char op1 = op >> 16; + if (op1 == 0xF2 || op1 == 0xF3 || op1 == 0x66) + { + GEN(op1); + if (c->Irex) + GEN(c->Irex | REX); + } + else + { + if (c->Irex) + GEN(c->Irex | REX); + GEN(op1); + } + GEN((op >> 8) & 0xFF); + GEN(op & 0xFF); + } + else + { + if (c->Irex) + GEN(c->Irex | REX); + GEN((op >> 8) & 0xFF); + GEN(op & 0xFF); + } + } + else + { + if (c->Irex) + GEN(c->Irex | REX); + GEN(op); + } + Lmodrm: + if (ins & M) /* if modregrm byte */ + { + rm = c->Irm; + GEN(rm); + + // Look for an address size override when working with the + // MOD R/M and SIB bytes + + if (is32bitaddr( I32, flags)) + { + if (issib(rm)) + GEN(c->Isib); + switch (rm & 0xC0) + { case 0x40: + do8bit((enum FL) c->IFL1,&c->IEV1); // 8 bit + break; + case 0: + if (!(issib(rm) && (c->Isib & 7) == 5 || + (rm & 7) == 5)) + break; + case 0x80: + { int flags = CFoff; + targ_size_t val = 0; + if (I64) + { + if ((rm & modregrm(3,0,7)) == modregrm(0,0,5)) // if disp32[RIP] + { flags |= CFpc32; + val = -4; + unsigned reg = rm & modregrm(0,7,0); + if (ins & T || + ((op == 0xF6 || op == 0xF7) && (reg == modregrm(0,0,0) || reg == modregrm(0,1,0)))) + { if (ins & E) + val = -5; + else if (c->Iflags & CFopsize) + val = -6; + else + val = -8; + } +#if TARGET_OSX + // Mach-O linkage already takes the 4 byte size into account + val += 4; +#endif + } + } + do32bit((enum FL)c->IFL1,&c->IEV1,flags,val); + break; + } + } + } + else + { + switch (rm & 0xC0) + { case 0x40: + do8bit((enum FL) c->IFL1,&c->IEV1); // 8 bit + break; + case 0: + if ((rm & 7) != 6) + break; + case 0x80: + do16bit((enum FL)c->IFL1,&c->IEV1,CFoff); + break; + } + } + } + else + { + if (op == 0xC8) + do16bit((enum FL)c->IFL1,&c->IEV1,0); + } + flags &= CFseg | CFoff | CFselfrel; + if (ins & T) /* if second operand */ + { if (ins & E) /* if data-8 */ + do8bit((enum FL) c->IFL2,&c->IEV2); + else if (!I16) + { + switch (op) + { case 0xC2: /* RETN imm16 */ + case 0xCA: /* RETF imm16 */ + do16: + do16bit((enum FL)c->IFL2,&c->IEV2,flags); + break; + + case 0xA1: + case 0xA3: + if (I64 && c->Irex) + { + do64: + do64bit((enum FL)c->IFL2,&c->IEV2,flags); + break; + } + case 0xA0: /* MOV AL,byte ptr [] */ + case 0xA2: + if (c->Iflags & CFaddrsize && !I64) + goto do16; + else + do32: + do32bit((enum FL)c->IFL2,&c->IEV2,flags); + break; + case 0x9A: + case 0xEA: + if (c->Iflags & CFopsize) + goto ptr1616; + else + goto ptr1632; + + case 0x68: // PUSH immed32 + if ((enum FL)c->IFL2 == FLblock) + { + c->IFL2 = FLblockoff; + goto do32; + } + else + goto case_default; + + case CALL: // CALL rel + case JMP: // JMP rel + flags |= CFselfrel; + goto case_default; + + default: + if ((op|0xF) == 0x0F8F) // Jcc rel16 rel32 + flags |= CFselfrel; + if (I64 && (op & ~7) == 0xB8 && c->Irex & REX_W) + goto do64; + case_default: + if (c->Iflags & CFopsize) + goto do16; + else + goto do32; + break; + } + } + else + { + switch (op) { + case 0xC2: + case 0xCA: + goto do16; + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + if (c->Iflags & CFaddrsize) + goto do32; + else + goto do16; + break; + case 0x9A: + case 0xEA: + if (c->Iflags & CFopsize) + goto ptr1632; + else + goto ptr1616; + + ptr1616: + ptr1632: + //assert(c->IFL2 == FLfunc); + FLUSH(); + if (c->IFL2 == FLdatseg) + { + reftodatseg(cseg,offset,c->IEVpointer2, + c->IEVseg2,flags); + offset += 4; + } + else + { + s = c->IEVsym2; + offset += reftoident(cseg,offset,s,0,flags); + } + break; + + case 0x68: // PUSH immed16 + if ((enum FL)c->IFL2 == FLblock) + { c->IFL2 = FLblockoff; + goto do16; + } + else + goto case_default16; + + case CALL: + case JMP: + flags |= CFselfrel; + default: + case_default16: + if (c->Iflags & CFopsize) + goto do32; + else + goto do16; + break; + } + } + } + else if (op == 0xF6) /* TEST mem8,immed8 */ + { if ((rm & (7<<3)) == 0) + do8bit((enum FL)c->IFL2,&c->IEV2); + } + else if (op == 0xF7) + { if ((rm & (7<<3)) == 0) /* TEST mem16/32,immed16/32 */ + { + if ((I32 || I64) ^ ((c->Iflags & CFopsize) != 0)) + do32bit((enum FL)c->IFL2,&c->IEV2,flags); + else + do16bit((enum FL)c->IFL2,&c->IEV2,flags); + } + } +#ifdef DEBUG + if (OFFSET() - startoffset != calccodsize(c)) + { + printf("actual: %d, calc: %d\n", (int)(OFFSET() - startoffset), (int)calccodsize(c)); + c->print(); + assert(0); + } +#endif + } + FLUSH(); + Coffset = offset; + //printf("-codout(), Coffset = x%x\n", Coffset); + return offset; /* ending address */ +} + + +STATIC void do64bit(enum FL fl,union evc *uev,int flags) +{ char *p; + symbol *s; + targ_size_t ad; + + assert(I64); + switch (fl) + { + case FLconst: + ad = * (targ_size_t *) uev; + L1: + GENP(8,&ad); + return; + case FLdatseg: + FLUSH(); + reftodatseg(cseg,offset,uev->_EP.Vpointer,uev->_EP.Vseg,CFoffset64 | flags); + break; + case FLframehandler: + framehandleroffset = OFFSET(); + ad = 0; + goto L1; + case FLswitch: + FLUSH(); + ad = uev->Vswitch->Btableoffset; + if (config.flags & CFGromable) + reftocodseg(cseg,offset,ad); + else + reftodatseg(cseg,offset,ad,JMPSEG,CFoff); + break; +#if TARGET_SEGMENTED + case FLcsdata: + case FLfardata: +#if DEBUG + symbol_print(uev->sp.Vsym); +#endif +#endif + // NOTE: In ELFOBJ all symbol refs have been tagged FLextern + // strings and statics are treated like offsets from a + // un-named external with is the start of .rodata or .data + case FLextern: /* external data symbol */ + case FLtlsdata: +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + case FLgot: + case FLgotoff: +#endif + FLUSH(); + s = uev->sp.Vsym; /* symbol pointer */ + reftoident(cseg,offset,s,uev->sp.Voffset,CFoffset64 | flags); + break; + +#if TARGET_OSX + case FLgot: + funcsym_p->Slocalgotoffset = OFFSET(); + ad = 0; + goto L1; +#endif + + case FLfunc: /* function call */ + s = uev->sp.Vsym; /* symbol pointer */ + assert(TARGET_SEGMENTED || !tyfarfunc(s->ty())); + FLUSH(); + reftoident(cseg,offset,s,0,CFoffset64 | flags); + break; + + case FLblock: /* displacement to another block */ + ad = uev->Vblock->Boffset - OFFSET() - 4; + //printf("FLblock: funcoffset = %x, OFFSET = %x, Boffset = %x, ad = %x\n", funcoffset, OFFSET(), uev->Vblock->Boffset, ad); + goto L1; + + case FLblockoff: + FLUSH(); + assert(uev->Vblock); + //printf("FLblockoff: offset = %x, Boffset = %x, funcoffset = %x\n", offset, uev->Vblock->Boffset, funcoffset); + reftocodseg(cseg,offset,uev->Vblock->Boffset); + break; + + default: +#ifdef DEBUG + WRFL(fl); +#endif + assert(0); + } + offset += 8; +} + + +STATIC void do32bit(enum FL fl,union evc *uev,int flags, targ_size_t val) +{ char *p; + symbol *s; + targ_size_t ad; + + //printf("do32bit(flags = x%x)\n", flags); + switch (fl) + { + case FLconst: + assert(sizeof(targ_size_t) == 4 || sizeof(targ_size_t) == 8); + ad = * (targ_size_t *) uev; + L1: + GENP(4,&ad); + return; + case FLdatseg: + FLUSH(); + reftodatseg(cseg,offset,uev->_EP.Vpointer,uev->_EP.Vseg,flags); + break; + case FLframehandler: + framehandleroffset = OFFSET(); + ad = 0; + goto L1; + case FLswitch: + FLUSH(); + ad = uev->Vswitch->Btableoffset; + if (config.flags & CFGromable) + reftocodseg(cseg,offset,ad); + else + reftodatseg(cseg,offset,ad,JMPSEG,CFoff); + break; +#if TARGET_SEGMENTED + case FLcsdata: + case FLfardata: +#if DEBUG + symbol_print(uev->sp.Vsym); +#endif +#endif + // NOTE: In ELFOBJ all symbol refs have been tagged FLextern + // strings and statics are treated like offsets from a + // un-named external with is the start of .rodata or .data + case FLextern: /* external data symbol */ + case FLtlsdata: +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + case FLgot: + case FLgotoff: +#endif + FLUSH(); + s = uev->sp.Vsym; /* symbol pointer */ + reftoident(cseg,offset,s,uev->sp.Voffset + val,flags); + break; + +#if TARGET_OSX + case FLgot: + funcsym_p->Slocalgotoffset = OFFSET(); + ad = 0; + goto L1; +#endif + + case FLfunc: /* function call */ + s = uev->sp.Vsym; /* symbol pointer */ +#if TARGET_SEGMENTED + if (tyfarfunc(s->ty())) + { /* Large code references are always absolute */ + FLUSH(); + offset += reftoident(cseg,offset,s,0,flags) - 4; + } + else if (s->Sseg == cseg && + (s->Sclass == SCstatic || s->Sclass == SCglobal) && + s->Sxtrnnum == 0 && flags & CFselfrel) + { /* if we know it's relative address */ + ad = s->Soffset - OFFSET() - 4; + goto L1; + } + else +#endif + { + assert(TARGET_SEGMENTED || !tyfarfunc(s->ty())); + FLUSH(); + reftoident(cseg,offset,s,val,flags); + } + break; + + case FLblock: /* displacement to another block */ + ad = uev->Vblock->Boffset - OFFSET() - 4; + //printf("FLblock: funcoffset = %x, OFFSET = %x, Boffset = %x, ad = %x\n", funcoffset, OFFSET(), uev->Vblock->Boffset, ad); + goto L1; + + case FLblockoff: + FLUSH(); + assert(uev->Vblock); + //printf("FLblockoff: offset = %x, Boffset = %x, funcoffset = %x\n", offset, uev->Vblock->Boffset, funcoffset); + reftocodseg(cseg,offset,uev->Vblock->Boffset); + break; + + default: +#ifdef DEBUG + WRFL(fl); +#endif + assert(0); + } + offset += 4; +} + + +STATIC void do16bit(enum FL fl,union evc *uev,int flags) +{ char *p; + symbol *s; + targ_size_t ad; + + switch (fl) + { + case FLconst: + GENP(2,(char *) uev); + return; + case FLdatseg: + FLUSH(); + reftodatseg(cseg,offset,uev->_EP.Vpointer,uev->_EP.Vseg,flags); + break; + case FLswitch: + FLUSH(); + ad = uev->Vswitch->Btableoffset; + if (config.flags & CFGromable) + reftocodseg(cseg,offset,ad); + else + reftodatseg(cseg,offset,ad,JMPSEG,CFoff); + break; +#if TARGET_SEGMENTED + case FLcsdata: + case FLfardata: +#endif + case FLextern: /* external data symbol */ + case FLtlsdata: + assert(SIXTEENBIT || TARGET_SEGMENTED); + FLUSH(); + s = uev->sp.Vsym; /* symbol pointer */ + reftoident(cseg,offset,s,uev->sp.Voffset,flags); + break; + case FLfunc: /* function call */ + assert(SIXTEENBIT || TARGET_SEGMENTED); + s = uev->sp.Vsym; /* symbol pointer */ + if (tyfarfunc(s->ty())) + { /* Large code references are always absolute */ + FLUSH(); + offset += reftoident(cseg,offset,s,0,flags) - 2; + } + else if (s->Sseg == cseg && + (s->Sclass == SCstatic || s->Sclass == SCglobal) && + s->Sxtrnnum == 0 && flags & CFselfrel) + { /* if we know it's relative address */ + ad = s->Soffset - OFFSET() - 2; + goto L1; + } + else + { FLUSH(); + reftoident(cseg,offset,s,0,flags); + } + break; + case FLblock: /* displacement to another block */ + ad = uev->Vblock->Boffset - OFFSET() - 2; +#ifdef DEBUG + { + targ_ptrdiff_t delta = uev->Vblock->Boffset - OFFSET() - 2; + assert((signed short)delta == delta); + } +#endif + L1: + GENP(2,&ad); // displacement + return; + + case FLblockoff: + FLUSH(); + reftocodseg(cseg,offset,uev->Vblock->Boffset); + break; + + default: +#ifdef DEBUG + WRFL(fl); +#endif + assert(0); + } + offset += 2; +} + +STATIC void do8bit(enum FL fl,union evc *uev) +{ char c; + targ_ptrdiff_t delta; + + switch (fl) + { + case FLconst: + c = uev->Vuns; + break; + case FLblock: + delta = uev->Vblock->Boffset - OFFSET() - 1; + if ((signed char)delta != delta) + { +#if MARS + if (uev->Vblock->Bsrcpos.Slinnum) + fprintf(stderr, "%s(%d): ", uev->Vblock->Bsrcpos.Sfilename, uev->Vblock->Bsrcpos.Slinnum); +#endif + fprintf(stderr, "block displacement of %lld exceeds the maximum offset of -128 to 127.\n", (long long)delta); + err_exit(); + } + c = delta; +#ifdef DEBUG + assert(uev->Vblock->Boffset > OFFSET() || c != 0x7F); +#endif + break; + default: +#ifdef DEBUG + fprintf(stderr,"fl = %d\n",fl); +#endif + assert(0); + } + GEN(c); +} + + +/********************************** + */ + +#if HYDRATE +void code_hydrate(code **pc) +{ + code *c; + unsigned char ins,rm; + enum FL fl; + + assert(pc); + while (*pc) + { + c = (code *) ph_hydrate(pc); + if (c->Iflags & CFvex) + ins = vex_inssize(c); + else if ((c->Iop & 0xFFFD00) == 0x0F3800) + ins = inssize2[(c->Iop >> 8) & 0xFF]; + else if ((c->Iop & 0xFF00) == 0x0F00) + ins = inssize2[c->Iop & 0xFF]; + else + ins = inssize[c->Iop & 0xFF]; + switch (c->Iop) + { + default: + break; + + case ESCAPE | ESClinnum: + srcpos_hydrate(&c->IEV1.Vsrcpos); + goto done; + + case ESCAPE | ESCctor: + case ESCAPE | ESCdtor: + el_hydrate(&c->IEV1.Vtor); + goto done; + + case ASM: + ph_hydrate(&c->IEV1.as.bytes); + goto done; + } + if (!(ins & M) || + ((rm = c->Irm) & 0xC0) == 0xC0) + goto do2; /* if no first operand */ + if (is32bitaddr(I32,c->Iflags)) + { + + if ( + ((rm & 0xC0) == 0 && !((rm & 7) == 4 && (c->Isib & 7) == 5 || (rm & 7) == 5)) + ) + goto do2; /* if no first operand */ + } + else + { + if ( + ((rm & 0xC0) == 0 && !((rm & 7) == 6)) + ) + goto do2; /* if no first operand */ + } + fl = (enum FL) c->IFL1; + switch (fl) + { + case FLudata: + case FLdata: + case FLreg: + case FLauto: + case FLbprel: + case FLpara: +#if TARGET_SEGMENTED + case FLcsdata: + case FLfardata: +#endif + case FLtlsdata: + case FLfunc: + case FLpseudo: + case FLextern: + case FLtmp: + assert(flinsymtab[fl]); + symbol_hydrate(&c->IEVsym1); + symbol_debug(c->IEVsym1); + break; + case FLdatseg: + case FLfltreg: + case FLallocatmp: + case FLcs: + case FLndp: + case FLoffset: + case FLlocalsize: + case FLconst: + case FLframehandler: + assert(!flinsymtab[fl]); + break; + case FLcode: + (void) ph_hydrate(&c->IEV1.Vcode); + break; + case FLblock: + case FLblockoff: + (void) ph_hydrate(&c->IEV1.Vblock); + break; +#if SCPP + case FLctor: + case FLdtor: + el_hydrate(&c->IEV1.Vtor); + break; +#endif + case FLasm: + (void) ph_hydrate(&c->IEV1.as.bytes); + break; + default: +#ifdef DEBUG + WRFL(fl); +#endif + assert(0); + break; + } + do2: + /* Ignore TEST (F6 and F7) opcodes */ + if (!(ins & T)) + goto done; /* if no second operand */ + + fl = (enum FL) c->IFL2; + switch (fl) + { + case FLudata: + case FLdata: + case FLreg: + case FLauto: + case FLbprel: + case FLpara: +#if TARGET_SEGMENTED + case FLcsdata: + case FLfardata: +#endif + case FLtlsdata: + case FLfunc: + case FLpseudo: + case FLextern: + case FLtmp: + assert(flinsymtab[fl]); + symbol_hydrate(&c->IEVsym2); + symbol_debug(c->IEVsym2); + break; + case FLdatseg: + case FLfltreg: + case FLallocatmp: + case FLcs: + case FLndp: + case FLoffset: + case FLlocalsize: + case FLconst: + case FLframehandler: + assert(!flinsymtab[fl]); + break; + case FLcode: + (void) ph_hydrate(&c->IEV2.Vcode); + break; + case FLblock: + case FLblockoff: + (void) ph_hydrate(&c->IEV2.Vblock); + break; + default: +#ifdef DEBUG + WRFL(fl); +#endif + assert(0); + break; + } + done: + ; + + pc = &code_next(c); + } +} +#endif + +/********************************** + */ + +#if DEHYDRATE +void code_dehydrate(code **pc) +{ + code *c; + unsigned char ins,rm; + enum FL fl; + + while ((c = *pc) != NULL) + { + ph_dehydrate(pc); + + if (c->Iflags & CFvex) + ins = vex_inssize(c); + else if ((c->Iop & 0xFFFD00) == 0x0F3800) + ins = inssize2[(c->Iop >> 8) & 0xFF]; + else if ((c->Iop & 0xFF00) == 0x0F00) + ins = inssize2[c->Iop & 0xFF]; + else + ins = inssize[c->Iop & 0xFF]; + switch (c->Iop) + { + default: + break; + + case ESCAPE | ESClinnum: + srcpos_dehydrate(&c->IEV1.Vsrcpos); + goto done; + + case ESCAPE | ESCctor: + case ESCAPE | ESCdtor: + el_dehydrate(&c->IEV1.Vtor); + goto done; + + case ASM: + ph_dehydrate(&c->IEV1.as.bytes); + goto done; + } + + if (!(ins & M) || + ((rm = c->Irm) & 0xC0) == 0xC0) + goto do2; /* if no first operand */ + if (is32bitaddr(I32,c->Iflags)) + { + + if ( + ((rm & 0xC0) == 0 && !((rm & 7) == 4 && (c->Isib & 7) == 5 || (rm & 7) == 5)) + ) + goto do2; /* if no first operand */ + } + else + { + if ( + ((rm & 0xC0) == 0 && !((rm & 7) == 6)) + ) + goto do2; /* if no first operand */ + } + fl = (enum FL) c->IFL1; + switch (fl) + { + case FLudata: + case FLdata: + case FLreg: + case FLauto: + case FLbprel: + case FLpara: +#if TARGET_SEGMENTED + case FLcsdata: + case FLfardata: +#endif + case FLtlsdata: + case FLfunc: + case FLpseudo: + case FLextern: + case FLtmp: + assert(flinsymtab[fl]); + symbol_dehydrate(&c->IEVsym1); + break; + case FLdatseg: + case FLfltreg: + case FLallocatmp: + case FLcs: + case FLndp: + case FLoffset: + case FLlocalsize: + case FLconst: + case FLframehandler: + assert(!flinsymtab[fl]); + break; + case FLcode: + ph_dehydrate(&c->IEV1.Vcode); + break; + case FLblock: + case FLblockoff: + ph_dehydrate(&c->IEV1.Vblock); + break; +#if SCPP + case FLctor: + case FLdtor: + el_dehydrate(&c->IEV1.Vtor); + break; +#endif + case FLasm: + ph_dehydrate(&c->IEV1.as.bytes); + break; + default: +#ifdef DEBUG + WRFL(fl); +#endif + assert(0); + break; + } + do2: + /* Ignore TEST (F6 and F7) opcodes */ + if (!(ins & T)) + goto done; /* if no second operand */ + + fl = (enum FL) c->IFL2; + switch (fl) + { + case FLudata: + case FLdata: + case FLreg: + case FLauto: + case FLbprel: + case FLpara: +#if TARGET_SEGMENTED + case FLcsdata: + case FLfardata: +#endif + case FLtlsdata: + case FLfunc: + case FLpseudo: + case FLextern: + case FLtmp: + assert(flinsymtab[fl]); + symbol_dehydrate(&c->IEVsym2); + break; + case FLdatseg: + case FLfltreg: + case FLallocatmp: + case FLcs: + case FLndp: + case FLoffset: + case FLlocalsize: + case FLconst: + case FLframehandler: + assert(!flinsymtab[fl]); + break; + case FLcode: + ph_dehydrate(&c->IEV2.Vcode); + break; + case FLblock: + case FLblockoff: + ph_dehydrate(&c->IEV2.Vblock); + break; + default: +#ifdef DEBUG + WRFL(fl); +#endif + assert(0); + break; + } + done: + ; + pc = &code_next(c); + } +} +#endif + +/*************************** + * Debug code to dump code stucture. + */ + +#if DEBUG + +void WRcodlst(code *c) +{ for (; c; c = code_next(c)) + c->print(); +} + +void code::print() +{ + unsigned char ins; + unsigned char rexb; + code *c = this; + + if (c == CNIL) + { printf("code 0\n"); + return; + } + + unsigned op = c->Iop; + if (c->Iflags & CFvex) + ins = vex_inssize(c); + else if ((c->Iop & 0xFFFD00) == 0x0F3800) + ins = inssize2[(op >> 8) & 0xFF]; + else if ((c->Iop & 0xFF00) == 0x0F00) + ins = inssize2[op & 0xFF]; + else + ins = inssize[op & 0xFF]; + + printf("code %p: nxt=%p ",c,code_next(c)); + + if (c->Iflags & CFvex) + { + if (c->Iflags & CFvex3) + { printf("vex=0xC4"); + printf(" 0x%02X", VEX3_B1(c->Ivex)); + printf(" 0x%02X", VEX3_B2(c->Ivex)); + rexb = + ( c->Ivex.w ? REX_W : 0) | + (!c->Ivex.r ? REX_R : 0) | + (!c->Ivex.x ? REX_X : 0) | + (!c->Ivex.b ? REX_B : 0); + } + else + { printf("vex=0xC5"); + printf(" 0x%02X", VEX2_B1(c->Ivex)); + rexb = !c->Ivex.r ? REX_R : 0; + } + printf(" "); + } + else + rexb = c->Irex; + + if (rexb) + { printf("rex=0x%02X ", c->Irex); + if (rexb & REX_W) + printf("W"); + if (rexb & REX_R) + printf("R"); + if (rexb & REX_X) + printf("X"); + if (rexb & REX_B) + printf("B"); + printf(" "); + } + printf("op=0x%02X",op); + + if ((op & 0xFF) == ESCAPE) + { if ((op & 0xFF00) == ESClinnum) + { printf(" linnum = %d\n",c->IEV1.Vsrcpos.Slinnum); + return; + } + printf(" ESCAPE %d",c->Iop >> 8); + } + if (c->Iflags) + printf(" flg=%x",c->Iflags); + if (ins & M) + { unsigned rm = c->Irm; + printf(" rm=0x%02X=%d,%d,%d",rm,(rm>>6)&3,(rm>>3)&7,rm&7); + if (!I16 && issib(rm)) + { unsigned char sib = c->Isib; + printf(" sib=%02x=%d,%d,%d",sib,(sib>>6)&3,(sib>>3)&7,sib&7); + } + if ((rm & 0xC7) == BPRM || (rm & 0xC0) == 0x80 || (rm & 0xC0) == 0x40) + { + switch (c->IFL1) + { + case FLconst: + case FLoffset: + printf(" int = %4d",c->IEV1.Vuns); + break; + case FLblock: + printf(" block = %p",c->IEV1.Vblock); + break; + case FLswitch: + case FLblockoff: + case FLlocalsize: + case FLframehandler: + case 0: + break; + case FLdatseg: + printf(" %d.%llx",c->IEVseg1,(unsigned long long)c->IEVpointer1); + break; + case FLauto: + case FLreg: + case FLdata: + case FLudata: + case FLpara: + case FLtmp: + case FLbprel: + case FLtlsdata: + printf(" sym='%s'",c->IEVsym1->Sident); + break; + case FLextern: + printf(" FLextern offset = %4d",(int)c->IEVoffset1); + break; + default: + WRFL((enum FL)c->IFL1); + break; + } + } + } + if (ins & T) + { printf(" "); WRFL((enum FL)c->IFL2); + switch (c->IFL2) + { + case FLconst: + printf(" int = %4d",c->IEV2.Vuns); + break; + case FLblock: + printf(" block = %p",c->IEV2.Vblock); + break; + case FLswitch: + case FLblockoff: + case 0: + case FLlocalsize: + case FLframehandler: + break; + case FLdatseg: + printf(" %d.%llx",c->IEVseg2,(unsigned long long)c->IEVpointer2); + break; + case FLauto: + case FLreg: + case FLpara: + case FLtmp: + case FLbprel: + case FLfunc: + case FLdata: + case FLudata: + case FLtlsdata: + printf(" sym='%s'",c->IEVsym2->Sident); + break; + case FLcode: + printf(" code = %p",c->IEV2.Vcode); + break; + default: + WRFL((enum FL)c->IFL2); + break; + } + } + printf("\n"); +} +#endif + +#endif // !SPP diff --git a/backend/cod4.c b/backend/cod4.c new file mode 100644 index 00000000..fc6e1977 --- /dev/null +++ b/backend/cod4.c @@ -0,0 +1,3438 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include + +#include "cc.h" +#include "el.h" +#include "oper.h" +#include "code.h" +#include "type.h" +#include "global.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + + /* AX,CX,DX,BX */ +const unsigned dblreg[4] = { BX,DX,(unsigned)-1,CX }; + + +/******************************* + * Return number of times symbol s appears in tree e. + */ + +STATIC int intree(symbol *s,elem *e) +{ + if (EOP(e)) + return intree(s,e->E1) + (EBIN(e) ? intree(s,e->E2) : 0); + return e->Eoper == OPvar && e->EV.sp.Vsym == s; +} + +/*********************************** + * Determine if expression e can be evaluated directly into register + * variable s. + * Have to be careful about things like x=x+x+x, and x=a+x. + * Returns: + * !=0 can + * 0 can't + */ + +int doinreg(symbol *s, elem *e) +{ int in = 0; + int op; + + L1: + op = e->Eoper; + if (op == OPind || + OTcall(op) || + OTleaf(op) || + (in = intree(s,e)) == 0 || + (OTunary(op) && !EOP(e->E1)) + ) + return 1; + if (in == 1) + { + switch (op) + { + case OPadd: + case OPmin: + case OPand: + case OPor: + case OPxor: + case OPshl: + case OPmul: + if (!intree(s,e->E2)) + { + e = e->E1; + goto L1; + } + } + } + return 0; +} + +/**************************** + * Return code for saving common subexpressions if EA + * turns out to be a register. + * This is called just before modifying an EA. + */ + +code *modEA(code *c) +{ + if ((c->Irm & 0xC0) == 0xC0) // addressing mode refers to a register + { + unsigned reg = c->Irm & 7; + if (c->Irex & REX_B) + { reg |= 8; + assert(I64); + } + return getregs(mask[reg]); + } + return CNIL; +} + +#if TARGET_WINDOS +// This code is for CPUs that do not support the 8087 + +/**************************** + * Gen code for op= for doubles. + */ + +STATIC code * opassdbl(elem *e,regm_t *pretregs,unsigned op) +{ code *c1,*c2,*c3,*c4,*c5,*c6,cs; + unsigned clib; + regm_t retregs2,retregs,idxregs; + tym_t tym; + elem *e1; + + static unsigned clibtab[OPdivass - OPpostinc + 1] = + /* OPpostinc,OPpostdec,OPeq,OPaddass,OPminass,OPmulass,OPdivass */ + { CLIBdadd, CLIBdsub, (unsigned)-1, CLIBdadd,CLIBdsub,CLIBdmul,CLIBddiv }; + + if (config.inline8087) + return opass87(e,pretregs); + clib = clibtab[op - OPpostinc]; + e1 = e->E1; + tym = tybasic(e1->Ety); + c1 = getlvalue(&cs,e1,DOUBLEREGS | mBX | mCX); + + if (tym == TYfloat) + { + clib += CLIBfadd - CLIBdadd; /* convert to float operation */ + + /* Load EA into FLOATREGS */ + c1 = cat(c1,getregs(FLOATREGS)); + cs.Iop = 0x8B; + cs.Irm |= modregrm(0,AX,0); + c1 = gen(c1,&cs); + + if (!I32) + { + cs.Irm |= modregrm(0,DX,0); + getlvalue_msw(&cs); + c1 = gen(c1,&cs); + getlvalue_lsw(&cs); + + } + retregs2 = FLOATREGS2; + idxregs = FLOATREGS | idxregm(&cs); + retregs = FLOATREGS; + } + else + { + if (I32) + { + /* Load EA into DOUBLEREGS */ + c1 = cat(c1,getregs(DOUBLEREGS_32)); + cs.Iop = 0x8B; + cs.Irm |= modregrm(0,AX,0); + c1 = gen(c1,&cs); + cs.Irm |= modregrm(0,DX,0); + getlvalue_msw(&cs); + c1 = gen(c1,&cs); + getlvalue_lsw(&cs); + + retregs2 = DOUBLEREGS2_32; + idxregs = DOUBLEREGS_32 | idxregm(&cs); + } + else + { + /* Push EA onto stack */ + cs.Iop = 0xFF; + cs.Irm |= modregrm(0,6,0); + cs.IEVoffset1 += DOUBLESIZE - REGSIZE; + c1 = gen(c1,&cs); + getlvalue_lsw(&cs); + gen(c1,&cs); + getlvalue_lsw(&cs); + gen(c1,&cs); + getlvalue_lsw(&cs); + gen(c1,&cs); + stackpush += DOUBLESIZE; + + retregs2 = DOUBLEREGS_16; + idxregs = idxregm(&cs); + } + retregs = DOUBLEREGS; + } + + if ((cs.Iflags & CFSEG) == CFes) + idxregs |= mES; + cgstate.stackclean++; + c3 = scodelem(e->E2,&retregs2,idxregs,FALSE); + cgstate.stackclean--; + c4 = callclib(e,clib,&retregs,0); + if (e1->Ecount) + cssave(e1,retregs,EOP(e1)); /* if lvalue is a CSE */ + freenode(e1); + cs.Iop = 0x89; /* MOV EA,DOUBLEREGS */ + c5 = fltregs(&cs,tym); + c6 = fixresult(e,retregs,pretregs); + return cat6(c1,CNIL,c3,c4,c5,c6); +} + +/**************************** + * Gen code for OPnegass for doubles. + */ + +STATIC code * opnegassdbl(elem *e,regm_t *pretregs) +{ code *c1,*c2,*c3,*c,*cl,*cr,cs; + unsigned clib; + regm_t retregs2,retregs,idxregs; + tym_t tym; + elem *e1; + int sz; + + if (config.inline8087) + return cdnegass87(e,pretregs); + e1 = e->E1; + tym = tybasic(e1->Ety); + sz = tysize[tym]; + + cl = getlvalue(&cs,e1,*pretregs ? DOUBLEREGS | mBX | mCX : 0); + cr = modEA(&cs); + cs.Irm |= modregrm(0,6,0); + cs.Iop = 0x80; + cs.IEVoffset1 += sz - 1; + cs.IFL2 = FLconst; + cs.IEV2.Vuns = 0x80; + c = gen(NULL,&cs); // XOR 7[EA],0x80 + if (tycomplex(tym)) + { + cs.IEVoffset1 -= sz / 2; + gen(c,&cs); // XOR 7[EA],0x80 + } + c = cat3(cl,cr,c); + + if (*pretregs || e1->Ecount) + { + cs.IEVoffset1 -= sz - 1; + + if (tym == TYfloat) + { + // Load EA into FLOATREGS + c1 = getregs(FLOATREGS); + cs.Iop = 0x8B; + NEWREG(cs.Irm, AX); + c1 = gen(c1,&cs); + + if (!I32) + { + NEWREG(cs.Irm, DX); + getlvalue_msw(&cs); + c1 = gen(c1,&cs); + getlvalue_lsw(&cs); + + } + retregs = FLOATREGS; + } + else + { + if (I32) + { + // Load EA into DOUBLEREGS + c1 = getregs(DOUBLEREGS_32); + cs.Iop = 0x8B; + cs.Irm &= ~modregrm(0,7,0); + cs.Irm |= modregrm(0,AX,0); + c1 = gen(c1,&cs); + cs.Irm |= modregrm(0,DX,0); + getlvalue_msw(&cs); + c1 = gen(c1,&cs); + getlvalue_lsw(&cs); + } + else + { +#if 1 + cs.Iop = 0x8B; + c1 = fltregs(&cs,TYdouble); // MOV DOUBLEREGS, EA +#else + // Push EA onto stack + cs.Iop = 0xFF; + cs.Irm |= modregrm(0,6,0); + cs.IEVoffset1 += DOUBLESIZE - REGSIZE; + c1 = gen(NULL,&cs); + cs.IEVoffset1 -= REGSIZE; + gen(c1,&cs); + cs.IEVoffset1 -= REGSIZE; + gen(c1,&cs); + cs.IEVoffset1 -= REGSIZE; + gen(c1,&cs); + stackpush += DOUBLESIZE; +#endif + } + retregs = DOUBLEREGS; + } + if (e1->Ecount) + cssave(e1,retregs,EOP(e1)); /* if lvalue is a CSE */ + } + else + { retregs = 0; + assert(e1->Ecount == 0); + c1 = NULL; + } + + freenode(e1); + c3 = fixresult(e,retregs,pretregs); + return cat3(c,c1,c3); +} +#endif + + + +/************************ + * Generate code for an assignment. + */ + +code *cdeq(elem *e,regm_t *pretregs) +{ + tym_t tymll; + unsigned reg; + int i; + code *cl,*cr,*c,cs; + elem *e11; + bool regvar; /* TRUE means evaluate into register variable */ + regm_t varregm; + unsigned varreg; + targ_int postinc; + + //printf("cdeq(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); + elem *e1 = e->E1; + elem *e2 = e->E2; + int e2oper = e2->Eoper; + tym_t tyml = tybasic(e1->Ety); /* type of lvalue */ + regm_t retregs = *pretregs; + + if (tyxmmreg(tyml) && config.fpxmmregs) + return xmmeq(e, pretregs); + + if (tyfloating(tyml) && config.inline8087) + { + if (tycomplex(tyml)) + return complex_eq87(e, pretregs); + + if (!(retregs == 0 && + (e2oper == OPconst || e2oper == OPvar || e2oper == OPind)) + ) + return eq87(e,pretregs); + if (config.target_cpu >= TARGET_PentiumPro && + (e2oper == OPvar || e2oper == OPind) + ) + return eq87(e,pretregs); + if (tyml == TYldouble || tyml == TYildouble) + return eq87(e,pretregs); + } + + unsigned sz = tysize[tyml]; // # of bytes to transfer + assert((int)sz > 0); + + if (retregs == 0) /* if no return value */ + { int fl; + + if ((e2oper == OPconst || /* if rvalue is a constant */ + e2oper == OPrelconst && + !(I64 && config.flags3 & CFG3pic) && + ((fl = el_fl(e2)) == FLdata || + fl==FLudata || fl == FLextern) +#if TARGET_SEGMENTED + && !(e2->EV.sp.Vsym->ty() & mTYcs) +#endif + ) && + !evalinregister(e2) && + !e1->Ecount) /* and no CSE headaches */ + { + // Look for special case of (*p++ = ...), where p is a register variable + if (e1->Eoper == OPind && + ((e11 = e1->E1)->Eoper == OPpostinc || e11->Eoper == OPpostdec) && + e11->E1->Eoper == OPvar && + e11->E1->EV.sp.Vsym->Sfl == FLreg && + (!I16 || e11->E1->EV.sp.Vsym->Sregm & IDXREGS) + ) + { + postinc = e11->E2->EV.Vint; + if (e11->Eoper == OPpostdec) + postinc = -postinc; + cl = getlvalue(&cs,e11,RMstore); + freenode(e11->E2); + } + else + { postinc = 0; + cl = getlvalue(&cs,e1,RMstore); + + if (e2oper == OPconst && + config.flags4 & CFG4speed && + (config.target_cpu == TARGET_Pentium || + config.target_cpu == TARGET_PentiumMMX) && + (cs.Irm & 0xC0) == 0x80 + ) + { + if (I64 && sz == 8 && e2->EV.Vpointer) + { + // MOV reg,imm64 + // MOV EA,reg + regm_t rregm = allregs & ~idxregm(&cs); + unsigned reg; + cl = regwithvalue(cl,rregm,e2->EV.Vpointer,®,64); + cs.Iop = 0x89; + cs.Irm |= modregrm(0,reg & 7,0); + if (reg & 8) + cs.Irex |= REX_R; + c = gen(cl,&cs); + freenode(e2); + goto Lp; + } + if ((sz == REGSIZE || (I64 && sz == 4)) && e2->EV.Vint) + { + // MOV reg,imm + // MOV EA,reg + regm_t rregm = allregs & ~idxregm(&cs); + unsigned reg; + cl = regwithvalue(cl,rregm,e2->EV.Vint,®,0); + cs.Iop = 0x89; + cs.Irm |= modregrm(0,reg & 7,0); + if (reg & 8) + cs.Irex |= REX_R; + c = gen(cl,&cs); + freenode(e2); + goto Lp; + } + if (sz == 2 * REGSIZE && e2->EV.Vllong == 0) + { regm_t rregm; + unsigned reg; + + // MOV reg,imm + // MOV EA,reg + // MOV EA+2,reg + rregm = getscratch() & ~idxregm(&cs); + if (rregm) + { cl = regwithvalue(cl,rregm,e2->EV.Vint,®,0); + cs.Iop = 0x89; + cs.Irm |= modregrm(0,reg,0); + c = gen(cl,&cs); + getlvalue_msw(&cs); + c = gen(c,&cs); + freenode(e2); + goto Lp; + } + } + } + } + + /* If loading result into a register */ + if ((cs.Irm & 0xC0) == 0xC0) + { cl = cat(cl,modEA(&cs)); + if (sz == 2 * REGSIZE && cs.IFL1 == FLreg) + cl = cat(cl,getregs(cs.IEVsym1->Sregm)); + } + cs.Iop = (sz == 1) ? 0xC6 : 0xC7; + + if (e2oper == OPrelconst) + { + cs.IEVoffset2 = e2->EV.sp.Voffset; + cs.IFL2 = fl; + cs.IEVsym2 = e2->EV.sp.Vsym; + cs.Iflags |= CFoff; + cl = gen(cl,&cs); /* MOV EA,&variable */ + if (I64 && sz == 8) + code_orrex(cl, REX_W); + if (sz > REGSIZE) + { + cs.Iop = 0x8C; + getlvalue_msw(&cs); + cs.Irm |= modregrm(0,3,0); + cl = gen(cl,&cs); /* MOV EA+2,DS */ + } + } + else + { + assert(e2oper == OPconst); + cs.IFL2 = FLconst; + targ_size_t *p = (targ_size_t *) &(e2->EV); + cs.IEV2.Vsize_t = *p; + // Look for loading a register variable + if ((cs.Irm & 0xC0) == 0xC0) + { unsigned reg = cs.Irm & 7; + + if (cs.Irex & REX_B) + reg |= 8; + if (I64 && sz == 8) + cl = movregconst(cl,reg,*p,64); + else + cl = movregconst(cl,reg,*p,1 ^ (cs.Iop & 1)); + if (sz == 2 * REGSIZE) + { getlvalue_msw(&cs); + if (REGSIZE == 2) + cl = movregconst(cl,cs.Irm & 7,((unsigned short *)p)[1],0); + else if (REGSIZE == 4) + cl = movregconst(cl,cs.Irm & 7,((unsigned *)p)[1],0); + else if (REGSIZE == 8) + cl = movregconst(cl,cs.Irm & 7,p[1],0); + else + assert(0); + } + } + else if (I64 && sz == 8 && *p >= 0x80000000) + { // Use 64 bit MOV, as the 32 bit one gets sign extended + // MOV reg,imm64 + // MOV EA,reg + regm_t rregm = allregs & ~idxregm(&cs); + unsigned reg; + cl = regwithvalue(cl,rregm,e2->EV.Vpointer,®,64); + cs.Iop = 0x89; + cs.Irm |= modregrm(0,reg & 7,0); + if (reg & 8) + cs.Irex |= REX_R; + c = gen(cl,&cs); + freenode(e2); + goto Lp; + } + else + { int regsize; + + i = sz; + do + { regsize = REGSIZE; + retregs = (sz == 1) ? BYTEREGS : allregs; + if (i >= 4 && I16 && I386) + { + regsize = 4; + cs.Iflags |= CFopsize; // use opsize to do 32 bit operation + } + else + { + if (reghasvalue(retregs,*p,®)) + { + cs.Iop = (cs.Iop & 1) | 0x88; + cs.Irm |= modregrm(0,reg & 7,0); // MOV EA,reg + if (reg & 8) + cs.Irex |= REX_R; + if (I64 && sz == 1 && reg >= 4) + cs.Irex |= REX; + } + if (!I16 && i == 2) // if 16 bit operand + cs.Iflags |= CFopsize; + if (I64 && sz == 8) + cs.Irex |= REX_W; + } + cl = gen(cl,&cs); /* MOV EA,const */ + + p = (targ_size_t *)((char *) p + regsize); + cs.Iop = (cs.Iop & 1) | 0xC6; + cs.Irm &= ~modregrm(0,7,0); + cs.Irex &= ~REX_R; + cs.IEVoffset1 += regsize; + cs.IEV2.Vint = *p; + i -= regsize; + } while (i > 0); + } + } + freenode(e2); + c = cl; + goto Lp; + } + retregs = allregs; /* pick a reg, any reg */ + if (sz == 2 * REGSIZE) + retregs &= ~mBP; // BP cannot be used for register pair + } + if (retregs == mPSW) + { retregs = allregs; + if (sz == 2 * REGSIZE) + retregs &= ~mBP; // BP cannot be used for register pair + } + cs.Iop = 0x89; + if (sz == 1) // must have byte regs + { cs.Iop = 0x88; + retregs &= BYTEREGS; + if (!retregs) + retregs = BYTEREGS; + } + else if (retregs & mES +#if TARGET_SEGMENTED + && ( + (e1->Eoper == OPind && + ((tymll = tybasic(e1->E1->Ety)) == TYfptr || tymll == TYhptr)) || + (e1->Eoper == OPvar && e1->EV.sp.Vsym->Sfl == FLfardata) + ) +#endif + ) + // getlvalue() needs ES, so we can't return it + retregs = allregs; /* no conflicts with ES */ + else if (tyml == TYdouble || tyml == TYdouble_alias || retregs & mST0) + retregs = DOUBLEREGS; + regvar = FALSE; + varregm = 0; + if (config.flags4 & CFG4optimized) + { + // Be careful of cases like (x = x+x+x). We cannot evaluate in + // x if x is in a register. + if (isregvar(e1,&varregm,&varreg) && // if lvalue is register variable + doinreg(e1->EV.sp.Vsym,e2) && // and we can compute directly into it + !(sz == 1 && e1->EV.sp.Voffset == 1) + ) + { regvar = TRUE; + retregs = varregm; + reg = varreg; /* evaluate directly in target register */ + if (tysize(e1->Ety) == REGSIZE && + tysize(e1->EV.sp.Vsym->Stype->Tty) == 2 * REGSIZE) + { + if (e1->EV.sp.Voffset) + retregs &= mMSW; + else + retregs &= mLSW; + reg = findreg(retregs); + } + } + } + if (*pretregs & mPSW && !EOP(e1)) /* if evaluating e1 couldn't change flags */ + { /* Be careful that this lines up with jmpopcode() */ + retregs |= mPSW; + *pretregs &= ~mPSW; + } + cr = scodelem(e2,&retregs,0,TRUE); /* get rvalue */ + + // Look for special case of (*p++ = ...), where p is a register variable + if (e1->Eoper == OPind && + ((e11 = e1->E1)->Eoper == OPpostinc || e11->Eoper == OPpostdec) && + e11->E1->Eoper == OPvar && + e11->E1->EV.sp.Vsym->Sfl == FLreg && + (!I16 || e11->E1->EV.sp.Vsym->Sregm & IDXREGS) + ) + { + postinc = e11->E2->EV.Vint; + if (e11->Eoper == OPpostdec) + postinc = -postinc; + cl = getlvalue(&cs,e11,RMstore | retregs); + freenode(e11->E2); + if (I64 && sz < 8) + cs.Irex &= ~REX_W; // incorrectly set by getlvalue() + } + else + { postinc = 0; + cl = getlvalue(&cs,e1,RMstore | retregs); // get lvalue (cl == CNIL if regvar) + } + + c = getregs(varregm); + + assert(!(retregs & mES && (cs.Iflags & CFSEG) == CFes)); +#if TARGET_SEGMENTED + if ((tyml == TYfptr || tyml == TYhptr) && retregs & mES) + { + reg = findreglsw(retregs); + cs.Irm |= modregrm(0,reg,0); + c = gen(c,&cs); /* MOV EA,reg */ + getlvalue_msw(&cs); // point to where segment goes + cs.Iop = 0x8C; + NEWREG(cs.Irm,0); + gen(c,&cs); /* MOV EA+2,ES */ + } + else +#endif + { + if (!I16) + { + reg = findreg(retregs & + ((sz > REGSIZE) ? mBP | mLSW : mBP | ALLREGS)); + cs.Irm |= modregrm(0,reg & 7,0); + if (reg & 8) + cs.Irex |= REX_R; + for (; TRUE; sz -= REGSIZE) + { + // Do not generate mov from register onto itself + if (regvar && reg == ((cs.Irm & 7) | (cs.Irex & REX_B ? 8 : 0))) + break; + if (sz == 2) // if 16 bit operand + cs.Iflags |= CFopsize; + else if (sz == 1 && reg >= 4) + cs.Irex |= REX; + c = gen(c,&cs); // MOV EA+offset,reg + if (sz <= REGSIZE) + break; + getlvalue_msw(&cs); + reg = findregmsw(retregs); + code_newreg(&cs, reg); + } + } + else + { + if (sz > REGSIZE) + cs.IEVoffset1 += sz - REGSIZE; /* 0,2,6 */ + reg = findreg(retregs & + (sz > REGSIZE ? mMSW : ALLREGS)); + if (tyml == TYdouble || tyml == TYdouble_alias) + reg = AX; + cs.Irm |= modregrm(0,reg,0); + /* Do not generate mov from register onto itself */ + if (!regvar || reg != (cs.Irm & 7)) + for (; TRUE; sz -= REGSIZE) /* 1,2,4 */ + { + c = gen(c,&cs); /* MOV EA+offset,reg */ + if (sz <= REGSIZE) + break; + cs.IEVoffset1 -= REGSIZE; + if (tyml == TYdouble || tyml == TYdouble_alias) + reg = dblreg[reg]; + else + reg = findreglsw(retregs); + NEWREG(cs.Irm,reg); + } + } + } + if (e1->Ecount || /* if lvalue is a CSE or */ + regvar) /* rvalue can't be a CSE */ + { + c = cat(c,getregs_imm(retregs)); // necessary if both lvalue and + // rvalue are CSEs (since a reg + // can hold only one e at a time) + cssave(e1,retregs,EOP(e1)); /* if lvalue is a CSE */ + } + + c = cat4(cr,cl,c,fixresult(e,retregs,pretregs)); +Lp: + if (postinc) + { + int reg = findreg(idxregm(&cs)); + if (*pretregs & mPSW) + { // Use LEA to avoid touching the flags + unsigned rm = cs.Irm & 7; + if (cs.Irex & REX_B) + rm |= 8; + c = genc1(c,0x8D,buildModregrm(2,reg,rm),FLconst,postinc); + if (tysize(e11->E1->Ety) == 8) + code_orrex(c, REX_W); + } + else if (I64) + { + c = genc2(c,0x81,modregrmx(3,0,reg),postinc); + if (tysize(e11->E1->Ety) == 8) + code_orrex(c, REX_W); + } + else + { + if (postinc == 1) + c = gen1(c,0x40 + reg); // INC reg + else if (postinc == -(targ_int)1) + c = gen1(c,0x48 + reg); // DEC reg + else + { + c = genc2(c,0x81,modregrm(3,0,reg),postinc); + } + } + } + freenode(e1); + return c; +} + + +/************************ + * Generate code for += -= &= |= ^= negass + */ + +code *cdaddass(elem *e,regm_t *pretregs) +{ regm_t retregs,forccs,forregs; + tym_t tyml; + unsigned reg,op,op1,op2,mode,wantres; + int byte; + code *cl,*cr,*c,*ce,cs; + elem *e1; + elem *e2; + unsigned opsize; + unsigned reverse; + int sz; + regm_t varregm; + unsigned varreg; + unsigned cflags; + + //printf("cdaddass(e=%p, *pretregs = x%x)\n",e,*pretregs); + op = e->Eoper; + retregs = 0; + reverse = 0; + e1 = e->E1; + tyml = tybasic(e1->Ety); // type of lvalue + sz = tysize[tyml]; + byte = (sz == 1); // 1 for byte operation, else 0 + + // See if evaluate in XMM registers + if (config.fpxmmregs && tyxmmreg(tyml) && op != OPnegass && !(*pretregs & mST0)) + return xmmopass(e,pretregs); + + if (tyfloating(tyml)) + { +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (op == OPnegass) + c = cdnegass87(e,pretregs); + else + c = opass87(e,pretregs); +#else + if (op == OPnegass) + c = opnegassdbl(e,pretregs); + else + c = opassdbl(e,pretregs,op); +#endif + return c; + } + opsize = (I16 && tylong(tyml) && config.target_cpu >= TARGET_80386) + ? CFopsize : 0; + cflags = 0; + forccs = *pretregs & mPSW; // return result in flags + forregs = *pretregs & ~mPSW; // return result in regs + /* TRUE if we want the result in a register */ + wantres = forregs || (e1->Ecount && EOP(e1)); + + switch (op) /* select instruction opcodes */ + { case OPpostinc: op = OPaddass; /* i++ => += */ + case OPaddass: op1 = 0x01; op2 = 0x11; + cflags = CFpsw; + mode = 0; break; /* ADD, ADC */ + case OPpostdec: op = OPminass; /* i-- => -= */ + case OPminass: op1 = 0x29; op2 = 0x19; + cflags = CFpsw; + mode = 5; break; /* SUB, SBC */ + case OPandass: op1 = op2 = 0x21; + mode = 4; break; /* AND, AND */ + case OPorass: op1 = op2 = 0x09; + mode = 1; break; /* OR , OR */ + case OPxorass: op1 = op2 = 0x31; + mode = 6; break; /* XOR, XOR */ + case OPnegass: op1 = 0xF7; // NEG + break; + default: + assert(0); + } + op1 ^= byte; /* bit 0 is 0 for byte operation */ + + if (op == OPnegass) + { + cl = getlvalue(&cs,e1,0); + cr = modEA(&cs); + cs.Irm |= modregrm(0,3,0); + cs.Iop = op1; + switch (tysize[tyml]) + { case CHARSIZE: + c = gen(CNIL,&cs); + break; + + case SHORTSIZE: + c = gen(CNIL,&cs); + if (!I16 && *pretregs & mPSW) + c->Iflags |= CFopsize | CFpsw; + break; + + case LONGSIZE: + if (!I16 || opsize) + { c = gen(CNIL,&cs); + c->Iflags |= opsize; + break; + } + neg_2reg: + getlvalue_msw(&cs); + c = gen(CNIL,&cs); // NEG EA+2 + getlvalue_lsw(&cs); + gen(c,&cs); // NEG EA + code_orflag(c,CFpsw); + cs.Iop = 0x81; + getlvalue_msw(&cs); + cs.IFL2 = FLconst; + cs.IEV2.Vuns = 0; + gen(c,&cs); // SBB EA+2,0 + break; + + case LLONGSIZE: + if (I16) + assert(0); // not implemented yet + goto neg_2reg; + + default: + assert(0); + } + c = cat3(cl,cr,c); + forccs = 0; // flags already set by NEG + *pretregs &= ~mPSW; + } + else if ((e2 = e->E2)->Eoper == OPconst && // if rvalue is a const + el_signx32(e2) && + // Don't evaluate e2 in register if we can use an INC or DEC + (((sz <= REGSIZE || tyfv(tyml)) && + (op == OPaddass || op == OPminass) && + (el_allbits(e2, 1) || el_allbits(e2, -1)) + ) || + (!evalinregister(e2) +#if TARGET_SEGMENTED + && tyml != TYhptr +#endif + ) + ) + ) + { + cl = getlvalue(&cs,e1,0); + cl = cat(cl,modEA(&cs)); + cs.IFL2 = FLconst; + cs.IEV2.Vint = e2->EV.Vint; + if (sz <= REGSIZE || tyfv(tyml) || opsize) + { + targ_int i = cs.IEV2.Vint; + + /* Handle shortcuts. Watch out for if result has */ + /* to be in flags. */ + + if (reghasvalue(ALLREGS,i,®) && i != 1 && i != -1 && + !opsize) + { + cs.Iop = op1; + cs.Irm |= modregrm(0,reg,0); + } + else + { + cs.Iop = 0x81; + cs.Irm |= modregrm(0,mode,0); + switch (op) + { case OPminass: /* convert to += */ + cs.Irm ^= modregrm(0,5,0); + i = -i; + cs.IEV2.Vsize_t = i; + /* FALL-THROUGH */ + case OPaddass: + if (i == 1) /* INC EA */ + goto L1; + else if (i == -1) /* DEC EA */ + { cs.Irm |= modregrm(0,1,0); + L1: cs.Iop = 0xFF; + } + break; + } + } + cs.Iop ^= byte; /* for byte operations */ + cs.Iflags |= opsize; + if (forccs) + cs.Iflags |= CFpsw; + else if (!I16 && cs.Iflags & CFopsize) + { + switch (op) + { case OPorass: + case OPxorass: + cs.IEV2.Vsize_t &= 0xFFFF; + cs.Iflags &= ~CFopsize; // don't worry about MSW + break; + case OPandass: + cs.IEV2.Vsize_t |= ~0xFFFFLL; + cs.Iflags &= ~CFopsize; // don't worry about MSW + break; + case OPminass: + case OPaddass: +#if 1 + if ((cs.Irm & 0xC0) == 0xC0) // EA is register + cs.Iflags &= ~CFopsize; +#else + if ((cs.Irm & 0xC0) == 0xC0 && // EA is register and + e1->Eoper == OPind) // not a register var + cs.Iflags &= ~CFopsize; +#endif + break; + default: + assert(0); + break; + } + } + + // For scheduling purposes, we wish to replace: + // OP EA + // with: + // MOV reg,EA + // OP reg + // MOV EA,reg + if (forregs && sz <= REGSIZE && (cs.Irm & 0xC0) != 0xC0 && + (config.target_cpu == TARGET_Pentium || + config.target_cpu == TARGET_PentiumMMX) && + config.flags4 & CFG4speed) + { regm_t sregm; + code cs2; + + // Determine which registers to use + sregm = allregs & ~idxregm(&cs); + if (byte) + sregm &= BYTEREGS; + if (sregm & forregs) + sregm &= forregs; + + cr = allocreg(&sregm,®,tyml); // allocate register + + cs2 = cs; + cs2.Iflags &= ~CFpsw; + cs2.Iop = 0x8B ^ byte; + code_newreg(&cs2, reg); + cr = gen(cr,&cs2); // MOV reg,EA + + cs.Irm = (cs.Irm & modregrm(0,7,0)) | modregrm(3,0,reg & 7); + if (reg & 8) + cs.Irex |= REX_B; + gen(cr,&cs); // OP reg + + cs2.Iop ^= 2; + gen(cr,&cs2); // MOV EA,reg + + c = cat(cl,cr); + retregs = sregm; + wantres = 0; + if (e1->Ecount) + cssave(e1,retregs,EOP(e1)); + } + else + { + c = gen(cl,&cs); + cs.Iflags &= ~opsize; + cs.Iflags &= ~CFpsw; + if (I16 && opsize) // if DWORD operand + cs.IEVoffset1 += 2; // compensate for wantres code + } + } + else if (sz == 2 * REGSIZE) + { targ_uns msw; + + cs.Iop = 0x81; + cs.Irm |= modregrm(0,mode,0); + c = cl; + cs.Iflags |= cflags; + c = gen(c,&cs); + cs.Iflags &= ~CFpsw; + + getlvalue_msw(&cs); // point to msw + msw = MSREG(e->E2->EV.Vllong); + cs.IEV2.Vuns = msw; /* msw of constant */ + switch (op) + { case OPminass: + cs.Irm ^= modregrm(0,6,0); /* SUB => SBB */ + break; + case OPaddass: + cs.Irm |= modregrm(0,2,0); /* ADD => ADC */ + break; + } + c = gen(c,&cs); + } + else + assert(0); + freenode(e->E2); /* don't need it anymore */ + } + else if (isregvar(e1,&varregm,&varreg) && + (e2->Eoper == OPvar || e2->Eoper == OPind) && + !evalinregister(e2) && + sz <= REGSIZE) // deal with later + { + cr = getlvalue(&cs,e2,0); + freenode(e2); + cl = getregs(varregm); + code_newreg(&cs, varreg); + if (I64 && sz == 1 && varreg >= 4) + cs.Irex |= REX; + cs.Iop = op1 ^ 2; // toggle direction bit + if (forccs) + cs.Iflags |= CFpsw; + reverse = 2; // remember we toggled it + cl = gen(cl,&cs); + c = cat(cr,cl); + retregs = 0; /* to trigger a bug if we attempt to use it */ + } + else // evaluate e2 into register + { + retregs = (byte) ? BYTEREGS : ALLREGS; // pick working reg +#if TARGET_SEGMENTED + if (tyml == TYhptr) + retregs &= ~mCX; // need CX for shift count +#endif + cr = scodelem(e->E2,&retregs,0,TRUE); // get rvalue + cl = getlvalue(&cs,e1,retregs); // get lvalue + cl = cat(cl,modEA(&cs)); + cs.Iop = op1; + if (sz <= REGSIZE || tyfv(tyml)) + { reg = findreg(retregs); + code_newreg(&cs, reg); // OP1 EA,reg + if (sz == 1 && reg >= 4 && I64) + cs.Irex |= REX; + } +#if TARGET_SEGMENTED + else if (tyml == TYhptr) + { unsigned mreg,lreg; + + mreg = findregmsw(retregs); + lreg = findreglsw(retregs); + cl = cat(cl,getregs(retregs | mCX)); + + // If h -= l, convert to h += -l + if (e->Eoper == OPminass) + { + cl = gen2(cl,0xF7,modregrm(3,3,mreg)); // NEG mreg + gen2(cl,0xF7,modregrm(3,3,lreg)); // NEG lreg + code_orflag(cl,CFpsw); + genc2(cl,0x81,modregrm(3,3,mreg),0); // SBB mreg,0 + } + cs.Iop = 0x01; + cs.Irm |= modregrm(0,lreg,0); + cl = gen(cl,&cs); // ADD EA,lreg + code_orflag(cl,CFpsw); + genc2(cl,0x81,modregrm(3,2,mreg),0); // ADC mreg,0 + genshift(cl); // MOV CX,offset __AHSHIFT + gen2(cl,0xD3,modregrm(3,4,mreg)); // SHL mreg,CL + NEWREG(cs.Irm,mreg); // ADD EA+2,mreg + getlvalue_msw(&cs); + } +#endif + else if (sz == 2 * REGSIZE) + { + cs.Irm |= modregrm(0,findreglsw(retregs),0); + cl = gen(cl,&cs); /* OP1 EA,reg+1 */ + code_orflag(cl,cflags); + cs.Iop = op2; + NEWREG(cs.Irm,findregmsw(retregs)); /* OP2 EA+1,reg */ + getlvalue_msw(&cs); + } + else + assert(0); + cl = gen(cl,&cs); + c = cat(cr,cl); + retregs = 0; /* to trigger a bug if we attempt to use it */ + } + + /* See if we need to reload result into a register. */ + /* Need result in registers in case we have a 32 bit */ + /* result and we want the flags as a result. */ + if (wantres || (sz > REGSIZE && forccs)) + { + if (sz <= REGSIZE) + { regm_t possregs; + + possregs = ALLREGS; + if (byte) + possregs = BYTEREGS; + retregs = forregs & possregs; + if (!retregs) + retregs = possregs; + + // If reg field is destination + if (cs.Iop & 2 && cs.Iop < 0x40 && (cs.Iop & 7) <= 5) + { + reg = (cs.Irm >> 3) & 7; + if (cs.Irex & REX_R) + reg |= 8; + retregs = mask[reg]; + ce = allocreg(&retregs,®,tyml); + } + // If lvalue is a register, just use that register + else if ((cs.Irm & 0xC0) == 0xC0) + { + reg = cs.Irm & 7; + if (cs.Irex & REX_B) + reg |= 8; + retregs = mask[reg]; + ce = allocreg(&retregs,®,tyml); + } + else + { + ce = allocreg(&retregs,®,tyml); + cs.Iop = 0x8B ^ byte ^ reverse; + code_newreg(&cs, reg); + if (I64 && byte && reg >= 4) + cs.Irex |= REX_W; + ce = gen(ce,&cs); // MOV reg,EA + } + } +#if TARGET_SEGMENTED + else if (tyfv(tyml) || tyml == TYhptr) + { regm_t idxregs; + + if (tyml == TYhptr) + getlvalue_lsw(&cs); + idxregs = idxregm(&cs); + retregs = forregs & ~idxregs; + if (!(retregs & IDXREGS)) + retregs |= IDXREGS & ~idxregs; + if (!(retregs & mMSW)) + retregs |= mMSW & ALLREGS; + ce = allocreg(&retregs,®,tyml); + NEWREG(cs.Irm,findreglsw(retregs)); + if (retregs & mES) /* if want ES loaded */ + { cs.Iop = 0xC4; + ce = gen(ce,&cs); /* LES lreg,EA */ + } + else + { cs.Iop = 0x8B; + ce = gen(ce,&cs); /* MOV lreg,EA */ + getlvalue_msw(&cs); + if (I32) + cs.Iflags |= CFopsize; + NEWREG(cs.Irm,reg); + gen(ce,&cs); /* MOV mreg,EA+2 */ + } + } +#endif + else if (sz == 2 * REGSIZE) + { regm_t idx; + code *cm,*cl; + + idx = idxregm(&cs); + retregs = forregs; + if (!retregs) + retregs = ALLREGS; + ce = allocreg(&retregs,®,tyml); + cs.Iop = 0x8B; + NEWREG(cs.Irm,reg); + cm = gen(NULL,&cs); // MOV reg,EA+2 + NEWREG(cs.Irm,findreglsw(retregs)); + getlvalue_lsw(&cs); + cl = gen(NULL,&cs); // MOV reg+1,EA + if (mask[reg] & idx) + ce = cat3(ce,cl,cm); + else + ce = cat3(ce,cm,cl); + } + else + assert(0); + c = cat(c,ce); + if (e1->Ecount) /* if we gen a CSE */ + cssave(e1,retregs,EOP(e1)); + } + freenode(e1); + if (sz <= REGSIZE) + *pretregs &= ~mPSW; // flags are already set + return cat(c,fixresult(e,retregs,pretregs)); +} + +/******************************** + * Generate code for *= /= %= + */ + +code *cdmulass(elem *e,regm_t *pretregs) +{ + code *cr,*cl,*cg,*c,cs; + regm_t retregs; + unsigned resreg,reg,opr,lib,byte; + + //printf("cdmulass(e=%p, *pretregs = %s)\n",e,regm_str(*pretregs)); + elem *e1 = e->E1; + elem *e2 = e->E2; + unsigned op = e->Eoper; // OPxxxx + + tym_t tyml = tybasic(e1->Ety); // type of lvalue + char uns = tyuns(tyml) || tyuns(e2->Ety); + unsigned sz = tysize[tyml]; + + unsigned rex = (I64 && sz == 8) ? REX_W : 0; + unsigned grex = rex << 16; // 64 bit operands + + // See if evaluate in XMM registers + if (config.fpxmmregs && tyxmmreg(tyml) && op != OPmodass && !(*pretregs & mST0)) + return xmmopass(e,pretregs); + + if (tyfloating(tyml)) + { +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + return opass87(e,pretregs); +#else + return opassdbl(e,pretregs,op); +#endif + } + + if (sz <= REGSIZE) /* if word or byte */ + { byte = (sz == 1); /* 1 for byte operation */ + resreg = AX; /* result register for * or / */ + if (uns) /* if unsigned operation */ + opr = 4; /* MUL */ + else /* else signed */ + opr = 5; /* IMUL */ + if (op != OPmulass) /* if /= or %= */ + { opr += 2; /* MUL => DIV, IMUL => IDIV */ + if (op == OPmodass) + resreg = DX; /* remainder is in DX */ + } + if (op == OPmulass) /* if multiply */ + { + if (config.target_cpu >= TARGET_80286 && + e2->Eoper == OPconst && !byte) + { + targ_size_t e2factor = el_tolong(e2); + if (I64 && sz == 8 && e2factor != (int)e2factor) + goto L1; + freenode(e2); + cr = CNIL; + cl = getlvalue(&cs,e1,0); /* get EA */ + regm_t idxregs = idxregm(&cs); + retregs = *pretregs & (ALLREGS | mBP) & ~idxregs; + if (!retregs) + retregs = ALLREGS & ~idxregs; + cg = allocreg(&retregs,&resreg,tyml); + cs.Iop = 0x69; /* IMUL reg,EA,e2value */ + cs.IFL2 = FLconst; + cs.IEV2.Vint = e2factor; + opr = resreg; + } + else if (!I16 && !byte) + { + L1: + retregs = *pretregs & (ALLREGS | mBP); + if (!retregs) + retregs = ALLREGS; + cr = codelem(e2,&retregs,FALSE); /* load rvalue in reg */ + cl = getlvalue(&cs,e1,retregs); /* get EA */ + cg = getregs(retregs); /* destroy these regs */ + cs.Iop = 0x0FAF; // IMUL resreg,EA + resreg = findreg(retregs); + opr = resreg; + } + else + { + retregs = mAX; + cr = codelem(e2,&retregs,FALSE); // load rvalue in AX + cl = getlvalue(&cs,e1,mAX); // get EA + cg = getregs(byte ? mAX : mAX | mDX); // destroy these regs + cs.Iop = 0xF7 ^ byte; // [I]MUL EA + } + code_newreg(&cs,opr); + c = gen(CNIL,&cs); + } + else // /= or %= + { targ_size_t e2factor; + int pow2; + + assert(!byte); // should never happen + assert(I16 || sz != SHORTSIZE); + if (config.flags4 & CFG4speed && + e2->Eoper == OPconst && !uns && + (sz == REGSIZE || (I64 && sz == 4)) && + (pow2 = ispow2(e2factor = el_tolong(e2))) != -1 && + e2factor == (int)e2factor && + !(config.target_cpu < TARGET_80286 && pow2 != 1 && op == OPdivass) + ) + { + // Signed divide or modulo by power of 2 + cr = NULL; + c = NULL; + cl = getlvalue(&cs,e1,mAX | mDX); + cs.Iop = 0x8B; + code_newreg(&cs, AX); + cl = gen(cl,&cs); // MOV AX,EA + freenode(e2); + cg = getregs(mAX | mDX); // trash these regs + cg = gen1(cg,0x99); // CWD + code_orrex(cg, rex); + if (pow2 == 1) + { + if (op == OPdivass) + { gen2(cg,0x2B,grex | modregrm(3,AX,DX)); // SUB AX,DX + gen2(cg,0xD1,grex | modregrm(3,7,AX)); // SAR AX,1 + resreg = AX; + } + else // OPmod + { gen2(cg,0x33,grex | modregrm(3,AX,DX)); // XOR AX,DX + genc2(cg,0x81,grex | modregrm(3,4,AX),1); // AND AX,1 + gen2(cg,0x03,grex | modregrm(3,DX,AX)); // ADD DX,AX + resreg = DX; + } + } + else + { + assert(pow2 < 32); + targ_ulong m = (1 << pow2) - 1; + if (op == OPdivass) + { genc2(cg,0x81,grex | modregrm(3,4,DX),m); // AND DX,m + gen2(cg,0x03,grex | modregrm(3,AX,DX)); // ADD AX,DX + // Be careful not to generate this for 8088 + assert(config.target_cpu >= TARGET_80286); + genc2(cg,0xC1,grex | modregrm(3,7,AX),pow2); // SAR AX,pow2 + resreg = AX; + } + else // OPmodass + { gen2(cg,0x33,grex | modregrm(3,AX,DX)); // XOR AX,DX + gen2(cg,0x2B,grex | modregrm(3,AX,DX)); // SUB AX,DX + genc2(cg,0x81,grex | modregrm(3,4,AX),m); // AND AX,m + gen2(cg,0x33,grex | modregrm(3,AX,DX)); // XOR AX,DX + gen2(cg,0x2B,grex | modregrm(3,AX,DX)); // SUB AX,DX + resreg = AX; + } + } + } + else + { + retregs = ALLREGS & ~(mAX|mDX); // DX gets sign extension + cr = codelem(e2,&retregs,FALSE); // load rvalue in retregs + reg = findreg(retregs); + cl = getlvalue(&cs,e1,mAX | mDX | retregs); // get EA + cg = getregs(mAX | mDX); // destroy these regs + cs.Irm |= modregrm(0,AX,0); + cs.Iop = 0x8B; + c = gen(CNIL,&cs); // MOV AX,EA + if (uns) // if unsigned + movregconst(c,DX,0,0); // CLR DX + else // else signed + { gen1(c,0x99); // CWD + code_orrex(c,rex); + } + c = cat(c,getregs(mDX | mAX)); // DX and AX will be destroyed + genregs(c,0xF7,opr,reg); // OPR reg + code_orrex(c,rex); + } + } + cs.Iop = 0x89 ^ byte; + code_newreg(&cs,resreg); + c = gen(c,&cs); // MOV EA,resreg + if (e1->Ecount) // if we gen a CSE + cssave(e1,mask[resreg],EOP(e1)); + freenode(e1); + c = cat(c,fixresult(e,mask[resreg],pretregs)); + return cat4(cr,cl,cg,c); + } + else if (sz == 2 * REGSIZE) + { + lib = CLIBlmul; + if (op == OPdivass || op == OPmodass) + { lib = (uns) ? CLIBuldiv : CLIBldiv; + if (op == OPmodass) + lib++; + } + retregs = mCX | mBX; + cr = codelem(e2,&retregs,FALSE); + cl = getlvalue(&cs,e1,mDX|mAX | mCX|mBX); + cl = cat(cl,getregs(mDX | mAX)); + cs.Iop = 0x8B; + cl = gen(cl,&cs); /* MOV AX,EA */ + getlvalue_msw(&cs); + cs.Irm |= modregrm(0,DX,0); + gen(cl,&cs); /* MOV DX,EA+2 */ + getlvalue_lsw(&cs); + retregs = 0; + if (config.target_cpu >= TARGET_PentiumPro && op == OPmulass) + { + /* IMUL ECX,EAX + IMUL EDX,EBX + ADD ECX,EDX + MUL EBX + ADD EDX,ECX + */ + c = getregs(mAX|mDX|mCX); + c = gen2(c,0x0FAF,modregrm(3,CX,AX)); + gen2(c,0x0FAF,modregrm(3,DX,BX)); + gen2(c,0x03,modregrm(3,CX,DX)); + gen2(c,0xF7,modregrm(3,4,BX)); + gen2(c,0x03,modregrm(3,DX,CX)); + retregs = mDX | mAX; + } + else + c = callclib(e,lib,&retregs,idxregm(&cs)); + reg = (op == OPmodass) ? BX : AX; + retregs = mask[reg]; + cs.Iop = 0x89; + NEWREG(cs.Irm,reg); + gen(c,&cs); /* MOV EA,lsreg */ + reg = (op == OPmodass) ? CX : DX; + retregs |= mask[reg]; + NEWREG(cs.Irm,reg); + getlvalue_msw(&cs); + gen(c,&cs); /* MOV EA+2,msreg */ + if (e1->Ecount) /* if we gen a CSE */ + cssave(e1,retregs,EOP(e1)); + freenode(e1); + cg = fixresult(e,retregs,pretregs); + return cat4(cr,cl,c,cg); + } + else + { assert(0); + /* NOTREACHED */ + return 0; + } +} + + +/******************************** + * Generate code for <<= and >>= + */ + +code *cdshass(elem *e,regm_t *pretregs) +{ elem *e1,*e2; + code *cr,*cl,*cg,*c,cs,*ce; + tym_t tym,tyml; + regm_t retregs; + unsigned shiftcnt,op1,op2,reg,v,oper,byte,conste2; + unsigned loopcnt; + unsigned sz; + + e1 = e->E1; + e2 = e->E2; + + tyml = tybasic(e1->Ety); /* type of lvalue */ + sz = tysize[tyml]; + byte = tybyte(e->Ety) != 0; /* 1 for byte operations */ + tym = tybasic(e->Ety); /* type of result */ + oper = e->Eoper; + assert(tysize(e2->Ety) <= REGSIZE); + + unsigned rex = (I64 && sz == 8) ? REX_W : 0; + + // if our lvalue is a cse, make sure we evaluate for result in register + if (e1->Ecount && !(*pretregs & (ALLREGS | mBP)) && !isregvar(e1,&retregs,®)) + *pretregs |= ALLREGS; + +#if SCPP + // Do this until the rest of the compiler does OPshr/OPashr correctly + if (oper == OPshrass) + oper = tyuns(tyml) ? OPshrass : OPashrass; +#endif + + // Select opcodes. op2 is used for msw for long shifts. + + switch (oper) + { case OPshlass: + op1 = 4; // SHL + op2 = 2; // RCL + break; + case OPshrass: + op1 = 5; // SHR + op2 = 3; // RCR + break; + case OPashrass: + op1 = 7; // SAR + op2 = 3; // RCR + break; + default: + assert(0); + } + + + v = 0xD3; /* for SHIFT xx,CL cases */ + loopcnt = 1; + conste2 = FALSE; + cr = CNIL; + shiftcnt = 0; // avoid "use before initialized" warnings + if (cnst(e2)) + { + conste2 = TRUE; /* e2 is a constant */ + shiftcnt = e2->EV.Vint; /* byte ordering of host */ + if (config.target_cpu >= TARGET_80286 && + sz <= REGSIZE && + shiftcnt != 1) + v = 0xC1; // SHIFT xx,shiftcnt + else if (shiftcnt <= 3) + { loopcnt = shiftcnt; + v = 0xD1; // SHIFT xx,1 + } + } + if (v == 0xD3) /* if COUNT == CL */ + { retregs = mCX; + cr = codelem(e2,&retregs,FALSE); + } + else + freenode(e2); + cl = getlvalue(&cs,e1,mCX); /* get lvalue, preserve CX */ + cl = cat(cl,modEA(&cs)); // check for modifying register + + if (*pretregs == 0 || /* if don't return result */ + (*pretregs == mPSW && conste2 && tysize[tym] <= REGSIZE) || + sz > REGSIZE + ) + { retregs = 0; // value not returned in a register + cs.Iop = v ^ byte; + c = CNIL; + while (loopcnt--) + { + NEWREG(cs.Irm,op1); /* make sure op1 is first */ + if (sz <= REGSIZE) + { + if (conste2) + { cs.IFL2 = FLconst; + cs.IEV2.Vint = shiftcnt; + } + c = gen(c,&cs); /* SHIFT EA,[CL|1] */ + if (*pretregs & mPSW && !loopcnt && conste2) + code_orflag(c,CFpsw); + } + else /* TYlong */ + { cs.Iop = 0xD1; /* plain shift */ + ce = gennop(CNIL); /* ce: NOP */ + if (v == 0xD3) + { c = getregs(mCX); + if (!conste2) + { assert(loopcnt == 0); + c = genjmp(c,JCXZ,FLcode,(block *) ce); /* JCXZ ce */ + } + } + if (oper == OPshlass) + { cg = gen(CNIL,&cs); // cg: SHIFT EA + c = cat(c,cg); + getlvalue_msw(&cs); + NEWREG(cs.Irm,op2); + gen(c,&cs); /* SHIFT EA */ + getlvalue_lsw(&cs); + } + else + { getlvalue_msw(&cs); + cg = gen(CNIL,&cs); + c = cat(c,cg); + NEWREG(cs.Irm,op2); + getlvalue_lsw(&cs); + gen(c,&cs); + } + if (v == 0xD3) /* if building a loop */ + { genjmp(c,LOOP,FLcode,(block *) cg); /* LOOP cg */ + regimmed_set(CX,0); /* note that now CX == 0 */ + } + c = cat(c,ce); + } + } + + /* If we want the result, we must load it from the EA */ + /* into a register. */ + + if (sz == 2 * REGSIZE && *pretregs) + { retregs = *pretregs & (ALLREGS | mBP); + if (retregs) + { ce = allocreg(&retregs,®,tym); + cs.Iop = 0x8B; + + /* be careful not to trash any index regs */ + /* do MSW first (which can't be an index reg) */ + getlvalue_msw(&cs); + NEWREG(cs.Irm,reg); + cg = gen(CNIL,&cs); + getlvalue_lsw(&cs); + reg = findreglsw(retregs); + NEWREG(cs.Irm,reg); + gen(cg,&cs); + if (*pretregs & mPSW) + cg = cat(cg,tstresult(retregs,tyml,TRUE)); + } + else /* flags only */ + { retregs = ALLREGS & ~idxregm(&cs); + ce = allocreg(&retregs,®,TYint); + cs.Iop = 0x8B; + NEWREG(cs.Irm,reg); + cg = gen(CNIL,&cs); /* MOV reg,EA */ + cs.Iop = 0x0B; /* OR reg,EA+2 */ + cs.Iflags |= CFpsw; + getlvalue_msw(&cs); + gen(cg,&cs); + } + c = cat3(c,ce,cg); + } + cg = CNIL; + } + else /* else must evaluate in register */ + { + if (sz <= REGSIZE) + { + regm_t possregs = ALLREGS & ~mCX & ~idxregm(&cs); + if (byte) + possregs &= BYTEREGS; + retregs = *pretregs & possregs; + if (retregs == 0) + retregs = possregs; + cg = allocreg(&retregs,®,tym); + cs.Iop = 0x8B ^ byte; + code_newreg(&cs, reg); + if (byte && I64 && (reg >= 4)) + cs.Irex |= REX; + c = ce = gen(CNIL,&cs); /* MOV reg,EA */ + if (!I16) + { + assert(!byte || (mask[reg] & BYTEREGS)); + ce = genc2(CNIL,v ^ byte,modregrmx(3,op1,reg),shiftcnt); + if (byte && I64 && (reg >= 4)) + ce->Irex |= REX; + code_orrex(ce, rex); + /* We can do a 32 bit shift on a 16 bit operand if */ + /* it's a left shift and we're not concerned about */ + /* the flags. Remember that flags are not set if */ + /* a shift of 0 occurs. */ + if (tysize[tym] == SHORTSIZE && + (oper == OPshrass || oper == OPashrass || + (*pretregs & mPSW && conste2))) + ce->Iflags |= CFopsize; /* 16 bit operand */ + cat(c,ce); + } + else + { + while (loopcnt--) + { /* Generate shift instructions. */ + genc2(ce,v ^ byte,modregrm(3,op1,reg),shiftcnt); + } + } + if (*pretregs & mPSW && conste2) + { assert(shiftcnt); + *pretregs &= ~mPSW; // result is already in flags + code_orflag(ce,CFpsw); + } + + cs.Iop = 0x89 ^ byte; + if (byte && I64 && (reg >= 4)) + cs.Irex |= REX; + gen(ce,&cs); /* MOV EA,reg */ + + // If result is not in correct register + cat(ce,fixresult(e,retregs,pretregs)); + retregs = *pretregs; + } + else + assert(0); + } + if (e1->Ecount && !(retregs & regcon.mvar)) // if lvalue is a CSE + cssave(e1,retregs,EOP(e1)); + freenode(e1); + *pretregs = retregs; + return cat4(cr,cl,cg,c); +} + + +/********************************** + * Generate code for compares. + * Handles lt,gt,le,ge,eqeq,ne for all data types. + */ + +code *cdcmp(elem *e,regm_t *pretregs) +{ regm_t retregs,rretregs; + unsigned reg,rreg,op,jop,byte; + tym_t tym; + code *cl,*cr,*c,cs,*ce,*cg; + elem *e1,*e2; + bool eqorne; + unsigned reverse; + unsigned sz; + int fl; + int flag; + + //printf("cdcmp(e = %p, retregs = %s)\n",e,regm_str(*pretregs)); + // Collect extra parameter. This is pretty ugly... + flag = cdcmp_flag; + cdcmp_flag = 0; + + e1 = e->E1; + e2 = e->E2; + if (*pretregs == 0) /* if don't want result */ + { cl = codelem(e1,pretregs,FALSE); + *pretregs = 0; /* in case e1 changed it */ + cr = codelem(e2,pretregs,FALSE); + return cat(cl,cr); + } + + jop = jmpopcode(e); // must be computed before + // leaves are free'd + reverse = 0; + cl = cr = CNIL; + op = e->Eoper; + assert(OTrel(op)); + eqorne = (op == OPeqeq) || (op == OPne); + + tym = tybasic(e1->Ety); + sz = tysize[tym]; + byte = sz == 1; + + unsigned rex = (I64 && sz == 8) ? REX_W : 0; + unsigned grex = rex << 16; // 64 bit operands + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (tyfloating(tym)) /* if floating operation */ + { + retregs = mPSW; + if (tyxmmreg(tym) && config.fpxmmregs) + c = orthxmm(e,&retregs); + else + c = orth87(e,&retregs); + goto L3; + } +#else + if (tyfloating(tym)) /* if floating operation */ + { + if (config.inline8087) + { retregs = mPSW; + c = orth87(e,&retregs); + } + else + { int clib; + + retregs = 0; /* skip result for now */ + if (iffalse(e2)) /* second operand is constant 0 */ + { assert(!eqorne); /* should be OPbool or OPnot */ + if (tym == TYfloat) + { retregs = FLOATREGS; + clib = CLIBftst0; + } + else + { retregs = DOUBLEREGS; + clib = CLIBdtst0; + } + if (rel_exception(op)) + clib += CLIBdtst0exc - CLIBdtst0; + cl = codelem(e1,&retregs,FALSE); + retregs = 0; + c = callclib(e,clib,&retregs,0); + freenode(e2); + } + else + { clib = CLIBdcmp; + if (rel_exception(op)) + clib += CLIBdcmpexc - CLIBdcmp; + c = opdouble(e,&retregs,clib); + } + } + goto L3; + } +#endif + + /* If it's a signed comparison of longs, we have to call a library */ + /* routine, because we don't know the target of the signed branch */ + /* (have to set up flags so that jmpopcode() will do it right) */ + if (!eqorne && + (I16 && tym == TYlong && tybasic(e2->Ety) == TYlong || + I32 && tym == TYllong && tybasic(e2->Ety) == TYllong) + ) + { retregs = mDX | mAX; + cl = codelem(e1,&retregs,FALSE); + retregs = mCX | mBX; + cr = scodelem(e2,&retregs,mDX | mAX,FALSE); + + if (I16) + { + retregs = 0; + c = callclib(e,CLIBlcmp,&retregs,0); /* gross, but it works */ + } + else + { + /* Generate: + * CMP EDX,ECX + * JNE C1 + * XOR EDX,EDX + * CMP EAX,EBX + * JZ C1 + * JA C3 + * DEC EDX + * JMP C1 + * C3: INC EDX + * C1: + */ + c = getregs(mDX); + c = genregs(c,0x39,CX,DX); // CMP EDX,ECX + code *c1 = gennop(CNIL); + genjmp(c,JNE,FLcode,(block *)c1); // JNE C1 + movregconst(c,DX,0,0); // XOR EDX,EDX + genregs(c,0x39,BX,AX); // CMP EAX,EBX + genjmp(c,JE,FLcode,(block *)c1); // JZ C1 + code *c3 = gen1(CNIL,0x40 + DX); // INC EDX + genjmp(c,JA,FLcode,(block *)c3); // JA C3 + gen1(c,0x48 + DX); // DEC EDX + genjmp(c,JMPS,FLcode,(block *)c1); // JMP C1 + c = cat4(c,c3,c1,getregs(mDX)); + retregs = mPSW; + } + goto L3; + } + + /* See if we should swap operands */ + if (e1->Eoper == OPvar && e2->Eoper == OPvar && evalinregister(e2)) + { e1 = e->E2; + e2 = e->E1; + reverse = 2; + } + + retregs = allregs; + if (byte) + retregs = BYTEREGS; + + c = CNIL; + ce = CNIL; + cs.Iflags = (!I16 && sz == SHORTSIZE) ? CFopsize : 0; + cs.Irex = rex; + if (sz > REGSIZE) + ce = gennop(ce); + + switch (e2->Eoper) + { + default: + L2: + cl = scodelem(e1,&retregs,0,TRUE); /* compute left leaf */ + L1: + rretregs = allregs & ~retregs; + if (byte) + rretregs &= BYTEREGS; + cr = scodelem(e2,&rretregs,retregs,TRUE); /* get right leaf */ + if (sz <= REGSIZE) /* CMP reg,rreg */ + { reg = findreg(retregs); /* get reg that e1 is in */ + rreg = findreg(rretregs); + c = genregs(CNIL,0x3B ^ byte ^ reverse,reg,rreg); + code_orrex(c, rex); + if (!I16 && sz == SHORTSIZE) + c->Iflags |= CFopsize; /* compare only 16 bits */ + if (I64 && byte && (reg >= 4 || rreg >= 4)) + c->Irex |= REX; // address byte registers + } + else + { assert(sz <= 2 * REGSIZE); + + /* Compare MSW, if they're equal then compare the LSW */ + reg = findregmsw(retregs); + rreg = findregmsw(rretregs); + c = genregs(CNIL,0x3B ^ reverse,reg,rreg); /* CMP reg,rreg */ + if (I32 && sz == 6) + c->Iflags |= CFopsize; /* seg is only 16 bits */ + else if (I64) + code_orrex(c, REX_W); + genjmp(c,JNE,FLcode,(block *) ce); /* JNE nop */ + + reg = findreglsw(retregs); + rreg = findreglsw(rretregs); + genregs(c,0x3B ^ reverse,reg,rreg); /* CMP reg,rreg */ + if (I64) + code_orrex(c, REX_W); + } + break; + case OPrelconst: + if (I64 && config.flags3 & CFG3pic) + goto L2; + fl = el_fl(e2); + switch (fl) + { case FLfunc: + fl = FLextern; // so it won't be self-relative + break; + case FLdata: + case FLudata: + case FLextern: + if (sz > REGSIZE) // compare against DS, not DGROUP + goto L2; + break; +#if TARGET_SEGMENTED + case FLfardata: + break; +#endif + default: + goto L2; + } + cs.IFL2 = fl; + cs.IEVsym2 = e2->EV.sp.Vsym; + if (sz > REGSIZE) + { cs.Iflags |= CFseg; + cs.IEVoffset2 = 0; + } + else + { cs.Iflags |= CFoff; + cs.IEVoffset2 = e2->EV.sp.Voffset; + } + goto L4; + + case OPconst: + // If compare against 0 + if (sz <= REGSIZE && *pretregs == mPSW && !boolres(e2) && + isregvar(e1,&retregs,®) + ) + { // Just do a TEST instruction + c = genregs(NULL,0x85 ^ byte,reg,reg); // TEST reg,reg + c->Iflags |= (cs.Iflags & CFopsize) | CFpsw; + code_orrex(c, rex); + if (I64 && byte && reg >= 4) + c->Irex |= REX; // address byte registers + retregs = mPSW; + break; + } + + if (!tyuns(tym) && !tyuns(e2->Ety) && + !boolres(e2) && !(*pretregs & mPSW) && + (sz == REGSIZE || (I64 && sz == 4)) && + (!I16 || op == OPlt || op == OPge)) + { + assert(*pretregs & (allregs)); + cl = codelem(e1,pretregs,FALSE); + reg = findreg(*pretregs); + c = getregs(mask[reg]); + switch (op) + { case OPle: + c = genc2(c,0x81,grex | modregrmx(3,0,reg & 7),(unsigned)-1); // ADD reg,-1 + code_orflag(c, CFpsw); + genc2(c,0x81,grex | modregrmx(3,2,reg & 7),0); // ADC reg,0 + goto oplt; + case OPgt: + c = gen2(c,0xF7,grex | modregrmx(3,3,reg & 7)); // NEG reg +#if TARGET_WINDOS + // What does the Windows platform do? + // lower INT_MIN by 1? See test exe9.c + // BUG: fix later + code_orflag(c, CFpsw); + genc2(c,0x81,grex | modregrmx(3,3,reg),0); // SBB reg,0 +#endif + goto oplt; + case OPlt: + oplt: + if (!I16) + c = genc2(c,0xC1,grex | modregrmx(3,5,reg),sz * 8 - 1); // SHR reg,31 + else + { /* 8088-286 do not have a barrel shifter, so use this + faster sequence + */ + c = genregs(c,0xD1,0,reg); /* ROL reg,1 */ + unsigned regi; + if (reghasvalue(allregs,1,®i)) + c = genregs(c,0x23,reg,regi); /* AND reg,regi */ + else + c = genc2(c,0x81,modregrm(3,4,reg),1); /* AND reg,1 */ + } + break; + case OPge: + c = genregs(c,0xD1,4,reg); /* SHL reg,1 */ + code_orrex(c,rex); + code_orflag(c, CFpsw); + genregs(c,0x19,reg,reg); /* SBB reg,reg */ + code_orrex(c,rex); + if (I64) + { + c = gen2(c,0xFF,modregrmx(3,0,reg)); // INC reg + code_orrex(c, rex); + } + else + c = gen1(c,0x40 + reg); // INC reg + break; + + default: + assert(0); + } + freenode(e2); + goto ret; + } + + cs.IFL2 = FLconst; + if (sz == 16) + cs.IEV2.Vsize_t = e2->EV.Vcent.msw; + else if (sz > REGSIZE) + cs.IEV2.Vint = MSREG(e2->EV.Vllong); + else + cs.IEV2.Vsize_t = e2->EV.Vllong; + + // The cmp immediate relies on sign extension of the 32 bit immediate value + if (I64 && sz >= REGSIZE && cs.IEV2.Vsize_t != (int)cs.IEV2.Vint) + goto L2; + L4: + cs.Iop = 0x81 ^ byte; + + /* if ((e1 is data or a '*' reference) and it's not a + * common subexpression + */ + + if ((e1->Eoper == OPvar && datafl[el_fl(e1)] || + e1->Eoper == OPind) && + !evalinregister(e1)) + { cl = getlvalue(&cs,e1,RMload); + freenode(e1); + if (evalinregister(e2)) + { + retregs = idxregm(&cs); + if ((cs.Iflags & CFSEG) == CFes) + retregs |= mES; /* take no chances */ + rretregs = allregs & ~retregs; + if (byte) + rretregs &= BYTEREGS; + cr = scodelem(e2,&rretregs,retregs,TRUE); + cs.Iop = 0x39 ^ byte ^ reverse; + if (sz > REGSIZE) + { + rreg = findregmsw(rretregs); + cs.Irm |= modregrm(0,rreg,0); + getlvalue_msw(&cs); + c = gen(CNIL,&cs); /* CMP EA+2,rreg */ + if (I32 && sz == 6) + c->Iflags |= CFopsize; /* seg is only 16 bits */ + if (I64 && byte && rreg >= 4) + c->Irex |= REX; + genjmp(c,JNE,FLcode,(block *) ce); /* JNE nop */ + rreg = findreglsw(rretregs); + NEWREG(cs.Irm,rreg); + getlvalue_lsw(&cs); + } + else + { + rreg = findreg(rretregs); + code_newreg(&cs, rreg); + if (I64 && byte && rreg >= 4) + cs.Irex |= REX; + } + } + else + { + cs.Irm |= modregrm(0,7,0); + if (sz > REGSIZE) + { +#if !TARGET_SEGMENTED + if (sz == 6) + assert(0); +#endif + if (e2->Eoper == OPrelconst) + { cs.Iflags = (cs.Iflags & ~(CFoff | CFseg)) | CFseg; + cs.IEVoffset2 = 0; + } + getlvalue_msw(&cs); + c = gen(CNIL,&cs); /* CMP EA+2,const */ + if (!I16 && sz == 6) + c->Iflags |= CFopsize; /* seg is only 16 bits */ + genjmp(c,JNE,FLcode,(block *) ce); /* JNE nop */ + if (e2->Eoper == OPconst) + cs.IEV2.Vint = e2->EV.Vllong; + else if (e2->Eoper == OPrelconst) + { /* Turn off CFseg, on CFoff */ + cs.Iflags ^= CFseg | CFoff; + cs.IEVoffset2 = e2->EV.sp.Voffset; + } + else + assert(0); + getlvalue_lsw(&cs); + } + freenode(e2); + } + c = gen(c,&cs); + break; + } + + if (evalinregister(e2) && !OTassign(e1->Eoper) && + !isregvar(e1,NULL,NULL)) + { regm_t m; + + m = allregs & ~regcon.mvar; + if (byte) + m &= BYTEREGS; + if (m & (m - 1)) // if more than one free register + goto L2; + } + if ((e1->Eoper == OPstrcmp || (OTassign(e1->Eoper) && sz <= REGSIZE)) && + !boolres(e2) && !evalinregister(e1)) + { + retregs = mPSW; + cl = scodelem(e1,&retregs,0,FALSE); + freenode(e2); + break; + } + if (sz <= REGSIZE && !boolres(e2) && e1->Eoper == OPadd && *pretregs == mPSW) + { + retregs |= mPSW; + cl = scodelem(e1,&retregs,0,FALSE); + freenode(e2); + break; + } + cl = scodelem(e1,&retregs,0,TRUE); /* compute left leaf */ + if (sz == 1) + { + reg = findreg(retregs & allregs); // get reg that e1 is in + cs.Irm = modregrm(3,7,reg & 7); + if (reg & 8) + cs.Irex |= REX_B; + if (e1->Eoper == OPvar && e1->EV.sp.Voffset == 1 && e1->EV.sp.Vsym->Sfl == FLreg) + { assert(reg < 4); + cs.Irm |= 4; // use upper register half + } + if (I64 && reg >= 4) + cs.Irex |= REX; // address byte registers + } + else if (sz <= REGSIZE) + { /* CMP reg,const */ + reg = findreg(retregs & allregs); // get reg that e1 is in + rretregs = allregs & ~retregs; + if (cs.IFL2 == FLconst && reghasvalue(rretregs,cs.IEV2.Vint,&rreg)) + { + code *cc = genregs(CNIL,0x3B,reg,rreg); + code_orrex(cc, rex); + if (!I16) + cc->Iflags |= cs.Iflags & CFopsize; + c = cat(c,cc); + freenode(e2); + break; + } + cs.Irm = modregrm(3,7,reg & 7); + if (reg & 8) + cs.Irex |= REX_B; + } + else if (sz <= 2 * REGSIZE) + { + reg = findregmsw(retregs); // get reg that e1 is in + cs.Irm = modregrm(3,7,reg); + c = gen(CNIL,&cs); /* CMP reg,MSW */ + if (I32 && sz == 6) + c->Iflags |= CFopsize; /* seg is only 16 bits */ + genjmp(c,JNE,FLcode,(block *) ce); /* JNE ce */ + + reg = findreglsw(retregs); + cs.Irm = modregrm(3,7,reg); + if (e2->Eoper == OPconst) + cs.IEV2.Vint = e2->EV.Vlong; + else if (e2->Eoper == OPrelconst) + { /* Turn off CFseg, on CFoff */ + cs.Iflags ^= CFseg | CFoff; + cs.IEVoffset2 = e2->EV.sp.Voffset; + } + else + assert(0); + } + else + assert(0); + c = gen(c,&cs); /* CMP sucreg,LSW */ + freenode(e2); + break; + + case OPind: + if (e2->Ecount) + goto L2; + goto L5; + + case OPvar: +#if TARGET_OSX + if (movOnly(e2)) + goto L2; +#endif + if ((e1->Eoper == OPvar && + isregvar(e2,&rretregs,®) && + sz <= REGSIZE + ) || + (e1->Eoper == OPind && + isregvar(e2,&rretregs,®) && + !evalinregister(e1) && + sz <= REGSIZE + ) + ) + { + // CMP EA,e2 + cl = getlvalue(&cs,e1,RMload); + freenode(e1); + cs.Iop = 0x39 ^ byte ^ reverse; + code_newreg(&cs,reg); + if (I64 && byte && reg >= 4) + cs.Irex |= REX; // address byte registers + c = gen(c,&cs); + freenode(e2); + break; + } + L5: + cl = scodelem(e1,&retregs,0,TRUE); /* compute left leaf */ + if (sz <= REGSIZE) /* CMP reg,EA */ + { + reg = findreg(retregs & allregs); // get reg that e1 is in + unsigned opsize = cs.Iflags & CFopsize; + c = cat(c,loadea(e2,&cs,0x3B ^ byte ^ reverse,reg,0,RMload | retregs,0)); + code_orflag(c,opsize); + } + else if (sz <= 2 * REGSIZE) + { + reg = findregmsw(retregs); /* get reg that e1 is in */ + // CMP reg,EA + c = loadea(e2,&cs,0x3B ^ reverse,reg,REGSIZE,RMload | retregs,0); + if (I32 && sz == 6) + c->Iflags |= CFopsize; /* seg is only 16 bits */ + genjmp(c,JNE,FLcode,(block *) ce); /* JNE ce */ + reg = findreglsw(retregs); + if (e2->Eoper == OPind) + { + NEWREG(cs.Irm,reg); + getlvalue_lsw(&cs); + c = gen(c,&cs); + } + else + c = cat(c,loadea(e2,&cs,0x3B ^ reverse,reg,0,RMload | retregs,0)); + } + else + assert(0); + freenode(e2); + break; + } + c = cat(c,ce); + +L3: + if ((retregs = (*pretregs & (ALLREGS | mBP))) != 0) // if return result in register + { code *nop = CNIL; + regm_t save = regcon.immed.mval; + cg = allocreg(&retregs,®,TYint); + regcon.immed.mval = save; + if ((*pretregs & mPSW) == 0 && + (jop == JC || jop == JNC)) + { + cg = cat(cg,getregs(retregs)); + cg = genregs(cg,0x19,reg,reg); /* SBB reg,reg */ + if (rex) + code_orrex(cg, rex); + if (flag) + ; // cdcond() will handle it + else if (jop == JNC) + { + if (I64) + { + cg = gen2(cg,0xFF,modregrmx(3,0,reg)); // INC reg + code_orrex(cg, rex); + } + else + gen1(cg,0x40 + reg); // INC reg + } + else + { gen2(cg,0xF7,modregrmx(3,3,reg)); /* NEG reg */ + code_orrex(cg, rex); + } + } + else if (I64 && sz == 8) + { + assert(!flag); + cg = movregconst(cg,reg,1,64|8); // MOV reg,1 + nop = gennop(nop); + cg = genjmp(cg,jop,FLcode,(block *) nop); // Jtrue nop + // MOV reg,0 + movregconst(cg,reg,0,(*pretregs & mPSW) ? 64|8 : 64); + regcon.immed.mval &= ~mask[reg]; + } + else + { + assert(!flag); + cg = movregconst(cg,reg,1,8); // MOV reg,1 + nop = gennop(nop); + cg = genjmp(cg,jop,FLcode,(block *) nop); // Jtrue nop + // MOV reg,0 + movregconst(cg,reg,0,(*pretregs & mPSW) ? 8 : 0); + regcon.immed.mval &= ~mask[reg]; + } + *pretregs = retregs; + c = cat3(c,cg,nop); + } +ret: + return cat3(cl,cr,c); +} + + +/********************************** + * Generate code for signed compare of longs. + * Input: + * targ block* or code* + */ + +code *longcmp(elem *e,bool jcond,unsigned fltarg,code *targ) +{ regm_t retregs,rretregs; + unsigned reg,rreg,op,jop; + code *cl,*cr,*c,cs,*ce; + code *cmsw,*clsw; + elem *e1,*e2; + /* <= > < >= */ + static const unsigned char jopmsw[4] = {JL, JG, JL, JG }; + static const unsigned char joplsw[4] = {JBE, JA, JB, JAE }; + + //printf("longcmp(e = %p)\n", e); + cr = CNIL; + e1 = e->E1; + e2 = e->E2; + op = e->Eoper; + + /* See if we should swap operands */ + if (e1->Eoper == OPvar && e2->Eoper == OPvar && evalinregister(e2)) + { e1 = e->E2; + e2 = e->E1; + op = swaprel(op); + } + + cs.Iflags = 0; + cs.Irex = 0; + + ce = gennop(CNIL); + retregs = ALLREGS; + + switch (e2->Eoper) + { + default: + L2: + cl = scodelem(e1,&retregs,0,TRUE); /* compute left leaf */ + rretregs = ALLREGS & ~retregs; + cr = scodelem(e2,&rretregs,retregs,TRUE); /* get right leaf */ + /* Compare MSW, if they're equal then compare the LSW */ + reg = findregmsw(retregs); + rreg = findregmsw(rretregs); + cmsw = genregs(CNIL,0x3B,reg,rreg); /* CMP reg,rreg */ + + reg = findreglsw(retregs); + rreg = findreglsw(rretregs); + clsw = genregs(CNIL,0x3B,reg,rreg); /* CMP reg,rreg */ + break; + case OPconst: + cs.IEV2.Vint = MSREG(e2->EV.Vllong); // MSW first + cs.IFL2 = FLconst; + cs.Iop = 0x81; + + /* if ((e1 is data or a '*' reference) and it's not a + * common subexpression + */ + + if ((e1->Eoper == OPvar && datafl[el_fl(e1)] || + e1->Eoper == OPind) && + !evalinregister(e1)) + { cl = getlvalue(&cs,e1,0); + freenode(e1); + if (evalinregister(e2)) + { + retregs = idxregm(&cs); + if ((cs.Iflags & CFSEG) == CFes) + retregs |= mES; /* take no chances */ + rretregs = ALLREGS & ~retregs; + cr = scodelem(e2,&rretregs,retregs,TRUE); + rreg = findregmsw(rretregs); + cs.Iop = 0x39; + cs.Irm |= modregrm(0,rreg,0); + getlvalue_msw(&cs); + cmsw = gen(CNIL,&cs); /* CMP EA+2,rreg */ + rreg = findreglsw(rretregs); + NEWREG(cs.Irm,rreg); + } + else + { cs.Irm |= modregrm(0,7,0); + getlvalue_msw(&cs); + cmsw = gen(CNIL,&cs); /* CMP EA+2,const */ + cs.IEV2.Vint = e2->EV.Vlong; + freenode(e2); + } + getlvalue_lsw(&cs); + clsw = gen(CNIL,&cs); /* CMP EA,rreg/const */ + break; + } + if (evalinregister(e2)) + goto L2; + + cl = scodelem(e1,&retregs,0,TRUE); /* compute left leaf */ + reg = findregmsw(retregs); /* get reg that e1 is in */ + cs.Irm = modregrm(3,7,reg); + + cmsw = gen(CNIL,&cs); /* CMP reg,MSW */ + reg = findreglsw(retregs); + cs.Irm = modregrm(3,7,reg); + cs.IEV2.Vint = e2->EV.Vlong; + clsw = gen(CNIL,&cs); /* CMP sucreg,LSW */ + freenode(e2); + break; + case OPvar: + if (!e1->Ecount && e1->Eoper == OPs32_64) + { unsigned msreg; + + retregs = allregs; + cl = scodelem(e1->E1,&retregs,0,TRUE); + freenode(e1); + reg = findreg(retregs); + retregs = allregs & ~retregs; + cr = allocreg(&retregs,&msreg,TYint); + cr = genmovreg(cr,msreg,reg); // MOV msreg,reg + cr = genc2(cr,0xC1,modregrm(3,7,msreg),REGSIZE * 8 - 1); // SAR msreg,31 + cmsw = loadea(e2,&cs,0x3B,msreg,REGSIZE,mask[reg],0); + clsw = loadea(e2,&cs,0x3B,reg,0,mask[reg],0); + freenode(e2); + } + else + { + cl = scodelem(e1,&retregs,0,TRUE); // compute left leaf + reg = findregmsw(retregs); // get reg that e1 is in + cmsw = loadea(e2,&cs,0x3B,reg,REGSIZE,retregs,0); + reg = findreglsw(retregs); + clsw = loadea(e2,&cs,0x3B,reg,0,retregs,0); + freenode(e2); + } + break; + } + + jop = jopmsw[op - OPle]; + if (!(jcond & 1)) jop ^= (JL ^ JG); // toggle jump condition + genjmp(cmsw,jop,fltarg,(block *) targ); /* Jx targ */ + genjmp(cmsw,jop ^ (JL ^ JG),FLcode,(block *) ce); /* Jy nop */ + + jop = joplsw[op - OPle]; + if (!(jcond & 1)) jop ^= 1; // toggle jump condition + genjmp(clsw,jop,fltarg,(block *) targ); /* Jcond targ */ + + c = cse_flush(1); // flush CSE's to memory + freenode(e); + return cat6(cl,cr,c,cmsw,clsw,ce); +} + +/***************************** + * Do conversions. + * Depends on OPd_s32 and CLIBdbllng being in sequence. + */ + +code *cdcnvt(elem *e, regm_t *pretregs) +{ regm_t retregs; + code *c1,*c2; + int i; + static unsigned char clib[][2] = + { OPd_s32, CLIBdbllng, + OPs32_d, CLIBlngdbl, + OPd_s16, CLIBdblint, + OPs16_d, CLIBintdbl, + OPd_u16, CLIBdbluns, + OPu16_d, CLIBunsdbl, + OPd_u32, CLIBdblulng, +#if TARGET_WINDOS + OPu32_d, CLIBulngdbl, +#endif + OPd_s64, CLIBdblllng, + OPs64_d, CLIBllngdbl, + OPd_u64, CLIBdblullng, + OPu64_d, CLIBullngdbl, + OPd_f, CLIBdblflt, + OPf_d, CLIBfltdbl, +#if TARGET_SEGMENTED + OPvp_fp, CLIBvptrfptr, + OPcvp_fp, CLIBcvptrfptr, +#endif + }; + +//printf("cdcnvt: *pretregs = %s\n", regm_str(*pretregs)); +//elem_print(e); + + if (!*pretregs) + return codelem(e->E1,pretregs,FALSE); + if (config.inline8087) + { switch (e->Eoper) + { + case OPld_d: + case OPd_ld: + if (tycomplex(e->E1->Ety)) + { + Lcomplex: + retregs = mST01 | (*pretregs & mPSW); + c1 = codelem(e->E1, &retregs, FALSE); + c2 = fixresult_complex87(e, retregs, pretregs); + return cat(c1, c2); + } + retregs = mST0 | (*pretregs & mPSW); + c1 = codelem(e->E1, &retregs, FALSE); + c2 = fixresult87(e, retregs, pretregs); + return cat(c1, c2); + + case OPf_d: + case OPd_f: + if (config.fpxmmregs && *pretregs & XMMREGS) + return xmmcnvt(e, pretregs); + + /* if won't do us much good to transfer back and */ + /* forth between 8088 registers and 8087 registers */ + if (OTcall(e->E1->Eoper) && !(*pretregs & allregs)) + { + retregs = regmask(e->E1->Ety, e->E1->E1->Ety); + if (retregs & (mXMM1 | mXMM0 |mST01 | mST0)) // if return in ST0 + { + c1 = codelem(e->E1,pretregs,FALSE); + if (*pretregs & mST0) + note87(e, 0, 0); + return c1; + } + else + break; + } + if (tycomplex(e->E1->Ety)) + goto Lcomplex; + goto Lload87; + + case OPs64_d: + if (!I64) + goto Lload87; + /* FALL-THROUGH */ + case OPs32_d: + if (config.fpxmmregs && *pretregs & XMMREGS) + return xmmcnvt(e, pretregs); + /* FALL-THROUGH */ + case OPs16_d: + case OPu16_d: + Lload87: + return load87(e,0,pretregs,NULL,-1); + case OPu32_d: + if (I64 && config.fpxmmregs && *pretregs & XMMREGS) + return xmmcnvt(e,pretregs); + else if (!I16) + { + unsigned retregs = ALLREGS; + c1 = codelem(e->E1, &retregs, FALSE); + unsigned reg = findreg(retregs); + c1 = genfltreg(c1, 0x89, reg, 0); + regwithvalue(c1,ALLREGS,0,®,0); + genfltreg(c1, 0x89, reg, 4); + + cat(c1, push87()); + genfltreg(c1,0xDF,5,0); // FILD m64int + + retregs = mST0 /*| (*pretregs & mPSW)*/; + c2 = fixresult87(e, retregs, pretregs); + return cat(c1, c2); + } + break; + case OPd_s64: + if (!I64) + goto Lcnvt87; + /* FALL-THROUGH */ + case OPd_s32: + if (config.fpxmmregs) + return xmmcnvt(e,pretregs); + /* FALL-THROUGH */ + case OPd_s16: + case OPd_u16: + Lcnvt87: + return cnvt87(e,pretregs); + case OPd_u32: // use subroutine, not 8087 +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + retregs = mST0; +#else + retregs = DOUBLEREGS; +#endif + goto L1; + + case OPd_u64: + retregs = DOUBLEREGS; + goto L1; + case OPu64_d: + if (*pretregs & mST0) + { + retregs = I64 ? mAX : mAX|mDX; + c1 = codelem(e->E1,&retregs,FALSE); + c2 = callclib(e,CLIBu64_ldbl,pretregs,0); + return cat(c1,c2); + } + break; + case OPld_u64: + retregs = mST0; + c1 = codelem(e->E1,&retregs,FALSE); + c2 = callclib(e,CLIBld_u64,pretregs,0); + return cat(c1,c2); + } + } + retregs = regmask(e->E1->Ety, TYnfunc); +L1: + c1 = codelem(e->E1,&retregs,FALSE); + for (i = 0; 1; i++) + { assert(i < arraysize(clib)); + if (clib[i][0] == e->Eoper) + { c2 = callclib(e,clib[i][1],pretregs,0); + break; + } + } + return cat(c1,c2); +} + + +/*************************** + * Convert short to long. + * For OPs16_32, OPu16_32, OPnp_fp, OPu32_64, OPs32_64 + */ + +code *cdshtlng(elem *e,regm_t *pretregs) +{ code *c,*ce,*c1,*c2,*c3,*c4; + unsigned reg; + regm_t retregs; + + //printf("cdshtlng(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); + int e1comsub = e->E1->Ecount; + unsigned char op = e->Eoper; + if ((*pretregs & (ALLREGS | mBP)) == 0) // if don't need result in regs + c = codelem(e->E1,pretregs,FALSE); /* then conversion isn't necessary */ + + else if ( +#if TARGET_SEGMENTED + op == OPnp_fp || +#endif + (I16 && op == OPu16_32) || + (I32 && op == OPu32_64) + ) + { + regm_t regm; + tym_t tym1; + + retregs = *pretregs & mLSW; + assert(retregs); + tym1 = tybasic(e->E1->Ety); + c = codelem(e->E1,&retregs,FALSE); + + regm = *pretregs & (mMSW & ALLREGS); + if (regm == 0) /* *pretregs could be mES */ + regm = mMSW & ALLREGS; + ce = allocreg(®m,®,TYint); + if (e1comsub) + ce = cat(ce,getregs(retregs)); +#if TARGET_SEGMENTED + if (op == OPnp_fp) + { int segreg; + + /* BUG: what about pointers to functions? */ + switch (tym1) + { + case TYnptr: segreg = SEG_DS; break; + case TYcptr: segreg = SEG_CS; break; + case TYsptr: segreg = SEG_SS; break; + default: assert(0); + } + ce = gen2(ce,0x8C,modregrm(3,segreg,reg)); /* MOV reg,segreg */ + } + else +#endif + ce = movregconst(ce,reg,0,0); /* 0 extend */ + + c = cat3(c,ce,fixresult(e,retregs | regm,pretregs)); + } + else if (I64 && op == OPu32_64) + { + elem *e1 = e->E1; + retregs = *pretregs; + if (e1->Eoper == OPvar || (e1->Eoper == OPind && !e1->Ecount)) + { code cs; + + c1 = allocreg(&retregs,®,TYint); + c2 = NULL; + c3 = loadea(e1,&cs,0x8B,reg,0,retregs,retregs); // MOV Ereg,EA + freenode(e1); + } + else + { + *pretregs &= ~mPSW; // flags are set by eval of e1 + c1 = codelem(e1,&retregs,FALSE); + c2 = getregs(retregs); + reg = findreg(retregs); + c3 = genregs(NULL,0x89,reg,reg); // MOV Ereg,Ereg + } + c4 = fixresult(e,retregs,pretregs); + c = cat4(c1,c2,c3,c4); + } + else if (!I16 && (op == OPs16_32 || op == OPu16_32) || + I64 && op == OPs32_64) + { + elem *e11; + + elem *e1 = e->E1; + + if (e1->Eoper == OPu8_16 && !e1->Ecount && + ((e11 = e1->E1)->Eoper == OPvar || (e11->Eoper == OPind && !e11->Ecount)) + ) + { code cs; + + retregs = *pretregs & BYTEREGS; + if (!retregs) + retregs = BYTEREGS; + c1 = allocreg(&retregs,®,TYint); + c2 = movregconst(NULL,reg,0,0); // XOR reg,reg + c3 = loadea(e11,&cs,0x8A,reg,0,retregs,retregs); // MOV regL,EA + freenode(e11); + freenode(e1); + } + else if (e1->Eoper == OPvar || + (e1->Eoper == OPind && !e1->Ecount)) + { code cs; + unsigned opcode; + + if (op == OPu16_32 && config.flags4 & CFG4speed) + goto L2; + retregs = *pretregs; + c1 = allocreg(&retregs,®,TYint); + opcode = (op == OPu16_32) ? 0x0FB7 : 0x0FBF; /* MOVZX/MOVSX reg,EA */ + if (op == OPs32_64) + { + assert(I64); + // MOVSXD reg,e1 + c2 = loadea(e1,&cs,0x63,reg,0,0,retregs); + code_orrex(c2, REX_W); + } + else + c2 = loadea(e1,&cs,opcode,reg,0,0,retregs); + c3 = CNIL; + freenode(e1); + } + else + { + L2: + retregs = *pretregs; + if (op == OPs32_64) + retregs = mAX | (*pretregs & mPSW); + *pretregs &= ~mPSW; /* flags are already set */ + c1 = codelem(e1,&retregs,FALSE); + c2 = getregs(retregs); + if (op == OPu16_32 && c1) + { + code *cx = code_last(c1); + if (cx->Iop == 0x81 && (cx->Irm & modregrm(3,7,0)) == modregrm(3,4,0)) + { + // Convert AND of a word to AND of a dword, zeroing upper word + retregs = mask[cx->Irm & 7]; + if (cx->Irex & REX_B) + retregs = mask[8 | (cx->Irm & 7)]; + cx->Iflags &= ~CFopsize; + cx->IEV2.Vint &= 0xFFFF; + goto L1; + } + } + if (op == OPs16_32 && retregs == mAX) + c2 = gen1(c2,0x98); /* CWDE */ + else if (op == OPs32_64 && retregs == mAX) + { c2 = gen1(c2,0x98); /* CDQE */ + code_orrex(c2, REX_W); + } + else + { + reg = findreg(retregs); + if (config.flags4 & CFG4speed && op == OPu16_32) + { // AND reg,0xFFFF + c3 = genc2(NULL,0x81,modregrmx(3,4,reg),0xFFFFu); + } + else + { + unsigned iop = (op == OPu16_32) ? 0x0FB7 : 0x0FBF; /* MOVZX/MOVSX reg,reg */ + c3 = genregs(CNIL,iop,reg,reg); + } + c2 = cat(c2,c3); + } + L1: + c3 = e1comsub ? getregs(retregs) : CNIL; + } + c4 = fixresult(e,retregs,pretregs); + c = cat4(c1,c2,c3,c4); + } + else if (*pretregs & mPSW || config.target_cpu < TARGET_80286) + { + // OPs16_32, OPs32_64 + // CWD doesn't affect flags, so we can depend on the integer + // math to provide the flags. + retregs = mAX | mPSW; // want integer result in AX + *pretregs &= ~mPSW; // flags are already set + c1 = codelem(e->E1,&retregs,FALSE); + c2 = getregs(mDX); // sign extend into DX + c2 = gen1(c2,0x99); // CWD/CDQ + c3 = e1comsub ? getregs(retregs) : CNIL; + c4 = fixresult(e,mDX | retregs,pretregs); + c = cat4(c1,c2,c3,c4); + } + else + { + // OPs16_32, OPs32_64 + unsigned msreg,lsreg; + + retregs = *pretregs & mLSW; + assert(retregs); + c1 = codelem(e->E1,&retregs,FALSE); + retregs |= *pretregs & mMSW; + c2 = allocreg(&retregs,®,e->Ety); + msreg = findregmsw(retregs); + lsreg = findreglsw(retregs); + c3 = genmovreg(NULL,msreg,lsreg); // MOV msreg,lsreg + assert(config.target_cpu >= TARGET_80286); // 8088 can't handle SAR reg,imm8 + c3 = genc2(c3,0xC1,modregrm(3,7,msreg),REGSIZE * 8 - 1); // SAR msreg,31 + c4 = fixresult(e,retregs,pretregs); + c = cat4(c1,c2,c3,c4); + } + return c; +} + + +/*************************** + * Convert byte to int. + * For OPu8_16 and OPs8_16. + */ + +code *cdbyteint(elem *e,regm_t *pretregs) +{ code *c,*c0,*c1,*c2,*c3,*c4; + regm_t retregs; + unsigned reg; + char op; + char size; + elem *e1; + + + if ((*pretregs & (ALLREGS | mBP)) == 0) // if don't need result in regs + return codelem(e->E1,pretregs,FALSE); /* then conversion isn't necessary */ + + //printf("cdbyteint(e = %p, *pretregs = %s\n", e, regm_str(*pretregs)); + op = e->Eoper; + e1 = e->E1; + c0 = NULL; + if (e1->Eoper == OPcomma) + c0 = docommas(&e1); + if (!I16) + { + if (e1->Eoper == OPvar || (e1->Eoper == OPind && !e1->Ecount)) + { code cs; + unsigned opcode; + + retregs = *pretregs; + c1 = allocreg(&retregs,®,TYint); + if (config.flags4 & CFG4speed && + op == OPu8_16 && mask[reg] & BYTEREGS && + config.target_cpu < TARGET_PentiumPro) + { + c2 = movregconst(NULL,reg,0,0); // XOR reg,reg + c3 = loadea(e1,&cs,0x8A,reg,0,retregs,retregs); // MOV regL,EA + } + else + { + opcode = (op == OPu8_16) ? 0x0FB6 : 0x0FBE; // MOVZX/MOVSX reg,EA + c2 = loadea(e1,&cs,opcode,reg,0,0,retregs); + c3 = CNIL; + } + freenode(e1); + goto L2; + } + size = tysize(e->Ety); + retregs = *pretregs & BYTEREGS; + if (retregs == 0) + retregs = BYTEREGS; + retregs |= *pretregs & mPSW; + *pretregs &= ~mPSW; + } + else + { + if (op == OPu8_16) /* if unsigned conversion */ + { + retregs = *pretregs & BYTEREGS; + if (retregs == 0) + retregs = BYTEREGS; + } + else + { + /* CBW doesn't affect flags, so we can depend on the integer */ + /* math to provide the flags. */ + retregs = mAX | (*pretregs & mPSW); /* want integer result in AX */ + } + } + + c3 = CNIL; + c1 = codelem(e1,&retregs,FALSE); + reg = findreg(retregs); + if (!c1) + goto L1; + + for (c = c1; c->next; c = c->next) + ; /* find previous instruction */ + + /* If previous instruction is an AND bytereg,value */ + if (c->Iop == 0x80 && c->Irm == modregrm(3,4,reg & 7) && + (op == OPu8_16 || (c->IEV2.Vuns & 0x80) == 0)) + { + if (*pretregs & mPSW) + c->Iflags |= CFpsw; + c->Iop |= 1; /* convert to word operation */ + c->IEV2.Vuns &= 0xFF; /* dump any high order bits */ + *pretregs &= ~mPSW; /* flags already set */ + } + else + { + L1: + if (!I16) + { + if (op == OPs8_16 && reg == AX && size == 2) + { c3 = gen1(c3,0x98); /* CBW */ + c3->Iflags |= CFopsize; /* don't do a CWDE */ + } + else + { + /* We could do better by not forcing the src and dst */ + /* registers to be the same. */ + + if (config.flags4 & CFG4speed && op == OPu8_16) + { // AND reg,0xFF + c3 = genc2(c3,0x81,modregrmx(3,4,reg),0xFF); + } + else + { + unsigned iop = (op == OPu8_16) ? 0x0FB6 : 0x0FBE; // MOVZX/MOVSX reg,reg + c3 = genregs(c3,iop,reg,reg); + if (I64 && reg >= 4) + code_orrex(c3, REX); + } + } + } + else + { + if (op == OPu8_16) + c3 = genregs(c3,0x30,reg+4,reg+4); // XOR regH,regH + else + { + c3 = gen1(c3,0x98); /* CBW */ + *pretregs &= ~mPSW; /* flags already set */ + } + } + } + c2 = getregs(retregs); +L2: + c4 = fixresult(e,retregs,pretregs); + return cat6(c0,c1,c2,c3,c4,NULL); +} + + +/*************************** + * Convert long to short (OP32_16). + * Get offset of far pointer (OPoffset). + * Convert int to byte (OP16_8). + * Convert long long to long (OP64_32). + * OP128_64 + */ + +code *cdlngsht(elem *e,regm_t *pretregs) +{ regm_t retregs; + code *c; + +#ifdef DEBUG + switch (e->Eoper) + { + case OP32_16: +#if TARGET_SEGMENTED + case OPoffset: +#endif + case OP16_8: + case OP64_32: + case OP128_64: + break; + + default: + assert(0); + } +#endif + + if (e->Eoper == OP16_8) + { retregs = *pretregs ? BYTEREGS : 0; + c = codelem(e->E1,&retregs,FALSE); + } + else + { if (e->E1->Eoper == OPrelconst) + c = offsetinreg(e->E1,&retregs); + else + { retregs = *pretregs ? ALLREGS : 0; + c = codelem(e->E1,&retregs,FALSE); +#if TARGET_SEGMENTED + bool isOff = e->Eoper == OPoffset; +#else + bool isOff = false; +#endif + if (I16 || + I32 && (isOff || e->Eoper == OP64_32) || + I64 && (isOff || e->Eoper == OP128_64)) + retregs &= mLSW; /* want LSW only */ + } + } + + /* We "destroy" a reg by assigning it the result of a new e, even */ + /* though the values are the same. Weakness of our CSE strategy that */ + /* a register can only hold the contents of one elem at a time. */ + if (e->Ecount) + c = cat(c,getregs(retregs)); + else + useregs(retregs); + +#ifdef DEBUG + if (!(!*pretregs || retregs)) + WROP(e->Eoper), + printf(" *pretregs = x%x, retregs = x%x, e = %p\n",*pretregs,retregs,e); +#endif + assert(!*pretregs || retregs); + return cat(c,fixresult(e,retregs,pretregs)); /* lsw only */ +} + +/********************************************** + * Get top 32 bits of 64 bit value (I32) + * or top 16 bits of 32 bit value (I16) + * or top 64 bits of 128 bit value (I64). + * OPmsw + */ + +code *cdmsw(elem *e,regm_t *pretregs) +{ regm_t retregs; + code *c; + + //printf("cdmsw(e->Ecount = %d)\n", e->Ecount); + assert(e->Eoper == OPmsw); + + retregs = *pretregs ? ALLREGS : 0; + c = codelem(e->E1,&retregs,FALSE); + retregs &= mMSW; // want MSW only + + // We "destroy" a reg by assigning it the result of a new e, even + // though the values are the same. Weakness of our CSE strategy that + // a register can only hold the contents of one elem at a time. + if (e->Ecount) + c = cat(c,getregs(retregs)); + else + useregs(retregs); + +#ifdef DEBUG + if (!(!*pretregs || retregs)) + { WROP(e->Eoper); + printf(" *pretregs = %s, retregs = %s\n",regm_str(*pretregs),regm_str(retregs)); + elem_print(e); + } +#endif + assert(!*pretregs || retregs); + return cat(c,fixresult(e,retregs,pretregs)); // msw only +} + + + +/****************************** + * Handle operators OPinp and OPoutp. + */ + +code *cdport(elem *e,regm_t *pretregs) +{ regm_t retregs; + code *c1,*c2,*c3; + unsigned char op,port; + unsigned sz; + elem *e1; + + //printf("cdport\n"); + op = 0xE4; /* root of all IN/OUT opcodes */ + e1 = e->E1; + + // See if we can use immediate mode of IN/OUT opcodes + if (e1->Eoper == OPconst && e1->EV.Vuns <= 255 && + (!evalinregister(e1) || regcon.mvar & mDX)) + { port = e1->EV.Vuns; + freenode(e1); + c1 = CNIL; + } + else + { retregs = mDX; /* port number is always DX */ + c1 = codelem(e1,&retregs,FALSE); + op |= 0x08; /* DX version of opcode */ + port = 0; // not logically needed, but + // quiets "uninitialized var" complaints + } + + if (e->Eoper == OPoutp) + { + sz = tysize(e->E2->Ety); + retregs = mAX; /* byte/word to output is in AL/AX */ + c2 = scodelem(e->E2,&retregs,((op & 0x08) ? mDX : (regm_t) 0),TRUE); + op |= 0x02; /* OUT opcode */ + } + else // OPinp + { c2 = getregs(mAX); + sz = tysize(e->Ety); + } + + if (sz != 1) + op |= 1; /* word operation */ + c3 = genc2(CNIL,op,0,port); /* IN/OUT AL/AX,DX/port */ + if (op & 1 && sz != REGSIZE) // if need size override + c3->Iflags |= CFopsize; + retregs = mAX; + return cat4(c1,c2,c3,fixresult(e,retregs,pretregs)); +} + +/************************ + * Generate code for an asm elem. + */ + +code *cdasm(elem *e,regm_t *pretregs) +{ code *c; + +#if 1 + /* Assume only regs normally destroyed by a function are destroyed */ + c = getregs((ALLREGS | mES) & ~fregsaved); +#else + /* Assume all regs are destroyed */ + c = getregs(ALLREGS | mES); +#endif + c = genasm(c,e->EV.ss.Vstring,e->EV.ss.Vstrlen); + return cat(c,fixresult(e,(I16 ? mDX | mAX : mAX),pretregs)); +} + +#if TARGET_SEGMENTED +/************************ + * Generate code for OPnp_f16p and OPf16p_np. + */ + +code *cdfar16( elem *e, regm_t *pretregs) +{ code *c; + code *c1; + code *c3; + code *cnop; + code cs; + unsigned reg; + + assert(I32); + c = codelem(e->E1,pretregs,FALSE); + reg = findreg(*pretregs); + c = cat(c,getregs(*pretregs)); /* we will destroy the regs */ + + cs.Iop = 0xC1; + cs.Irm = modregrm(3,0,reg); + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL2 = FLconst; + cs.IEV2.Vuns = 16; + + c3 = gen(CNIL,&cs); /* ROL ereg,16 */ + cs.Irm |= modregrm(0,1,0); + c1 = gen(CNIL,&cs); /* ROR ereg,16 */ + cs.IEV2.Vuns = 3; + cs.Iflags |= CFopsize; + + if (e->Eoper == OPnp_f16p) + { + /* OR ereg,ereg + JE L1 + ROR ereg,16 + SHL reg,3 + MOV rx,SS + AND rx,3 ;mask off CPL bits + OR rl,4 ;run on LDT bit + OR regl,rl + ROL ereg,16 + L1: NOP + */ + int jop; + int byte; + unsigned rx; + regm_t retregs; + + retregs = BYTEREGS & ~*pretregs; + c = cat(c,allocreg(&retregs,&rx,TYint)); + cnop = gennop(CNIL); + jop = JCXZ; + if (reg != CX) + { + c = gentstreg(c,reg); + jop = JE; + } + c = genjmp(c,jop,FLcode,(block *)cnop); /* Jop L1 */ + NEWREG(cs.Irm,4); + gen(c1,&cs); /* SHL reg,3 */ + genregs(c1,0x8C,2,rx); /* MOV rx,SS */ + byte = (mask[reg] & BYTEREGS) == 0; + genc2(c1,0x80 | byte,modregrm(3,4,rx),3); /* AND rl,3 */ + genc2(c1,0x80,modregrm(3,1,rx),4); /* OR rl,4 */ + genregs(c1,0x0A | byte,reg,rx); /* OR regl,rl */ + } + else /* OPf16p_np */ + { + /* ROR ereg,16 + SHR reg,3 + ROL ereg,16 + */ + + cs.Irm |= modregrm(0,5,0); + gen(c1,&cs); /* SHR reg,3 */ + cnop = NULL; + } + return cat4(c,c1,c3,cnop); +} +#endif + +/************************* + * Generate code for OPbt, OPbtc, OPbtr, OPbts + */ + +code *cdbt(elem *e, regm_t *pretregs) +{ + elem *e1; + elem *e2; + code *c; + code *c2; + code cs; + regm_t idxregs; + regm_t retregs; + unsigned reg; + unsigned char word; + tym_t ty1; + int op; + int mode; + + switch (e->Eoper) + { + case OPbt: op = 0xA3; mode = 4; break; + case OPbtc: op = 0xBB; mode = 7; break; + case OPbtr: op = 0xB3; mode = 6; break; + case OPbts: op = 0xAB; mode = 5; break; + + default: + assert(0); + } + + e1 = e->E1; + e2 = e->E2; + cs.Iflags = 0; + c = getlvalue(&cs, e, RMload); // get addressing mode + if (e->Eoper == OPbt && *pretregs == 0) + return cat(c, codelem(e2,pretregs,FALSE)); + + ty1 = tybasic(e1->Ety); + word = (!I16 && tysize[ty1] == SHORTSIZE) ? CFopsize : 0; + idxregs = idxregm(&cs); // mask if index regs used + +// if (e2->Eoper == OPconst && e2->EV.Vuns < 0x100) // should do this instead? + if (e2->Eoper == OPconst) + { + cs.Iop = 0x0FBA; // BT rm,imm8 + cs.Irm |= modregrm(0,mode,0); + cs.Iflags |= CFpsw | word; + cs.IFL2 = FLconst; + if (tysize[ty1] == SHORTSIZE) + { + cs.IEVoffset1 += (e2->EV.Vuns & ~15) >> 3; + cs.IEV2.Vint = e2->EV.Vint & 15; + } + else if (tysize[ty1] == 4) + { + cs.IEVoffset1 += (e2->EV.Vuns & ~31) >> 3; + cs.IEV2.Vint = e2->EV.Vint & 31; + } + else + { + cs.IEVoffset1 += (e2->EV.Vuns & ~63) >> 3; + cs.IEV2.Vint = e2->EV.Vint & 63; + if (I64) + cs.Irex |= REX_W; + } + c2 = gen(CNIL,&cs); + } + else + { + retregs = ALLREGS & ~idxregs; + c2 = scodelem(e2,&retregs,idxregs,TRUE); + reg = findreg(retregs); + + cs.Iop = 0x0F00 | op; // BT rm,reg + code_newreg(&cs,reg); + cs.Iflags |= CFpsw | word; + c2 = gen(c2,&cs); + } + + if ((retregs = (*pretregs & (ALLREGS | mBP))) != 0) // if return result in register + { + code *nop = CNIL; + regm_t save = regcon.immed.mval; + code *cg = allocreg(&retregs,®,TYint); + regcon.immed.mval = save; + if ((*pretregs & mPSW) == 0) + { + cg = cat(cg,getregs(retregs)); + cg = genregs(cg,0x19,reg,reg); // SBB reg,reg + } + else + { + cg = movregconst(cg,reg,1,8); // MOV reg,1 + nop = gennop(nop); + cg = genjmp(cg,JC,FLcode,(block *) nop); // Jtrue nop + // MOV reg,0 + movregconst(cg,reg,0,8); + regcon.immed.mval &= ~mask[reg]; + } + *pretregs = retregs; + c2 = cat3(c2,cg,nop); + } + + return cat(c,c2); +} + +/************************************* + * Generate code for OPbsf and OPbsr. + */ + +code *cdbscan(elem *e, regm_t *pretregs) +{ + regm_t retregs; + unsigned reg; + int sz; + tym_t tyml; + code *cl,*cg; + code cs; + + //printf("cdbscan()\n"); + //elem_print(e); + if (*pretregs == 0) + return codelem(e->E1,pretregs,FALSE); + tyml = tybasic(e->E1->Ety); + sz = tysize[tyml]; + assert(sz == 2 || sz == 4 || sz == 8); + + if ((e->E1->Eoper == OPind && !e->E1->Ecount) || e->E1->Eoper == OPvar) + { + cl = getlvalue(&cs, e->E1, RMload); // get addressing mode + } + else + { + retregs = allregs; + cl = codelem(e->E1, &retregs, FALSE); + reg = findreg(retregs); + cs.Irm = modregrm(3,0,reg & 7); + cs.Iflags = 0; + cs.Irex = 0; + if (reg & 8) + cs.Irex |= REX_B; + } + + retregs = *pretregs & allregs; + if (!retregs) + retregs = allregs; + cg = allocreg(&retregs, ®, e->Ety); + + cs.Iop = (e->Eoper == OPbsf) ? 0x0FBC : 0x0FBD; // BSF/BSR reg,EA + code_newreg(&cs, reg); + if (!I16 && sz == SHORTSIZE) + cs.Iflags |= CFopsize; + cg = gen(cg,&cs); + if (sz == 8) + code_orrex(cg, REX_W); + + return cat3(cl,cg,fixresult(e,retregs,pretregs)); +} + +/******************************************* + * Generate code for OPpair, OPrpair. + */ + +code *cdpair(elem *e, regm_t *pretregs) +{ + regm_t retregs; + regm_t regs1; + regm_t regs2; + code *cg; + code *c1; + code *c2; + + if (*pretregs == 0) // if don't want result + { c1 = codelem(e->E1,pretregs,FALSE); // eval left leaf + *pretregs = 0; // in case they got set + return cat(c1,codelem(e->E2,pretregs,FALSE)); + } + + //printf("\ncdpair(e = %p, *pretregs = x%x)\n", e, *pretregs); + //printf("Ecount = %d\n", e->Ecount); + retregs = *pretregs & allregs; + if (!retregs) + retregs = allregs; + regs1 = retregs & (mLSW | mBP); + regs2 = retregs & mMSW; + if (e->Eoper == OPrpair) + { + regs1 = regs2; + regs2 = retregs & (mLSW | mBP); + } + //printf("1: regs1 = x%x, regs2 = x%x\n", regs1, regs2); + c1 = codelem(e->E1, ®s1, FALSE); + c2 = scodelem(e->E2, ®s2, regs1, FALSE); + + cg = NULL; + if (e->E1->Ecount) + cg = getregs(regs1); + if (e->E2->Ecount) + cg = cat(cg, getregs(regs2)); + + //printf("regs1 = x%x, regs2 = x%x\n", regs1, regs2); + return cat4(c1,c2,cg,fixresult(e,regs1 | regs2,pretregs)); +} + +#endif // !SPP diff --git a/backend/cod5.c b/backend/cod5.c new file mode 100644 index 00000000..1acb2de8 --- /dev/null +++ b/backend/cod5.c @@ -0,0 +1,207 @@ +// Copyright (C) 1995-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include "cc.h" +#include "el.h" +#include "oper.h" +#include "code.h" +#include "global.h" +#include "type.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +STATIC void pe_add(block *b); +STATIC int need_prolog(block *b); + +/******************************************************** + * Determine which blocks get the function prolog and epilog + * attached to them. + */ + +void cod5_prol_epi() +{ +#if 1 + cod5_noprol(); +#else + tym_t tym; + tym_t tyf; + block *b; + block *bp; + list_t bl; + int nepis; + + tyf = funcsym_p->ty(); + tym = tybasic(tyf); + + if (!(config.flags4 & CFG4optimized) || + anyiasm || + usedalloca || + usednteh || + tyf & (mTYnaked | mTYloadds) || + tym == TYifunc || + tym == TYmfunc || // can't yet handle ECX passed as parameter + tym == TYjfunc || // can't yet handle EAX passed as parameter + config.flags & (CFGalwaysframe | CFGtrace) || +// config.fulltypes || + (config.wflags & WFwindows && tyfarfunc(tym)) || + need_prolog(startblock) + ) + { // First block gets the prolog, all return blocks + // get the epilog. + //printf("not even a candidate\n"); + cod5_noprol(); + return; + } + + // Turn on BFLoutsideprolog for all blocks outside the ones needing the prolog. + + for (b = startblock; b; b = b->Bnext) + b->Bflags &= ~BFLoutsideprolog; // start with them all off + + pe_add(startblock); + + // Look for only one block (bp) that will hold the prolog + bp = NULL; + nepis = 0; + for (b = startblock; b; b = b->Bnext) + { int mark; + + if (b->Bflags & BFLoutsideprolog) + continue; + + // If all predecessors are marked + mark = 0; + assert(b->Bpred); + for (bl = b->Bpred; bl; bl = list_next(bl)) + { + if (list_block(bl)->Bflags & BFLoutsideprolog) + { + if (mark == 2) + goto L1; + mark = 1; + } + else + { + if (mark == 1) + goto L1; + mark = 2; + } + } + if (mark == 1) + { + if (bp) // if already have one + goto L1; + bp = b; + } + + // See if b is an epilog + mark = 0; + for (bl = b->Bsucc; bl; bl = list_next(bl)) + { + if (list_block(bl)->Bflags & BFLoutsideprolog) + { + if (mark == 2) + goto L1; + mark = 1; + } + else + { + if (mark == 1) + goto L1; + mark = 2; + } + } + if (mark == 1 || b->BC == BCret || b->BC == BCretexp) + { b->Bflags |= BFLepilog; + nepis++; + if (nepis > 1 && config.flags4 & CFG4space) + goto L1; + } + } + if (bp) + { bp->Bflags |= BFLprolog; + //printf("=============== prolog opt\n"); + } +#endif +} + +/********************************************** + * No prolog/epilog optimization. + */ + +void cod5_noprol() +{ + block *b; + + //printf("no prolog optimization\n"); + startblock->Bflags |= BFLprolog; + for (b = startblock; b; b = b->Bnext) + { + b->Bflags &= ~BFLoutsideprolog; + switch (b->BC) + { case BCret: + case BCretexp: + b->Bflags |= BFLepilog; + break; + default: + b->Bflags &= ~BFLepilog; + } + } +} + +/********************************************* + * Add block b, and its successors, to those blocks outside those requiring + * the function prolog. + */ + +STATIC void pe_add(block *b) +{ list_t bl; + + if (b->Bflags & BFLoutsideprolog || + need_prolog(b)) + return; + + b->Bflags |= BFLoutsideprolog; + for (bl = b->Bsucc; bl; bl = list_next(bl)) + pe_add(list_block(bl)); +} + +/********************************************** + * Determine if block needs the function prolog to be set up. + */ + +STATIC int need_prolog(block *b) +{ + if (b->Bregcon.used & fregsaved) + goto Lneed; + + // If block referenced a param in 16 bit code + if (!I32 && b->Bflags & BFLrefparam) + goto Lneed; + + // If block referenced a stack local + if (b->Bflags & BFLreflocal) + goto Lneed; + + return 0; + +Lneed: + return 1; +} + +#endif diff --git a/backend/code.c b/backend/code.c new file mode 100644 index 00000000..d4bd41bc --- /dev/null +++ b/backend/code.c @@ -0,0 +1,136 @@ +// Copyright (C) 1987-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include "cc.h" +#include "el.h" +#include "code.h" +#include "global.h" + +static code *code_list; + +/***************** + * Allocate code + */ + +#if SCPP && __SC__ && __INTSIZE == 4 && TX86 && !_DEBUG_TRACE && !MEM_DEBUG + +__declspec(naked) code *code_calloc() +{ + if (sizeof(code) != 0x28) + util_assert("code",__LINE__); + __asm + { + mov EAX,code_list + test EAX,EAX + je L20 + mov ECX,[EAX] + mov code_list,ECX + jmp L29 + +L20: push sizeof(code) + call mem_fmalloc + ;add ESP,4 +L29: + xor ECX,ECX + mov DWORD PTR [EAX],0 + + mov 4[EAX],ECX ;these pair + mov 8[EAX],ECX + + mov 12[EAX],ECX + mov 16[EAX],ECX + + mov 20[EAX],ECX + mov 24[EAX],ECX + + mov 28[EAX],ECX + mov 32[EAX],ECX + + mov 36[EAX],ECX + + ret + } +} + +#else + +code *code_calloc() +{ code *c; + static code czero; + + //printf("code %x\n", sizeof(code)); + c = code_list; + if (c) + code_list = code_next(c); + else + c = (code *)mem_fmalloc(sizeof(*c)); + *c = czero; // zero it out + //dbg_printf("code_calloc: %p\n",c); + return c; +} + +#endif + +/***************** + * Free code + */ + +void code_free(code *cstart) +{ code **pc; + code *c; + + for (pc = &cstart; (c = *pc) != NULL; pc = &code_next(c)) + { + if (c->Iop == ASM) + { + mem_free(c->IEV1.as.bytes); + } + } + *pc = code_list; + code_list = cstart; +} + +/***************** + * Terminate code + */ + +void code_term() +{ +#if TERMCODE + code *cn; + int count = 0; + + while (code_list) + { cn = code_next(code_list); + mem_ffree(code_list); + code_list = cn; + count++; + } +#ifdef DEBUG + printf("Max # of codes = %d\n",count); +#endif +#else +#ifdef DEBUG + int count = 0; + + for (code *cn = code_list; cn; cn = code_next(cn)) + count++; + printf("Max # of codes = %d\n",count); +#endif +#endif +} + +#endif // !SPP diff --git a/backend/code.h b/backend/code.h new file mode 100644 index 00000000..8c79aef2 --- /dev/null +++ b/backend/code.h @@ -0,0 +1,1049 @@ +// Copyright (C) 1985-1996 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if __cplusplus && TX86 +extern "C" { +#endif + +#if MARS +struct LabelDsymbol; +struct Declaration; +#endif + +#define CNIL ((code *) NULL) + +/* Register definitions */ + +#define AX 0 +#define CX 1 +#define DX 2 +#define BX 3 +#define SP 4 +#define BP 5 +#define SI 6 +#define DI 7 + +#define R8 8 +#define R9 9 +#define R10 10 +#define R11 11 +#define R12 12 +#define R13 13 +#define R14 14 +#define R15 15 + +#define XMM0 16 +#define XMM1 17 +#define XMM2 18 +#define XMM3 19 +#define XMM4 20 +#define XMM5 21 +#define XMM6 22 +#define XMM7 23 +/* There are also XMM8..XMM14 */ +#define XMM15 31 + + +#define ES 24 +#define PSW 25 +#define STACK 26 // top of stack +#define ST0 27 // 8087 top of stack register +#define ST01 28 // top two 8087 registers; for complex types + +#define NOREG 29 // no register + +#define AL 0 +#define CL 1 +#define DL 2 +#define BL 3 +#define AH 4 +#define CH 5 +#define DH 6 +#define BH 7 + +#define mAX 1 +#define mCX 2 +#define mDX 4 +#define mBX 8 +#define mSP 0x10 +#define mBP 0x20 +#define mSI 0x40 +#define mDI 0x80 + +#define mR8 (1 << R8) +#define mR9 (1 << R9) +#define mR10 (1 << R10) +#define mR11 (1 << R11) +#define mR12 (1 << R12) +#define mR13 (1 << R13) +#define mR14 (1 << R14) +#define mR15 (1 << R15) + +#define mXMM0 (1 << XMM0) +#define mXMM1 (1 << XMM1) +#define mXMM2 (1 << XMM2) +#define mXMM3 (1 << XMM3) +#define mXMM4 (1 << XMM4) +#define mXMM5 (1 << XMM5) +#define mXMM6 (1 << XMM6) +#define mXMM7 (1 << XMM7) +#define XMMREGS (mXMM0 |mXMM1 |mXMM2 |mXMM3 |mXMM4 |mXMM5 |mXMM6 |mXMM7) + +#define mES (1 << ES) // 0x1000000 +#define mPSW (1 << PSW) // 0x2000000 + +#define mSTACK (1 << STACK) // 0x4000000 + +#define mST0 (1 << ST0) // 0x20000000 +#define mST01 (1 << ST01) // 0x40000000 + +// Flags for getlvalue (must fit in regm_t) +#define RMload (1 << 30) +#define RMstore (1 << 31) + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + // To support positional independent code, + // must be able to remove BX from available registers +extern regm_t ALLREGS; +#define ALLREGS_INIT (mAX|mBX|mCX|mDX|mSI|mDI) +#define ALLREGS_INIT_PIC (mAX|mCX|mDX|mSI|mDI) +extern regm_t BYTEREGS; +#define BYTEREGS_INIT (mAX|mBX|mCX|mDX) +#define BYTEREGS_INIT_PIC (mAX|mCX|mDX) +#else +#define ALLREGS (mAX|mBX|mCX|mDX|mSI|mDI) +#define ALLREGS_INIT ALLREGS +#undef BYTEREGS +#define BYTEREGS (mAX|mBX|mCX|mDX) +#endif + +/* We use the same IDXREGS for the 386 as the 8088, because if + we used ALLREGS, it would interfere with mMSW + */ +#define IDXREGS (mBX|mSI|mDI) + +#define FLOATREGS_64 mAX +#define FLOATREGS2_64 mDX +#define DOUBLEREGS_64 mAX +#define DOUBLEREGS2_64 mDX + +#define FLOATREGS_32 mAX +#define FLOATREGS2_32 mDX +#define DOUBLEREGS_32 (mAX|mDX) +#define DOUBLEREGS2_32 (mCX|mBX) + +#define FLOATREGS_16 (mDX|mAX) +#define FLOATREGS2_16 (mCX|mBX) +#define DOUBLEREGS_16 (mAX|mBX|mCX|mDX) + +/*#define _8087REGS (mST0|mST1|mST2|mST3|mST4|mST5|mST6|mST7)*/ + +/* Segment registers */ +#define SEG_ES 0 +#define SEG_CS 1 +#define SEG_SS 2 +#define SEG_DS 3 + +/********************* + * Masks for register pairs. + * Note that index registers are always LSWs. This is for the convenience + * of implementing far pointers. + */ + +#if 0 +// Give us an extra one so we can enregister a long +#define mMSW (mCX|mDX|mDI|mES) // most significant regs +#define mLSW (mAX|mBX|mSI) // least significant regs +#else +#define mMSW (mCX|mDX|mES) /* most significant regs */ +#define mLSW (mAX|mBX|mSI|mDI) /* least significant regs */ +#endif + +/* Return !=0 if there is a SIB byte */ +#define issib(rm) (((rm) & 7) == 4 && ((rm) & 0xC0) != 0xC0) + +#if 0 +// relocation field size is always 32bits +#define is32bitaddr(x,Iflags) (1) +#else +// +// is32bitaddr works correctly only when x is 0 or 1. This is +// true today for the current definition of I32, but if the definition +// of I32 changes, this macro will need to change as well +// +// Note: even for linux targets, CFaddrsize can be set by the inline +// assembler. +#define is32bitaddr(x,Iflags) (I64 || ((x) ^(((Iflags) & CFaddrsize) !=0))) +#endif + +/******************* + * Some instructions. + */ + +#define SEGES 0x26 +#define SEGCS 0x2E +#define SEGSS 0x36 +#define SEGDS 0x3E +#define SEGFS 0x64 +#define SEGGS 0x65 + +#define CALL 0xE8 +#define JMP 0xE9 /* Intra-Segment Direct */ +#define JMPS 0xEB /* JMP SHORT */ +#define JCXZ 0xE3 +#define LOOP 0xE2 +#define LES 0xC4 +#define LEA 0x8D + +#define JO 0x70 +#define JNO 0x71 +#define JC 0x72 +#define JB 0x72 +#define JNC 0x73 +#define JAE 0x73 +#define JE 0x74 +#define JNE 0x75 +#define JBE 0x76 +#define JA 0x77 +#define JS 0x78 +#define JNS 0x79 +#define JP 0x7A +#define JNP 0x7B +#define JL 0x7C +#define JGE 0x7D +#define JLE 0x7E +#define JG 0x7F + +/* NOP is used as a placeholder in the linked list of instructions, no */ +/* actual code will be generated for it. */ +#define NOP 0x2E /* actually CS: (we don't use 0x90 because the */ + /* silly Windows stuff wants to output 0x90's) */ + +#define ESCAPE 0x3E // marker that special information is here + // (Iop2 is the type of special information) + // (Same as DS:, but we will never generate + // a separate DS: opcode anyway) + #define ESClinnum (1 << 8) // line number information + #define ESCctor (2 << 8) // object is constructed + #define ESCdtor (3 << 8) // object is destructed + #define ESCmark (4 << 8) // mark eh stack + #define ESCrelease (5 << 8) // release eh stack + #define ESCoffset (6 << 8) // set code offset for eh + #define ESCadjesp (7 << 8) // adjust ESP by IEV2.Vint + #define ESCmark2 (8 << 8) // mark eh stack + #define ESCrelease2 (9 << 8) // release eh stack + #define ESCframeptr (10 << 8) // replace with load of frame pointer + #define ESCdctor (11 << 8) // D object is constructed + #define ESCddtor (12 << 8) // D object is destructed + #define ESCadjfpu (13 << 8) // adjust fpustackused by IEV2.Vint + +#define ASM 0x36 // string of asm bytes, actually an SS: opcode + +/********************************* + * Macros to ease generating code + * modregrm: generate mod reg r/m field + * modregxrm: reg could be R8..R15 + * modregrmx: rm could be R8..R15 + * modregxrmx: reg or rm could be R8..R15 + * NEWREG: change reg field of x to r + * genorreg: OR t,f + */ + +#define modregrm(m,r,rm) (((m)<<6)|((r)<<3)|(rm)) +#define modregxrm(m,r,rm) ((((r)&8)<<15)|modregrm((m),(r)&7,rm)) +#define modregrmx(m,r,rm) ((((rm)&8)<<13)|modregrm((m),r,(rm)&7)) +#define modregxrmx(m,r,rm) ((((r)&8)<<15)|(((rm)&8)<<13)|modregrm((m),(r)&7,(rm)&7)) + +#define NEWREXR(x,r) ((x)=((x)&~REX_R)|(((r)&8)>>1)) +#define NEWREG(x,r) ((x)=((x)&~(7<<3))|((r)<<3)) +#define code_newreg(c,r) (NEWREG((c)->Irm,(r)&7),NEWREXR((c)->Irex,(r))) + +#define genorreg(c,t,f) genregs((c),0x09,(f),(t)) + +#define REX 0x40 // REX prefix byte, OR'd with the following bits: +#define REX_W 8 // 0 = default operand size, 1 = 64 bit operand size +#define REX_R 4 // high bit of reg field of modregrm +#define REX_X 2 // high bit of sib index reg +#define REX_B 1 // high bit of rm field, sib base reg, or opcode reg + +#define VEX2_B1(ivex) \ + ( \ + ivex.r << 7 | \ + ivex.vvvv << 3 | \ + ivex.l << 2 | \ + ivex.pp \ + ) + +#define VEX3_B1(ivex) \ + ( \ + ivex.r << 7 | \ + ivex.x << 6 | \ + ivex.b << 5 | \ + ivex.mmmm \ + ) + +#define VEX3_B2(ivex) \ + ( \ + ivex.w << 7 | \ + ivex.vvvv << 3 | \ + ivex.l << 2 | \ + ivex.pp \ + ) + +/********************** + * C library routines. + * See callclib(). + */ + +enum CLIB +{ + CLIBlcmp, + CLIBlmul, + CLIBldiv, + CLIBlmod, + CLIBuldiv, + CLIBulmod, + +#if TARGET_WINDOS + CLIBdmul,CLIBddiv,CLIBdtst0,CLIBdtst0exc,CLIBdcmp,CLIBdcmpexc,CLIBdneg,CLIBdadd,CLIBdsub, + CLIBfmul,CLIBfdiv,CLIBftst0,CLIBftst0exc,CLIBfcmp,CLIBfcmpexc,CLIBfneg,CLIBfadd,CLIBfsub, +#endif + + CLIBdbllng,CLIBlngdbl,CLIBdblint,CLIBintdbl, + CLIBdbluns,CLIBunsdbl, + CLIBdblulng, +#if TARGET_WINDOS + // used the GNU way of converting unsigned long long to signed + CLIBulngdbl, +#endif + CLIBdblflt,CLIBfltdbl, + CLIBdblllng, + CLIBllngdbl, + CLIBdblullng, + CLIBullngdbl, + CLIBdtst, + CLIBvptrfptr,CLIBcvptrfptr, + + CLIB87topsw,CLIBfltto87,CLIBdblto87,CLIBdblint87,CLIBdbllng87, + CLIBftst, + CLIBfcompp, + CLIBftest, + CLIBftest0, + CLIBfdiv87, + + // Complex numbers + CLIBcmul, + CLIBcdiv, + CLIBccmp, + + CLIBu64_ldbl, + CLIBld_u64, + + CLIBMAX +}; + +/********************************** + * Code data type + */ + +union evc +{ + targ_int Vint; // also used for tmp numbers (FLtmp) + targ_uns Vuns; + targ_long Vlong; + targ_llong Vllong; + targ_size_t Vsize_t; + struct + { targ_size_t Vpointer; + int Vseg; // segment the pointer is in + }_EP; + Srcpos Vsrcpos; // source position for OPlinnum + struct elem *Vtor; // OPctor/OPdtor elem + struct block *Vswitch; // when FLswitch and we have a switch table + code *Vcode; // when code is target of a jump (FLcode) + struct block *Vblock; // when block " (FLblock) + struct + { + targ_size_t Voffset; // offset from symbol + symbol *Vsym; // pointer to symbol table (FLfunc,FLextern) + } sp; +#if MARS + struct + { + targ_size_t Voffset; // offset from symbol + Declaration *Vsym; // pointer to D symbol table + } dsp; + struct + { + targ_size_t Voffset; // offset from symbol + LabelDsymbol *Vsym; // pointer to Label + } lab; +#endif + struct + { size_t len; + char *bytes; + } as; // asm node (FLasm) +}; + +struct code +{ + code *next; + unsigned Iflags; +#define CFes 1 // generate an ES: segment override for this instr +#define CFjmp16 2 // need 16 bit jump offset (long branch) +#define CFtarg 4 // this code is the target of a jump +#define CFseg 8 // get segment of immediate value +#define CFoff 0x10 // get offset of immediate value +#define CFss 0x20 // generate an SS: segment override (not with + // CFes at the same time, though!) +#define CFpsw 0x40 // we need the flags result after this instruction +#define CFopsize 0x80 // prefix with operand size +#define CFaddrsize 0x100 // prefix with address size +#define CFds 0x200 // need DS override (not with es, ss, or cs ) +#define CFcs 0x400 // need CS override +#define CFfs 0x800 // need FS override +#define CFgs (CFcs | CFfs) // need GS override +#define CFwait 0x1000 // If I32 it indicates when to output a WAIT +#define CFselfrel 0x2000 // if self-relative +#define CFunambig 0x4000 // indicates cannot be accessed by other addressing + // modes +#define CFtarg2 0x8000 // like CFtarg, but we can't optimize this away +#define CFvolatile 0x10000 // volatile reference, do not schedule +#define CFclassinit 0x20000 // class init code +#define CFoffset64 0x40000 // offset is 64 bits +#define CFpc32 0x80000 // I64: PC relative 32 bit fixup + +#define CFvex 0x100000 // vex prefix +#define CFvex3 0x200000 // 3 byte vex prefix + +#define CFPREFIX (CFSEG | CFopsize | CFaddrsize) +#define CFSEG (CFes | CFss | CFds | CFcs | CFfs | CFgs) + + union { + unsigned _Iop; + struct { +#pragma pack(1) + unsigned char op; + unsigned short pp : 2; + unsigned short l : 1; + unsigned short vvvv : 4; + unsigned short w : 1; + unsigned short mmmm : 5; + unsigned short b : 1; + unsigned short x : 1; + unsigned short r : 1; + unsigned char pfx; // always 0xC4 +#pragma pack() + } _Ivex; + } _OP; + + /* The _EA is the "effective address" for the instruction, and consists of the modregrm byte, + * the sib byte, and the REX prefix byte. The 16 bit code generator just used the modregrm, + * the 32 bit x86 added the sib, and the 64 bit one added the rex. + */ + union + { unsigned _Iea; + struct + { + unsigned char _Irm; // reg/mode + unsigned char _Isib; // SIB byte + unsigned char _Irex; // REX prefix + } _ea; + } _EA; + +#define Iop _OP._Iop +#define Ivex _OP._Ivex +#define Iea _EA._Iea +#define Irm _EA._ea._Irm +#define Isib _EA._ea._Isib +#define Irex _EA._ea._Irex + + + /* IFL1 and IEV1 are the first operand, which usually winds up being the offset to the Effective + * Address. IFL1 is the tag saying which variant type is in IEV1. IFL2 and IEV2 is the second + * operand, usually for immediate instructions. + */ + + unsigned char IFL1,IFL2; // FLavors of 1st, 2nd operands + union evc IEV1; // 1st operand, if any + #define IEVpointer1 IEV1._EP.Vpointer + #define IEVseg1 IEV1._EP.Vseg + #define IEVsym1 IEV1.sp.Vsym + #define IEVdsym1 IEV1.dsp.Vsym + #define IEVoffset1 IEV1.sp.Voffset + #define IEVlsym1 IEV1.lab.Vsym + #define IEVint1 IEV1.Vint + union evc IEV2; // 2nd operand, if any + #define IEVpointer2 IEV2._EP.Vpointer + #define IEVseg2 IEV2._EP.Vseg + #define IEVsym2 IEV2.sp.Vsym + #define IEVdsym2 IEV2.dsp.Vsym + #define IEVoffset2 IEV2.sp.Voffset + #define IEVlsym2 IEV2.lab.Vsym + #define IEVint2 IEV2.Vint + #define IEVllong2 IEV2.Vllong + void print(); // pretty-printer + + code() { Irex = 0; Isib = 0; } // constructor + + void orReg(unsigned reg) + { if (reg & 8) + Irex |= REX_R; + Irm |= modregrm(0, reg & 7, 0); + } + + void setReg(unsigned reg) + { + Irex &= ~REX_R; + Irm &= ~modregrm(0, 7, 0); + orReg(reg); + } +}; + +// !=0 if we have to add FWAIT to floating point ops +#define ADDFWAIT() (config.target_cpu <= TARGET_80286) + +/********************** PUBLIC FUNCTIONS *******************/ + +code *code_calloc(void); +void code_free (code *); +void code_term(void); + +#define code_next(c) ((c)->next) + +//#define code_calloc() ((code *) mem_calloc(sizeof(code))) + +typedef code *code_p; + +/********************************** + * Set value in regimmed for reg. + * NOTE: For 16 bit generator, this is always a (targ_short) sign-extended + * value. + */ + +#define regimmed_set(reg,e) \ + (regcon.immed.value[reg] = (e),regcon.immed.mval |= 1 << (reg)) + +extern con_t regcon; + +/**************************** + * Table of common subexpressions stored on the stack. + * csextab[] array of info on saved CSEs + * CSEpe pointer to saved elem + * CSEregm mask of register that was saved (so for multi- + * register variables we know which part we have) + * cstop = # of entries in table + */ + +struct CSE +{ elem *e; // pointer to elem + code csimple; // if CSEsimple, this is the code to regenerate it + regm_t regm; // mask of register stored there + char flags; // flag bytes +#define CSEload 1 // set if the CSE was ever loaded +#define CSEsimple 2 // CSE can be regenerated easilly + +// != 0 if CSE was ever loaded +#define CSE_loaded(i) (csextab[i].flags & CSEload) +}; + +/************************************ + */ + +#if TX86 +struct NDP +{ + elem *e; // which elem is stored here (NULL if none) + unsigned offset; // offset from e (used for complex numbers) + + static NDP *save; + static int savemax; // # of entries in save[] + static int savetop; // # of entries used in save[] +}; + +extern NDP _8087elems[8]; +#endif + +/************************************ + * Register save state. + */ + +extern "C++" +{ +struct REGSAVE +{ + targ_size_t off; // offset on stack + unsigned top; // high water mark + unsigned idx; // current number in use + int alignment; // 8 or 16 + + void reset() { off = 0; top = 0; idx = 0; alignment = REGSIZE; } + code *save(code *c, int reg, unsigned *pidx); + code *restore(code *c, int reg, unsigned idx); +}; +} +extern REGSAVE regsave; + +/******************************* + * As we generate code, collect information about + * what parts of NT exception handling we need. + */ + +extern unsigned usednteh; + +#define NTEH_try 1 // used _try statement +#define NTEH_except 2 // used _except statement +#define NTEHexcspec 4 // had C++ exception specification +#define NTEHcleanup 8 // destructors need to be called +#define NTEHtry 0x10 // had C++ try statement +#define NTEHcpp (NTEHexcspec | NTEHcleanup | NTEHtry) +#define EHcleanup 0x20 +#define EHtry 0x40 +#define NTEHjmonitor 0x80 // uses Jupiter monitor +#define NTEHpassthru 0x100 + +/********************** Code Generator State ***************/ + +typedef struct CGstate +{ + int stackclean; // if != 0, then clean the stack after function call +} CGstate; + +extern CGstate cgstate; + +/***********************************************************/ + +extern regm_t msavereg,mfuncreg,allregs; + +/*long cxmalloc,cxcalloc,cx1;*/ + +typedef code *cd_t (elem *e , regm_t *pretregs ); + +extern int BPRM; +extern regm_t FLOATREGS; +extern regm_t FLOATREGS2; +extern regm_t DOUBLEREGS; +extern const char datafl[],stackfl[],segfl[],flinsymtab[]; +extern char needframe,usedalloca,gotref; +extern targ_size_t localsize,Toff,Poff,Aoff, + Poffset,funcoffset, + framehandleroffset, + Aoffset,Toffset,EEoffset; +extern int Aalign; +extern int cseg; +extern int STACKALIGN; +#if TARGET_OSX +extern targ_size_t localgotoffset; +#endif + +/* cgcod.c */ +extern int pass; +#define PASSinit 0 // initial pass through code generator +#define PASSreg 1 // register assignment pass +#define PASSfinal 2 // final pass + +extern int dfoidx; +extern struct CSE *csextab; +extern unsigned cstop; +#if TX86 +extern bool floatreg; +#endif +extern targ_size_t retoffset; +extern unsigned stackpush; +extern int stackchanged; +extern int refparam; +extern int reflocal; +extern char anyiasm; +extern char calledafunc; +extern code *(*cdxxx[])(elem *,regm_t *); + +void stackoffsets(int); +void codgen (void ); +#ifdef DEBUG +unsigned findreg (regm_t regm , int line , const char *file ); +#define findreg(regm) findreg((regm),__LINE__,__FILE__) +#else +unsigned findreg (regm_t regm ); +#endif +#define findregmsw(regm) findreg((regm) & mMSW) +#define findreglsw(regm) findreg((regm) & (mLSW | mBP)) +void freenode (elem *e ); +int isregvar (elem *e , regm_t *pregm , unsigned *preg ); +#ifdef DEBUG +code *allocreg (regm_t *pretregs , unsigned *preg , tym_t tym , int line , const char *file ); +#define allocreg(a,b,c) allocreg((a),(b),(c),__LINE__,__FILE__) +#else +code *allocreg (regm_t *pretregs , unsigned *preg , tym_t tym ); +#endif +void useregs (regm_t regm ); +code *getregs (regm_t r ); +code *getregs_imm (regm_t r ); +code *cse_flush(int); +void cssave (elem *e , regm_t regm , unsigned opsflag ); +bool evalinregister (elem *e ); +regm_t getscratch(); +code *codelem (elem *e , regm_t *pretregs , bool constflag ); +code *scodelem (elem *e , regm_t *pretregs , regm_t keepmsk , bool constflag ); +const char *regm_str(regm_t rm); +int numbitsset(regm_t); + +/* cod1.c */ +extern int clib_inited; + +int isscaledindex(elem *); +int ssindex(int op,targ_uns product); +void buildEA(code *c,int base,int index,int scale,targ_size_t disp); +unsigned buildModregrm(int mod, int reg, int rm); +void andregcon (con_t *pregconsave); +void genEEcode(); +code *docommas (elem **pe ); +void gensaverestore(regm_t, code **, code **); +void gensaverestore2(regm_t regm,code **csave,code **crestore); +code *genstackclean(code *c,unsigned numpara,regm_t keepmsk); +code *logexp (elem *e , int jcond , unsigned fltarg , code *targ ); +code *loadea (elem *e , code *cs , unsigned op , unsigned reg , targ_size_t offset , regm_t keepmsk , regm_t desmsk ); +unsigned getaddrmode (regm_t idxregs ); +void setaddrmode(code *c, regm_t idxregs); +void getlvalue_msw(code *); +void getlvalue_lsw(code *); +code *getlvalue (code *pcs , elem *e , regm_t keepmsk ); +code *fltregs (code *pcs , tym_t tym ); +code *tstresult (regm_t regm , tym_t tym , unsigned saveflag ); +code *fixresult (elem *e , regm_t retregs , regm_t *pretregs ); +code *callclib (elem *e , unsigned clib , regm_t *pretregs , regm_t keepmask ); +cd_t cdfunc; +cd_t cdstrthis; +code *params(elem *, unsigned); +code *offsetinreg (elem *e , regm_t *pretregs ); +code *loaddata (elem *e , regm_t *pretregs ); + +/* cod2.c */ +int movOnly(elem *e); +regm_t idxregm(code *c); +#if TARGET_WINDOS +code *opdouble (elem *e , regm_t *pretregs , unsigned clib ); +#endif +cd_t cdorth; +cd_t cdmul; +cd_t cdnot; +cd_t cdcom; +cd_t cdbswap; +cd_t cdcond; +void WRcodlst (code *c ); +cd_t cdcomma; +cd_t cdloglog; +cd_t cdshift; +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +cd_t cdindpic; +#endif +cd_t cdind; +cd_t cdstrlen; +cd_t cdstrcmp; +cd_t cdstrcpy; +cd_t cdmemchr; +cd_t cdmemcmp; +cd_t cdmemcpy; +cd_t cdmemset; +cd_t cdstreq; +cd_t cdrelconst; +code *getoffset (elem *e , unsigned reg ); +cd_t cdneg; +cd_t cdabs; +cd_t cdpost; +cd_t cderr; +cd_t cdinfo; +cd_t cddctor; +cd_t cdddtor; +cd_t cdctor; +cd_t cddtor; +cd_t cdmark; +cd_t cdnullcheck; +cd_t cdclassinit; + +/* cod3.c */ +extern int BPoff; + +int cod3_EA(code *c); +regm_t cod3_useBP(); +void cod3_set32 (void ); +void cod3_set64 (void ); +void cod3_align (void ); +regm_t regmask(tym_t tym, tym_t tyf); +void outblkexitcode(block *bl, code*& c, int& anyspill, const char* sflsave, symbol** retsym, const regm_t mfuncregsave ); +void doswitch (block *b ); +void outjmptab (block *b ); +void outswitab (block *b ); +int jmpopcode (elem *e ); +void cod3_ptrchk(code **pc,code *pcs,regm_t keepmsk); +code *genregs (code *c , unsigned op , unsigned dstreg , unsigned srcreg ); +code *gentstreg (code *c , unsigned reg ); +code *genpush (code *c , unsigned reg ); +code *genpop (code *c , unsigned reg ); +code* gensavereg(unsigned& reg, targ_uns slot); +code *genmovreg (code *c , unsigned to , unsigned from ); +code *genmulimm(code *c,unsigned r1,unsigned r2,targ_int imm); +code *genshift(code *); +code *movregconst (code *c , unsigned reg , targ_size_t value , regm_t flags ); +code *genjmp (code *c , unsigned op , unsigned fltarg , block *targ ); +code *prolog (void ); +void epilog (block *b); +code *gen_spill_reg(Symbol *s, bool toreg); +cd_t cdframeptr; +cd_t cdgot; +code *load_localgot(); +targ_size_t cod3_spoff(); +code *cod3_load_got(); +void makeitextern (symbol *s ); +void fltused(void); +int branch(block *bl, int flag); +void cod3_adjSymOffsets(); +void assignaddr (block *bl ); +void assignaddrc (code *c ); +targ_size_t cod3_bpoffset(symbol *s); +void pinholeopt (code *c , block *bn ); +void jmpaddr (code *c ); +int code_match(code *c1,code *c2); +unsigned calcblksize (code *c); +unsigned calccodsize(code *c); +unsigned codout (code *c ); +void addtofixlist (symbol *s , targ_size_t soffset , int seg , targ_size_t val , int flags ); +void searchfixlist (symbol *s ); +void outfixlist (void ); +void code_hydrate(code **pc); +void code_dehydrate(code **pc); + +/* cod4.c */ +extern const unsigned dblreg[]; +extern int cdcmp_flag; + +int doinreg(symbol *s, elem *e); +code *modEA(code *c); +cd_t cdeq; +cd_t cdaddass; +cd_t cdmulass; +cd_t cdshass; +cd_t cdcmp; +cd_t cdcnvt; +cd_t cdshtlng; +cd_t cdbyteint; +cd_t cdlngsht; +cd_t cdmsw; +cd_t cdport; +cd_t cdasm; +cd_t cdsetjmp; +cd_t cdvoid; +cd_t cdhalt; +cd_t cdfar16; +cd_t cdbt; +cd_t cdbscan; +cd_t cdpair; +code *longcmp (elem *,bool,unsigned,code *); + +/* cod5.c */ +void cod5_prol_epi(); +void cod5_noprol(); + +/* cgxmm.c */ +code *movxmmconst(unsigned reg, unsigned sz, targ_size_t value, regm_t flags); +code *orthxmm(elem *e, regm_t *pretregs); +code *xmmeq(elem *e, regm_t *pretregs); +code *xmmcnvt(elem *e,regm_t *pretregs); +code *xmmopass(elem *e, regm_t *pretregs); +code *xmmneg(elem *e, regm_t *pretregs); +unsigned xmmload(tym_t tym); +unsigned xmmstore(tym_t tym); +code *cdvector(elem *e, regm_t *pretregs); + +/* cg87.c */ +void note87(elem *e, unsigned offset, int i); +#ifdef DEBUG +void pop87(int, const char *); +#else +void pop87(); +#endif +code *push87 (void ); +code *save87 (void ); +code *save87regs(unsigned n); +void gensaverestore87(regm_t, code **, code **); +code *genfltreg(code *c,unsigned opcode,unsigned reg,targ_size_t offset); +code *genfwait(code *c); +code *comsub87(elem *e, regm_t *pretregs); +code *fixresult87 (elem *e , regm_t retregs , regm_t *pretregs ); +code *fixresult_complex87(elem *e,regm_t retregs,regm_t *pretregs); +code *orth87 (elem *e , regm_t *pretregs ); +code *load87(elem *e, unsigned eoffset, regm_t *pretregs, elem *eleft, int op); +int cmporder87 (elem *e ); +code *eq87 (elem *e , regm_t *pretregs ); +code *complex_eq87 (elem *e , regm_t *pretregs ); +code *opass87 (elem *e , regm_t *pretregs ); +code *cdnegass87 (elem *e , regm_t *pretregs ); +code *post87 (elem *e , regm_t *pretregs ); +code *cnvt87 (elem *e , regm_t *pretregs ); +code *cnvteq87 (elem *e , regm_t *pretregs ); +cd_t cdrndtol; +cd_t cdscale; +code *neg87 (elem *e , regm_t *pretregs ); +code *neg_complex87(elem *e, regm_t *pretregs); +code *cdind87(elem *e,regm_t *pretregs); +#if TX86 +extern int stackused; +#endif +code *cdconvt87(elem *e, regm_t *pretregs); +code *cload87(elem *e, regm_t *pretregs); + +#ifdef DEBUG +#define pop87() pop87(__LINE__,__FILE__) +#endif + +/* iasm.c */ +void iasm_term( void ); +regm_t iasm_regs( block *bp ); + +// nteh.c +code *nteh_prolog(void); +code *nteh_epilog(void); +void nteh_usevars(void); +void nteh_filltables(void); +void nteh_gentables(void); +code *nteh_setsp(int op); +code *nteh_filter(block *b); +void nteh_framehandler(symbol *); +code *nteh_gensindex(int); +#define GENSINDEXSIZE 7 +code *nteh_monitor_prolog(Symbol *shandle); +code *nteh_monitor_epilog(regm_t retregs); + +// cgen.c +code *code_last(code *c); +void code_orflag(code *c,unsigned flag); +void code_orrex(code *c,unsigned rex); +code *setOpcode(code *c, code *cs, unsigned op); +code * __pascal cat (code *c1 , code *c2 ); +code * cat3 (code *c1 , code *c2 , code *c3 ); +code * cat4 (code *c1 , code *c2 , code *c3 , code *c4 ); +code * cat6 (code *c1 , code *c2 , code *c3 , code *c4 , code *c5 , code *c6 ); +code *gen (code *c , code *cs ); +code *gen1 (code *c , unsigned op ); +code *gen2 (code *c , unsigned op , unsigned rm ); +code *gen2sib(code *c,unsigned op,unsigned rm,unsigned sib); +code *genasm (code *c , char *s , unsigned slen ); +code *gencsi (code *c , unsigned op , unsigned rm , unsigned FL2 , SYMIDX si ); +code *gencs (code *c , unsigned op , unsigned rm , unsigned FL2 , symbol *s ); +code *genc2 (code *c , unsigned op , unsigned rm , targ_size_t EV2 ); +code *genc1 (code *c , unsigned op , unsigned rm , unsigned FL1 , targ_size_t EV1 ); +code *genc (code *c , unsigned op , unsigned rm , unsigned FL1 , targ_size_t EV1 , unsigned FL2 , targ_size_t EV2 ); +code *genlinnum(code *,Srcpos); +void cgen_linnum(code **pc,Srcpos srcpos); +void cgen_prelinnum(code **pc,Srcpos srcpos); +code *genadjesp(code *c, int offset); +code *genadjfpu(code *c, int offset); +code *gennop(code *); +code *gencodelem(code *c,elem *e,regm_t *pretregs,bool constflag); +bool reghasvalue (regm_t regm , targ_size_t value , unsigned *preg ); +code *regwithvalue (code *c , regm_t regm , targ_size_t value , unsigned *preg , regm_t flags ); + +// cgreg.c +void cgreg_init(); +void cgreg_term(); +void cgreg_reset(); +void cgreg_used(unsigned bi,regm_t used); +void cgreg_spillreg_prolog(block *b,Symbol *s,code **pcstore,code **pcload); +void cgreg_spillreg_epilog(block *b,Symbol *s,code **pcstore,code **pcload); +int cgreg_assign(Symbol *retsym); +void cgreg_unregister(regm_t conflict); + +// cgsched.c +void cgsched_block(block *b); + +// Data and code can be in any number of sections +// +// Generalize the Windows platform concept of CODE,DATA,UDATA,etc +// into multiple segments + +#if OMFOBJ + +struct Ledatarec; + +struct seg_data +{ + int SDseg; // index into SegData[] + targ_size_t SDoffset; // starting offset for data + + bool isfarseg; + int segidx; // internal object file segment number + int lnameidx; // lname idx of segment name + int classidx; // lname idx of class name + unsigned attr; // segment attribute + targ_size_t origsize; // original size + long seek; // seek position in output file + Ledatarec *ledata; // current one we're filling in +}; + +//extern targ_size_t Coffset; + +#endif + +#if ELFOBJ || MACHOBJ + +typedef unsigned int IDXSTR; +typedef unsigned int IDXSEC; +typedef unsigned int IDXSYM; + +struct linnum_data +{ + const char *filename; + unsigned filenumber; // corresponding file number for DW_LNS_set_file + + unsigned linoff_count; + unsigned linoff_max; + unsigned (*linoff)[2]; // [0] = line number, [1] = offset +}; + +struct seg_data +{ + int SDseg; // segment index into SegData[] + IDXSEC SDshtidx; // section header table index + targ_size_t SDoffset; // starting offset for data + Outbuffer *SDbuf; // buffer to hold data + Outbuffer *SDrel; // buffer to hold relocation info +#if ELFOBJ + IDXSYM SDsymidx; // each section is in the symbol table + IDXSEC SDrelidx; // section header for relocation info + targ_size_t SDrelmaxoff; // maximum offset encountered + int SDrelindex; // maximum offset encountered + int SDrelcnt; // number of relocations added + IDXSEC SDshtidxout; // final section header table index + Symbol *SDsym; // if !=NULL, comdat symbol +#endif + + unsigned SDaranges_offset; // if !=0, offset in .debug_aranges + + unsigned SDlinnum_count; + unsigned SDlinnum_max; + linnum_data *SDlinnum_data; // array of line number / offset data + + int isCode(); +}; + + +#endif + +extern seg_data **SegData; +#define Offset(seg) SegData[seg]->SDoffset +#define Doffset SegData[DATA]->SDoffset +#define CDoffset SegData[CDATA]->SDoffset +#define Coffset SegData[cseg]->SDoffset + +#if __cplusplus && TX86 +} +#endif + diff --git a/backend/cppman.c b/backend/cppman.c new file mode 100644 index 00000000..bcb82357 --- /dev/null +++ b/backend/cppman.c @@ -0,0 +1,746 @@ +// Copyright (C) 1987-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +/* C++ name mangling routines */ + +#include +#include +#include +#include "cc.h" + +#if !NEWMANGLE + +#define NEW_UNMANGLER 1 + +#include "parser.h" +#include "token.h" +#include "global.h" +#include "oper.h" +#include "el.h" +#include "type.h" +#include "cpp.h" +#include "filespec.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +//char *cpp_name = NULL; +char cpp_name[2 * IDMAX + 1] = { 0 }; + +/* Names for special variables */ +char cpp_name_new[] = "__nw"; +char cpp_name_delete[] = "__dl"; +char cpp_name_ct[] = "__ct"; +char cpp_name_dt[] = "__dt"; +char cpp_name_as[] = "__as"; +char cpp_name_vc[] = "__vc"; +char cpp_name_primdt[] = "__pd"; +char cpp_name_scaldeldt[] = "__sd"; +static symbol *ssymbol; + +/**************************** + */ + +struct OPTABLE oparray[] = +{ + { TKnew, OPnew, cpp_name_new, "new" }, + { TKdelete, OPdelete, cpp_name_delete,"del" }, + { TKadd, OPadd, "__pl", "+" }, + { TKadd, OPuadd, "__pl", "+" }, + { TKmin, OPmin, "__mi", "-" }, + { TKmin, OPneg, "__mi", "-" }, + { TKstar, OPmul, "__ml", "*" }, + { TKstar, OPind, "__ml", "*" }, + { TKdiv, OPdiv, "__dv", "/" }, + { TKmod, OPmod, "__md", "%" }, + { TKxor, OPxor, "__er", "^" }, + { TKand, OPand, "__ad", "&" }, + { TKand, OPaddr, "__ad", "&" }, + { TKor, OPor, "__or", "|" }, + { TKcom, OPcom, "__co", "~" }, + { TKnot, OPnot, "__nt", "!" }, + { TKeq, OPeq, "__as", "=" }, + { TKeq, OPstreq, "__as", "=" }, + { TKlt, OPlt, "__lt", "<" }, + { TKgt, OPgt, "__gt", ">" }, + { TKunord, OPunord, "__uno", "!<>=" }, + { TKlg, OPlg, "__lg", "<>" }, + { TKleg, OPleg, "__leg", "<>=" }, + { TKule, OPule, "__ule", "!>" }, + { TKul, OPul, "__ul", "!>=" }, + { TKuge, OPuge, "__uge", "!<" }, + { TKug, OPug, "__ug", "!<=" }, + { TKue, OPue, "__ue", "!<>" }, + { TKaddass, OPaddass, "__apl", "+=" }, + { TKminass, OPminass, "__ami", "-=" }, + { TKmulass, OPmulass, "__amu", "*=" }, + { TKdivass, OPdivass, "__adv", "/=" }, + { TKmodass, OPmodass, "__amd", "%=" }, + { TKxorass, OPxorass, "__aer", "^=" }, + { TKandass, OPandass, "__aad", "&=" }, + { TKorass, OPorass, "__aor", "|=" }, + { TKshl, OPshl, "__ls", "<<" }, + { TKshr, OPshr, "__rs", ">>" }, + { TKshrass, OPshrass, "__ars", "<<=" }, + { TKshlass, OPshlass, "__als", ">>=" }, + { TKeqeq, OPeqeq, "__eq", "==" }, + { TKne, OPne, "__ne", "!=" }, + { TKle, OPle, "__le", "<=" }, + { TKge, OPge, "__ge", ">=" }, + { TKandand, OPandand, "__aa", "&&" }, + { TKoror, OPoror, "__oo", "||" }, + { TKplpl, OPpostinc, "__pp", "++" }, + { TKplpl, OPpreinc, "__pp", "++" }, + { TKmimi, OPpostdec, "__mm", "--" }, + { TKmimi, OPpredec, "__mm", "--" }, + { TKlpar, OPcall, "__cl", "()" }, + { TKlbra, OPbrack, "__vc", "[]" }, + { TKarrow, OParrow, "__rf", "->" }, + { TKcomma, OPcomma, "__cm", "," }, + { TKarrowstar, OParrowstar, "__rm", "->*" }, +}; + +/*********************************** + * Cat together two names into a static buffer. + * n1 can be the same as the static buffer. + */ + + +char *cpp_catname(char *n1,char *n2) +{ + static char cpp_name[IDMAX + 1]; + +#ifdef DEBUG + assert(n1 && n2); +#endif + if (strlen(n1) + strlen(n2) >= sizeof(cpp_name)) + { +#if SCPP + lexerr(EM_ident2big); // identifier is too long +#else + assert(0); +#endif + cpp_name[0] = 0; + } + else + strcat(strcpy(cpp_name,n1),n2); + return cpp_name; +} + +/*********************************** + * 'Combine' a class and a member name into one name. + */ + +char *cpp_genname(char *cl_name,char *mem_name) +{ +#if NEWMANGLE + return cpp_catname(alloca_strdup2(mem_name,cl_name),"@"); +#else + char format[2 + 3 + 1]; + + sprintf(format,"__%d",strlen(cl_name)); + return cpp_catname(cpp_catname(mem_name,format),cl_name); +#endif +} + +/**************************************** + * Convert from identifier to operator + */ + +char *cpp_unmangleident(const char *p) +{ int i; + + for (i = 0; i < arraysize(oparray); i++) + { if (strcmp(p,oparray[i].string) == 0) + { + strcpy(cpp_name,"operator "); + strcat(cpp_name,oparray[i].pretty); + p = cpp_name; + break; + } + } + return (char *)p; +} + +/**************************************** + * Find index in oparray[] for operator. + * Returns: + * index or -1 if not found + */ + +int cpp_opidx(int op) +{ int i; + + for (i = 0; i < arraysize(oparray); i++) + if (oparray[i].oper == (char) op) + return i; + return -1; +} + +/*************************************** + * Find identifier string associated with operator. + * Returns: + * NULL if not found + */ + +char *cpp_opident(int op) +{ int i; + + i = cpp_opidx(op); + return (i == -1) ? NULL : oparray[i].string; +} + +/******************************** + * 'Mangle' a name for output. + * Returns: + * pointer to mangled name (a static buffer) + */ + +char *cpp_mangle(symbol *s) +{ char *p; + symbol *sclass; + + if (!CPP) + return s->Sident; + + ssymbol = s; + + symbol_debug(s); + //dbg_printf("cpp_mangle(%s)\n",s->Sident); + p = symbol_ident(s); + sclass = s->Sscope; + if (sclass) + { symbol_debug(sclass); + p = cpp_genname(symbol_ident(sclass),p); + while (1) + { + char format[10 + 1]; + char *cl_name; + + sclass = sclass->Sscope; + if (!sclass) + break; + + cl_name = symbol_ident(sclass); + sprintf(format,"%d",strlen(cl_name)); + p = cpp_catname(cpp_catname(p,format),cl_name); + } + } + type_debug(s->Stype); + // Function symbols defined statically don't have Sfunc + if (tyfunc(s->Stype->Tty) && + s->Sfunc && s->Sfunc->Fflags & Ftypesafe) + { if (!s->Sscope) + p = cpp_catname(p,"__"); + p = cpp_typetostring(s->Stype,p); + } + /*dbg_printf("cpp_mangle(%s)\n",p);*/ + ssymbol = NULL; + return p; +} + +/********************************** + * Convert from operator token to name. + * Returns: + * pointer to corresponding name + */ + +#if SCPP + +char *cpp_operator(int *poper,type **pt) +{ + int i; + type *typ_spec; + + *pt = NULL; + stoken(); /* skip over operator keyword */ + for (i = 0; i < arraysize(oparray); i++) + { if (oparray[i].tokn == tok.TKval) + goto L1; + } + + /* Look for type conversion */ + if (type_specifier(&typ_spec,NULL ARG_FALSE)) + { type *t; + + t = ptr_operator(typ_spec); // parse ptr-operator + fixdeclar(t); + type_free(typ_spec); + *pt = t; + return cpp_typetostring(t,"__op"); + } + + cpperr(EM_not_overloadable); // that token cannot be overloaded + stoken(); + return "_"; + +L1: + *poper = oparray[i].oper; + switch (*poper) + { case OPcall: + if (stoken() != TKrpar) + synerr(EM_rpar); /* ')' expected */ + break; + case OPbrack: + if (stoken() != TKrbra) + synerrEM_rbra); /* ']' expected */ + break; + } + stoken(); + return oparray[i].string; +} + +#endif + +/*********************************** + * Generate and return a pointer to a string constructed from + * the type, appended to the prefix. + * Since these generated strings determine the uniqueness of names, + * they are also used to determine if two types are the same. + * Returns: + * pointer to static name[] + */ + +char *cpp_typetostring(type *t,char *prefix) +{ int i; + param_t *p; + type *tstart; + bool dofuncret = FALSE; /* BUG: this should be passed in */ + static int nest = 0; + symbol *s; + + if (prefix) + { strcpy(cpp_name, prefix); + i = strlen(prefix); + } + else + i = 0; + /*dbg_printf("cpp_typetostring:\n"); + type_print(t);*/ + tstart = t; + for (; t; t = t->Tnext, dofuncret = TRUE) + { char c1,c2; + int nestclass; + + type_debug(t); + if (i > IDMAX - 4) /* if not room for 4 more + 0 */ + { //cpperr(EM_type_complex); // type is too complex + assert(0); + i = 0; + } + + if (t->Tty & mTYconst) + cpp_name[i++] = 'C'; + if (t->Tty & mTYvolatile) + cpp_name[i++] = 'V'; + c1 = 0; + nestclass = 0; + /* Function return types are ignored */ + switch (tybasic(t->Tty)) + { + case TYschar: c1 = 'S'; goto L2; + case TYuchar: c1 = 'U'; goto L2; + case TYchar: L2: c2 = 'c'; break; + case TYushort: c1 = 'U'; + case TYshort: c2 = 's'; break; + case TYuint: c1 = 'U'; + case TYint: c2 = 'i'; break; +#if LONGLONG && __INTSIZE == 4 // DJB + case TYullong: c1 = 'U'; + case TYllong: c2 = 'x'; break; +#endif + case TYulong: c1 = 'U'; + case TYlong: c2 = 'l'; break; +#if M_UNIX || M_XENIX + case TYnptr: // For Gnu gdb and ARM compatibility +#endif + case TYfptr: c2 = 'P'; break; + case TYvptr: c2 = 'h'; break; + case TYfloat: c2 = 'f'; break; + case TYldouble: c2 = 'r'; break; + case TYdouble: c2 = 'd'; break; + case TYvoid: c2 = 'v'; break; +#if TX86 + case TYnref: + case TYfref: +#endif + case TYref: c2 = 'R'; break; +#if M_UNIX || M_XENIX + case TYmfunc: + case TYnfunc: + case TYnpfunc: // Near functions under Unix are coded as F + case TYnsysfunc: // see ARM page 124 +#endif + case TYfpfunc: c2 = 'F'; goto L4; +#if TX86 + case TYfsysfunc: +#endif + case TYffunc: c2 = 'D'; goto L4; +#if TX86 + case TYsptr: c2 = 'b'; break; +#if !(M_UNIX || M_XENIX) + case TYnptr: c2 = 'p'; break; +#endif + case TYcptr: c2 = 'E'; break; + case TYf16ptr: c2 = 'g'; break; + case TYf16func: c2 = 'G'; goto L4; + case TYhptr: c2 = 'H'; break; +#if !(M_UNIX || M_XENIX) + case TYnpfunc: c2 = 'N'; goto L4; + case TYmfunc: + case TYnsysfunc: + case TYnfunc: c2 = 'B'; goto L4; +#endif + case TYfsfunc: c2 = 'I'; goto L4; + case TYnsfunc: c2 = 'j'; goto L4; +#else + case TYpsfunc: c2 = 'F'; goto L4; + case TYcomp: c2 = 'o'; break; + case TYmemptr: c2 = 'm'; break; +#endif + L4: + cpp_name[i++] = c2; + if (i > IDMAX - 2) + { //cpperr(EM_type_complex); + assert(0); + i = 0; + } + /* Append the types of the parameters to the name */ + { int n; + int paramidx[10]; /* previous parameter indices */ + + n = 1; /* parameter number */ + for (p = t->Tparamtypes; p; p = p->Pnext) + { int len; + + cpp_name[i] = 0; + nest++; + cpp_typetostring(p->Ptype,cpp_name); + nest--; + len = strlen(cpp_name); + if (n < arraysize(paramidx)) + { paramidx[n] = i; + if (len - i > 2) /* only if we get real savings */ + { int j; + + /* 'common subexpression' with any previous */ + /* matching type, if match, replace with */ + /* 'T' parameter_number */ + for (j = 1; j < n; j++) + if (memcmp(&cpp_name[paramidx[j]],&cpp_name[i],len - i) == 0) + { sprintf(cpp_name + i,"T%d",j); + len = i + 2; + break; + } + } + } + if (len > IDMAX - 2) + { //cpperr(EM_type_complex); + assert(0); + len = 0; + n = 0; + } + i = len; + n++; + } + } + if (variadic(t)) + cpp_name[i++] = 'e'; + else if (t->Tflags & TFfixed && !t->Tparamtypes) + cpp_name[i++] = 'v'; /* func(void) */ + + /* Determine if function return types should be considered */ + if (dofuncret || nest) + { cpp_name[i++] = '_'; + continue; + } + else + goto L1; /* ignore what the function returns */ + +#if TX86 + case TYmemptr: + cpp_name[i++] = 'm'; +#endif + case TYstruct: + s = t->Ttag; + L6: + if (s->Sstruct->Sflags & STRnotagname) + { + s->Sstruct->Sflags &= ~STRnotagname; +#if SCPP + warerr(WM_notagname,ssymbol ? (char *)ssymbol->Sident : "Unknown" ); /* no tag name for struct */ +#endif + } + goto L5; + case TYenum: + s = t->Ttag; + if (s->Senum->SEflags & SENnotagname) + { + s->Senum->SEflags &= ~SENnotagname; +#if SCPP + warerr(WM_notagname, ssymbol ? (char *)ssymbol->Sident : "Unknown" ); /* no tag name for struct */ +#endif + } + L5: + { int len; + char *p; + + /* Append the tag to the name */ + p = symbol_ident(s); + len = strlen(p); + if (i + len + nestclass > IDMAX - sizeof(len) * 3) + { //cpperr(EM_type_complex); /* type is too complex */ + assert(0); + goto L1; + } + sprintf(cpp_name + i,("X%d%s" + 1 - nestclass),len,p); + + /* Handle nested classes */ + s = s->Sscope; + if (s) + { nestclass = 1; + i = strlen(cpp_name); + goto L6; + } + + goto L3; + } + case TYarray: + if (i > IDMAX - 1 - sizeof(t->Tdim) * 3) + { //cpperr(EM_type_complex); // type is too complex + assert(0); + goto L1; + } + sprintf(cpp_name + i,"A%d",t->Tdim); + L3: i = strlen(cpp_name); + continue; + default: + debug(type_print(t)); + assert(0); + } + if (c1) + cpp_name[i++] = c1; + cpp_name[i++] = c2; + } +L1: + cpp_name[i] = 0; // terminate the string + return cpp_name; +} + +/*********************************** + * Create mangled name for template instantiation. + */ + +#if SCPP + +char *template_mangle(symbol *s,param_t *arglist) +{ + /* mangling ::= "__PT" N template_name { type | expr } + N ::= number of characters in template_name + type ::= mangled type + expr ::= "V" value + value ::= integer | string | address | float | double | long_double | numeric  + integer ::= digit { digit } + string ::= "S" integer "_" { char } + address ::= "R" integer "_" { char } + float ::= "F" hex_digits + double ::= "D" hex_digits + long_double ::= "L" hex_digits + */ + char *n; + param_t *p; + + ssymbol = s; + + assert(s); + symbol_debug(s); + assert(s->Sclass == SCtemplate); + n = cpp_catname("__PT",unsstr(strlen((char *)s->Sident))); + n = cpp_catname(n,(char *)s->Sident); + for (p = arglist; p; p = p->Pnext) + { + if (p->Ptype) + { /* Argument is a type */ + n = cpp_typetostring(p->Ptype,n); + } + else + { /* Argument is an expression */ + elem *e = p->Pelem; + tym_t ty = tybasic(e->ET->Tty); + char *p; + char a[2]; + int ni; +#if NEW_UNMANGLER + double d; +#endif + + n = cpp_catname(n,"V"); + /*n = cpp_typetostring(e->ET,n);*/ + switch (e->Eoper) + { case OPconst: + switch (ty) + { +#if !(NEW_UNMANGLER) + case TYfloat: ni = FLOATSIZE; a[0] = 'F'; goto L1; + case TYdouble: ni = DOUBLESIZE; a[0] = 'D'; goto L1; + case TYldouble: ni = LNGDBLSIZE; a[0] = 'L'; goto L1; + L1: + a[1] = 0; + n = cpp_catname(n,a); + p = (char *)&e->EV.Vdouble; + +#elif !NEW_UNMANGLER + case TYfloat: + float f; + ni = FLOATSIZE; + a[0] = 'F'; + f = e->EV.Vfloat; + p = (char *)&f; + goto L1; + case TYdouble: + double d; + ni = tysize[TYdouble]; + a[0] = 'D'; + d = e->EV.Vdouble; + p = (char *)&d; + goto L1; + case TYldouble: + ni = tysize[TYldouble]; + a[0] = 'L'; + if (config.flags & CFGldblisdbl) + p = (char *)&e->EV.Vdouble; + else + { + d = e->EV.Vldouble; + } + p = (char *)&d; +// ni = tysize[TYdouble]; + ni = sizeof(long double); // just until new unmangler is in + L1: + a[1] = 0; + n = cpp_catname(n,a); +#endif +#if !NEW_UNMANGLER + while (ni--) + { char c; + static char hex[17] = "0123456789ABCDEF"; + static char buf[3]; + + c = *p++; + buf[0] = hex[c & 15]; + buf[1] = hex[(c >> 4) & 15]; + n = cpp_catname(n,buf); + } + break; +#else // NEW_UNMANGLER + case TYfloat: d = e->EV.Vfloat; goto L1; + case TYdouble: d = e->EV.Vdouble; goto L1; + case TYldouble: if (config.flags & CFGldblisdbl) + d = e->EV.Vdouble; + else + d = e->EV.Vldouble; + L1: char buf[32]; + n = cpp_catname(n,"N"); + ni = sprintf(buf, "%g", d); + p = buf-1; + while (ni--) + { char c; + c = *++p; + if (c == '-') + *p = 'n'; + else if (c == '+') + *p = 'p'; + else if (c == '.') + *p = 'd'; + } + p = buf; + goto L2; +#endif // NEW_UNMANGLER + default: + if (tyintegral(ty)) + { char buf[sizeof(long) * 3 + 1]; + sprintf(buf,"%lu",el_tolong(e)); + cpp_catname(n,buf); + break; + } + assert(0); + } + break; + case OPstring: + p = e->EV.ss.Vstring; + n = cpp_catname(n,"S"); + goto L2; + case OPrelconst: + p = (char *)e->EV.sp.Vsym->Sident; + n = cpp_catname(n,"R"); + L2: + n = cpp_catname(n,unsstr(strlen(p))); + n = cpp_catname(n,"_"); + n = cpp_catname(n,p); + break; + default: + assert(errcnt); + break; + } + } + } /* for */ + ssymbol = NULL; + return n; +} + +#endif + +/********************************* + * Mangle a vtbl or vbtbl name. + * Returns: + * pointer to generated symbol with mangled name + */ + +#if SCPP + +symbol *mangle_tbl( + int flag, // 0: vtbl, 1: vbtbl + type *t, // type for symbol + Classsym *stag, // class we're putting tbl in + Classsym *sbase) // base class (NULL if none) +{ const char *id; + symbol *s; + + if (flag == 0) + id = config.flags3 & CFG3rtti ? "rttivtbl" : "vtbl"; + else + id = "vbtbl"; + if (sbase) + id = cpp_genname((char *)stag->Sident,cpp_genname((char *)sbase->Sident,id)); + else + id = cpp_genname((char *)stag->Sident,id); + +// +// This can happen for MI cases, the virtual table could already be defined +// + + s = scope_search( id, SCTglobal | SCTnspace | SCTlocal ); + if (s) + return(s); + s = scope_define(id,SCTglobal | SCTnspace | SCTlocal, SCunde); + s->Stype = t; + t->Tcount++; +#if XCOFF_OBJ || CFM68K || CFMV2 + if (config.CFMOption && config.CFMxf) // cross fragment C++ + s->Scfmflags = stag->Scfmflags; // Copy the flags from the stag +#endif + return s; +} + +#endif + +#endif diff --git a/backend/cv4.h b/backend/cv4.h new file mode 100644 index 00000000..f19eeeb9 --- /dev/null +++ b/backend/cv4.h @@ -0,0 +1,126 @@ +//_ cv4.h +// Codeview 4 stuff +// See "Microsoft Symbol and Type OMF" document + +#define OEM 0x42 // Digital Mars OEM number (picked at random) + +// Symbol Indices +#define S_COMPILE 1 +#define S_REGISTER 2 +#define S_CONST 3 +#define S_UDT 4 +#define S_SSEARCH 5 +#define S_END 6 +#define S_SKIP 7 +#define S_CVRESERVE 8 +#define S_OBJNAME 9 +#define S_ENDARG 0x0A +#define S_COBOLUDT 0x0B +#define S_MANYREG 0x0C +#define S_RETURN 0x0D +#define S_ENTRYTHIS 0x0E +#define S_TDBNAME 0x0F + +#define S_BPREL16 0x100 +#define S_LDATA16 0x101 +#define S_GDATA16 0x102 +#define S_PUB16 0x103 +#define S_LPROC16 0x104 +#define S_GPROC16 0x105 +#define S_THUNK16 0x106 +#define S_BLOCK16 0x107 +#define S_WITH16 0x108 +#define S_LABEL16 0x109 +#define S_CEXMODEL16 0x10A +#define S_VFTPATH16 0x10B + +#define S_BPREL32 0x200 +#define S_LDATA32 0x201 +#define S_GDATA32 0x202 +#define S_PUB32 0x203 +#define S_LPROC32 0x204 +#define S_GPROC32 0x205 +#define S_THUNK32 0x206 +#define S_BLOCK32 0x207 +#define S_WITH32 0x208 +#define S_LABEL32 0x209 +#define S_CEXMODEL32 0x20A +#define S_VFTPATH32 0x20B + +// Leaf Indices +#define LF_MODIFIER 1 +#define LF_POINTER 2 +#define LF_ARRAY 3 +#define LF_CLASS 4 +#define LF_STRUCTURE 5 +#define LF_UNION 6 +#define LF_ENUM 7 +#define LF_PROCEDURE 8 +#define LF_MFUNCTION 9 +#define LF_VTSHAPE 0x0A +#define LF_COBOL0 0x0B +#define LF_COBOL1 0x0C +#define LF_BARRAY 0x0D +#define LF_LABEL 0x0E +#define LF_NULL 0x0F +#define LF_NOTTRAN 0x10 +#define LF_DIMARRAY 0x11 +#define LF_VFTPATH 0x12 +#define LF_PRECOMP 0x13 +#define LF_ENDPRECOMP 0x14 +#define LF_OEM 0x15 +#define LF_TYPESERVER 0x16 + +// D extensions (not used, causes linker to fail) +#define LF_DYN_ARRAY 0x17 +#define LF_ASSOC_ARRAY 0x18 +#define LF_DELEGATE 0x19 + +#define LF_SKIP 0x200 +#define LF_ARGLIST 0x201 +#define LF_DEFARG 0x202 +#define LF_LIST 0x203 +#define LF_FIELDLIST 0x204 +#define LF_DERIVED 0x205 +#define LF_BITFIELD 0x206 +#define LF_METHODLIST 0x207 +#define LF_DIMCONU 0x208 +#define LF_DIMCONLU 0x209 +#define LF_DIMVARU 0x20A +#define LF_DIMVARLU 0x20B +#define LF_REFSYM 0x20C + +#define LF_BCLASS 0x400 +#define LF_VBCLASS 0x401 +#define LF_IVBCLASS 0x402 +#define LF_ENUMERATE 0x403 +#define LF_FRIENDFCN 0x404 +#define LF_INDEX 0x405 +#define LF_MEMBER 0x406 +#define LF_STMEMBER 0x407 +#define LF_METHOD 0x408 +#define LF_NESTTYPE 0x409 +#define LF_VFUNCTAB 0x40A +#define LF_FRIENDCLS 0x40B + +#define LF_NUMERIC 0x8000 +#define LF_CHAR 0x8000 +#define LF_SHORT 0x8001 +#define LF_USHORT 0x8002 +#define LF_LONG 0x8003 +#define LF_ULONG 0x8004 +#define LF_REAL32 0x8005 +#define LF_REAL64 0x8006 +#define LF_REAL80 0x8007 +#define LF_REAL128 0x8008 +#define LF_QUADWORD 0x8009 +#define LF_UQUADWORD 0x800A +#define LF_REAL48 0x800B + +#define LF_COMPLEX32 0x800C +#define LF_COMPLEX64 0x800D +#define LF_COMPLEX80 0x800E +#define LF_COMPLEX128 0x800F + +#define LF_VARSTRING 0x8010 + diff --git a/backend/debug.c b/backend/debug.c new file mode 100644 index 00000000..f46390e1 --- /dev/null +++ b/backend/debug.c @@ -0,0 +1,415 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#ifdef DEBUG +#if !SPP + +#include +#include + +#include "cc.h" +#include "oper.h" +#include "type.h" +#include "el.h" +#include "token.h" +#include "global.h" +#include "vec.h" +#include "go.h" +#include "code.h" +#include "debtab.c" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +#define ferr(p) dbg_printf("%s",(p)) + +/******************************* + * Write out storage class. + */ + +char *str_class(enum SC c) +{ static char sc[SCMAX][10] = + { + #define X(a,b) #a, + ENUMSCMAC + #undef X + }; + static char buffer[9 + 3]; + + (void) assert(arraysize(sc) == SCMAX); + if ((unsigned) c < (unsigned) SCMAX) + sprintf(buffer,"SC%s",sc[(int) c]); + else + sprintf(buffer,"SC%u",(unsigned)c); + return buffer; +} + +void WRclass(enum SC c) +{ + dbg_printf("%11s ",str_class(c)); +} + +/*************************** + * Write out oper numbers. + */ + +void WROP(unsigned oper) +{ + if (oper >= OPMAX) + { dbg_printf("op = x%x, OPMAX = %d\n",oper,OPMAX); + assert(0); + } + ferr(debtab[oper]); + ferr(" "); +} + +/******************************* + * Write TYxxxx + */ + +void WRTYxx(tym_t t) +{ +#if TX86 + if (t & mTYnear) + dbg_printf("mTYnear|"); +#if TARGET_SEGMENTED + if (t & mTYfar) + dbg_printf("mTYfar|"); + if (t & mTYcs) + dbg_printf("mTYcs|"); +#endif +#endif + if (t & mTYconst) + dbg_printf("mTYconst|"); + if (t & mTYvolatile) + dbg_printf("mTYvolatile|"); +#if !MARS && (linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4) + if (t & mTYtransu) + dbg_printf("mTYtransu|"); +#endif + t = tybasic(t); + if (t >= TYMAX) + { dbg_printf("TY %lx\n",(long)t); + assert(0); + } + dbg_printf("TY%s ",tystring[tybasic(t)]); +} + +void WRBC(unsigned bc) +{ static char bcs[][7] = + {"unde ","goto ","true ","ret ","retexp", + "exit ","asm ","switch","ifthen","jmptab", + "try ","catch ","jump ", + "_try ","_filte","_final","_ret ","_excep", + "jcatch", + "jplace", + }; + + assert(sizeof(bcs) / sizeof(bcs[0]) == BCMAX); + assert(bc < BCMAX); + dbg_printf("BC%s",bcs[bc]); +} + +/************************ + * Write arglst + */ + +void WRarglst(list_t a) +{ int n = 1; + + if (!a) dbg_printf("0 args\n"); + while (a) + { const char* c = (const char*)list_ptr(a); + dbg_printf("arg %d: '%s'\n", n, c ? c : "NULL"); + a = a->next; + n++; + } +} + +/*************************** + * Write out equation elem. + */ + +void WReqn(elem *e) +{ static int nest; + + if (!e) + return; + if (OTunary(e->Eoper)) + { + WROP(e->Eoper); + if (OTbinary(e->E1->Eoper)) + { nest++; + ferr("("); + WReqn(e->E1); + ferr(")"); + nest--; + } + else + WReqn(e->E1); + } + else if (e->Eoper == OPcomma && !nest) + { WReqn(e->E1); + dbg_printf(";\n\t"); + WReqn(e->E2); + } + else if (OTbinary(e->Eoper)) + { + if (OTbinary(e->E1->Eoper)) + { nest++; + ferr("("); + WReqn(e->E1); + ferr(")"); + nest--; + } + else + WReqn(e->E1); + ferr(" "); + WROP(e->Eoper); + if (e->Eoper == OPstreq) + dbg_printf("%ld",(long)type_size(e->ET)); + ferr(" "); + if (OTbinary(e->E2->Eoper)) + { nest++; + ferr("("); + WReqn(e->E2); + ferr(")"); + nest--; + } + else + WReqn(e->E2); + } + else + { + switch (e->Eoper) + { case OPconst: + switch (tybasic(e->Ety)) + { + case TYfloat: + dbg_printf("%g ",e->EV.Vfloat); + break; + case TYdouble: + dbg_printf("%g ",e->EV.Vdouble); + break; + case TYldouble: + dbg_printf("%Lg ",e->EV.Vldouble); + break; + case TYcent: + case TYucent: + dbg_printf("%lld+%lld ", e->EV.Vcent.msw, e->EV.Vcent.lsw); + break; + default: + dbg_printf("%lld ",el_tolong(e)); + break; + } + break; + case OPrelconst: + ferr("#"); + /* FALL-THROUGH */ + case OPvar: + dbg_printf("%s",e->EV.sp.Vsym->Sident); + if (e->EV.sp.Vsym->Ssymnum != -1) + dbg_printf("(%d)",e->EV.sp.Vsym->Ssymnum); + if (e->Eoffset != 0) + { + if (sizeof(e->Eoffset) == 8) + dbg_printf(".x%llx", e->Eoffset); + else + dbg_printf(".%ld",(long)e->Eoffset); + } + break; + case OPasm: + case OPstring: + dbg_printf("\"%s\"",e->EV.ss.Vstring); + if (e->EV.ss.Voffset) + dbg_printf("+%ld",(long)e->EV.ss.Voffset); + break; + case OPmark: + case OPgot: + case OPframeptr: + case OPhalt: + WROP(e->Eoper); + break; + case OPstrthis: + break; + default: + WROP(e->Eoper); + assert(0); + } + } +} + +void WRblocklist(list_t bl) +{ + for (; bl; bl = list_next(bl)) + { register block *b = list_block(bl); + + if (b && b->Bweight) + dbg_printf("B%d (%p) ",b->Bdfoidx,b); + else + dbg_printf("%p ",b); + } + ferr("\n"); +} + +void WRdefnod() +{ register int i; + + for (i = 0; i < deftop; i++) + { dbg_printf("defnod[%d] in B%d = (",defnod[i].DNblock->Bdfoidx,i); + WReqn(defnod[i].DNelem); + dbg_printf(");\n"); + } +} + +void WRFL(enum FL fl) +{ static char fls[FLMAX][7] = + {"unde ","const ","oper ","func ","data ", + "reg ", + "pseudo", + "auto ","para ","extrn ","tmp ", + "code ","block ","udata ","cs ","swit ", + "fltrg ","offst ","datsg ", + "ctor ","dtor ","regsav","asm ", +#if TX86 + "ndp ", +#endif +#if TARGET_SEGMENTED + "farda ","csdat ", +#endif + "local ","tlsdat", + "bprel ","frameh","blocko","alloca", + "stack ","dsym ", +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + "got ","gotoff", +#endif + }; + + if ((unsigned)fl >= (unsigned)FLMAX) + dbg_printf("FL%d",fl); + else + dbg_printf("FL%s",fls[fl]); +} + +/*********************** + * Write out block. + */ + +void WRblock(block *b) +{ + if (OPTIMIZER) + { + if (b && b->Bweight) + dbg_printf("B%d: (%p), weight=%d",b->Bdfoidx,b,b->Bweight); + else + dbg_printf("block %p",b); + if (!b) + { ferr("\n"); + return; + } + dbg_printf(" flags=x%x weight=%d",b->Bflags,b->Bweight); +#if 0 + dbg_printf("\tfile %p, line %d",b->Bfilptr,b->Blinnum); +#endif + dbg_printf(" "); + WRBC(b->BC); + dbg_printf(" Btry=%p Bindex=%d",b->Btry,b->Bindex); +#if SCPP + if (b->BC == BCtry) + dbg_printf(" catchvar = %p",b->catchvar); +#endif + dbg_printf("\n"); + dbg_printf("\tBpred: "); WRblocklist(b->Bpred); + dbg_printf("\tBsucc: "); WRblocklist(b->Bsucc); + if (b->Belem) + { if (debugf) /* if full output */ + elem_print(b->Belem); + else + { ferr("\t"); + WReqn(b->Belem); + dbg_printf(";\n"); + } + } + if (b->Bcode) + b->Bcode->print(); + ferr("\n"); + } + else + { + targ_llong *pu; + int ncases; + list_t bl; + + assert(b); + dbg_printf("********* Basic Block %p ************\n",b); + if (b->Belem) elem_print(b->Belem); + dbg_printf("block: "); + WRBC(b->BC); + dbg_printf(" Btry=%p Bindex=%d",b->Btry,b->Bindex); + dbg_printf("\n"); + dbg_printf("\tBpred:\n"); + for (bl = b->Bpred; bl; bl = list_next(bl)) + dbg_printf("\t%p\n",list_block(bl)); + bl = b->Bsucc; + switch (b->BC) + { + case BCswitch: + pu = b->BS.Bswitch; + assert(pu); + ncases = *pu; + dbg_printf("\tncases = %d\n",ncases); + dbg_printf("\tdefault: %p\n",list_block(bl)); + while (ncases--) + { bl = list_next(bl); + dbg_printf("\tcase %lld: %p\n",*++pu,list_block(bl)); + } + break; + case BCiftrue: + case BCgoto: + case BCasm: +#if SCPP + case BCtry: + case BCcatch: +#endif + case BCjcatch: + case BC_try: + case BC_filter: + case BC_finally: + case BC_ret: + case BC_except: + + Lsucc: + dbg_printf("\tBsucc:\n"); + for ( ; bl; bl = list_next(bl)) + dbg_printf("\t%p\n",list_block(bl)); + break; + case BCret: + case BCretexp: + case BCexit: + break; + default: + assert(0); + } + } +} + +void WRfunc() +{ + block *b; + + dbg_printf("func: '%s'\n",funcsym_p->Sident); + for (b = startblock; b; b = b->Bnext) + WRblock(b); +} + +#endif /* DEBUG */ +#endif /* !SPP */ diff --git a/backend/dt.c b/backend/dt.c new file mode 100644 index 00000000..6b67386e --- /dev/null +++ b/backend/dt.c @@ -0,0 +1,383 @@ +// Copyright (C) 1984-1998 by Symantec +// Copyright (C) 2000-2010 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include +#include "cc.h" +#include "oper.h" +#include "global.h" +#include "el.h" +#include "type.h" +#include "dt.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +static dt_t *dt_freelist; + +/********************************************** + * Allocate a data definition struct. + */ + +dt_t *dt_calloc(char dtx) +{ + dt_t *dt; + static dt_t dtzero; + + if (dt_freelist) + { + dt = dt_freelist; + dt_freelist = dt->DTnext; + *dt = dtzero; + } + else + dt = (dt_t *) mem_fcalloc(sizeof(dt_t)); + dt->dt = dtx; + return dt; +} + +/********************************************** + * Free a data definition struct. + */ + +void dt_free(dt_t *dt) +{ dt_t *dtn; + + for (; dt; dt = dtn) + { + switch (dt->dt) + { + case DT_abytes: + case DT_nbytes: + mem_free(dt->DTpbytes); + break; + } + dtn = dt->DTnext; + dt->DTnext = dt_freelist; + dt_freelist = dt; + } +} + +/********************************* + * Free free list. + */ + +void dt_term() +{ +#if TERMCODE + dt_t *dtn; + + while (dt_freelist) + { dtn = dt_freelist->DTnext; + mem_ffree(dt_freelist); + dt_freelist = dtn; + } +#endif +} + + +/********************** + * Construct a DT_azeros record, and return it. + * Increment dsout. + */ + +dt_t **dtnzeros(dt_t **pdtend,targ_size_t size) +{ dt_t *dt; + + //printf("dtnzeros(x%x)\n",size); + assert((long) size >= 0); + while (*pdtend) + pdtend = &((*pdtend)->DTnext); + if (size) + { dt = dt_calloc(DT_azeros); + dt->DTazeros = size; + *pdtend = dt; + pdtend = &dt->DTnext; +#if SCPP + dsout += size; +#endif + } + return pdtend; +} + +/********************** + * Construct a DTsymsize record. + */ + +void dtsymsize(symbol *s) +{ + symbol_debug(s); + s->Sdt = dt_calloc(DT_symsize); +} + +/********************** + * Construct a DTnbytes record, and return it. + */ + +dt_t ** dtnbytes(dt_t **pdtend,targ_size_t size,const char *ptr) +{ dt_t *dt; + + while (*pdtend) + pdtend = &((*pdtend)->DTnext); + if (size) + { if (size == 1) + { dt = dt_calloc(DT_1byte); + dt->DTonebyte = *ptr; + } + else if (size <= 7) + { dt = dt_calloc(DT_ibytes); + dt->DTn = size; + memcpy(dt->DTdata,ptr,size); + } + else + { + dt = dt_calloc(DT_nbytes); + dt->DTnbytes = size; + dt->DTpbytes = (char *) MEM_PH_MALLOC(size); + memcpy(dt->DTpbytes,ptr,size); + } + *pdtend = dt; + pdtend = &dt->DTnext; + } + return pdtend; +} + +/********************** + * Construct a DTabytes record, and return it. + */ + +dt_t **dtabytes(dt_t **pdtend,tym_t ty, targ_size_t offset, targ_size_t size, const char *ptr) +{ dt_t *dt; + + while (*pdtend) + pdtend = &((*pdtend)->DTnext); + + dt = dt_calloc(DT_abytes); + dt->DTnbytes = size; + dt->DTpbytes = (char *) MEM_PH_MALLOC(size); + dt->Dty = ty; + dt->DTabytes = offset; + memcpy(dt->DTpbytes,ptr,size); + + *pdtend = dt; + pdtend = &dt->DTnext; + return pdtend; +} + +/********************** + * Construct a DTibytes record, and return it. + */ + +dt_t ** dtdword(dt_t **pdtend, int value) +{ dt_t *dt; + + while (*pdtend) + pdtend = &((*pdtend)->DTnext); + dt = dt_calloc(DT_ibytes); + dt->DTn = 4; + + union { char* cp; int* lp; } u; + u.cp = dt->DTdata; + *u.lp = value; + + *pdtend = dt; + pdtend = &dt->DTnext; + return pdtend; +} + +dt_t ** dtsize_t(dt_t **pdtend, targ_size_t value) +{ dt_t *dt; + + while (*pdtend) + pdtend = &((*pdtend)->DTnext); + dt = dt_calloc(DT_ibytes); + dt->DTn = NPTRSIZE; + + union { char* cp; int* lp; } u; + u.cp = dt->DTdata; + *u.lp = value; + if (NPTRSIZE == 8) + u.lp[1] = value >> 32; + + *pdtend = dt; + pdtend = &dt->DTnext; + return pdtend; +} + +/********************** + * Concatenate two dt_t's. + */ + +dt_t ** dtcat(dt_t **pdtend,dt_t *dt) +{ + while (*pdtend) + pdtend = &((*pdtend)->DTnext); + *pdtend = dt; + pdtend = &dt->DTnext; + return pdtend; +} + +/********************** + * Construct a DTcoff record, and return it. + */ + +dt_t ** dtcoff(dt_t **pdtend,targ_size_t offset) +{ dt_t *dt; + + while (*pdtend) + pdtend = &((*pdtend)->DTnext); + dt = dt_calloc(DT_coff); +#if TARGET_SEGMENTED + dt->Dty = TYcptr; +#else + dt->Dty = TYnptr; +#endif + dt->DToffset = offset; + *pdtend = dt; + pdtend = &dt->DTnext; + return pdtend; +} + +/********************** + * Construct a DTxoff record, and return it. + */ + +dt_t ** dtxoff(dt_t **pdtend,symbol *s,targ_size_t offset,tym_t ty) +{ dt_t *dt; + + symbol_debug(s); + while (*pdtend) + pdtend = &((*pdtend)->DTnext); + dt = dt_calloc(DT_xoff); + dt->DTsym = s; + dt->DToffset = offset; + dt->Dty = ty; + *pdtend = dt; + pdtend = &dt->DTnext; + return pdtend; +} + +/************************** + * 'Optimize' a list of dt_t's. + * (Try to collapse it into one DT_azeros object.) + */ + +void dt_optimize(dt_t *dt) +{ dt_t *dtn; + + if (dt) + { for (; 1; dt = dtn) + { + dtn = dt->DTnext; + if (!dtn) + break; + switch (dt->dt) + { + case DT_azeros: + if (dtn->dt == DT_1byte && dtn->DTonebyte == 0) + { + dt->DTazeros += 1; + goto L1; + } + else if (dtn->dt == DT_azeros) + { + dt->DTazeros += dtn->DTazeros; + goto L1; + } + break; + + case DT_1byte: + if (dt->DTonebyte == 0) + { + if (dtn->dt == DT_1byte && dtn->DTonebyte == 0) + { + dt->DTazeros = 2; + goto L1; + } + else if (dtn->dt == DT_azeros) + { + dt->DTazeros = 1 + dtn->DTazeros; + L1: + dt->dt = DT_azeros; + dt->DTnext = dtn->DTnext; + dtn->DTnext = NULL; + dt_free(dtn); + dtn = dt; + } + } + break; + } + } + } +} + +/************************** + * Make a common block for s. + */ + +void init_common(symbol *s) +{ + //printf("init_common('%s')\n", s->Sident); + dtnzeros(&s->Sdt,type_size(s->Stype)); + if (s->Sdt) + s->Sdt->dt = DT_common; +} + +/********************************** + * Compute size of a dt + */ + +unsigned dt_size(dt_t *dtstart) +{ dt_t *dt; + unsigned datasize; + + datasize = 0; + for (dt = dtstart; dt; dt = dt->DTnext) + { + switch (dt->dt) + { case DT_abytes: + datasize += size(dt->Dty); + break; + case DT_ibytes: + datasize += dt->DTn; + break; + case DT_nbytes: + datasize += dt->DTnbytes; + break; + case DT_symsize: + case DT_azeros: + datasize += dt->DTazeros; + break; + case DT_common: + break; + case DT_xoff: + case DT_coff: + datasize += size(dt->Dty); + break; + case DT_1byte: + datasize++; + break; + default: +#ifdef DEBUG + dbg_printf("dt = %p, dt = %d\n",dt,dt->dt); +#endif + assert(0); + } + } + return datasize; +} + +#endif /* !SPP */ diff --git a/backend/dt.h b/backend/dt.h new file mode 100644 index 00000000..99e722ae --- /dev/null +++ b/backend/dt.h @@ -0,0 +1,114 @@ +// Copyright (C) 1984-1995 by Symantec +// Copyright (C) 2000-2010 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +//#pragma once +#ifndef DT_H +#define DT_H 1 + +/********************************** + * Data definitions + * DTibytes 1..7 bytes + * DT1byte one byte of data follows + * n + * DTabytes offset of bytes of data + * a { a data bytes } + * DTnbytes bytes of data + * a { a data bytes } + * a = offset + * DTazeros # of 0 bytes + * a + * DTsymsize same as DTazeros, but the type of the symbol gives + * the size + * DTcommon # of 0 bytes (in a common block) + * a + * DTxoff offset from symbol + * w a + * w = symbol number (pointer for CPP) + * a = offset + * DTcoff offset into code segment + * DTend mark end of list + */ + +struct dt_t +{ dt_t *DTnext; // next in list + char dt; // type (DTxxxx) + unsigned char Dty; // pointer type + union + { + struct // DTibytes + { char DTn_; // number of bytes + #define DTn _DU._DI.DTn_ + char DTdata_[8]; // data + #define DTdata _DU._DI.DTdata_ + }_DI; + char DTonebyte_; // DT1byte + #define DTonebyte _DU.DTonebyte_ + targ_size_t DTazeros_; // DTazeros,DTcommon,DTsymsize + #define DTazeros _DU.DTazeros_ + struct // DTabytes + { + char *DTpbytes_; // pointer to the bytes + #define DTpbytes _DU._DN.DTpbytes_ + unsigned DTnbytes_; // # of bytes + #define DTnbytes _DU._DN.DTnbytes_ +#if TX86 + int DTseg_; // segment it went into + #define DTseg _DU._DN.DTseg_ +#endif + targ_size_t DTabytes_; // offset of abytes for DTabytes + #define DTabytes _DU._DN.DTabytes_ + }_DN; + struct // DTxoff + { + symbol *DTsym_; // symbol pointer + #define DTsym _DU._DS.DTsym_ + targ_size_t DToffset_; // offset from symbol + #define DToffset _DU._DS.DToffset_ + }_DS; + }_DU; +}; + +enum +{ + DT_abytes, + DT_azeros, // 1 + DT_xoff, + DT_1byte, + DT_nbytes, + DT_common, + DT_symsize, + DT_coff, + DT_ibytes, // 8 +}; + +#if TX86 +dt_t *dt_calloc(char dtx); +void dt_free(dt_t *); +void dt_term(void); +#endif + +dt_t **dtnbytes(dt_t **,targ_size_t,const char *); +dt_t **dtabytes(dt_t **pdtend,tym_t ty, targ_size_t offset, targ_size_t size, const char *ptr); +dt_t **dtdword(dt_t **, int value); +dt_t **dtsize_t(dt_t **, targ_size_t value); +dt_t **dtnzeros(dt_t **pdtend,targ_size_t size); +dt_t **dtxoff(dt_t **pdtend,symbol *s,targ_size_t offset,tym_t ty); +dt_t **dtselfoff(dt_t **pdtend,targ_size_t offset,tym_t ty); +dt_t **dtcoff(dt_t **pdtend,targ_size_t offset); +dt_t ** dtcat(dt_t **pdtend,dt_t *dt); +void dt_optimize(dt_t *dt); +void dtsymsize(symbol *); +void init_common(symbol *); +unsigned dt_size(dt_t *dtstart); + +#endif /* DT_H */ + diff --git a/backend/dwarf.c b/backend/dwarf.c new file mode 100644 index 00000000..c3a473a3 --- /dev/null +++ b/backend/dwarf.c @@ -0,0 +1,2531 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gpl.txt. +// See the included readme.txt for details. + +// Emit Dwarf symbolic debug info + +#if !SPP +#include +#include +#include +#include +#include +#include +#include + +#if __DMC__ || linux +#include +#endif + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#include +#include +#include +#endif + +#include "cc.h" +#include "global.h" +#include "code.h" +#include "type.h" +#include "outbuf.h" +#include "filespec.h" +#include "cv4.h" +#include "cgcv.h" +#include "dt.h" + +#include "aa.h" +#include "tinfo.h" + +#if ELFOBJ +#include "melf.h" +#endif +#if MACHOBJ +#include "mach.h" +#endif + +#if ELFOBJ || MACHOBJ + +#if MARS +#include "mars.h" +#endif + +#include "dwarf.h" +#include "dwarf2.h" + +extern int seg_count; + +static char __file__[] = __FILE__; // for tassert.h +#include "tassert.h" + +#if ELFOBJ +#define MAP_SEG2SYMIDX(seg) (SegData[seg]->SDsymidx) +#else +#define MAP_SEG2SYMIDX(seg) (assert(0)) +#endif + +#define OFFSET_FAC REGSIZE + +int dwarf_getsegment(const char *name, int align) +{ +#if ELFOBJ + return elf_getsegment(name, NULL, SHT_PROGDEF, 0, align * 4); +#elif MACHOBJ + return mach_getsegment(name, "__DWARF", align * 2, S_ATTR_DEBUG); +#else + assert(0); + return 0; +#endif +} + +// machobj.c +#define RELaddr 0 // straight address +#define RELrel 1 // relative to location to be fixed up + +void dwarf_addrel(int seg, targ_size_t offset, int targseg, targ_size_t val = 0) +{ +#if ELFOBJ + elf_addrel(seg, offset, I64 ? R_X86_64_32 : RI_TYPE_SYM32, MAP_SEG2SYMIDX(targseg), val); +#elif MACHOBJ + mach_addrel(seg, offset, NULL, targseg, RELaddr, val); +#else + assert(0); +#endif +} + +void dwarf_addrel64(int seg, targ_size_t offset, int targseg, targ_size_t val) +{ +#if ELFOBJ + elf_addrel(seg, offset, R_X86_64_64, MAP_SEG2SYMIDX(targseg), val); +#elif MACHOBJ + mach_addrel(seg, offset, NULL, targseg, RELaddr, val); +#else + assert(0); +#endif +} + +void dwarf_appreladdr(int seg, Outbuffer *buf, int targseg, targ_size_t val) +{ + if (I64) + { + dwarf_addrel64(seg, buf->size(), targseg, val); + buf->write64(0); + } + else + { + dwarf_addrel(seg, buf->size(), targseg, 0); + buf->write32(val); + } +} + +void append_addr(Outbuffer *buf, targ_size_t addr) +{ + if (I64) + buf->write64(addr); + else + buf->write32(addr); +} + + +/************************ DWARF DEBUG OUTPUT ********************************/ + +// Dwarf Symbolic Debugging Information + +struct CFA_reg +{ + int offset; // offset from CFA +}; + +// Current CFA state for .debug_frame +struct CFA_state +{ + size_t location; + int reg; // CFA register number + int offset; // CFA register offset + CFA_reg regstates[17]; // register states +}; + +int dwarf_regno(int reg) +{ + assert(reg <= R15); + if (I16 || I32) + return reg; + else + { + static const int to_amd64_reg_map[8] = + { 0 /*AX*/, 2 /*CX*/, 3 /*DX*/, 1 /*BX*/, + 7 /*SP*/, 6 /*BP*/, 4 /*SI*/, 5 /*DI*/ }; + return reg < 8 ? to_amd64_reg_map[reg] : reg; + } +} + +static CFA_state CFA_state_init_32 = // initial CFA state as defined by CIE +{ 0, // location + dwarf_regno(SP), // register + 4, // offset + { { 0 }, // 0: EAX + { 0 }, // 1: ECX + { 0 }, // 2: EDX + { 0 }, // 3: EBX + { 0 }, // 4: ESP + { 0 }, // 5: EBP + { 0 }, // 6: ESI + { 0 }, // 7: EDI + { -4 }, // 8: EIP + } +}; + +static CFA_state CFA_state_init_64 = // initial CFA state as defined by CIE +{ 0, // location + dwarf_regno(SP), // register + 8, // offset + { { 0 }, // 0: RAX + { 0 }, // 1: RBX + { 0 }, // 2: RCX + { 0 }, // 3: RDX + { 0 }, // 4: RSI + { 0 }, // 5: RDI + { 0 }, // 6: RBP + { 0 }, // 7: RSP + { 0 }, // 8: R8 + { 0 }, // 9: R9 + { 0 }, // 10: R10 + { 0 }, // 11: R11 + { 0 }, // 12: R12 + { 0 }, // 13: R13 + { 0 }, // 14: R14 + { 0 }, // 15: R15 + { -8 }, // 16: RIP + } +}; + +static CFA_state CFA_state_current; // current CFA state +static Outbuffer cfa_buf; // CFA instructions + +void dwarf_CFA_set_loc(size_t location) +{ + assert(location >= CFA_state_current.location); + size_t inc = location - CFA_state_current.location; + if (inc <= 63) + cfa_buf.writeByte(DW_CFA_advance_loc + inc); + else if (inc <= 255) + { cfa_buf.writeByte(DW_CFA_advance_loc1); + cfa_buf.writeByte(inc); + } + else if (inc <= 0xFFFF) + { cfa_buf.writeByte(DW_CFA_advance_loc2); + cfa_buf.writeWord(inc); + } + else + { cfa_buf.writeByte(DW_CFA_advance_loc4); + cfa_buf.write32(inc); + } + CFA_state_current.location = location; +} + +void dwarf_CFA_set_reg_offset(int reg, int offset) +{ + int dw_reg = dwarf_regno(reg); + if (dw_reg != CFA_state_current.reg) + { + if (offset == CFA_state_current.offset) + { + cfa_buf.writeByte(DW_CFA_def_cfa_register); + cfa_buf.writeuLEB128(dw_reg); + } + else if (offset < 0) + { + cfa_buf.writeByte(DW_CFA_def_cfa_sf); + cfa_buf.writeuLEB128(dw_reg); + cfa_buf.writesLEB128(offset / -OFFSET_FAC); + } + else + { + cfa_buf.writeByte(DW_CFA_def_cfa); + cfa_buf.writeuLEB128(dw_reg); + cfa_buf.writeuLEB128(offset); + } + } + else if (offset < 0) + { + cfa_buf.writeByte(DW_CFA_def_cfa_offset_sf); + cfa_buf.writesLEB128(offset / -OFFSET_FAC); + } + else + { + cfa_buf.writeByte(DW_CFA_def_cfa_offset); + cfa_buf.writeuLEB128(offset); + } + CFA_state_current.reg = dw_reg; + CFA_state_current.offset = offset; +} + +void dwarf_CFA_offset(int reg, int offset) +{ + int dw_reg = dwarf_regno(reg); + if (CFA_state_current.regstates[dw_reg].offset != offset) + { + if (offset <= 0) + { + cfa_buf.writeByte(DW_CFA_offset + dw_reg); + cfa_buf.writeuLEB128(offset / -OFFSET_FAC); + } + else + { + cfa_buf.writeByte(DW_CFA_offset_extended_sf); + cfa_buf.writeuLEB128(dw_reg); + cfa_buf.writesLEB128(offset / -OFFSET_FAC); + } + } + CFA_state_current.regstates[dw_reg].offset = offset; +} + +void dwarf_CFA_args_size(size_t sz) +{ + cfa_buf.writeByte(DW_CFA_GNU_args_size); + cfa_buf.writeuLEB128(sz); +} + +// .debug_frame +static IDXSEC debug_frame_secidx; + +// .debug_str +static IDXSEC debug_str_secidx; +static Outbuffer *debug_str_buf; + +// .debug_pubnames +static IDXSEC debug_pubnames_secidx; +static Outbuffer *debug_pubnames_buf; + +// .debug_aranges +static IDXSEC debug_aranges_seg; +static IDXSEC debug_aranges_secidx; +static Outbuffer *debug_aranges_buf; + +// .debug_ranges +static IDXSEC debug_ranges_seg; +static IDXSEC debug_ranges_secidx; +static Outbuffer *debug_ranges_buf; + +// .debug_loc +static IDXSEC debug_loc_seg; +static IDXSEC debug_loc_secidx; +static Outbuffer *debug_loc_buf; + +// .debug_abbrev +static IDXSEC abbrevseg; +static Outbuffer *abbrevbuf; + +/* DWARF 7.5.3: "Each declaration begins with an unsigned LEB128 number + * representing the abbreviation code itself." + */ +static unsigned abbrevcode = 1; +static AArray *abbrev_table; +static int hasModname; // 1 if has DW_TAG_module + +// .debug_info +static IDXSEC infoseg; +static Outbuffer *infobuf; +static AArray *infoFileName_table; + +static AArray *type_table; +static AArray *functype_table; // not sure why this cannot be combined with type_table +static Outbuffer *functypebuf; + +// typeinfo declarations for hash of char* + +struct Abuf +{ + const unsigned char *buf; + size_t length; +}; + +struct TypeInfo_Abuf : TypeInfo +{ + const char* toString(); + hash_t getHash(void *p); + int equals(void *p1, void *p2); + int compare(void *p1, void *p2); + size_t tsize(); + void swap(void *p1, void *p2); +}; + +TypeInfo_Abuf ti_abuf; + +const char* TypeInfo_Abuf::toString() +{ + return "Abuf"; +} + +hash_t TypeInfo_Abuf::getHash(void *p) +{ + Abuf a = *(Abuf *)p; + + hash_t hash = 0; + for (size_t i = 0; i < a.length; i++) + hash = hash * 11 + a.buf[i]; + + return hash; +} + +int TypeInfo_Abuf::equals(void *p1, void *p2) +{ + Abuf a1 = *(Abuf*)p1; + Abuf a2 = *(Abuf*)p2; + + return a1.length == a2.length && + memcmp(a1.buf, a2.buf, a1.length) == 0; +} + +int TypeInfo_Abuf::compare(void *p1, void *p2) +{ + Abuf a1 = *(Abuf*)p1; + Abuf a2 = *(Abuf*)p2; + + if (a1.length == a2.length) + return memcmp(a1.buf, a2.buf, a1.length); + else if (a1.length < a2.length) + return -1; + else + return 1; +} + +size_t TypeInfo_Abuf::tsize() +{ + return sizeof(Abuf); +} + +void TypeInfo_Abuf::swap(void *p1, void *p2) +{ + assert(0); +} + +#pragma pack(1) +struct DebugInfoHeader +{ unsigned total_length; + unsigned short version; + unsigned abbrev_offset; + unsigned char address_size; +}; +#pragma pack() + +static DebugInfoHeader debuginfo_init = +{ 0, // total_length + 2, // version + 0, // abbrev_offset + 4 // address_size +}; + +static DebugInfoHeader debuginfo; + +// .debug_line +static IDXSEC lineseg; +static Outbuffer *linebuf; +static size_t linebuf_filetab_end; + +#pragma pack(1) +struct DebugLineHeader +{ unsigned total_length; + unsigned short version; + unsigned prologue_length; + unsigned char minimum_instruction_length; + unsigned char default_is_stmt; + signed char line_base; + unsigned char line_range; + unsigned char opcode_base; + unsigned char standard_opcode_lengths[9]; +}; +#pragma pack() + +static DebugLineHeader debugline_init = +{ 0, // total_length + 2, // version + 0, // prologue_length + 1, // minimum_instruction_length + TRUE, // default_is_stmt + -5, // line_base + 14, // line_range + 10, // opcode_base + { 0,1,1,1,1,0,0,0,1 } +}; + +static DebugLineHeader debugline; + +unsigned typidx_tab[TYMAX]; + +#if MACHOBJ +const char* debug_frame = "__debug_frame"; +const char* debug_str = "__debug_str"; +const char* debug_ranges = "__debug_ranges"; +const char* debug_loc = "__debug_loc"; +const char* debug_line = "__debug_line"; +const char* debug_abbrev = "__debug_abbrev"; +const char* debug_info = "__debug_info"; +const char* debug_pubnames = "__debug_pubnames"; +const char* debug_aranges = "__debug_aranges"; +#elif ELFOBJ +const char* debug_frame = ".debug_frame"; +const char* debug_str = ".debug_str"; +const char* debug_ranges = ".debug_ranges"; +const char* debug_loc = ".debug_loc"; +const char* debug_line = ".debug_line"; +const char* debug_abbrev = ".debug_abbrev"; +const char* debug_info = ".debug_info"; +const char* debug_pubnames = ".debug_pubnames"; +const char* debug_aranges = ".debug_aranges"; +#endif + +void dwarf_initfile(const char *filename) +{ + #pragma pack(1) + struct DebugFrameHeader + { + unsigned length; + unsigned CIE_id; + unsigned char version; + unsigned char augmentation; + unsigned char code_alignment_factor; + unsigned char data_alignment_factor; + unsigned char return_address_register; + unsigned char opcodes[11]; + }; + #pragma pack() + static DebugFrameHeader debugFrameHeader = + { 16, // length + 0xFFFFFFFF, // CIE_id + 1, // version + 0, // augmentation + 1, // code alignment factor + 0x7C, // data alignment factor (-4) + 8, // return address register + { + DW_CFA_def_cfa, 4,4, // r4,4 [r7,8] + DW_CFA_offset +8,1, // r8,1 [r16,1] + DW_CFA_nop, DW_CFA_nop, + DW_CFA_nop, DW_CFA_nop, // 64 padding + DW_CFA_nop, DW_CFA_nop, // 64 padding + } + }; + if (I64) + { debugFrameHeader.length = 20; + debugFrameHeader.data_alignment_factor = 0x78; // (-8) + debugFrameHeader.return_address_register = 16; + debugFrameHeader.opcodes[1] = 7; // RSP + debugFrameHeader.opcodes[2] = 8; + debugFrameHeader.opcodes[3] = DW_CFA_offset + 16; // RIP + } + assert(debugFrameHeader.data_alignment_factor == 0x80 - OFFSET_FAC); + + int seg = dwarf_getsegment(debug_frame, 1); + debug_frame_secidx = SegData[seg]->SDshtidx; + Outbuffer *debug_frame_buf = SegData[seg]->SDbuf; + debug_frame_buf->reserve(1000); + + debug_frame_buf->writen(&debugFrameHeader,debugFrameHeader.length + 4); + + /* ======================================== */ + + seg = dwarf_getsegment(debug_str, 0); + debug_str_secidx = SegData[seg]->SDshtidx; + debug_str_buf = SegData[seg]->SDbuf; + debug_str_buf->reserve(1000); + + /* ======================================== */ + + debug_ranges_seg = dwarf_getsegment(debug_ranges, 0); + debug_ranges_secidx = SegData[debug_ranges_seg]->SDshtidx; + debug_ranges_buf = SegData[debug_ranges_seg]->SDbuf; + debug_ranges_buf->reserve(1000); + + /* ======================================== */ + + debug_loc_seg = dwarf_getsegment(debug_loc, 0); + debug_loc_secidx = SegData[debug_loc_seg]->SDshtidx; + debug_loc_buf = SegData[debug_loc_seg]->SDbuf; + debug_loc_buf->reserve(1000); + + /* ======================================== */ + + if (infoFileName_table) + { delete infoFileName_table; + infoFileName_table = NULL; + } + + lineseg = dwarf_getsegment(debug_line, 0); + linebuf = SegData[lineseg]->SDbuf; + + debugline = debugline_init; + + linebuf->write(&debugline, sizeof(debugline)); + + // include_directories +#if SCPP + list_t pl; + for (pl = pathlist; pl; pl = list_next(pl)) + { + linebuf->writeString((char *)list_ptr(pl)); + linebuf->writeByte(0); + } +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + for (pl = pathsyslist; pl; pl = list_next(pl)) + { + linebuf->writeString((char *)list_ptr(pl)); + linebuf->writeByte(0); + } +#endif +#endif +#if 0 && MARS + for (int i = 0; i < global.params.imppath->dim; i++) + { + linebuf->writeString(global.params.imppath->tdata()[i]); + linebuf->writeByte(0); + } +#endif + linebuf->writeByte(0); // terminated with 0 byte + + /* ======================================== */ + + abbrevseg = dwarf_getsegment(debug_abbrev, 0); + abbrevbuf = SegData[abbrevseg]->SDbuf; + abbrevcode = 1; + + // Free only if starting another file. Waste of time otherwise. + if (abbrev_table) + { delete abbrev_table; + abbrev_table = NULL; + } + + static unsigned char abbrevHeader[] = + { + 1, // abbreviation code + DW_TAG_compile_unit, + 1, + DW_AT_producer, DW_FORM_string, + DW_AT_language, DW_FORM_data1, + DW_AT_name, DW_FORM_string, + DW_AT_comp_dir, DW_FORM_string, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_entry_pc, DW_FORM_addr, + DW_AT_ranges, DW_FORM_data4, + DW_AT_stmt_list, DW_FORM_data4, + 0, 0, + }; + + abbrevbuf->write(abbrevHeader,sizeof(abbrevHeader)); + + /* ======================================== */ + + infoseg = dwarf_getsegment(debug_info, 0); + infobuf = SegData[infoseg]->SDbuf; + + debuginfo = debuginfo_init; + if (I64) + debuginfo.address_size = 8; + + infobuf->write(&debuginfo, sizeof(debuginfo)); +#if ELFOBJ + dwarf_addrel(infoseg,6,abbrevseg); +#endif + + infobuf->writeuLEB128(1); // abbreviation code +#if MARS + infobuf->write("Digital Mars D "); + infobuf->writeString(global.version); // DW_AT_producer + // DW_AT_language + infobuf->writeByte((config.fulltypes == CVDWARF_D) ? DW_LANG_D : DW_LANG_C89); +#elif SCPP + infobuf->write("Digital Mars C "); + infobuf->writeString(global.version); // DW_AT_producer + infobuf->writeByte(DW_LANG_C89); // DW_AT_language +#else + assert(0); +#endif + infobuf->writeString(filename); // DW_AT_name +#if 0 + // This relies on an extension to POSIX.1 not always implemented + char *cwd = getcwd(NULL, 0); +#else + char *cwd; + size_t sz = 80; + while (1) + { + errno = 0; + cwd = (char *)malloc(sz + 1); + if (!cwd) + err_nomem(); + char *buf = getcwd(cwd, sz); + if (buf) + { cwd[sz] = 0; // man page doesn't say if always 0 terminated + break; + } + if (errno == ERANGE) + { + sz += 80; + free(cwd); + continue; + } + cwd[0] = 0; + break; + } +#endif + //infobuf->write32(elf_addstr(debug_str_buf, cwd)); // DW_AT_comp_dir as DW_FORM_strp, doesn't work on some systems + infobuf->writeString(cwd); // DW_AT_comp_dir as DW_FORM_string + free(cwd); + + append_addr(infobuf, 0); // DW_AT_low_pc + append_addr(infobuf, 0); // DW_AT_entry_pc + +#if ELFOBJ + dwarf_addrel(infoseg,infobuf->size(),debug_ranges_seg); +#endif + infobuf->write32(0); // DW_AT_ranges + +#if ELFOBJ + dwarf_addrel(infoseg,infobuf->size(),lineseg); +#endif + infobuf->write32(0); // DW_AT_stmt_list + + memset(typidx_tab, 0, sizeof(typidx_tab)); + + /* ======================================== */ + + seg = dwarf_getsegment(debug_pubnames, 0); + debug_pubnames_secidx = SegData[seg]->SDshtidx; + debug_pubnames_buf = SegData[seg]->SDbuf; + debug_pubnames_buf->reserve(1000); + + debug_pubnames_buf->write32(0); // unit_length + debug_pubnames_buf->writeWord(2); // version +#if ELFOBJ + dwarf_addrel(seg,debug_pubnames_buf->size(),infoseg); +#endif + debug_pubnames_buf->write32(0); // debug_info_offset + debug_pubnames_buf->write32(0); // debug_info_length + + /* ======================================== */ + + debug_aranges_seg = dwarf_getsegment(debug_aranges, 0); + debug_aranges_secidx = SegData[debug_aranges_seg]->SDshtidx; + debug_aranges_buf = SegData[debug_aranges_seg]->SDbuf; + debug_aranges_buf->reserve(1000); + + debug_aranges_buf->write32(0); // unit_length + debug_aranges_buf->writeWord(2); // version +#if ELFOBJ + dwarf_addrel(debug_aranges_seg,debug_aranges_buf->size(),infoseg); +#endif + debug_aranges_buf->write32(0); // debug_info_offset + debug_aranges_buf->writeByte(I64 ? 8 : 4); // address_size + debug_aranges_buf->writeByte(0); // segment_size + debug_aranges_buf->write32(0); // pad to 16 +} + + +/************************************* + * Add a file to the .debug_line header + */ +int dwarf_line_addfile(const char* filename) +{ + if (!infoFileName_table) { + infoFileName_table = new AArray(&ti_abuf, sizeof(unsigned)); + linebuf_filetab_end = linebuf->size(); + } + + Abuf abuf; + abuf.buf = (const unsigned char*)filename; + abuf.length = strlen(filename)-1; + + unsigned *pidx = (unsigned *)infoFileName_table->get(&abuf); + if (!*pidx) // if no idx assigned yet + { + *pidx = infoFileName_table->length(); // assign newly computed idx + + size_t before = linebuf->size(); + linebuf->writeString(filename); + linebuf->writeByte(0); // directory table index + linebuf->writeByte(0); // mtime + linebuf->writeByte(0); // length + linebuf_filetab_end += linebuf->size() - before; + } + + return *pidx; +} + +void dwarf_initmodule(const char *filename, const char *modname) +{ + if (modname) + { + static unsigned char abbrevModule[] = + { + DW_TAG_module, + //1, // one children + 0, // no children + DW_AT_name, DW_FORM_string, // module name + 0, 0, + }; + abbrevcode++; + abbrevbuf->writeuLEB128(abbrevcode); + abbrevbuf->write(abbrevModule,sizeof(abbrevModule)); + infobuf->writeuLEB128(abbrevcode); // abbreviation code + infobuf->writeString(modname); // DW_AT_name + //hasModname = 1; + } + else + hasModname = 0; + + dwarf_line_addfile(filename); +} + +void dwarf_termmodule() +{ + if (hasModname) + infobuf->writeByte(0); // end of DW_TAG_module's children +} + +/************************************* + * Finish writing Dwarf debug info to object file. + */ + +void dwarf_termfile() +{ + //printf("dwarf_termfile()\n"); + + /* ======================================== */ + + // Put out line number info + + // file_names + unsigned last_filenumber = 0; + const char* last_filename = NULL; + for (unsigned seg = 1; seg <= seg_count; seg++) + { + for (unsigned i = 0; i < SegData[seg]->SDlinnum_count; i++) + { + linnum_data *ld = &SegData[seg]->SDlinnum_data[i]; + const char *filename; +#if MARS + filename = ld->filename; +#else + Sfile *sf = ld->filptr; + if (sf) + filename = sf->SFname; + else + filename = ::filename; +#endif + if (last_filename == filename) + { + ld->filenumber = last_filenumber; + } + else + { + ld->filenumber = dwarf_line_addfile(filename); + + last_filenumber = ld->filenumber; + last_filename = filename; + } + } + } + // assert we haven't emitted anything but file table entries + assert(linebuf->size() == linebuf_filetab_end); + linebuf->writeByte(0); // end of file_names + + debugline.prologue_length = linebuf->size() - 10; + + for (unsigned seg = 1; seg <= seg_count; seg++) + { + seg_data *sd = SegData[seg]; + unsigned addressmax = 0; + unsigned linestart = ~0; + + if (!sd->SDlinnum_count) + continue; +#if ELFOBJ + if (!sd->SDsym) // gdb ignores line number data without a DW_AT_name + continue; +#endif + + //printf("sd = %x, SDlinnum_count = %d\n", sd, sd->SDlinnum_count); + for (int i = 0; i < sd->SDlinnum_count; i++) + { linnum_data *ld = &sd->SDlinnum_data[i]; + + // Set address to start of segment with DW_LNE_set_address + linebuf->writeByte(0); + linebuf->writeByte(NPTRSIZE + 1); + linebuf->writeByte(DW_LNE_set_address); + + dwarf_appreladdr(lineseg,linebuf,seg,0); + + // Dwarf2 6.2.2 State machine registers + unsigned address = 0; // instruction address + unsigned file = ld->filenumber; + unsigned line = 1; // line numbers beginning with 1 + + linebuf->writeByte(DW_LNS_set_file); + linebuf->writeuLEB128(file); + + for (int j = 0; j < ld->linoff_count; j++) + { int lininc = ld->linoff[j][0] - line; + int addinc = ld->linoff[j][1] - address; + + //printf("\tld[%d] line = %d offset = x%x lininc = %d addinc = %d\n", j, ld->linoff[j][0], ld->linoff[j][1], lininc, addinc); + + //assert(addinc >= 0); + if (addinc < 0) + continue; + if (j && lininc == 0 && !(addinc && j + 1 == ld->linoff_count)) + continue; + line += lininc; + if (line < linestart) + linestart = line; + address += addinc; + if (address >= addressmax) + addressmax = address + 1; + if (lininc >= debugline.line_base && lininc < debugline.line_base + debugline.line_range) + { unsigned opcode = lininc - debugline.line_base + + debugline.line_range * addinc + + debugline.opcode_base; + + if (opcode <= 255) + { linebuf->writeByte(opcode); + continue; + } + } + if (lininc) + { + linebuf->writeByte(DW_LNS_advance_line); + linebuf->writesLEB128((long)lininc); + } + if (addinc) + { + linebuf->writeByte(DW_LNS_advance_pc); + linebuf->writeuLEB128((unsigned long)addinc); + } + if (lininc || addinc) + linebuf->writeByte(DW_LNS_copy); + } + + // Write DW_LNS_advance_pc to cover the function prologue + linebuf->writeByte(DW_LNS_advance_pc); + linebuf->writeuLEB128((unsigned long)(sd->SDbuf->size() - address)); + + // Write DW_LNE_end_sequence + linebuf->writeByte(0); + linebuf->writeByte(1); + linebuf->writeByte(1); + + // reset linnum_data + ld->linoff_count = 0; + } + } + + debugline.total_length = linebuf->size() - 4; + memcpy(linebuf->buf, &debugline, sizeof(debugline)); + + /* ================================================= */ + + abbrevbuf->writeByte(0); + + /* ================================================= */ + + infobuf->writeByte(0); // ending abbreviation code + + debuginfo.total_length = infobuf->size() - 4; + memcpy(infobuf->buf, &debuginfo, sizeof(debuginfo)); + + /* ================================================= */ + + // Terminate by offset field containing 0 + debug_pubnames_buf->write32(0); + + // Plug final sizes into header + *(unsigned *)debug_pubnames_buf->buf = debug_pubnames_buf->size() - 4; + *(unsigned *)(debug_pubnames_buf->buf + 10) = infobuf->size(); + + /* ================================================= */ + + // Terminate by address/length fields containing 0 + append_addr(debug_aranges_buf, 0); + append_addr(debug_aranges_buf, 0); + + // Plug final sizes into header + *(unsigned *)debug_aranges_buf->buf = debug_aranges_buf->size() - 4; + + /* ================================================= */ + + // Terminate by beg address/end address fields containing 0 + append_addr(debug_ranges_buf, 0); + append_addr(debug_ranges_buf, 0); + + /* ================================================= */ + + // Free only if starting another file. Waste of time otherwise. + if (type_table) + { delete type_table; + type_table = NULL; + } + if (functype_table) + { delete functype_table; + functype_table = NULL; + } + if (functypebuf) + functypebuf->setsize(0); +} + +/***************************************** + * Start of code gen for function. + */ +void dwarf_func_start(Symbol *sfunc) +{ + if (I16 || I32) + CFA_state_current = CFA_state_init_32; + else if (I64) + CFA_state_current = CFA_state_init_64; + else + assert(0); + assert(CFA_state_current.offset == OFFSET_FAC); + cfa_buf.reset(); +} + +/***************************************** + * End of code gen for function. + */ +void dwarf_func_term(Symbol *sfunc) +{ + //printf("dwarf_func_term(sfunc = '%s')\n", sfunc->Sident); + unsigned funcabbrevcode; + + /* Put out the start of the debug_frame entry for this function + */ + Outbuffer *debug_frame_buf; + unsigned debug_frame_buf_offset; + + if (I64) + { + #pragma pack(1) + struct DebugFrameFDE + { + unsigned length; + unsigned CIE_pointer; + unsigned long long initial_location; + unsigned long long address_range; + }; + #pragma pack() + static DebugFrameFDE debugFrameFDE = + { 20, // length + 0, // CIE_pointer + 0, // initial_location + 0, // address_range + }; + + // Pad to 8 byte boundary + int n; + for (n = (-cfa_buf.size() & 7); n; n--) + cfa_buf.writeByte(DW_CFA_nop); + + debugFrameFDE.length = 20 + cfa_buf.size(); + debugFrameFDE.address_range = sfunc->Ssize; + // Do we need this? + //debugFrameFDE.initial_location = sfunc->Soffset; + + IDXSEC dfseg; + dfseg = dwarf_getsegment(debug_frame, 1); + debug_frame_secidx = SegData[dfseg]->SDshtidx; + debug_frame_buf = SegData[dfseg]->SDbuf; + debug_frame_buf_offset = debug_frame_buf->p - debug_frame_buf->buf; + debug_frame_buf->reserve(1000); + debug_frame_buf->writen(&debugFrameFDE,sizeof(debugFrameFDE)); + debug_frame_buf->write(&cfa_buf); + +#if ELFOBJ + dwarf_addrel(dfseg,debug_frame_buf_offset + 4,dfseg); +#endif + dwarf_addrel64(dfseg,debug_frame_buf_offset + 8,sfunc->Sseg,0); + } + else + { + #pragma pack(1) + struct DebugFrameFDE + { + unsigned length; + unsigned CIE_pointer; + unsigned initial_location; + unsigned address_range; + }; + #pragma pack() + static DebugFrameFDE debugFrameFDE = + { 12, // length + 0, // CIE_pointer + 0, // initial_location + 0, // address_range + }; + + // Pad to 4 byte boundary + int n; + for (n = (-cfa_buf.size() & 3); n; n--) + cfa_buf.writeByte(DW_CFA_nop); + + debugFrameFDE.length = 12 + cfa_buf.size(); + debugFrameFDE.address_range = sfunc->Ssize; + // Do we need this? + //debugFrameFDE.initial_location = sfunc->Soffset; + + IDXSEC dfseg; + dfseg = dwarf_getsegment(debug_frame, 1); + debug_frame_secidx = SegData[dfseg]->SDshtidx; + debug_frame_buf = SegData[dfseg]->SDbuf; + debug_frame_buf_offset = debug_frame_buf->p - debug_frame_buf->buf; + debug_frame_buf->reserve(1000); + debug_frame_buf->writen(&debugFrameFDE,sizeof(debugFrameFDE)); + debug_frame_buf->write(&cfa_buf); + +#if ELFOBJ + dwarf_addrel(dfseg,debug_frame_buf_offset + 4,dfseg); +#endif + dwarf_addrel(dfseg,debug_frame_buf_offset + 8,sfunc->Sseg); + } + + IDXSEC seg = sfunc->Sseg; + seg_data *sd = SegData[seg]; + +#if MARS + const char* filename = sfunc->Sfunc->Fstartline.Sfilename; + int filenum = dwarf_line_addfile(filename); +#else + int filenum = 1; +#endif + + unsigned ret_type = dwarf_typidx(sfunc->Stype->Tnext); + if (tybasic(sfunc->Stype->Tnext->Tty) == TYvoid) + ret_type = 0; + + // See if there are any parameters + int haveparameters = 0; + unsigned formalcode = 0; + unsigned autocode = 0; + SYMIDX si; + for (si = 0; si < globsym.top; si++) + { symbol *sa = globsym.tab[si]; + + static unsigned char formal[] = + { + DW_TAG_formal_parameter, + 0, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref4, + DW_AT_location, DW_FORM_block1, + 0, 0, + }; + + switch (sa->Sclass) + { case SCparameter: + case SCregpar: + case SCfastpar: + dwarf_typidx(sa->Stype); + formal[0] = DW_TAG_formal_parameter; + if (!formalcode) + formalcode = dwarf_abbrev_code(formal,sizeof(formal)); + haveparameters = 1; + break; + + case SCauto: + case SCbprel: + case SCregister: + case SCpseudo: + dwarf_typidx(sa->Stype); + formal[0] = DW_TAG_variable; + if (!autocode) + autocode = dwarf_abbrev_code(formal,sizeof(formal)); + haveparameters = 1; + break; + } + } + + Outbuffer abuf; + abuf.writeByte(DW_TAG_subprogram); + abuf.writeByte(haveparameters); // have children? + if (haveparameters) + { + abuf.writeByte(DW_AT_sibling); abuf.writeByte(DW_FORM_ref4); + } + abuf.writeByte(DW_AT_name); abuf.writeByte(DW_FORM_string); + abuf.writeuLEB128(DW_AT_MIPS_linkage_name); abuf.writeByte(DW_FORM_string); + abuf.writeByte(DW_AT_decl_file); abuf.writeByte(DW_FORM_data1); + abuf.writeByte(DW_AT_decl_line); abuf.writeByte(DW_FORM_data2); + if (ret_type) + { + abuf.writeByte(DW_AT_type); abuf.writeByte(DW_FORM_ref4); + } + if (sfunc->Sclass == SCglobal) + { + abuf.writeByte(DW_AT_external); abuf.writeByte(DW_FORM_flag); + } + abuf.writeByte(DW_AT_low_pc); abuf.writeByte(DW_FORM_addr); + abuf.writeByte(DW_AT_high_pc); abuf.writeByte(DW_FORM_addr); + abuf.writeByte(DW_AT_frame_base); abuf.writeByte(DW_FORM_data4); + abuf.writeByte(0); abuf.writeByte(0); + + funcabbrevcode = dwarf_abbrev_code(abuf.buf, abuf.size()); + + unsigned idxsibling = 0; + unsigned siblingoffset; + + unsigned infobuf_offset = infobuf->size(); + infobuf->writeuLEB128(funcabbrevcode); // abbreviation code + if (haveparameters) + { + siblingoffset = infobuf->size(); + infobuf->write32(idxsibling); // DW_AT_sibling + } + + const char *name; +#if MARS + name = sfunc->prettyIdent ? sfunc->prettyIdent : sfunc->Sident; +#else + name = sfunc->Sident; +#endif + infobuf->writeString(name); // DW_AT_name + infobuf->writeString(sfunc->Sident); // DW_AT_MIPS_linkage_name + infobuf->writeByte(filenum); // DW_AT_decl_file + infobuf->writeWord(sfunc->Sfunc->Fstartline.Slinnum); // DW_AT_decl_line + if (ret_type) + infobuf->write32(ret_type); // DW_AT_type + + if (sfunc->Sclass == SCglobal) + infobuf->writeByte(1); // DW_AT_external + + // DW_AT_low_pc and DW_AT_high_pc + dwarf_appreladdr(infoseg, infobuf, seg, funcoffset); + dwarf_appreladdr(infoseg, infobuf, seg, funcoffset + sfunc->Ssize); + +#if ELFOBJ + dwarf_addrel(infoseg,infobuf->size(),debug_loc_seg, 0); +#endif + infobuf->write32(debug_loc_buf->size()); // DW_AT_frame_base + + if (haveparameters) + { + for (si = 0; si < globsym.top; si++) + { symbol *sa = globsym.tab[si]; + unsigned vcode; + + switch (sa->Sclass) + { + case SCparameter: + case SCregpar: + case SCfastpar: + vcode = formalcode; + goto L1; + case SCauto: + case SCregister: + case SCpseudo: + case SCbprel: + vcode = autocode; + L1: + { unsigned soffset; + unsigned tidx = dwarf_typidx(sa->Stype); + + infobuf->writeuLEB128(vcode); // abbreviation code + infobuf->writeString(sa->Sident); // DW_AT_name + infobuf->write32(tidx); // DW_AT_type + soffset = infobuf->size(); + infobuf->writeByte(2); // DW_FORM_block1 + if (sa->Sfl == FLreg || sa->Sclass == SCpseudo) + { // BUG: register pairs not supported in Dwarf? + infobuf->writeByte(DW_OP_reg0 + sa->Sreglsw); + } + else + { + infobuf->writeByte(DW_OP_fbreg); + if (sa->Sclass == SCregpar || + sa->Sclass == SCparameter) + infobuf->writesLEB128(sa->Soffset); + else if (sa->Sclass == SCfastpar) + infobuf->writesLEB128(Aoff + BPoff - Poff + sa->Soffset); + else if (sa->Sclass == SCbprel) + infobuf->writesLEB128(-Poff + sa->Soffset); + else + infobuf->writesLEB128(Aoff + BPoff - Poff + sa->Soffset); + } + infobuf->buf[soffset] = infobuf->size() - soffset - 1; + break; + } + } + } + infobuf->writeByte(0); // end of parameter children + + idxsibling = infobuf->size(); + *(unsigned *)(infobuf->buf + siblingoffset) = idxsibling; + } + + /* ============= debug_pubnames =========================== */ + + debug_pubnames_buf->write32(infobuf_offset); + // Should be the fully qualified name, not the simple DW_AT_name + debug_pubnames_buf->writeString(sfunc->Sident); + + /* ============= debug_aranges =========================== */ + + if (sd->SDaranges_offset) + // Extend existing entry size + *(unsigned long long *)(debug_aranges_buf->buf + sd->SDaranges_offset + NPTRSIZE) = funcoffset + sfunc->Ssize; + else + { // Add entry + sd->SDaranges_offset = debug_aranges_buf->size(); + // address of start of .text segment + dwarf_appreladdr(debug_aranges_seg, debug_aranges_buf, seg, 0); + // size of .text segment + append_addr(debug_aranges_buf, funcoffset + sfunc->Ssize); + } + + /* ============= debug_ranges =========================== */ + + /* Each function gets written into its own segment, + * indicate this by adding to the debug_ranges + */ + // start of function and end of function + dwarf_appreladdr(debug_ranges_seg, debug_ranges_buf, seg, funcoffset); + dwarf_appreladdr(debug_ranges_seg, debug_ranges_buf, seg, funcoffset + sfunc->Ssize); + + /* ============= debug_loc =========================== */ + + assert(Poff >= 2 * REGSIZE); + assert(Poff < 63); // avoid sLEB128 encoding + unsigned short op_size = 0x0002; + unsigned short loc_op; + + // set the entry for this function in .debug_loc segment + // after call + dwarf_appreladdr(debug_loc_seg, debug_loc_buf, seg, funcoffset + 0); + dwarf_appreladdr(debug_loc_seg, debug_loc_buf, seg, funcoffset + 1); + + loc_op = ((Poff - REGSIZE) << 8) | (DW_OP_breg0 + dwarf_regno(SP)); + debug_loc_buf->write32(loc_op << 16 | op_size); + + // after push EBP + dwarf_appreladdr(debug_loc_seg, debug_loc_buf, seg, funcoffset + 1); + dwarf_appreladdr(debug_loc_seg, debug_loc_buf, seg, funcoffset + 3); + + loc_op = ((Poff) << 8) | (DW_OP_breg0 + dwarf_regno(SP)); + debug_loc_buf->write32(loc_op << 16 | op_size); + + // after mov EBP, ESP + dwarf_appreladdr(debug_loc_seg, debug_loc_buf, seg, funcoffset + 3); + dwarf_appreladdr(debug_loc_seg, debug_loc_buf, seg, funcoffset + sfunc->Ssize); + + loc_op = ((Poff) << 8) | (DW_OP_breg0 + dwarf_regno(BP)); + debug_loc_buf->write32(loc_op << 16 | op_size); + + // 2 zero addresses to end loc_list + append_addr(debug_loc_buf, 0); + append_addr(debug_loc_buf, 0); +} + + +/****************************************** + * Write out symbol table for current function. + */ + +void cv_outsym(symbol *s) +{ + //printf("cv_outsym('%s')\n",s->Sident); + //symbol_print(s); + + symbol_debug(s); +#if MARS + if (s->Sflags & SFLnodebug) + return; +#endif + type *t = s->Stype; + type_debug(t); + tym_t tym = tybasic(t->Tty); + if (tyfunc(tym) && s->Sclass != SCtypedef) + return; + + Outbuffer abuf; + unsigned code; + unsigned typidx; + unsigned soffset; + switch (s->Sclass) + { + case SCglobal: + typidx = dwarf_typidx(t); + + abuf.writeByte(DW_TAG_variable); + abuf.writeByte(0); // no children + abuf.writeByte(DW_AT_name); abuf.writeByte(DW_FORM_string); + abuf.writeByte(DW_AT_type); abuf.writeByte(DW_FORM_ref4); + abuf.writeByte(DW_AT_external); abuf.writeByte(DW_FORM_flag); + abuf.writeByte(DW_AT_location); abuf.writeByte(DW_FORM_block1); + abuf.writeByte(0); abuf.writeByte(0); + code = dwarf_abbrev_code(abuf.buf, abuf.size()); + + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeString(s->Sident); // DW_AT_name + infobuf->write32(typidx); // DW_AT_type + infobuf->writeByte(1); // DW_AT_external + + soffset = infobuf->size(); + infobuf->writeByte(2); // DW_FORM_block1 + + infobuf->writeByte(DW_OP_addr); + dwarf_addrel(infoseg,infobuf->size(),s->Sseg); + infobuf->write32(0); // address of global + + infobuf->buf[soffset] = infobuf->size() - soffset - 1; + break; + } +} + + +/****************************************** + * Write out any deferred symbols. + */ + +void cv_outlist() +{ +} + + +/****************************************** + * Write out symbol table for current function. + */ + +void cv_func(Funcsym *s) +{ +} + +/* =================== Cached Types in debug_info ================= */ + +struct Atype +{ + Outbuffer *buf; + size_t start; + size_t end; +}; + +struct TypeInfo_Atype : TypeInfo +{ + const char* toString(); + hash_t getHash(void *p); + int equals(void *p1, void *p2); + int compare(void *p1, void *p2); + size_t tsize(); + void swap(void *p1, void *p2); +}; + +TypeInfo_Atype ti_atype; + +const char* TypeInfo_Atype::toString() +{ + return "Atype"; +} + +hash_t TypeInfo_Atype::getHash(void *p) +{ Atype a; + hash_t hash = 0; + size_t i; + + a = *(Atype *)p; + for (i = a.start; i < a.end; i++) + { + hash = hash * 11 + a.buf->buf[i]; + } + return hash; +} + +int TypeInfo_Atype::equals(void *p1, void *p2) +{ + Atype a1 = *(Atype*)p1; + Atype a2 = *(Atype*)p2; + size_t len = a1.end - a1.start; + + return len == a2.end - a2.start && + memcmp(a1.buf->buf + a1.start, a2.buf->buf + a2.start, len) == 0; +} + +int TypeInfo_Atype::compare(void *p1, void *p2) +{ + Atype a1 = *(Atype*)p1; + Atype a2 = *(Atype*)p2; + size_t len = a1.end - a1.start; + if (len == a2.end - a2.start) + return memcmp(a1.buf->buf + a1.start, a2.buf->buf + a2.start, len); + else if (len < a2.end - a2.start) + return -1; + else + return 1; +} + +size_t TypeInfo_Atype::tsize() +{ + return sizeof(Atype); +} + +void TypeInfo_Atype::swap(void *p1, void *p2) +{ + assert(0); +} + +/* ======================= Type Index ============================== */ + +unsigned dwarf_typidx(type *t) +{ unsigned idx = 0; + unsigned nextidx; + unsigned keyidx; + unsigned pvoididx; + unsigned code; + type *tnext; + type *tbase; + const char *p; + + static unsigned char abbrevTypeBasic[] = + { + DW_TAG_base_type, + 0, // no children + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_data1, + DW_AT_encoding, DW_FORM_data1, + 0, 0, + }; + static unsigned char abbrevWchar[] = + { + DW_TAG_typedef, + 0, // no children + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref4, + DW_AT_decl_file, DW_FORM_data1, + DW_AT_decl_line, DW_FORM_data2, + 0, 0, + }; + static unsigned char abbrevTypePointer[] = + { + DW_TAG_pointer_type, + 0, // no children + DW_AT_byte_size, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, + 0, 0, + }; + static unsigned char abbrevTypePointerVoid[] = + { + DW_TAG_pointer_type, + 0, // no children + DW_AT_byte_size, DW_FORM_data1, + 0, 0, + }; +#ifdef USE_DWARF_D_EXTENSIONS + static unsigned char abbrevTypeDArray[] = + { + DW_TAG_darray_type, + 0, // no children + DW_AT_byte_size, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, + 0, 0, + }; + static unsigned char abbrevTypeDArrayVoid[] = + { + DW_TAG_darray_type, + 0, // no children + DW_AT_byte_size, DW_FORM_data1, + 0, 0, + }; + static unsigned char abbrevTypeAArray[] = + { + DW_TAG_aarray_type, + 0, // no children + DW_AT_byte_size, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, // element type + DW_AT_containing_type, DW_FORM_ref4, // key type + 0, 0, + }; + static unsigned char abbrevTypeDelegate[] = + { + DW_TAG_delegate_type, + 0, // no children + DW_AT_byte_size, DW_FORM_data1, + DW_AT_containing_type, DW_FORM_ref4, // this type + DW_AT_type, DW_FORM_ref4, // function type + 0, 0, + }; +#endif // USE_DWARF_D_EXTENSIONS + static unsigned char abbrevTypeConst[] = + { + DW_TAG_const_type, + 0, // no children + DW_AT_type, DW_FORM_ref4, + 0, 0, + }; + static unsigned char abbrevTypeConstVoid[] = + { + DW_TAG_const_type, + 0, // no children + 0, 0, + }; + static unsigned char abbrevTypeVolatile[] = + { + DW_TAG_volatile_type, + 0, // no children + DW_AT_type, DW_FORM_ref4, + 0, 0, + }; + static unsigned char abbrevTypeVolatileVoid[] = + { + DW_TAG_volatile_type, + 0, // no children + 0, 0, + }; + + if (!t) + return 0; + + if (t->Tty & mTYconst) + { // We make a copy of the type to strip off the const qualifier and + // recurse, and then add the const abbrev code. To avoid ending in a + // loop if the type references the const version of itself somehow, + // we need to set TFforward here, because setting TFforward during + // member generation of dwarf_typidx(tnext) has no effect on t itself. + unsigned short old_flags = t->Tflags; + t->Tflags |= TFforward; + + tnext = type_copy(t); + tnext->Tcount++; + tnext->Tty &= ~mTYconst; + nextidx = dwarf_typidx(tnext); + + t->Tflags = old_flags; + + code = nextidx + ? dwarf_abbrev_code(abbrevTypeConst, sizeof(abbrevTypeConst)) + : dwarf_abbrev_code(abbrevTypeConstVoid, sizeof(abbrevTypeConstVoid)); + goto Lcv; + } + + if (t->Tty & mTYvolatile) + { tnext = type_copy(t); + tnext->Tcount++; + tnext->Tty &= ~mTYvolatile; + nextidx = dwarf_typidx(tnext); + code = nextidx + ? dwarf_abbrev_code(abbrevTypeVolatile, sizeof(abbrevTypeVolatile)) + : dwarf_abbrev_code(abbrevTypeVolatileVoid, sizeof(abbrevTypeVolatileVoid)); + Lcv: + idx = infobuf->size(); + infobuf->writeuLEB128(code); // abbreviation code + if (nextidx) + infobuf->write32(nextidx); // DW_AT_type + goto Lret; + } + + tym_t ty; + ty = tybasic(t->Tty); + idx = typidx_tab[ty]; + if (idx) + return idx; + + unsigned char ate; + ate = tyuns(t->Tty) ? DW_ATE_unsigned : DW_ATE_signed; + switch (tybasic(t->Tty)) + { + Lnptr: + nextidx = dwarf_typidx(t->Tnext); + code = nextidx + ? dwarf_abbrev_code(abbrevTypePointer, sizeof(abbrevTypePointer)) + : dwarf_abbrev_code(abbrevTypePointerVoid, sizeof(abbrevTypePointerVoid)); + idx = infobuf->size(); + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeByte(tysize(t->Tty)); // DW_AT_byte_size + if (nextidx) + infobuf->write32(nextidx); // DW_AT_type + break; + + case TYullong: + case TYucent: + if (!t->Tnext) + { p = (tybasic(t->Tty) == TYullong) ? "unsigned long long" : "ucent"; + goto Lsigned; + } + +#ifndef USE_DWARF_D_EXTENSIONS + static unsigned char abbrevTypeStruct[] = + { + DW_TAG_structure_type, + 1, // children + DW_AT_sibling, DW_FORM_ref4, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_data1, + 0, 0, + }; + + static unsigned char abbrevTypeMember[] = + { + DW_TAG_member, + 0, // no children + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref4, + DW_AT_data_member_location, DW_FORM_block1, + 0, 0, + }; +#endif + + /* It's really TYdarray, and Tnext is the + * element type + */ +#ifdef USE_DWARF_D_EXTENSIONS + nextidx = dwarf_typidx(t->Tnext); + code = nextidx + ? dwarf_abbrev_code(abbrevTypeDArray, sizeof(abbrevTypeDArray)) + : dwarf_abbrev_code(abbrevTypeDArrayVoid, sizeof(abbrevTypeDArrayVoid)); + idx = infobuf->size(); + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeByte(tysize(t->Tty)); // DW_AT_byte_size + if (nextidx) + infobuf->write32(nextidx); // DW_AT_type +#else + { + unsigned lenidx = I64 ? dwarf_typidx(tsulong) : dwarf_typidx(tsuns); + + { + type *tdata = type_alloc(TYnptr); + tdata->Tnext = t->Tnext; + t->Tnext->Tcount++; + tdata->Tcount++; + nextidx = dwarf_typidx(tdata); + type_free(tdata); + } + + code = dwarf_abbrev_code(abbrevTypeStruct, sizeof(abbrevTypeStruct)); + idx = infobuf->size(); + infobuf->writeuLEB128(code); // abbreviation code + unsigned siblingoffset = infobuf->size(); + unsigned idxsibling = 0; + infobuf->write32(idxsibling); // DW_AT_sibling + infobuf->write("_Array_", 7); // DW_AT_name + if (tybasic(t->Tnext->Tty)) + infobuf->writeString(tystring[tybasic(t->Tnext->Tty)]); + else + infobuf->writeByte(0); + infobuf->writeByte(tysize(t->Tty)); // DW_AT_byte_size + + // length + code = dwarf_abbrev_code(abbrevTypeMember, sizeof(abbrevTypeMember)); + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeString("length"); // DW_AT_name + infobuf->write32(lenidx); // DW_AT_type + + infobuf->writeByte(2); // DW_AT_data_member_location + infobuf->writeByte(DW_OP_plus_uconst); + infobuf->writeByte(0); + + // ptr + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeString("ptr"); // DW_AT_name + infobuf->write32(nextidx); // DW_AT_type + + infobuf->writeByte(2); // DW_AT_data_member_location + infobuf->writeByte(DW_OP_plus_uconst); + infobuf->writeByte(I64 ? 8 : 4); + + infobuf->writeByte(0); // no more siblings + idxsibling = infobuf->size(); + *(unsigned *)(infobuf->buf + siblingoffset) = idxsibling; + } +#endif + break; + + case TYllong: + case TYcent: + if (!t->Tnext) + { p = (tybasic(t->Tty) == TYllong) ? "long long" : "cent"; + goto Lsigned; + } + /* It's really TYdelegate, and Tnext is the + * function type + */ +#ifdef USE_DWARF_D_EXTENSIONS + { type *tv = type_fake(TYnptr); + tv->Tcount++; + pvoididx = dwarf_typidx(tv); // void* is the 'this' type + type_free(tv); + } + nextidx = dwarf_typidx(t->Tnext); + code = dwarf_abbrev_code(abbrevTypeDelegate, sizeof(abbrevTypeDelegate)); + idx = infobuf->size(); + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeByte(tysize(t->Tty)); // DW_AT_byte_size + infobuf->write32(pvoididx); // DW_AT_containing_type + infobuf->write32(nextidx); // DW_AT_type +#else + { + { + type *tp = type_fake(TYnptr); + tp->Tcount++; + pvoididx = dwarf_typidx(tp); // void* + + tp->Tnext = t->Tnext; // fptr* + tp->Tnext->Tcount++; + nextidx = dwarf_typidx(tp); + type_free(tp); + } + + code = dwarf_abbrev_code(abbrevTypeStruct, sizeof(abbrevTypeStruct)); + idx = infobuf->size(); + infobuf->writeuLEB128(code); // abbreviation code + unsigned siblingoffset = infobuf->size(); + unsigned idxsibling = 0; + infobuf->write32(idxsibling); // DW_AT_sibling + infobuf->writeString("_Delegate"); // DW_AT_name + infobuf->writeByte(tysize(t->Tty)); // DW_AT_byte_size + + // ctxptr + code = dwarf_abbrev_code(abbrevTypeMember, sizeof(abbrevTypeMember)); + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeString("ctxptr"); // DW_AT_name + infobuf->write32(pvoididx); // DW_AT_type + + infobuf->writeByte(2); // DW_AT_data_member_location + infobuf->writeByte(DW_OP_plus_uconst); + infobuf->writeByte(0); + + // funcptr + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeString("funcptr"); // DW_AT_name + infobuf->write32(nextidx); // DW_AT_type + + infobuf->writeByte(2); // DW_AT_data_member_location + infobuf->writeByte(DW_OP_plus_uconst); + infobuf->writeByte(I64 ? 8 : 4); + + infobuf->writeByte(0); // no more siblings + idxsibling = infobuf->size(); + *(unsigned *)(infobuf->buf + siblingoffset) = idxsibling; + } +#endif + break; + + case TYnref: + case TYref: + case TYnptr: + if (!t->Tkey) + goto Lnptr; + + /* It's really TYaarray, and Tnext is the + * element type, Tkey is the key type + */ +#ifdef USE_DWARF_D_EXTENSIONS + keyidx = dwarf_typidx(t->Tkey); + nextidx = dwarf_typidx(t->Tnext); + code = dwarf_abbrev_code(abbrevTypeAArray, sizeof(abbrevTypeAArray)); + idx = infobuf->size(); + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeByte(tysize(t->Tty)); // DW_AT_byte_size + infobuf->write32(nextidx); // DW_AT_type + infobuf->write32(keyidx); // DW_AT_containing_type +#else + { + { + type *tp = type_fake(TYnptr); + tp->Tcount++; + pvoididx = dwarf_typidx(tp); // void* + } + + code = dwarf_abbrev_code(abbrevTypeStruct, sizeof(abbrevTypeStruct)); + idx = infobuf->size(); + infobuf->writeuLEB128(code); // abbreviation code + unsigned siblingoffset = infobuf->size(); + unsigned idxsibling = 0; + infobuf->write32(idxsibling); // DW_AT_sibling + infobuf->write("_AArray_", 8); // DW_AT_name + if (tybasic(t->Tkey->Tty)) + p = tystring[tybasic(t->Tkey->Tty)]; + else + p = "key"; + infobuf->write(p, strlen(p)); + + infobuf->writeByte('_'); + if (tybasic(t->Tnext->Tty)) + p = tystring[tybasic(t->Tnext->Tty)]; + else + p = "value"; + infobuf->writeString(p); + + infobuf->writeByte(tysize(t->Tty)); // DW_AT_byte_size + + // ptr + code = dwarf_abbrev_code(abbrevTypeMember, sizeof(abbrevTypeMember)); + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeString("ptr"); // DW_AT_name + infobuf->write32(pvoididx); // DW_AT_type + + infobuf->writeByte(2); // DW_AT_data_member_location + infobuf->writeByte(DW_OP_plus_uconst); + infobuf->writeByte(0); + + infobuf->writeByte(0); // no more siblings + idxsibling = infobuf->size(); + *(unsigned *)(infobuf->buf + siblingoffset) = idxsibling; + } +#endif + break; + + case TYvoid: return 0; + case TYbool: p = "_Bool"; ate = DW_ATE_boolean; goto Lsigned; + case TYchar: p = "char"; ate = (config.flags & CFGuchar) ? DW_ATE_unsigned_char : DW_ATE_signed_char; goto Lsigned; + case TYschar: p = "signed char"; ate = DW_ATE_signed_char; goto Lsigned; + case TYuchar: p = "unsigned char"; ate = DW_ATE_unsigned_char; goto Lsigned; + case TYshort: p = "short"; goto Lsigned; + case TYushort: p = "unsigned short"; goto Lsigned; + case TYint: p = "int"; goto Lsigned; + case TYuint: p = "unsigned"; goto Lsigned; + case TYlong: p = "long"; goto Lsigned; + case TYulong: p = "unsigned long"; goto Lsigned; + case TYdchar: p = "dchar"; goto Lsigned; + case TYfloat: p = "float"; ate = DW_ATE_float; goto Lsigned; + case TYdouble_alias: + case TYdouble: p = "double"; ate = DW_ATE_float; goto Lsigned; + case TYldouble: p = "long double"; ate = DW_ATE_float; goto Lsigned; + case TYifloat: p = "imaginary float"; ate = DW_ATE_imaginary_float; goto Lsigned; + case TYidouble: p = "imaginary double"; ate = DW_ATE_imaginary_float; goto Lsigned; + case TYildouble: p = "imaginary long double"; ate = DW_ATE_imaginary_float; goto Lsigned; + case TYcfloat: p = "complex float"; ate = DW_ATE_complex_float; goto Lsigned; + case TYcdouble: p = "complex double"; ate = DW_ATE_complex_float; goto Lsigned; + case TYcldouble: p = "complex long double"; ate = DW_ATE_complex_float; goto Lsigned; + Lsigned: + code = dwarf_abbrev_code(abbrevTypeBasic, sizeof(abbrevTypeBasic)); + idx = infobuf->size(); + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeString(p); // DW_AT_name + infobuf->writeByte(tysize(t->Tty)); // DW_AT_byte_size + infobuf->writeByte(ate); // DW_AT_encoding + typidx_tab[ty] = idx; + return idx; + + case TYnsfunc: + case TYnpfunc: + case TYjfunc: + + case TYnfunc: + { + /* The dwarf typidx for the function type is completely determined by + * the return type typidx and the parameter typidx's. Thus, by + * caching these, we can cache the function typidx. + * Cache them in functypebuf[] + */ + Outbuffer tmpbuf; + nextidx = dwarf_typidx(t->Tnext); // function return type + tmpbuf.write32(nextidx); + unsigned params = 0; + for (param_t *p = t->Tparamtypes; p; p = p->Pnext) + { params = 1; + unsigned paramidx = dwarf_typidx(p->Ptype); + //printf("1: paramidx = %d\n", paramidx); +#ifdef DEBUG + if (!paramidx) type_print(p->Ptype); +#endif + assert(paramidx); + tmpbuf.write32(paramidx); + } + + if (!functypebuf) + functypebuf = new Outbuffer(); + unsigned functypebufidx = functypebuf->size(); + functypebuf->write(tmpbuf.buf, tmpbuf.size()); + /* If it's in the cache already, return the existing typidx + */ + if (!functype_table) + functype_table = new AArray(&ti_atype, sizeof(unsigned)); + Atype functype; + functype.buf = functypebuf; + functype.start = functypebufidx; + functype.end = functypebuf->size(); + unsigned *pidx = (unsigned *)functype_table->get(&functype); + if (*pidx) + { // Reuse existing typidx + functypebuf->setsize(functypebufidx); + return *pidx; + } + + /* Not in the cache, create a new typidx + */ + Outbuffer abuf; // for abbrev + abuf.writeByte(DW_TAG_subroutine_type); + if (params) + { + abuf.writeByte(1); // children + abuf.writeByte(DW_AT_sibling); abuf.writeByte(DW_FORM_ref4); + } + else + abuf.writeByte(0); // no children + abuf.writeByte(DW_AT_prototyped); abuf.writeByte(DW_FORM_flag); + if (nextidx != 0) // Don't write DW_AT_type for void + { abuf.writeByte(DW_AT_type); abuf.writeByte(DW_FORM_ref4); + } + + abuf.writeByte(0); abuf.writeByte(0); + code = dwarf_abbrev_code(abuf.buf, abuf.size()); + + unsigned paramcode; + if (params) + { abuf.reset(); + abuf.writeByte(DW_TAG_formal_parameter); + abuf.writeByte(0); + abuf.writeByte(DW_AT_type); abuf.writeByte(DW_FORM_ref4); + abuf.writeByte(0); abuf.writeByte(0); + paramcode = dwarf_abbrev_code(abuf.buf, abuf.size()); + } + + unsigned idxsibling = 0; + unsigned siblingoffset; + + idx = infobuf->size(); + infobuf->writeuLEB128(code); + siblingoffset = infobuf->size(); + if (params) + infobuf->write32(idxsibling); // DW_AT_sibling + infobuf->writeByte(1); // DW_AT_prototyped + if (nextidx) // if return type is not void + infobuf->write32(nextidx); // DW_AT_type + + if (params) + { unsigned *pparamidx = (unsigned *)(functypebuf->buf + functypebufidx); + //printf("2: functypebufidx = %x, pparamidx = %p, size = %x\n", functypebufidx, pparamidx, functypebuf->size()); + for (param_t *p = t->Tparamtypes; p; p = p->Pnext) + { infobuf->writeuLEB128(paramcode); + //unsigned x = dwarf_typidx(p->Ptype); + unsigned paramidx = *++pparamidx; + //printf("paramidx = %d\n", paramidx); + assert(paramidx); + infobuf->write32(paramidx); // DW_AT_type + } + infobuf->writeByte(0); // end parameter list + + // This is why the usual typidx caching does not work; this is unique every time + idxsibling = infobuf->size(); + *(unsigned *)(infobuf->buf + siblingoffset) = idxsibling; + } + + *pidx = idx; // remember it in the functype_table[] cache + break; + } + + case TYarray: + { static unsigned char abbrevTypeArray[] = + { + DW_TAG_array_type, + 1, // child (the subrange type) + DW_AT_sibling, DW_FORM_ref4, + DW_AT_type, DW_FORM_ref4, + 0, 0, + }; + static unsigned char abbrevTypeArrayVoid[] = + { + DW_TAG_array_type, + 1, // child (the subrange type) + DW_AT_sibling, DW_FORM_ref4, + 0, 0, + }; + static unsigned char abbrevTypeSubrange[] = + { + DW_TAG_subrange_type, + 0, // no children + DW_AT_type, DW_FORM_ref4, + DW_AT_upper_bound, DW_FORM_data4, + 0, 0, + }; + static unsigned char abbrevTypeSubrange2[] = + { + DW_TAG_subrange_type, + 0, // no children + DW_AT_type, DW_FORM_ref4, + 0, 0, + }; + unsigned code2 = (t->Tflags & TFsizeunknown) + ? dwarf_abbrev_code(abbrevTypeSubrange2, sizeof(abbrevTypeSubrange2)) + : dwarf_abbrev_code(abbrevTypeSubrange, sizeof(abbrevTypeSubrange)); + unsigned idxbase = dwarf_typidx(tssize); + unsigned idxsibling = 0; + unsigned siblingoffset; + nextidx = dwarf_typidx(t->Tnext); + unsigned code1 = nextidx ? dwarf_abbrev_code(abbrevTypeArray, sizeof(abbrevTypeArray)) + : dwarf_abbrev_code(abbrevTypeArrayVoid, sizeof(abbrevTypeArrayVoid)); + idx = infobuf->size(); + + infobuf->writeuLEB128(code1); // DW_TAG_array_type + siblingoffset = infobuf->size(); + infobuf->write32(idxsibling); // DW_AT_sibling + if (nextidx) + infobuf->write32(nextidx); // DW_AT_type + + infobuf->writeuLEB128(code2); // DW_TAG_subrange_type + infobuf->write32(idxbase); // DW_AT_type + if (!(t->Tflags & TFsizeunknown)) + infobuf->write32(t->Tdim ? t->Tdim - 1 : 0); // DW_AT_upper_bound + + infobuf->writeByte(0); // no more siblings + idxsibling = infobuf->size(); + *(unsigned *)(infobuf->buf + siblingoffset) = idxsibling; + break; + } + + // SIMD vector types + case TYfloat4: tbase = tsfloat; goto Lvector; + case TYdouble2: tbase = tsdouble; goto Lvector; + case TYschar16: tbase = tsschar; goto Lvector; + case TYuchar16: tbase = tsuchar; goto Lvector; + case TYshort8: tbase = tsshort; goto Lvector; + case TYushort8: tbase = tsushort; goto Lvector; + case TYlong4: tbase = tslong; goto Lvector; + case TYulong4: tbase = tsulong; goto Lvector; + case TYllong2: tbase = tsllong; goto Lvector; + case TYullong2: tbase = tsullong; goto Lvector; + Lvector: + { static unsigned char abbrevTypeArray[] = + { + DW_TAG_array_type, + 1, // child (the subrange type) + (DW_AT_GNU_vector & 0x7F) | 0x80, DW_AT_GNU_vector >> 7, DW_FORM_flag, + DW_AT_type, DW_FORM_ref4, + DW_AT_sibling, DW_FORM_ref4, + 0, 0, + }; + static unsigned char abbrevTypeBaseTypeSibling[] = + { + DW_TAG_base_type, + 0, // no children + DW_AT_byte_size, DW_FORM_data1, // sizeof(tssize_t) + DW_AT_encoding, DW_FORM_data1, // DW_ATE_unsigned + 0, 0, + }; + + unsigned code2 = dwarf_abbrev_code(abbrevTypeBaseTypeSibling, sizeof(abbrevTypeBaseTypeSibling)); + unsigned code1 = dwarf_abbrev_code(abbrevTypeArray, sizeof(abbrevTypeArray)); + unsigned idxbase = dwarf_typidx(tbase); + unsigned idxsibling = 0; + unsigned siblingoffset; + + idx = infobuf->size(); + + infobuf->writeuLEB128(code1); // DW_TAG_array_type + infobuf->writeByte(1); // DW_AT_GNU_vector + infobuf->write32(idxbase); // DW_AT_type + siblingoffset = infobuf->size(); + infobuf->write32(idxsibling); // DW_AT_sibling + + idxsibling = infobuf->size(); + *(unsigned *)(infobuf->buf + siblingoffset) = idxsibling; + + // Not sure why this is necessary instead of using dwarf_typidx(tssize), but gcc does it + infobuf->writeuLEB128(code2); // DW_TAG_base_type + infobuf->writeByte(tysize(tssize->Tty)); // DW_AT_byte_size + infobuf->writeByte(DW_ATE_unsigned); // DT_AT_encoding + + infobuf->writeByte(0); // no more siblings + break; + } + + case TYwchar_t: + { + unsigned code = dwarf_abbrev_code(abbrevWchar, sizeof(abbrevWchar)); + unsigned typebase = dwarf_typidx(tsint); + idx = infobuf->size(); + infobuf->writeuLEB128(code); // abbreviation code + infobuf->writeString("wchar_t"); // DW_AT_name + infobuf->write32(typebase); // DW_AT_type + infobuf->writeByte(1); // DW_AT_decl_file + infobuf->writeWord(1); // DW_AT_decl_line + typidx_tab[ty] = idx; + break; + } + + + case TYstruct: + { + Classsym *s = t->Ttag; + struct_t *st = s->Sstruct; + + if (s->Stypidx) + return s->Stypidx; + + static unsigned char abbrevTypeStruct0[] = + { + DW_TAG_structure_type, + 0, // no children + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_data1, + 0, 0, + }; + static unsigned char abbrevTypeStruct1[] = + { + DW_TAG_structure_type, + 0, // no children + DW_AT_name, DW_FORM_string, + DW_AT_declaration, DW_FORM_flag, + 0, 0, + }; + + if (t->Tflags & (TFsizeunknown | TFforward)) + { + abbrevTypeStruct1[0] = (st->Sflags & STRunion) + ? DW_TAG_union_type : DW_TAG_structure_type; + code = dwarf_abbrev_code(abbrevTypeStruct1, sizeof(abbrevTypeStruct1)); + idx = infobuf->size(); + infobuf->writeuLEB128(code); + infobuf->writeString(s->Sident); // DW_AT_name + infobuf->writeByte(1); // DW_AT_declaration + break; // don't set Stypidx + } + + Outbuffer fieldidx; + + // Count number of fields + unsigned nfields = 0; + symlist_t sl; + t->Tflags |= TFforward; + for (sl = st->Sfldlst; sl; sl = list_next(sl)) + { symbol *sf = list_symbol(sl); + + switch (sf->Sclass) + { + case SCmember: + fieldidx.write32(dwarf_typidx(sf->Stype)); + nfields++; + break; + } + } + t->Tflags &= ~TFforward; + if (nfields == 0) + { + abbrevTypeStruct0[0] = (st->Sflags & STRunion) + ? DW_TAG_union_type : DW_TAG_structure_type; + abbrevTypeStruct0[1] = 0; // no children + abbrevTypeStruct0[5] = DW_FORM_data1; // DW_AT_byte_size + code = dwarf_abbrev_code(abbrevTypeStruct0, sizeof(abbrevTypeStruct0)); + idx = infobuf->size(); + infobuf->writeuLEB128(code); + infobuf->writeString(s->Sident); // DW_AT_name + infobuf->writeByte(0); // DW_AT_byte_size + } + else + { + Outbuffer abuf; // for abbrev + abuf.writeByte((st->Sflags & STRunion) + ? DW_TAG_union_type : DW_TAG_structure_type); + abuf.writeByte(1); // children + abuf.writeByte(DW_AT_sibling); abuf.writeByte(DW_FORM_ref4); + abuf.writeByte(DW_AT_name); abuf.writeByte(DW_FORM_string); + abuf.writeByte(DW_AT_byte_size); + + size_t sz = st->Sstructsize; + if (sz <= 0xFF) + abuf.writeByte(DW_FORM_data1); // DW_AT_byte_size + else if (sz <= 0xFFFF) + abuf.writeByte(DW_FORM_data2); // DW_AT_byte_size + else + abuf.writeByte(DW_FORM_data4); // DW_AT_byte_size + abuf.writeByte(0); abuf.writeByte(0); + + code = dwarf_abbrev_code(abuf.buf, abuf.size()); + + unsigned membercode; + abuf.reset(); + abuf.writeByte(DW_TAG_member); + abuf.writeByte(0); // no children + abuf.writeByte(DW_AT_name); + abuf.writeByte(DW_FORM_string); + abuf.writeByte(DW_AT_type); + abuf.writeByte(DW_FORM_ref4); + abuf.writeByte(DW_AT_data_member_location); + abuf.writeByte(DW_FORM_block1); + abuf.writeByte(0); + abuf.writeByte(0); + membercode = dwarf_abbrev_code(abuf.buf, abuf.size()); + + unsigned idxsibling = 0; + unsigned siblingoffset; + + idx = infobuf->size(); + infobuf->writeuLEB128(code); + siblingoffset = infobuf->size(); + infobuf->write32(idxsibling); // DW_AT_sibling + infobuf->writeString(s->Sident); // DW_AT_name + if (sz <= 0xFF) + infobuf->writeByte(sz); // DW_AT_byte_size + else if (sz <= 0xFFFF) + infobuf->writeWord(sz); // DW_AT_byte_size + else + infobuf->write32(sz); // DW_AT_byte_size + + s->Stypidx = idx; + unsigned n = 0; + for (sl = st->Sfldlst; sl; sl = list_next(sl)) + { symbol *sf = list_symbol(sl); + size_t soffset; + + switch (sf->Sclass) + { + case SCmember: + infobuf->writeuLEB128(membercode); + infobuf->writeString(sf->Sident); + //infobuf->write32(dwarf_typidx(sf->Stype)); + unsigned fi = ((unsigned *)fieldidx.buf)[n]; + infobuf->write32(fi); + n++; + soffset = infobuf->size(); + infobuf->writeByte(2); + infobuf->writeByte(DW_OP_plus_uconst); + infobuf->writeuLEB128(sf->Smemoff); + infobuf->buf[soffset] = infobuf->size() - soffset - 1; + break; + } + } + + infobuf->writeByte(0); // no more siblings + idxsibling = infobuf->size(); + *(unsigned *)(infobuf->buf + siblingoffset) = idxsibling; + } + s->Stypidx = idx; + return idx; // no need to cache it + } + + case TYenum: + { static unsigned char abbrevTypeEnum[] = + { + DW_TAG_enumeration_type, + 1, // child (the subrange type) + DW_AT_sibling, DW_FORM_ref4, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_data1, + 0, 0, + }; + static unsigned char abbrevTypeEnumMember[] = + { + DW_TAG_enumerator, + 0, // no children + DW_AT_name, DW_FORM_string, + DW_AT_const_value, DW_FORM_data1, + 0, 0, + }; + + symbol *s = t->Ttag; + enum_t *se = s->Senum; + type *tbase = s->Stype->Tnext; + unsigned sz = type_size(tbase); + symlist_t sl; + + if (s->Stypidx) + return s->Stypidx; + + if (se->SEflags & SENforward) + { + static unsigned char abbrevTypeEnumForward[] = + { + DW_TAG_enumeration_type, + 0, // no children + DW_AT_name, DW_FORM_string, + DW_AT_declaration, DW_FORM_flag, + 0, 0, + }; + code = dwarf_abbrev_code(abbrevTypeEnumForward, sizeof(abbrevTypeEnumForward)); + idx = infobuf->size(); + infobuf->writeuLEB128(code); + infobuf->writeString(s->Sident); // DW_AT_name + infobuf->writeByte(1); // DW_AT_declaration + break; // don't set Stypidx + } + + Outbuffer abuf; // for abbrev + abuf.write(abbrevTypeEnum, sizeof(abbrevTypeEnum)); + code = dwarf_abbrev_code(abuf.buf, abuf.size()); + + unsigned membercode; + abuf.reset(); + abuf.writeByte(DW_TAG_enumerator); + abuf.writeByte(0); + abuf.writeByte(DW_AT_name); + abuf.writeByte(DW_FORM_string); + abuf.writeByte(DW_AT_const_value); + if (tyuns(tbase->Tty)) + abuf.writeByte(DW_FORM_udata); + else + abuf.writeByte(DW_FORM_sdata); + abuf.writeByte(0); + abuf.writeByte(0); + membercode = dwarf_abbrev_code(abuf.buf, abuf.size()); + + unsigned idxsibling = 0; + unsigned siblingoffset; + + idx = infobuf->size(); + infobuf->writeuLEB128(code); + siblingoffset = infobuf->size(); + infobuf->write32(idxsibling); // DW_AT_sibling + infobuf->writeString(s->Sident); // DW_AT_name + infobuf->writeByte(sz); // DW_AT_byte_size + + for (sl = s->Senumlist; sl; sl = list_next(sl)) + { symbol *sf = (symbol *)list_ptr(sl); + unsigned long value = el_tolongt(sf->Svalue); + + infobuf->writeuLEB128(membercode); + infobuf->writeString(sf->Sident); + if (tyuns(tbase->Tty)) + infobuf->writeuLEB128(value); + else + infobuf->writesLEB128(value); + } + + infobuf->writeByte(0); // no more siblings + idxsibling = infobuf->size(); + *(unsigned *)(infobuf->buf + siblingoffset) = idxsibling; + + s->Stypidx = idx; + return idx; // no need to cache it + } + + default: + return 0; + } +Lret: + /* If infobuf->buf[idx .. size()] is already in infobuf, + * discard this one and use the previous one. + */ + Atype atype; + atype.buf = infobuf; + atype.start = idx; + atype.end = infobuf->size(); + + if (!type_table) + /* unsigned[Adata] type_table; + * where the table values are the type indices + */ + type_table = new AArray(&ti_atype, sizeof(unsigned)); + + unsigned *pidx; + pidx = (unsigned *)type_table->get(&atype); + if (!*pidx) // if no idx assigned yet + { + *pidx = idx; // assign newly computed idx + } + else + { // Reuse existing code + infobuf->setsize(idx); // discard current + idx = *pidx; + } + return idx; +} + +/* ======================= Abbreviation Codes ====================== */ + +struct Adata +{ + size_t start; + size_t end; +}; + +struct TypeInfo_Adata : TypeInfo +{ + const char* toString(); + hash_t getHash(void *p); + int equals(void *p1, void *p2); + int compare(void *p1, void *p2); + size_t tsize(); + void swap(void *p1, void *p2); +}; + +TypeInfo_Adata ti_adata; + +const char* TypeInfo_Adata::toString() +{ + return "Adata"; +} + +hash_t TypeInfo_Adata::getHash(void *p) +{ Adata a; + hash_t hash = 0; + size_t i; + + a = *(Adata *)p; + for (i = a.start; i < a.end; i++) + { + //printf("%02x ", abbrevbuf->buf[i]); + hash = hash * 11 + abbrevbuf->buf[i]; + } + //printf("\nhash = %x, length = %d\n", hash, a.end - a.start); + return hash; +} + +int TypeInfo_Adata::equals(void *p1, void *p2) +{ + Adata a1 = *(Adata*)p1; + Adata a2 = *(Adata*)p2; + size_t len = a1.end - a1.start; + + return len == a2.end - a2.start && + memcmp(abbrevbuf->buf + a1.start, abbrevbuf->buf + a2.start, len) == 0; +} + +int TypeInfo_Adata::compare(void *p1, void *p2) +{ + Adata a1 = *(Adata*)p1; + Adata a2 = *(Adata*)p2; + size_t len = a1.end - a1.start; + if (len == a2.end - a2.start) + return memcmp(abbrevbuf->buf + a1.start, abbrevbuf->buf + a2.start, len); + else if (len < a2.end - a2.start) + return -1; + else + return 1; +} + +size_t TypeInfo_Adata::tsize() +{ + return sizeof(Adata); +} + +void TypeInfo_Adata::swap(void *p1, void *p2) +{ + assert(0); +} + + +unsigned dwarf_abbrev_code(unsigned char *data, size_t nbytes) +{ + if (!abbrev_table) + /* unsigned[Adata] abbrev_table; + * where the table values are the abbreviation codes. + */ + abbrev_table = new AArray(&ti_adata, sizeof(unsigned)); + + /* Write new entry into abbrevbuf + */ + Adata adata; + + unsigned idx = abbrevbuf->size(); + abbrevcode++; + abbrevbuf->writeuLEB128(abbrevcode); + adata.start = abbrevbuf->size(); + abbrevbuf->write(data, nbytes); + adata.end = abbrevbuf->size(); + + /* If abbrevbuf->buf[idx .. size()] is already in abbrevbuf, + * discard this one and use the previous one. + */ + + unsigned *pcode; + pcode = (unsigned *)abbrev_table->get(&adata); + if (!*pcode) // if no code assigned yet + { + *pcode = abbrevcode; // assign newly computed code + } + else + { // Reuse existing code + abbrevbuf->setsize(idx); // discard current + abbrevcode--; + } + return *pcode; +} + +#endif +#endif diff --git a/backend/dwarf.h b/backend/dwarf.h new file mode 100644 index 00000000..17ef6c65 --- /dev/null +++ b/backend/dwarf.h @@ -0,0 +1,18 @@ + +#ifndef DWARF_H +#define DWARF_H + +/* ==================== Dwarf debug ======================= */ + +// #define USE_DWARF_D_EXTENSIONS + +void dwarf_initfile(const char *filename); +void dwarf_termfile(); +void dwarf_initmodule(const char *filename, const char *modulename); +void dwarf_termmodule(); +void dwarf_func_start(Symbol *sfunc); +void dwarf_func_term(Symbol *sfunc); +unsigned dwarf_typidx(type *t); +unsigned dwarf_abbrev_code(unsigned char *data, size_t nbytes); + +#endif diff --git a/backend/dwarf2.h b/backend/dwarf2.h new file mode 100644 index 00000000..7aa9d7f4 --- /dev/null +++ b/backend/dwarf2.h @@ -0,0 +1,470 @@ + +// dwarf2.h +// Reflects declarations from the Dwarf 3 spec, not the Digital Mars +// dwarf implementation + +enum +{ + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0A, + DW_TAG_lexical_block = 0x0B, + DW_TAG_member = 0x0D, + DW_TAG_pointer_type = 0x0F, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1A, + DW_TAG_common_inclusion = 0x1B, + DW_TAG_inheritance = 0x1C, + DW_TAG_inlined_subroutine = 0x1D, + DW_TAG_module = 0x1E, + DW_TAG_ptr_to_member_type = 0x1F, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2A, + DW_TAG_namelist = 0x2B, + DW_TAG_namelist_item = 0x2C, + DW_TAG_packed_type = 0x2D, + DW_TAG_subprogram = 0x2E, + DW_TAG_template_type_param = 0x2F, + DW_TAG_template_value_param = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3A, + DW_TAG_unspecified_type = 0x3B, + DW_TAG_partial_unit = 0x3C, + DW_TAG_imported_unit = 0x3D, + DW_TAG_condition = 0x3F, + DW_TAG_shared_type = 0x40, + + // D programming language extensions +#ifdef USE_DWARF_D_EXTENSIONS + DW_TAG_darray_type = 0x41, + DW_TAG_aarray_type = 0x42, + DW_TAG_delegate_type = 0x43, +#endif + + DW_TAG_lo_user = 0x4080, + DW_TAG_hi_user = 0xFFFF, +}; + +enum +{ + DW_CHILDREN_no = 0x00, + DW_CHILDREN_yes = 0x01, +}; + +enum +{ + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + DW_AT_ordering = 0x09, + DW_AT_byte_size = 0x0B, + DW_AT_bit_offset = 0x0C, + DW_AT_bit_size = 0x0D, + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1A, + DW_AT_comp_dir = 0x1B, + DW_AT_const_value = 0x1C, + DW_AT_containing_type = 0x1D, + DW_AT_default_value = 0x1E, + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + DW_AT_producer = 0x25, + DW_AT_prototyped = 0x27, + DW_AT_return_addr = 0x2A, + DW_AT_start_scope = 0x2C, + DW_AT_stride_size = 0x2E, + DW_AT_upper_bound = 0x2F, + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3A, + DW_AT_decl_line = 0x3B, + DW_AT_declaration = 0x3C, + DW_AT_discr_list = 0x3D, + DW_AT_encoding = 0x3E, + DW_AT_external = 0x3F, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, + DW_AT_namelist_item = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4A, + DW_AT_variable_parameter = 0x4B, + DW_AT_virtuality = 0x4C, + DW_AT_vtable_elem_location = 0x4D, + + DW_AT_allocated = 0x4E, + DW_AT_associated = 0x4F, + DW_AT_data_location = 0x50, + DW_AT_byte_stride = 0x51, + DW_AT_entry_pc = 0x52, + DW_AT_use_UTF8 = 0x53, + DW_AT_extension = 0x54, + DW_AT_ranges = 0x55, + DW_AT_trampoline = 0x56, + DW_AT_call_column = 0x57, + DW_AT_call_file = 0x58, + DW_AT_call_line = 0x59, + DW_AT_description = 0x5A, + DW_AT_binary_scale = 0x5B, + DW_AT_decimal_scale = 0x5C, + DW_AT_small = 0x5D, + DW_AT_decimal_sign = 0x5E, + DW_AT_digit_count = 0x5F, + DW_AT_picture_string = 0x60, + DW_AT_mutable = 0x61, + DW_AT_threads_scaled = 0x62, + DW_AT_explicit = 0x63, + DW_AT_object_pointer = 0x64, + DW_AT_endianity = 0x65, + DW_AT_elemental = 0x66, + DW_AT_pure = 0x67, + DW_AT_recursive = 0x68, + + DW_AT_lo_user = 0x2000, + DW_AT_MIPS_linkage_name = 0x2007, + DW_AT_GNU_vector = 0x2107, + DW_AT_hi_user = 0x3FFF, +}; + +enum +{ + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0A, + DW_FORM_data1 = 0x0B, + DW_FORM_flag = 0x0C, + DW_FORM_sdata = 0x0D, + DW_FORM_strp = 0x0E, + DW_FORM_udata = 0x0F, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, +}; + +enum +{ + DW_OP_addr = 0x03, + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, + DW_OP_const1s = 0x09, + DW_OP_const2u = 0x0a, + DW_OP_const2s = 0x0b, + DW_OP_const4u = 0x0c, + DW_OP_const4s = 0x0d, + DW_OP_const8u = 0x0e, + DW_OP_const8s = 0x0f, + DW_OP_constu = 0x10, + DW_OP_consts = 0x11, + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1a, + DW_OP_div = 0x1b, + DW_OP_minus = 0x1c, + DW_OP_mod = 0x1d, + DW_OP_mul = 0x1e, + DW_OP_neg = 0x1f, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2f, + DW_OP_bra = 0x28, + DW_OP_eq = 0x29, + DW_OP_ge = 0x2a, + + DW_OP_gt = 0x2b, + DW_OP_le = 0x2c, + DW_OP_lt = 0x2d, + DW_OP_ne = 0x2e, + DW_OP_lit0 = 0x30, + DW_OP_lit1 = 0x31, + DW_OP_lit31 = 0x4f, + DW_OP_reg0 = 0x50, + DW_OP_reg1 = 0x51, + DW_OP_reg31 = 0x6f, + DW_OP_breg0 = 0x70, + DW_OP_breg1 = 0x71, + DW_OP_breg31 = 0x8f, + DW_OP_regx = 0x90, + DW_OP_fbreg = 0x91, + DW_OP_bregx = 0x92, + DW_OP_piece = 0x93, + DW_OP_deref_size = 0x94, + DW_OP_xderef_size = 0x95, + DW_OP_nop = 0x96, + DW_OP_push_object_address = 0x97, + DW_OP_call2 = 0x98, + DW_OP_call4 = 0x99, + DW_OP_call_ref = 0x9a, + DW_OP_form_tls_address = 0x9b, + DW_OP_call_frame_cfa = 0x9c, + DW_OP_bit_piece = 0x9d, + DW_OP_lo_user = 0xe0, + DW_OP_hi_user = 0xff, +}; + +enum +{ + DW_ATE_address = 0x01, + DW_ATE_boolean = 0x02, + DW_ATE_complex_float = 0x03, + DW_ATE_float = 0x04, + DW_ATE_signed = 0x05, + DW_ATE_signed_char = 0x06, + DW_ATE_unsigned = 0x07, + DW_ATE_unsigned_char = 0x08, + DW_ATE_imaginary_float = 0x09, + DW_ATE_packed_decimal = 0x0a, + DW_ATE_numeric_string = 0x0b, + DW_ATE_editted = 0x0c, + DW_ATE_signed_fixed = 0x0d, + DW_ATE_unsigned_fixed = 0x0e, + DW_ATE_decimal_float = 0x0f, + DW_ATE_lo_user = 0x80, + DW_ATE_hi_user = 0xff, +}; + +enum +{ + DW_DS_unsigned = 0x01, + DW_DS_leading_overpunch = 0x02, + DW_DS_trailing_overpunch = 0x03, + DW_DS_leading_separate = 0x04, + DW_DS_trailing_separate = 0x05, +}; + +enum +{ + DW_END_default = 0x00, + DW_END_big = 0x01, + DW_END_little = 0x02, + DW_END_lo_user = 0x40, + DW_END_hi_user = 0xff, +}; + +enum +{ + DW_ACCESS_public = 0x01, + DW_ACCESS_protected = 0x02, + DW_ACCESS_private = 0x03, +}; + +enum +{ + DW_VIS_local = 0x01, + DW_VIS_exported = 0x02, + DW_VIS_qualified = 0x03, +}; + +enum +{ + DW_VIRTUALITY_none = 0x00, + DW_VIRTUALITY_virtual = 0x01, + DW_VIRTUALITY_pure_virtual = 0x02, +}; + +enum +{ + DW_LANG_C89 = 0x0001, + DW_LANG_C = 0x0002, + DW_LANG_Ada83 = 0x0003, + DW_LANG_C_plus_plus = 0x0004, + DW_LANG_Cobol74 = 0x0005, + DW_LANG_Cobol85 = 0x0006, + DW_LANG_Fortran77 = 0x0007, + DW_LANG_Fortran90 = 0x0008, + DW_LANG_Pascal83 = 0x0009, + DW_LANG_Modula2 = 0x000a, + DW_LANG_Java = 0x000b, + DW_LANG_C99 = 0x000c, + DW_LANG_Ada95 = 0x000d, + DW_LANG_Fortran95 = 0x000e, + DW_LANG_PLI = 0x000f, + DW_LANG_ObjC = 0x0010, + DW_LANG_ObjC_plus_plus = 0x0011, + DW_LANG_UPC = 0x0012, + DW_LANG_D = 0x0013, + DW_LANG_lo_user = 0x8000, + DW_LANG_hi_user = 0xffff, +}; + +enum +{ + DW_ID_case_sensitive = 0x00, + DW_ID_up_case = 0x01, + DW_ID_down_case = 0x02, + DW_ID_case_insensitive = 0x03, +}; + +enum +{ + DW_CC_normal = 0x01, + DW_CC_program = 0x02, + DW_CC_nocall = 0x03, + DW_CC_lo_user = 0x40, + DW_CC_hi_user = 0xff, +}; + +enum +{ + DW_INL_not_inlined = 0x00, + DW_INL_inlined = 0x01, + DW_INL_declared_not_inlined = 0x02, + DW_INL_declared_inlined = 0x03, +}; + +enum +{ + DW_ORD_row_major = 0x00, + DW_ORD_col_major = 0x01, +}; + +enum +{ + DW_DSC_label = 0x00, + DW_DSC_range = 0x01, +}; + +enum +{ + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c, +}; + +enum +{ + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_define_file = 0x03, + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff, +}; + +enum +{ + DW_MACINFO_define = 0x01, + DW_MACINFO_undef = 0x02, + DW_MACINFO_start_file = 0x03, + DW_MACINFO_end_file = 0x04, + DW_MACINFO_vendor_ext = 0xff, +}; + +enum +{ + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + + DW_CFA_GNU_window_save = 0x2d, + DW_CFA_GNU_args_size = 0x2e, + DW_CFA_GNU_negative_offset_extended = 0x2f, + + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f, +}; + + diff --git a/backend/ee.c b/backend/ee.c new file mode 100644 index 00000000..3f21254d --- /dev/null +++ b/backend/ee.c @@ -0,0 +1,124 @@ +// Copyright (C) 1995-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +/* + * Code to handle debugger expression evaluation + */ + +#if !SPP + +#include +#include +#include +#include "cc.h" +#include "token.h" +#include "global.h" +#include "type.h" +#include "oper.h" +#include "el.h" +#include "exh.h" +#if TX86 +#include "cgcv.h" +#endif + +#if SCPP +#include "parser.h" +#endif + +#include "iasm.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +#if MARS +EEcontext eecontext; +#endif + +////////////////////////////////////// +// Convert any symbols generated for the debugger expression to SCstack +// storage class. + +void eecontext_convs(unsigned marksi) +{ unsigned u; + unsigned top; + symtab_t *ps; + + // Change all generated SCtmp's and SCauto's to SCstack's +#if SCPP + ps = &globsym; +#else + ps = cstate.CSpsymtab; +#endif + top = ps->top; + //printf("eecontext_convs(%d,%d)\n",marksi,top); + for (u = marksi; u < top; u++) + { symbol *s; + + s = ps->tab[u]; + switch (s->Sclass) + { + case SCauto: + case SCtmp: + case SCregister: + s->Sclass = SCstack; + s->Sfl = FLstack; + break; + } + } +} + +//////////////////////////////////////// +// Parse the debugger expression. + +#if SCPP + +void eecontext_parse() +{ + if (eecontext.EEimminent) + { type *t; + unsigned marksi; + symbol *s; + + //printf("imminent\n"); + marksi = globsym.top; + eecontext.EEin++; + s = symbol_genauto(tspvoid); + eecontext.EEelem = func_expr_dtor(TRUE); + t = eecontext.EEelem->ET; + if (tybasic(t->Tty) != TYvoid) + { unsigned op; + elem *e; + + e = el_unat(OPind,t,el_var(s)); + op = tyaggregate(t->Tty) ? OPstreq : OPeq; + eecontext.EEelem = el_bint(op,t,e,eecontext.EEelem); + } + eecontext.EEin--; + eecontext.EEimminent = 0; + eecontext.EEfunc = funcsym_p; + + eecontext_convs(marksi); + + // Generate the typedef + if (eecontext.EEtypedef && config.fulltypes) + { symbol *s; + + s = symbol_name(eecontext.EEtypedef,SCtypedef,t); + cv_outsym(s); + symbol_free(s); + } + } +} + +#endif +#endif diff --git a/backend/el.c b/backend/el.c new file mode 100644 index 00000000..4d2f76ad --- /dev/null +++ b/backend/el.c @@ -0,0 +1,3326 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +/* Routines to handle elems. */ + +#if !SPP + +#include +#include +#include +#include + +#include "cc.h" +#include "type.h" +#include "el.h" +#include "list.h" +#include "mem.h" +#include "oper.h" +#include "type.h" + +#include "code.h" + +#include "global.h" +#include "go.h" + +#if SCPP +#include "parser.h" +#endif + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +#ifdef STATS +static int elfreed = 0; /* number of freed elems */ +static int eprm_cnt; /* max # of allocs at any point */ +#endif + +#if TARGET_OSX +extern void slist_add(Symbol *s); +#endif + +/******************************* + * Do our own storage allocation of elems. + */ + +static elem *nextfree = NULL; /* pointer to next free elem */ + +static int elcount = 0; /* number of allocated elems */ +static int elem_size = sizeof(elem); + +#ifdef DEBUG +static int elmax; /* max # of allocs at any point */ +#endif + +///////////////////////////// +// Table to gather redundant strings in. + +static struct STAB +{ symbol *sym; // symbol that refers to the string + char *p; // pointer to the string + int len; // length of string p +} stable[16]; +static int stable_si; + +/************************ + * Initialize el package. + */ + +void el_init() +{ + if (!configv.addlinenumbers) + elem_size = sizeof(elem) - sizeof(Srcpos); +} + +/******************************* + * Initialize for another run through. + */ + +void el_reset() +{ + stable_si = 0; + for (int i = 0; i < arraysize(stable); i++) + mem_free(stable[i].p); + memset(stable,0,sizeof(stable)); +} + +#if TX86 +/************************ + * Terminate el package. + */ + +void el_term() +{ +#if TERMCODE + int i; + + for (i = 0; i < arraysize(stable); i++) + mem_free(stable[i].p); + +#ifdef DEBUG + dbg_printf("Max # of elems = %d\n",elmax); +#endif + if (elcount != 0) + dbg_printf("unfreed elems = %d\n",elcount); + while (nextfree) + { elem *e; + + e = nextfree->E1; + mem_ffree(nextfree); + nextfree = e; + } +#else + assert(elcount == 0); +#endif +} +#endif + +/*********************** + * Allocate an element. + */ + +#if SCPP && __SC__ && __INTSIZE == 4 && TX86 && !_DEBUG_TRACE && !MEM_DEBUG && _WIN32 && !defined(DEBUG) + +__declspec(naked) elem *el_calloc() +{ + __asm + { + mov EAX,elcount + push EDI + + inc EAX + mov EDI,nextfree + + test EDI,EDI + je L27 + + mov EDX,E1[EDI] + mov elcount,EAX + + xor EAX,EAX + mov nextfree,EDX + jmp L30 + +L27: push sizeof(elem) + mov elcount,EAX + call mem_fmalloc + mov EDI,EAX + xor EAX,EAX + +L30: mov EDX,EDI + mov ECX,(sizeof(elem) + 3) / 4 + #if DOS386 + push DS + pop ES + #endif + rep stosd + mov EAX,EDX + pop EDI + ret + } +} + +#else + +elem *el_calloc() +{ + elem *e; + static elem ezero; + + elcount++; + if (nextfree) + { e = nextfree; + nextfree = e->E1; + } + else + e = (elem *) mem_fmalloc(sizeof(elem)); +#ifdef STATS + eprm_cnt++; +#endif + *e = ezero; /* clear it */ + +#ifdef DEBUG + e->id = IDelem; + if (elcount > elmax) + elmax = elcount; +#endif + /*dbg_printf("el_calloc() = %p\n",e);*/ + return e; +} + +#endif + +/*************** + * Free element + */ + +void el_free(elem *e) +{ + int op; + tym_t ty; + +L1: + if (!e) return; + elem_debug(e); + //dbg_printf("el_free(%p)\n",e); + //elem_print(e); + if (SCPP && PARSER) + { + ty = e->ET ? e->ET->Tty : 0; + type_free(e->ET); + } + else if (e->Ecount--) + return; // usage count + elcount--; + op = e->Eoper; + switch (op) + { + case OPconst: +#if FLOATS_IN_CODE + if (!PARSER && FLT_CODESEG_CELEM(e)) + flt_free_elem(e); +#endif + break; + + case OPvar: + break; + case OPrelconst: +#if SCPP + if (0 && PARSER && tybasic(ty) == TYmemptr) + el_free(e->EV.sm.ethis); +#endif + break; + case OPstring: + case OPasm: + mem_free(e->EV.ss.Vstring); + break; + default: + debug_assert(op < OPMAX); + if (!OTleaf(op)) + { elem *en; + + if (OTbinary(op)) + el_free(e->E2); + en = e->E1; +#ifdef DEBUG + memset(e,0xFF,elem_size); +#endif + e->E1 = nextfree; + nextfree = e; + +#ifdef STATS + elfreed++; +#endif + e = en; + goto L1; + } + break; + } +#ifdef DEBUG + memset(e,0xFF,elem_size); +#endif + e->E1 = nextfree; + nextfree = e; + +#ifdef STATS + elfreed++; +#endif +} + +#ifdef STATS +/* count number of elems available on free list */ +void el_count_free() + { + elem *e; + int count; + + for(e=nextfree;e;e=e->E1) + count++; + dbg_printf("Requests for elems %d\n",elcount); + dbg_printf("Requests to free elems %d\n",elfreed); + dbg_printf("Number of elems %d\n",eprm_cnt); + dbg_printf("Number of elems currently on free list %d\n",count); + } +#endif + +/********************* + * Combine e1 and e2 with a comma-expression. + * Be careful about either or both being NULL. + */ + +elem * el_combine(elem *e1,elem *e2) +{ + if (e1) + { if (e2) + e1 = (SCPP && PARSER) ? el_bint(OPcomma,e2->ET,e1,e2) + : el_bin(OPcomma,e2->Ety,e1,e2); + } + else + e1 = e2; + return e1; +} + +/********************* + * Combine e1 and e2 as parameters to a function. + * Be careful about either or both being NULL. + */ + +elem * el_param(elem *e1,elem *e2) +{ + //printf("el_param(%p, %p)\n", e1, e2); +#if 0 + if (e2 && e2->Eoper != OPstrpar && tybasic(e2->Ety) == TYstruct) + *(char*)0=0; +#endif + if (e1) + { if (e2) + e1 = (SCPP && PARSER) ? el_bint(OPparam,tsvoid,e1,e2) + : el_bin(OPparam,TYvoid,e1,e2); + } + else + e1 = e2; + return e1; +} + +/********************************* + * Create parameter list, terminated by a NULL. + */ + +elem *el_params(elem *e1, ...) +{ + elem *e; + va_list ap; + + e = NULL; + for (va_start(ap, e1); e1; e1 = va_arg(ap, elem *)) + { + e = el_param(e, e1); + } + va_end(ap); + return e; +} + +/***************************************** + * Do an array of parameters as a balanced + * binary tree. + */ + +elem *el_params(void **args, int length) +{ + if (length == 0) + return NULL; + if (length == 1) + return (elem *)args[0]; + int mid = length >> 1; + return el_param(el_params(args, mid), + el_params(args + mid, length - mid)); +} + +/***************************************** + * Do an array of parameters as a balanced + * binary tree. + */ + +elem *el_combines(void **args, int length) +{ + if (length == 0) + return NULL; + if (length == 1) + return (elem *)args[0]; + int mid = length >> 1; + return el_combine(el_combines(args, mid), + el_combines(args + mid, length - mid)); +} + +/*************************************** + * Return a list of the parameters. + */ + +int el_nparams(elem *e) +{ + if (e->Eoper == OPparam) + { + return el_nparams(e->E1) + el_nparams(e->E2); + } + else + return 1; +} + +/************************************* + * Create a quad word out of two dwords. + */ + +elem *el_pair(tym_t tym, elem *lo, elem *hi) +{ +#if 0 + lo = el_una(OPu32_64, TYullong, lo); + hi = el_una(OPu32_64, TYullong, hi); + hi = el_bin(OPshl, TYullong, hi, el_long(TYint, 32)); + return el_bin(OPor, tym, lo, hi); +#else + return el_bin(OPpair, tym, lo, hi); +#endif +} + + +/************************* + * Copy an element (not the tree!). + */ + +void el_copy(elem *to,elem *from) +{ + assert(to && from); + elem_debug(from); + elem_debug(to); + memcpy(to,from,elem_size); + elem_debug(to); +} + +/*********************************** + * Allocate a temporary, and return temporary elem. + */ + +elem * el_alloctmp(tym_t ty) +{ + symbol *s; + + assert(MARS || !PARSER); + s = symbol_generate(SCtmp,type_fake(ty)); + symbol_add(s); + s->Sfl = FLtmp; + s->Sflags = SFLfree | SFLunambig | GTregcand; + return el_var(s); +} + +/******************************** + * Select the e1 child of e. + */ + +elem * el_selecte1(elem *e) +{ elem *e1; + + assert(!PARSER); + elem_debug(e); + assert(EOP(e)); + e1 = e->E1; + elem_debug(e1); + if (e->E2) elem_debug(e->E2); + e->E1 = NULL; // so e1 won't be freed + if (configv.addlinenumbers) + { + if (e->Esrcpos.Slinnum) + e1->Esrcpos = e->Esrcpos; + } + e1->Ety = e->Ety; +// if (tyaggregate(e1->Ety)) +// e1->Enumbytes = e->Enumbytes; +#if MARS + if (!e1->Ejty) + e1->Ejty = e->Ejty; +#endif + el_free(e); + return e1; +} + +/******************************** + * Select the e2 child of e. + */ + +elem * el_selecte2(elem *e) +{ elem *e2; + + //dbg_printf("el_selecte2(%p)\n",e); + elem_debug(e); + assert(EBIN(e)); + if (e->E1) + elem_debug(e->E1); + e2 = e->E2; + elem_debug(e2); + e->E2 = NULL; // so e2 won't be freed + if (configv.addlinenumbers) + { + if (e->Esrcpos.Slinnum) + e2->Esrcpos = e->Esrcpos; + } + if (PARSER) + el_settype(e2,e->ET); + else + { e2->Ety = e->Ety; +// if (tyaggregate(e->Ety)) +// e2->Enumbytes = e->Enumbytes; + } + el_free(e); + return e2; +} + +/************************* + * Create and return a duplicate of e, including its leaves. + * No CSEs. + */ + +elem * el_copytree(elem *e) +{ elem *d; + + if (!e) + return e; + elem_debug(e); + d = el_calloc(); + el_copy(d,e); + assert(!e->Ecount); + if (SCPP && PARSER) + { + type_debug(d->ET); + d->ET->Tcount++; + } + if (EOP(e)) + { d->E1 = el_copytree(e->E1); + if (EBIN(e)) + d->E2 = el_copytree(e->E2); + } + else + { + switch (e->Eoper) + { case OPstring: +#if 0 + if (OPTIMIZER) + { + /* Convert the string to a static symbol and + then just refer to it, because two OPstrings can't + refer to the same string. + */ + + el_convstring(e); // convert string to symbol + d->Eoper = OPrelconst; + d->EV.sp.Vsym = e->EV.sp.Vsym; + break; + } +#endif +#if 0 + case OPrelconst: + e->EV.sm.ethis = NULL; + break; +#endif + case OPasm: + d->EV.ss.Vstring = (char *) mem_malloc(d->EV.ss.Vstrlen); + memcpy(d->EV.ss.Vstring,e->EV.ss.Vstring,e->EV.ss.Vstrlen); + break; + } + } + return d; +} + +/************************* + * Similar to el_copytree(e). But if e has any side effects, it's replaced + * with (tmp = e) and tmp is returned. + */ + +elem * el_same(elem **pe) +{ elem *e = *pe; + + if (e && el_sideeffect(e)) + { + *pe = exp2_copytotemp(e); /* convert to ((tmp=e),tmp) */ + e = (*pe)->E2; /* point at tmp */ + } + return el_copytree(e); +} + +/************************** + * Replace symbol s1 with s2 in tree. + */ + +#if SCPP + +void el_replace_sym(elem *e,symbol *s1,symbol *s2) +{ + symbol_debug(s1); + symbol_debug(s2); + while (1) + { elem_debug(e); + if (EOP(e)) + { if (EBIN(e)) + el_replace_sym(e->E2,s1,s2); + e = e->E1; + } + else + { + switch (e->Eoper) + { + case OPvar: + case OPrelconst: + if (e->EV.sp.Vsym == s1) + e->EV.sp.Vsym = s2; + break; + } + break; + } + } +} + +#endif + +/************************************* + * Does symbol s appear in tree e? + * Returns: + * 1 yes + * 0 no + */ + +#if MARS + +int el_appears(elem *e,Symbol *s) +{ + symbol_debug(s); + while (1) + { elem_debug(e); + if (EOP(e)) + { if (EBIN(e) && el_appears(e->E2,s)) + return 1; + e = e->E1; + } + else + { + switch (e->Eoper) + { + case OPvar: + case OPrelconst: + if (e->EV.sp.Vsym == s) + return 1; + break; + } + break; + } + } + return 0; +} + +/***************************************** + * Look for symbol that is a base of addressing mode e. + * Returns: + * s symbol used as base + * NULL couldn't find a base symbol + */ + +#if 0 +Symbol *el_basesym(elem *e) +{ Symbol *s; + + s = NULL; + while (1) + { elem_debug(e); + switch (e->Eoper) + { + case OPvar: + s = e->EV.sp.Vsym; + break; + case OPcomma: + e = e->E2; + continue; + case OPind: + s = el_basesym(e->E1); + break; + case OPadd: + s = el_basesym(e->E1); + if (!s) + s = el_basesym(e->E2); + break; + } + break; + } + return s; +} +#endif + +/**************************************** + * Does any definition of lvalue ed appear in e? + * Returns: + * 1 yes + * 0 no + */ + +int el_anydef(elem *ed, elem *e) +{ int op; + int edop; + Symbol *s; + elem *e1; + + edop = ed->Eoper; + s = (edop == OPvar) ? ed->EV.sp.Vsym : NULL; + while (1) + { + op = e->Eoper; + if (!OTleaf(op)) + { + e1 = e->E1; + if (OTdef(op)) + { + if (e1->Eoper == OPvar && e1->EV.sp.Vsym == s) + return 1; + + // This doesn't cover all the cases + if (e1->Eoper == edop && el_match(e1,ed)) + return 1; + } + if (OTbinary(op) && el_anydef(ed,e->E2)) + return 1; + e = e1; + } + else + break; + } + return 0; +} + +#endif + +/************************ + * Make a binary operator node. + */ + +elem * el_bint(unsigned op,type *t,elem *e1,elem *e2) +{ elem *e; + + /* e2 is NULL when OPpostinc is built */ + assert(op < OPMAX && OTbinary(op) && e1); + assert(PARSER); + e = el_calloc(); + if (t) + { e->ET = t; + type_debug(t); + e->ET->Tcount++; + } + e->Eoper = op; + elem_debug(e1); + if (e2) + elem_debug(e2); + e->E1 = e1; + e->E2 = e2; + return e; +} + +elem * el_bin(unsigned op,tym_t ty,elem *e1,elem *e2) +{ elem *e; + +#if 0 + if (!(op < OPMAX && OTbinary(op) && e1 && e2)) + *(char *)0=0; +#endif + assert(op < OPMAX && OTbinary(op) && e1 && e2); + assert(MARS || !PARSER); + elem_debug(e1); + elem_debug(e2); + e = el_calloc(); + e->Ety = ty; + e->Eoper = op; + e->E1 = e1; + e->E2 = e2; + if (op == OPcomma && tyaggregate(ty)) + e->ET = e2->ET; + return e; +} + +/************************ + * Make a unary operator node. + */ + +elem * el_unat(unsigned op,type *t,elem *e1) +{ elem *e; + +#ifdef DEBUG + if (!(op < OPMAX && OTunary(op) && e1)) + dbg_printf("op = x%x, e1 = %p\n",op,e1); +#endif + assert(op < OPMAX && OTunary(op) && e1); + assert(PARSER); + elem_debug(e1); + e = el_calloc(); + e->Eoper = op; + e->E1 = e1; + if (t) + { + type_debug(t); + t->Tcount++; + e->ET = t; + } + return e; +} + +elem * el_una(unsigned op,tym_t ty,elem *e1) +{ elem *e; + +#ifdef DEBUG + if (!(op < OPMAX && OTunary(op) && e1)) + dbg_printf("op = x%x, e1 = %p\n",op,e1); +#endif + assert(op < OPMAX && OTunary(op) && e1); + assert(MARS || !PARSER); + elem_debug(e1); + e = el_calloc(); + e->Ety = ty; + e->Eoper = op; + e->E1 = e1; + return e; +} + +/******************* + * Make a constant node out of integral type. + */ + +elem * el_longt(type *t,targ_llong val) +{ elem *e; + + assert(PARSER); + e = el_calloc(); + e->Eoper = OPconst; + e->ET = t; + if (e->ET) + { type_debug(t); + e->ET->Tcount++; + } + e->EV.Vllong = val; + return e; +} + +elem * el_long(tym_t t,targ_llong val) +{ elem *e; + + assert(MARS || !PARSER); + e = el_calloc(); + e->Eoper = OPconst; + e->Ety = t; + switch (tybasic(t)) + { + case TYfloat: + case TYifloat: + e->EV.Vfloat = val; + break; + case TYdouble: + case TYidouble: + e->EV.Vdouble = val; + break; + case TYldouble: + case TYildouble: + e->EV.Vldouble = val; + break; + case TYcfloat: + case TYcdouble: + case TYcldouble: + assert(0); + break; + default: + e->EV.Vllong = val; + break; + } + return e; +} + +/******************************* + * If elem is a const that can be converted to an OPconst, + * do the conversion. + */ + +#if SCPP +void el_toconst(elem *e) +{ + elem_debug(e); + assert(PARSER); +#if TX86 + if (e->Eoper == OPvar && e->EV.sp.Vsym->Sflags & SFLvalue) +#else + if (e->Eoper == OPvar && e->EV.sp.Vsym->Sflags & SFLvalue && + tybasic(e->ET->Tty) != TYstruct) +#endif + { elem *es = e->EV.sp.Vsym->Svalue; + + type_debug(e->ET); + symbol_debug(e->EV.sp.Vsym); + elem_debug(es); + e->Eoper = es->Eoper; + assert(e->Eoper == OPconst); + e->EV = es->EV; + } +} +#endif + +/******************************* + * Set new type for elem. + */ + +elem * el_settype(elem *e,type *t) +{ +#if MARS + assert(0); +#else + assert(PARSER); + elem_debug(e); + type_debug(t); + type_settype(&e->ET,t); +#endif + return e; +} + +/******************************* + * Walk tree, replacing symbol s1 with s2. + */ + +#if SCPP + +void el_replacesym(elem *e,symbol *s1,symbol *s2) +{ + _chkstack(); + + assert(PARSER); + while (e) + { elem_debug(e); + if (EOP(e)) + { el_replacesym(e->E2,s1,s2); + e = e->E1; + } + else + { + if ((e->Eoper == OPvar || e->Eoper == OPrelconst) && + e->EV.sp.Vsym == s1) + e->EV.sp.Vsym = s2; + break; + } + } +} + +#endif + +/******************************* + * Create elem that is the size of a type. + */ + +elem * el_typesize(type *t) +{ +#if MARS + assert(0); + return NULL; +#else + assert(PARSER); + type_debug(t); + if (CPP && tybasic(t->Tty) == TYstruct && t->Tflags & TFsizeunknown) + { elem *e; + + symbol_debug(t->Ttag); + e = el_calloc(); + e->Eoper = OPsizeof; + e->EV.sp.Vsym = t->Ttag; + e->ET = tssize; + e->ET->Tcount++; + type_debug(tssize); + elem_debug(e); + return e; + } + else if (tybasic(t->Tty) == TYarray && type_isvla(t)) + { + type *troot = type_arrayroot(t); + elem *en; + + en = el_nelems(t); + return el_bint(OPmul, en->ET, en, el_typesize(troot)); + } + else + return el_longt(tssize,type_size(t)); +#endif +} + +/***************************** + * Return an elem that evaluates to the number of elems in a type + * (if it is an array). Returns NULL if t is not an array. + */ + +#if SCPP +elem * el_nelems(type *t) +{ elem *enelems; + + assert(PARSER); + type_debug(t); + if (tybasic(t->Tty) == TYarray) + { type *ts = tssize; + + enelems = el_longt(ts, 1); + do + { + if (t->Tflags & TFsizeunknown || + (t->Tflags & TFvla && !t->Tel)) + { synerr(EM_unknown_size,"array"); // size of array is unknown + t->Tflags &= ~TFsizeunknown; + } + else if (t->Tflags & TFvla) + { + enelems = el_bint(OPmul, ts, enelems, el_copytree(t->Tel)); + } + else if (enelems->Eoper == OPconst) + { enelems->EV.Vllong *= t->Tdim; + type_chksize(enelems->EV.Vllong); + } + else + enelems = el_bint(OPmul, enelems->ET, enelems, el_longt(ts, t->Tdim)); + t = t->Tnext; + } while (tybasic(t->Tty) == TYarray); + } + else + enelems = NULL; + return enelems; +} +#endif + +/************************************ + * Return != 0 if function has any side effects. + */ + +#if MARS + +int el_funcsideeff(elem *e) +{ Symbol *s; + + if (e->Eoper == OPvar && + tyfunc((s = e->EV.sp.Vsym)->Stype->Tty) && + ((s->Sfunc && s->Sfunc->Fflags3 & Fnosideeff) || s == funcsym_p) + ) + return 0; + return 1; // assume it does have side effects +} + +#endif + +/**************************** + * Return != 0 if elem has any side effects. + */ + +int el_sideeffect(elem *e) +{ int op; + + assert(e); + op = e->Eoper; + assert(op < OPMAX); + elem_debug(e); + return typemask(e) & mTYvolatile || + OTsideff(op) || + (OTunary(op) && el_sideeffect(e->E1)) || + (OTbinary(op) && (el_sideeffect(e->E1) || + el_sideeffect(e->E2))); +} + +#if TX86 +/****************************** + * Input: + * ea lvalue (might be an OPbit) + * Returns: + * 0 eb has no dependency on ea + * 1 eb might have a dependency on ea + * 2 eb definitely depends on ea + */ + +int el_depends(elem *ea,elem *eb) +{ + L1: + elem_debug(ea); + elem_debug(eb); + switch (ea->Eoper) + { + case OPbit: + ea = ea->E1; + goto L1; + case OPvar: + case OPind: + break; + default: + assert(0); + } + switch (eb->Eoper) + { + case OPconst: + case OPrelconst: + case OPstring: +#if SCPP + case OPsizeof: +#endif + goto Lnodep; + case OPvar: + if (ea->Eoper == OPvar && ea->EV.sp.Vsym != eb->EV.sp.Vsym) + goto Lnodep; + break; + default: + break; // this could use improvement + } + return 1; + +Lnodep: + return 0; +} +#endif + +/*************************************** + * Allocate localgot symbol. + */ + +symbol *el_alloc_localgot() +{ +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + /* Since localgot is a local variable to each function, + * localgot must be set back to NULL + * at the start of code gen for each function. + */ + if (I32 && !localgot) + { + //printf("el_alloc_localgot()\n"); + char name[15]; + static int tmpnum; + sprintf(name, "_LOCALGOT%d", tmpnum++); + localgot = symbol_name(name, SCtmp, type_fake(TYnptr)); + symbol_add(localgot); + localgot->Sfl = FLtmp; + localgot->Sflags = SFLfree | SFLunambig | GTregcand; + } + return localgot; +#else + return NULL; +#endif +} + + +/************************** + * Make an elem out of a symbol, PIC style. + */ + +#if TARGET_OSX + +elem *el_picvar(symbol *s) +{ elem *e; + int x; + + //printf("el_picvar(s = '%s')", s->Sident); printf(" Sclass = "); WRclass((enum SC) s->Sclass); printf("\n"); + //symbol_print(s); + symbol_debug(s); + type_debug(s->Stype); + e = el_calloc(); + e->Eoper = OPvar; + e->EV.sp.Vsym = s; + e->Ety = s->ty(); + + switch (s->Sclass) + { + case SCstatic: + case SClocstat: + x = 0; + goto case_got; + + case SCcomdat: + case SCcomdef: + if (0 && I64) + { + x = 0; + goto case_got; + } + case SCglobal: + case SCextern: +#if 0 + if (s->Stype->Tty & mTYthread) + x = 0; + else +#endif + x = 1; + case_got: + { + int op = e->Eoper; + tym_t tym = e->Ety; + e->Eoper = OPrelconst; + e->Ety = TYnptr; + if (I32) + e = el_bin(OPadd, TYnptr, e, el_var(el_alloc_localgot())); +#if 1 + if (s->Stype->Tty & mTYthread) + { + if (!tls_get_addr_sym) + { + /* void *___tls_get_addr(void *ptr); + * Parameter ptr is passed in EAX, matching TYjfunc calling convention. + */ + tls_get_addr_sym = symbol_name("___tls_get_addr",SCglobal,type_fake(TYjfunc)); + symbol_keep(tls_get_addr_sym); + slist_add(tls_get_addr_sym); + } + if (x == 1) + e = el_una(OPind, TYnptr, e); + e = el_bin(OPcallns, TYnptr, el_var(tls_get_addr_sym), e); + if (op == OPvar) + e = el_una(OPind, TYnptr, e); + } + else +#endif + { + switch (op * 2 + x) + { + case OPvar * 2 + 1: + e = el_una(OPind, TYnptr, e); + e = el_una(OPind, TYnptr, e); + break; + case OPvar * 2 + 0: + case OPrelconst * 2 + 1: + e = el_una(OPind, TYnptr, e); + break; + case OPrelconst * 2 + 0: + break; + default: + assert(0); + break; + } + } + e->Ety = tym; + break; + } + default: + break; + } + return e; +} +#endif +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + +elem *el_picvar(symbol *s) +{ elem *e; + int x; + + //printf("el_picvar(s = '%s')\n", s->Sident); + symbol_debug(s); + type_debug(s->Stype); + e = el_calloc(); + e->Eoper = OPvar; + e->EV.sp.Vsym = s; + e->Ety = s->ty(); + + /* For 32 bit: + * CALL __i686.get_pc_thunk.bx@PC32 + * ADD EBX,offset _GLOBAL_OFFSET_TABLE_@GOTPC[2] + * Generate for var locals: + * MOV reg,s@GOTOFF[014h][EBX] + * For var globals: + * MOV EAX,s@GOT32[EBX] + * MOV reg,[EAX] + * For TLS var locals and globals: + * MOV EAX,s@TLS_GD[EBX] + * CALL ___tls_get_addr@PLT32 + * MOV reg,[EAX] + ***************************************** + * Generate for var locals: + * MOV reg,s@PC32[RIP] + * For var globals: + * MOV RAX,s@GOTPCREL[RIP] + * MOV reg,[RAX] + * For TLS var locals and globals: + * 0x66 + * LEA DI,s@TLSGD[RIP] + * 0x66 + * 0x66 + * 0x48 (REX | REX_W) + * CALL __tls_get_addr@PLT32 + * MOV reg,[RAX] + */ + + if (I64) + { + elfobj_refGOTsym(); + switch (s->Sclass) + { + case SCstatic: + case SClocstat: + x = 0; + goto case_got64; + + case SCcomdat: + case SCcomdef: + case SCglobal: + case SCextern: + x = 1; + case_got64: + { + int op = e->Eoper; + tym_t tym = e->Ety; + e->Ety = TYnptr; + + if (s->Stype->Tty & mTYthread) + { + /* Add "volatile" to prevent e from being common subexpressioned. + * This is so we can preserve the magic sequence of instructions + * that the gnu linker patches: + * lea EDI,x@tlsgd[RIP], call __tls_get_addr@plt + * => + * mov EAX,gs[0], sub EAX,x@tpoff + */ + e->Eoper = OPrelconst; + e->Ety |= mTYvolatile; + if (!tls_get_addr_sym) + { + /* void *__tls_get_addr(void *ptr); + * Parameter ptr is passed in RDI, matching TYnfunc calling convention. + */ + tls_get_addr_sym = symbol_name("__tls_get_addr",SCglobal,type_fake(TYnfunc)); + symbol_keep(tls_get_addr_sym); + } + e = el_bin(OPcall, TYnptr, el_var(tls_get_addr_sym), e); + } + + switch (op * 2 + x) + { + case OPvar * 2 + 1: + e = el_una(OPind, TYnptr, e); + break; + case OPvar * 2 + 0: + case OPrelconst * 2 + 1: + break; + case OPrelconst * 2 + 0: + e = el_una(OPaddr, TYnptr, e); + break; + default: + assert(0); + break; + } + e->Ety = tym; + break; + } + default: + break; + } + } + else + switch (s->Sclass) + { + /* local (and thread) symbols get only one level of indirection; + * all globally known symbols get two. + */ + case SCstatic: + case SClocstat: + x = 0; + goto case_got; + + case SCcomdat: + case SCcomdef: + case SCglobal: + case SCextern: + if (s->Stype->Tty & mTYthread) + x = 0; + else + x = 1; + case_got: + { + int op = e->Eoper; + tym_t tym = e->Ety; + e->Eoper = OPrelconst; + e->Ety = TYnptr; + e = el_bin(OPadd, TYnptr, e, el_var(el_alloc_localgot())); + + if (s->Stype->Tty & mTYthread) + { + /* Add "volatile" to prevent e from being common subexpressioned. + * This is so we can preserve the magic sequence of instructions + * that the gnu linker patches: + * lea EAX,x@tlsgd[EBX], call __tls_get_addr@plt + * => + * mov EAX,gs[0], sub EAX,x@tpoff + * elf32-i386.c + */ + e->Ety |= mTYvolatile; + if (!tls_get_addr_sym) + { + /* void *___tls_get_addr(void *ptr); + * Parameter ptr is passed in EAX, matching TYjfunc calling convention. + */ + tls_get_addr_sym = symbol_name("___tls_get_addr",SCglobal,type_fake(TYjfunc)); + symbol_keep(tls_get_addr_sym); + } + e = el_bin(OPcall, TYnptr, el_var(tls_get_addr_sym), e); + } + + switch (op * 2 + x) + { + case OPvar * 2 + 1: + e = el_una(OPind, TYnptr, e); + e = el_una(OPind, TYnptr, e); + break; + case OPvar * 2 + 0: + case OPrelconst * 2 + 1: + e = el_una(OPind, TYnptr, e); + break; + case OPrelconst * 2 + 0: + break; + default: + assert(0); + break; + } + e->Ety = tym; + break; + } + default: + break; + } + return e; +} +#endif + +/************************** + * Make an elem out of a symbol. + */ + +#if MARS +elem * el_var(symbol *s) +{ elem *e; + + //printf("el_var(s = '%s')\n", s->Sident); + //printf("%x\n", s->Stype->Tty); +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + // OSX is currently always pic + if (config.flags3 & CFG3pic && +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + (!(s->Stype->Tty & mTYthread) || I64) && +#endif + !tyfunc(s->ty())) + // Position Independent Code + return el_picvar(s); +#endif +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (config.flags3 & CFG3pic && tyfunc(s->ty())) + { + switch (s->Sclass) + { + case SCcomdat: + case SCcomdef: + case SCglobal: + case SCextern: + el_alloc_localgot(); + break; + } + } +#endif + symbol_debug(s); + type_debug(s->Stype); + e = el_calloc(); + e->Eoper = OPvar; + e->EV.sp.Vsym = s; + type_debug(s->Stype); + e->Ety = s->ty(); + if (s->Stype->Tty & mTYthread) + { + //printf("thread local %s\n", s->Sident); +#if TARGET_OSX + ; +#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + /* For 32 bit: + * Generate for var locals: + * MOV reg,GS:[00000000] // add GS: override in back end + * ADD reg, offset s@TLS_LE + * e => *(&s + *(GS:0)) + * For var globals: + * MOV reg,GS:[00000000] + * ADD reg, s@TLS_IE + * e => *(s + *(GS:0)) + * note different fixup + ***************************************** + * For 64 bit: + * Generate for var locals: + * MOV reg,FS:s@TPOFF32 + * For var globals: + * MOV RAX,s@GOTTPOFF[RIP] + * MOV reg,FS:[RAX] + * + * For address of locals: + * MOV RAX,FS:[00] + * LEA reg,s@TPOFF32[RAX] + * e => &s + *(FS:0) + * For address of globals: + * MOV reg,FS:[00] + * MOV RAX,s@GOTTPOFF[RIP] + * ADD reg,RAX + * e => s + *(FS:0) + * This leaves us with a problem, as the 'var' version cannot simply have + * its address taken, as what is the address of FS:s ? The (not so efficient) + * solution is to just use the second address form, and * it. + * Turns out that is identical to the 32 bit version, except GS => FS and the + * fixups are different. + * In the future, we should figure out a way to optimize to the 'var' version. + */ + if (I64) + elfobj_refGOTsym(); + elem *e1 = el_calloc(); + e1->EV.sp.Vsym = s; + if (s->Sclass == SCstatic || s->Sclass == SClocstat) + { e1->Eoper = OPrelconst; + e1->Ety = TYnptr; + } + else + { + e1->Eoper = OPvar; + e1->Ety = TYnptr; + } + + /* Fake GS:[0000] as a load of _tls_array, and then in the back end recognize + * the fake and rewrite it as GS:[0000] (or FS:[0000] for I64), because there is + * no way to represent segment overrides in the elem nodes. + */ + elem *e2 = el_calloc(); + e2->Eoper = OPvar; + e2->EV.sp.Vsym = rtlsym[RTLSYM_TLS_ARRAY]; + e2->Ety = e2->EV.sp.Vsym->ty(); + + e->Eoper = OPind; + e->E1 = el_bin(OPadd,e1->Ety,e2,e1); + e->E2 = NULL; +#else + /* + mov EAX,FS:__tls_array + mov ECX,__tls_index + mov EAX,[ECX*4][EAX] + inc dword ptr _t[EAX] + + e => *(&s + *(FS:_tls_array + _tls_index * 4)) + + If this is an executable app, not a dll, _tls_index + can be assumed to be 0. + */ + elem *e1,*e2,*ea; + + e1 = el_calloc(); + e1->Eoper = OPrelconst; + e1->EV.sp.Vsym = s; + e1->Ety = TYnptr; + + if (config.wflags & WFexe) + { + // e => *(&s + *(FS:_tls_array)) + e2 = el_var(rtlsym[RTLSYM_TLS_ARRAY]); + } + else + { + e2 = el_bin(OPmul,TYint,el_var(rtlsym[RTLSYM_TLS_INDEX]),el_long(TYint,4)); + ea = el_var(rtlsym[RTLSYM_TLS_ARRAY]); + e2 = el_bin(OPadd,ea->Ety,ea,e2); + } + e2 = el_una(OPind,TYint,e2); + + e->Eoper = OPind; + e->E1 = el_bin(OPadd,e1->Ety,e1,e2); + e->E2 = NULL; +#endif + } + return e; +} +#elif SCPP +elem * el_var(symbol *s) +{ elem *e; + + //printf("el_var(s = '%s')\n", s->Sident); +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (config.flags3 & CFG3pic && !tyfunc(s->ty())) + return el_picvar(s); +#endif + symbol_debug(s); + type_debug(s->Stype); + e = el_calloc(); + e->Eoper = OPvar; + e->EV.sp.Vsym = s; + if (SCPP && PARSER) +#if TX86 && TARGET_WINDOS + { type *t = s->Stype; + + type_debug(t); + e->ET = t; + t->Tcount++; + switch (t->Tty & (mTYimport | mTYthread)) + { case mTYimport: + obj_import(e); + break; + case mTYthread: + /* + mov EAX,FS:__tls_array + mov ECX,__tls_index + mov EAX,[ECX*4][EAX] + inc dword ptr _t[EAX] + + e => *(&s + *(FS:_tls_array + _tls_index * 4)) + */ +#if MARS + assert(0); +#else + { elem *e1,*e2,*ea; + + e1 = el_calloc(); + e1->Eoper = OPrelconst; + e1->EV.sp.Vsym = s; + e1->ET = newpointer(s->Stype); + e1->ET->Tcount++; + + e2 = el_bint(OPmul,tsint,el_var(rtlsym[RTLSYM_TLS_INDEX]),el_longt(tsint,4)); + ea = el_var(rtlsym[RTLSYM_TLS_ARRAY]); + e2 = el_bint(OPadd,ea->ET,ea,e2); + e2 = el_unat(OPind,tsint,e2); + + e->Eoper = OPind; + e->E1 = el_bint(OPadd,e1->ET,e1,e2); + e->E2 = NULL; + } +#endif + break; + case mTYthread | mTYimport: + assert(SCPP); +#if SCPP + tx86err(EM_thread_and_dllimport,s->Sident); // can't be both thread and import +#endif + break; + } + } +#else + { type_debug(s->Stype); + e->ET = s->Stype; + e->ET->Tcount++; + } +#endif + else + e->Ety = s->ty(); + return e; +} +#endif + +/************************** + * Make a pointer to an elem out of a symbol. + */ + +elem * el_ptr(symbol *s) +{ + elem *e; + + //printf("el_ptr(s = '%s')\n", s->Sident); + //printf("el_ptr\n"); + symbol_debug(s); + type_debug(s->Stype); +#if TARGET_OSX + if (config.flags3 & CFG3pic && tyfunc(s->ty()) && I32) + { + /* Cannot access address of code from code. + * Instead, create a data variable, put the address of the + * code in that data variable, and return the elem for + * that data variable. + */ + symbol *sd = symboldata(Doffset, TYnptr); + Doffset += reftoident(DATA, Doffset, s, 0, CFoff); + e = el_picvar(sd); + return e; + } +#endif +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (config.flags3 & CFG3pic && + tyfunc(s->ty())) + e = el_picvar(s); + else +#endif + e = el_var(s); +#if SCPP + if (PARSER) + { type_debug(e->ET); + e = el_unat(OPaddr,type_ptr(e,e->ET),e); + } + else +#endif + if (e->Eoper == OPvar) + { + e->Ety = TYnptr; + e->Eoper = OPrelconst; + } + else + { e = el_una(OPaddr, TYnptr, e); + e = doptelem(e, GOALvalue | GOALflags); + } + return e; +} + +/************************** + * Make a pointer to an elem out of a symbol at offset. + */ + +#if SCPP + +elem * el_ptr_offset(symbol *s,targ_size_t offset) +{ elem *e; + elem *e1; + + e = el_ptr(s); /* e is an elem which is a pointer to s */ + e1 = e->E1; + if (e1->Eoper == OPvar) + ; + // The following case happens if symbol s is in thread local storage + else if (e1->Eoper == OPind && + e1->E1->Eoper == OPadd && + e1->E1->E1->Eoper == OPrelconst) + e1 = e1->E1->E1; + else + assert(0); + assert(e1->EV.sp.Vsym == s); + e1->EV.sp.Voffset = offset; + return e; +} + +#endif + +/************************* + * Returns: + * !=0 elem evaluates right-to-left + * 0 elem evaluates left-to-right + */ + +HINT ERTOL(elem *e) +{ + elem_debug(e); + assert(!PARSER); +#if TX86 + return OTrtol(e->Eoper) && + (!OTopeq(e->Eoper) || config.inline8087 || !tyfloating(e->Ety)); +#else + return OTrtol(e->Eoper); +#endif +} + +/******************************** + * Return !=0 if expression never returns. + * Does not detect all cases, errs on the side of saying it returns. + */ + +int el_noreturn(elem *e) +{ int result = 0; + + while (1) + { elem_debug(e); + switch (e->Eoper) + { case OPcomma: + if (result |= el_noreturn(e->E1)) + break; + e = e->E2; + continue; + + case OPcall: + case OPucall: + e = e->E1; + if (e->Eoper == OPvar && e->EV.sp.Vsym->Sflags & SFLexit) + result = 1; + break; + + case OPhalt: + result = 1; + break; + + default: + break; + } + break; + } + return result; +} + +/******************************** + * Scan down commas and return the controlling elem. + */ + +elem *el_scancommas(elem *e) +{ + while (e->Eoper == OPcomma) + e = e->E2; + return e; +} + +/*************************** + * Count number of commas in the expression. + */ + +int el_countCommas(elem *e) +{ int ncommas = 0; + while (1) + { + if (EBIN(e)) + { + ncommas += (e->Eoper == OPcomma) + el_countCommas(e->E2); + } + else if (EUNA(e)) + { + } + else + break; + e = e->E1; + } + return ncommas; +} + +/************************************ + * Convert floating point constant to a read-only symbol. + * Needed iff floating point code can't load immediate constants. + */ + +elem *el_convfloat(elem *e) +{ + unsigned char buffer[32]; + +#if TX86 + assert(config.inline8087); + + // Do not convert if the constants can be loaded with the special FPU instructions + if (tycomplex(e->Ety)) + { + if (loadconst(e, 0) && loadconst(e, 1)) + return e; + } + else if (loadconst(e, 0)) + return e; + + changes++; + tym_t ty = e->Ety; + int sz = tysize(ty); + assert(sz <= sizeof(buffer)); + void *p; + switch (tybasic(ty)) + { + case TYfloat: + case TYifloat: + p = &e->EV.Vfloat; + assert(sz == sizeof(e->EV.Vfloat)); + break; + + case TYdouble: + case TYidouble: + case TYdouble_alias: + p = &e->EV.Vdouble; + assert(sz == sizeof(e->EV.Vdouble)); + break; + + case TYldouble: + case TYildouble: + /* The size, alignment, and padding of long doubles may be different + * from host to target + */ + p = buffer; + memset(buffer, 0, sz); // ensure padding is 0 + memcpy(buffer, &e->EV.Vldouble, 10); + break; + + case TYcfloat: + p = &e->EV.Vcfloat; + assert(sz == sizeof(e->EV.Vcfloat)); + break; + + case TYcdouble: + p = &e->EV.Vcdouble; + assert(sz == sizeof(e->EV.Vcdouble)); + break; + + case TYcldouble: + p = buffer; + memset(buffer, 0, sz); + memcpy(buffer, &e->EV.Vcldouble.re, 10); + memcpy(buffer + tysize(TYldouble), &e->EV.Vcldouble.im, 10); + break; + + default: + assert(0); + } + +#if 0 + printf("%gL+%gLi\n", (double)e->EV.Vcldouble.re, (double)e->EV.Vcldouble.im); + printf("el_convfloat() %g %g sz=%d\n", e->EV.Vcdouble.re, e->EV.Vcdouble.im, sz); +printf("el_convfloat(): sz = %d\n", sz); +unsigned short *p = (unsigned short *)&e->EV.Vcldouble; +for (int i = 0; i < sz/2; i++) printf("%04x ", p[i]); +printf("\n"); +#endif + + symbol *s = out_readonly_sym(ty, p, sz); + el_free(e); + e = el_var(s); + e->Ety = ty; + if (e->Eoper == OPvar) + e->Ety |= mTYconst; + //printf("s: %s %d:x%x\n", s->Sident, s->Sseg, s->Soffset); +#endif + return e; +} + +/************************************ + * Convert vector constant to a read-only symbol. + * Needed iff vector code can't load immediate constants. + */ + +elem *el_convxmm(elem *e) +{ + unsigned char buffer[16]; + +#if TX86 + // Do not convert if the constants can be loaded with the special XMM instructions +#if 0 + if (loadconst(e)) + return e; +#endif + + changes++; + tym_t ty = e->Ety; + int sz = tysize(ty); + assert(sz <= sizeof(buffer)); + void *p = &e->EV.Vcent; + +#if 0 +printf("el_convxmm(): sz = %d\n", sz); +for (size i = 0; i < sz; i++) printf("%02x ", ((unsigned char *)p)[i]); +printf("\n"); +#endif + + symbol *s = out_readonly_sym(ty, p, sz); + el_free(e); + e = el_var(s); + e->Ety = ty; + if (e->Eoper == OPvar) + e->Ety |= mTYconst; + //printf("s: %s %d:x%x\n", s->Sident, s->Sseg, s->Soffset); +#endif + return e; +} + +/******************************** + * Convert reference to a string to reference to a symbol + * stored in the static data segment. + */ + +elem *el_convstring(elem *e) +{ + //printf("el_convstring()\n"); + int i; + symbol *s; + char *p; + targ_size_t len; + + assert(!PARSER); + elem_debug(e); + assert(e->Eoper == OPstring); + p = e->EV.ss.Vstring; + e->EV.ss.Vstring = NULL; + len = e->EV.ss.Vstrlen; + +#if TARGET_SEGMENTED + // Handle strings that go into the code segment + if (tybasic(e->Ety) == TYcptr || + (tyfv(e->Ety) && config.flags3 & CFG3strcod)) + { + assert(OMFOBJ); // option not done yet for others + s = symbol_generate(SCstatic, type_fake(mTYcs | e->Ety)); + s->Sfl = FLcsdata; + s->Soffset = Coffset; + s->Sseg = cseg; + symbol_keep(s); + if (!eecontext.EEcompile || eecontext.EEin) + { obj_bytes(cseg,Coffset,len,p); + Coffset += len; + } + mem_free(p); + goto L1; + } +#endif + + if (eecontext.EEin) // if compiling debugger expression + { + s = out_readonly_sym(e->Ety, p, len); + mem_free(p); + goto L1; + } + + // See if e is already in the string table + for (i = 0; i < arraysize(stable); i++) + { if (stable[i].len == len && + memcmp(stable[i].p,p,len) == 0) + { + // Replace e with that symbol + MEM_PH_FREE(p); + s = stable[i].sym; + goto L1; + } + } + + // Replace string with a symbol that refers to that string + // in the DATA segment + + if (eecontext.EEcompile) + s = symboldata(Doffset,e->Ety); + else + s = out_readonly_sym(e->Ety,p,len); + + // Remember the string for possible reuse later + //dbg_printf("Adding %d, '%s'\n",stable_si,p); + mem_free(stable[stable_si].p); + stable[stable_si].p = p; + stable[stable_si].len = len; + stable[stable_si].sym = s; + stable_si = (stable_si + 1) & (arraysize(stable) - 1); + +L1: + // Refer e to the symbol generated + elem *ex = el_ptr(s); + ex->Ety = e->Ety; + if (e->EV.ss.Voffset) + { + if (ex->Eoper == OPrelconst) + ex->EV.sp.Voffset += e->EV.ss.Voffset; + else + ex = el_bin(OPadd, ex->Ety, ex, el_long(TYint, e->EV.ss.Voffset)); + } + el_free(e); + return ex; +} + +/******************************************** + * If e is a long double constant, and it is perfectly representable as a + * double constant, convert it to a double constant. + * Note that this must NOT be done in contexts where there are no further + * operations, since then it could change the type (eg, in the function call + * printf("%La", 2.0L); the 2.0 must stay as a long double). + */ +#if 1 +void shrinkLongDoubleConstantIfPossible(elem *e) +{ + if (e->Eoper == OPconst && e->Ety == TYldouble) + { + /* Check to see if it can be converted into a double (this happens + * when the low bits are all zero, and the exponent is in the + * double range). + * Use 'volatile' to prevent optimizer from folding away the conversions, + * and thereby missing the truncation in the conversion to double. + */ + volatile long double v = e->EV.Vldouble; + volatile double vDouble; + *(&vDouble) = v; + if (v == vDouble) // This will fail if compiler does NaN incorrectly! + { + // Yes, we can do it! + e->EV.Vdouble = vDouble; + e->Ety = TYdouble; + } + } +} +#endif + + +/************************* + * Run through a tree converting it to CODGEN. + */ + +elem *el_convert(elem *e) +{ int op; + + //printf("el_convert(%p)\n", e); + elem_debug(e); + op = e->Eoper; + switch (op) + { + case OPvar: + break; + + case OPconst: +#if TX86 + if (tyvector(e->Ety)) + e = el_convxmm(e); + else if (tyfloating(e->Ety) && config.inline8087) + e = el_convfloat(e); +#endif + break; + + case OPstring: + changes++; + e = el_convstring(e); + break; + + case OPnullptr: + e = el_long(e->Ety, 0); + break; + + case OPmul: + /* special floating-point case: allow x*2 to be x+x + * in this case, we preserve the constant 2. + */ + if (tyreal(e->Ety) && // don't bother with imaginary or complex + e->E2->Eoper == OPconst && el_toldouble(e->E2) == 2.0L) + { + e->E1 = el_convert(e->E1); + /* Don't call el_convert(e->E2), we want it to stay as a constant + * which will be detected by code gen. + */ + break; + } +#if 1 + case OPdiv: + case OPadd: + case OPmin: + // For a*b,a+b,a-b,a/b, if a long double constant is involved, convert it to a double constant. + if (tyreal(e->Ety)) + shrinkLongDoubleConstantIfPossible(e->E1); + if (tyreal(e->Ety)) + shrinkLongDoubleConstantIfPossible(e->E2); + // fall through... +#endif + default: + if (OTbinary(op)) + { + e->E1 = el_convert(e->E1); + e->E2 = el_convert(e->E2); + } + else if (OTunary(op)) + { + e->E1 = el_convert(e->E1); + } + break; + } + return e; +} + + +/************************ + * Make a constant elem. + * ty = type of elem + * *pconst = union of constant data + */ + +elem * el_const(tym_t ty,union eve *pconst) +{ elem *e; + + assert(MARS || !PARSER); + e = el_calloc(); + e->Eoper = OPconst; + e->Ety = ty; + memcpy(&e->EV,pconst,sizeof(e->EV)); + return e; +} + + +/************************** + * Insert constructor information into tree. + * e code to construct the object + * decl VarDeclaration of variable being constructed + */ + +#if MARS +elem *el_dctor(elem *e,void *decl) +{ + elem *ector = el_calloc(); + ector->Eoper = OPdctor; + ector->Ety = TYvoid; + ector->EV.ed.Edecl = decl; + if (e) + e = el_bin(OPinfo,e->Ety,ector,e); + else + /* Remember that a "constructor" may execute no code, hence + * the need for OPinfo if there is code to execute. + */ + e = ector; + return e; +} +#endif + +/************************** + * Insert destructor information into tree. + * e code to destruct the object + * decl VarDeclaration of variable being destructed + * (must match decl for corresponding OPctor) + */ + +#if MARS +elem *el_ddtor(elem *e,void *decl) +{ + /* A destructor always executes code, or we wouldn't need + * eh for it. + * An OPddtor must match 1:1 with an OPdctor + */ + elem *edtor = el_calloc(); + edtor->Eoper = OPddtor; + edtor->Ety = TYvoid; + edtor->EV.ed.Edecl = decl; + edtor->EV.ed.Eleft = e; + return edtor; +} +#endif + +/************************** + * Insert constructor information into tree. + * ector pointer to object being constructed + * e code to construct the object + * sdtor function to destruct the object + */ + +#if SCPP +elem *el_ctor(elem *ector,elem *e,symbol *sdtor) +{ + //printf("el_ctor(ector = %p, e = %p, sdtor = %p)\n", ector, e, sdtor); + //printf("stdor = '%s'\n", cpp_prettyident(sdtor)); + //printf("e:\n"); elem_print(e); + if (ector) + { + if (sdtor) + { + if (sdtor->Sfunc->Fbody) + { + n2_instantiate_memfunc(sdtor); + } + // Causes symbols to be written out prematurely when + // writing precompiled headers. + // Moved to outelem(). + //nwc_mustwrite(sdtor); + } + if (!sdtor || ector->Eoper == OPcall || + (ector->Eoper == OPrelconst && !(sytab[ector->EV.sp.Vsym->Sclass] & SCSS)) +#if TX86 + // Not ambient memory model + || (tyfarfunc(sdtor->ty()) ? !LARGECODE : LARGECODE) +#endif + ) + { + el_free(ector); + } + else + { + ector = el_unat(OPctor,ector->ET,ector); + ector->EV.eop.Edtor = sdtor; + symbol_debug(sdtor); + if (e) + e = el_bint(OPinfo,e->ET,ector,e); + else + e = ector; + } + } + return e; +} +#endif + +/************************** + * Insert destructor information into tree. + * edtor pointer to object being destructed + * e code to do the destruction + */ + +elem *el_dtor(elem *edtor,elem *e) +{ + if (edtor) + { + edtor = el_unat(OPdtor,edtor->ET,edtor); + if (e) + e = el_bint(OPcomma,e->ET,edtor,e); + else + e = edtor; + } + return e; +} + +/********************************** + * Create an elem of the constant 0, of the type t. + */ + +elem *el_zero(type *t) +{ + elem *e; + + assert(PARSER); + + e = el_calloc(); + e->Eoper = OPconst; + e->ET = t; + if (t) + { + type_debug(t); + e->ET->Tcount++; + } + return(e); +} + +/******************* + * Find and return pointer to parent of e starting at *pe. + * Return NULL if can't find it. + */ + +elem ** el_parent(elem *e,elem **pe) +{ + assert(e && pe && *pe); + elem_debug(e); + elem_debug(*pe); + if (e == *pe) + return pe; + else if (OTunary((*pe)->Eoper)) + return el_parent(e,&((*pe)->E1)); + else if (OTbinary((*pe)->Eoper)) + { elem **pe2; + + return ((pe2 = el_parent(e,&((*pe)->E1))) != 0) ? pe2 + : el_parent(e,&((*pe)->E2)); + } + else + return NULL; +} + +/******************************* + * Return !=0 if trees match. + */ + +static int gmatch2; // kludge for el_match2() + +int el_match(elem *n1,elem *n2) +{ unsigned op; + tym_t tym,tym2; + +L1: + if (n1 == n2) + return TRUE; + if (!n1 || !n2) + goto nomatch; + elem_debug(n1); + elem_debug(n2); + + if ((op = n1->Eoper) != n2->Eoper) + goto nomatch; + + if ((tym = typemask(n1)) != (tym2 = typemask(n2))) + { +#if TX86 + if ((tym & ~mTYbasic) != (tym2 & ~mTYbasic)) +#else + if ((tym & ~mTYbasic & ~mTYMAN) != (tym2 & ~mTYbasic & ~mTYMAN)) +#endif + { + if (!(gmatch2 & 2)) + goto nomatch; + } + tym = tybasic(tym); + tym2 = tybasic(tym2); + if (tyequiv[tym] != tyequiv[tym2] && + !((gmatch2 & 8) && touns(tym) == touns(tym2)) + ) + goto nomatch; + gmatch2 &= ~8; + } + + if (OTunary(op)) + { + L2: + if (PARSER) + { + n1 = n1->E1; + n2 = n2->E1; + assert(n1 && n2); + goto L1; + } + else if (OPTIMIZER) + { + if (op == OPstrpar || op == OPstrctor) + { if (/*n1->Enumbytes != n2->Enumbytes ||*/ n1->ET != n2->ET) + goto nomatch; + } + n1 = n1->E1; + n2 = n2->E1; + assert(n1 && n2); + goto L1; + } + else + { + if (n1->E1 == n2->E1) + goto ismatch; + n1 = n1->E1; + n2 = n2->E1; + assert(n1 && n2); + goto L1; + } + } + else if (OTbinary(op)) + { + if (!PARSER) + { + if (op == OPstreq) + { if (/*n1->Enumbytes != n2->Enumbytes ||*/ n1->ET != n2->ET) + goto nomatch; + } + } + if (el_match(n1->E2,n2->E2)) + { + goto L2; // check left tree + } + goto nomatch; + } + else /* leaf elem */ + { unsigned n; + + switch (op) + { + case OPconst: + if (gmatch2 & 1) + break; + Lagain: + switch (tybasic(tym)) + { + case TYshort: + case TYwchar_t: + case TYushort: + case TYchar16: + case_short: + if (n1->EV.Vshort != n2->EV.Vshort) + goto nomatch; + break; + case TYlong: + case TYulong: + case TYdchar: + case_long: + if (n1->EV.Vlong != n2->EV.Vlong) + goto nomatch; + break; + case TYllong: + case TYullong: + case_llong: + if (n1->EV.Vllong != n2->EV.Vllong) + goto nomatch; + break; + case TYcent: + case TYucent: + if (n1->EV.Vcent.lsw != n2->EV.Vcent.lsw || + n1->EV.Vcent.msw != n2->EV.Vcent.msw) + goto nomatch; + break; + case TYenum: + if (PARSER) + { tym = n1->ET->Tnext->Tty; + goto Lagain; + } + case TYint: + case TYuint: + if (intsize == SHORTSIZE) + goto case_short; + else + goto case_long; + +#if TX86 +#if JHANDLE + case TYjhandle: +#endif + case TYnullptr: + case TYnptr: +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: +#endif + if (NPTRSIZE == SHORTSIZE) + goto case_short; + else if (NPTRSIZE == LONGSIZE) + goto case_long; + else + { assert(NPTRSIZE == LLONGSIZE); + goto case_llong; + } +#endif + + case TYbool: + case TYchar: + case TYuchar: + case TYschar: + if (n1->EV.Vschar != n2->EV.Vschar) + goto nomatch; + break; +#if TARGET_SEGMENTED + case TYfptr: + case TYhptr: + case TYvptr: + + /* Far pointers on the 386 are longer than + any integral type... + */ + if (memcmp(&n1->EV,&n2->EV,tysize[tybasic(tym)])) + goto nomatch; + break; +#endif + /* Compare bit patterns w/o worrying about + exceptions, unordered comparisons, etc. + */ + case TYfloat: + case TYifloat: + if (memcmp(&n1->EV,&n2->EV,sizeof(n1->EV.Vfloat))) + goto nomatch; + break; + + case TYdouble: + case TYdouble_alias: + case TYidouble: + if (memcmp(&n1->EV,&n2->EV,sizeof(n1->EV.Vdouble))) + goto nomatch; + break; + + case TYldouble: + case TYildouble: +#if LNGDBLSIZE > 10 + /* sizeof is 12, but actual size is 10 */ + if (memcmp(&n1->EV,&n2->EV,10)) +#else + if (memcmp(&n1->EV,&n2->EV,sizeof(n1->EV.Vldouble))) +#endif + goto nomatch; + break; + + case TYcfloat: + if (memcmp(&n1->EV,&n2->EV,sizeof(n1->EV.Vcfloat))) + goto nomatch; + break; + + case TYcdouble: + if (memcmp(&n1->EV,&n2->EV,sizeof(n1->EV.Vcdouble))) + goto nomatch; + break; + + case TYcldouble: +#if LNGDBLSIZE > 10 + /* sizeof is 12, but actual size of each part is 10 */ + if (memcmp(&n1->EV,&n2->EV,10) || + memcmp(&n1->EV.Vldouble + 1, &n2->EV.Vldouble + 1, 10)) +#else + if (memcmp(&n1->EV,&n2->EV,sizeof(n1->EV.Vcldouble))) +#endif + goto nomatch; + break; + case TYvoid: + break; // voids always match +#if SCPP + case TYident: + assert(errcnt); + goto nomatch; +#endif + default: +#ifdef DEBUG + elem_print(n1); +#endif + assert(0); + } + break; + case OPrelconst: + case OPvar: +#if SCPP + case OPsizeof: +#endif + symbol_debug(n1->EV.sp.Vsym); + symbol_debug(n2->EV.sp.Vsym); + if (n1->EV.sp.Voffset != n2->EV.sp.Voffset) + goto nomatch; +#if SCPP + if (gmatch2 & 4) + { +#if 0 + printf("------- symbols ---------\n"); + symbol_print(n1->EV.sp.Vsym); + symbol_print(n2->EV.sp.Vsym); + printf("\n"); +#endif + if (/*strcmp(n1->EV.sp.Vsym->Sident, n2->EV.sp.Vsym->Sident) &&*/ + n1->EV.sp.Vsym != n2->EV.sp.Vsym && + (!n1->EV.sp.Vsym->Ssequence || n1->EV.sp.Vsym->Ssequence != n2->EV.sp.Vsym->Ssequence)) + goto nomatch; + } + else +#endif + { + if (n1->EV.sp.Vsym != n2->EV.sp.Vsym) + goto nomatch; + } + break; + case OPasm: + case OPstring: + case OPhstring: + if (n1->EV.ss.Vstrlen != (n = n2->EV.ss.Vstrlen) || + n1->EV.ss.Voffset != n2->EV.ss.Voffset || + memcmp(n1->EV.ss.Vstring,n2->EV.ss.Vstring,n)) + goto nomatch; /* check bytes in the string */ + break; + case OPstrthis: + case OPframeptr: + case OPhalt: + case OPgot: + break; +#if SCPP + case OPmark: + break; +#endif + default: +#ifdef DEBUG + WROP(op); +#endif + assert(0); + } +ismatch: + return TRUE; + } + assert(0); + /* NOTREACHED */ + +nomatch: + return FALSE; +} + +/********************************* + * Kludge on el_match(). Same, but ignore differences in OPconst. + */ + +int el_match2(elem *n1,elem *n2) +{ int result; + + gmatch2 = 1; + result = el_match(n1,n2); + gmatch2 = 0; + return result; +} + +/********************************* + * Kludge on el_match(). Same, but ignore differences in type modifiers. + */ + +int el_match3(elem *n1,elem *n2) +{ int result; + + gmatch2 = 2; + result = el_match(n1,n2); + gmatch2 = 0; + return result; +} + +/********************************* + * Kludge on el_match(). Same, but ignore differences in spelling of var's. + */ + +int el_match4(elem *n1,elem *n2) +{ int result; + + gmatch2 = 2|4; + result = el_match(n1,n2); + gmatch2 = 0; + return result; +} + +/********************************* + * Kludge on el_match(). Same, but regard signed/unsigned as equivalent. + */ + +int el_match5(elem *n1,elem *n2) +{ int result; + + gmatch2 = 8; + result = el_match(n1,n2); + gmatch2 = 0; + return result; +} + +/****************************** + * Extract long value from constant parser elem. + */ + +targ_llong el_tolongt(elem *e) +{ targ_llong result; + char parsersave = PARSER; + + PARSER = 1; + result = el_tolong(e); + PARSER = parsersave; + return result; +} + +/****************************** + * Extract long value from constant elem. + */ + +targ_llong el_tolong(elem *e) +{ targ_llong result; + tym_t ty; + + elem_debug(e); +#if SCPP + if (e->Eoper == OPsizeof) + { + e->Eoper = OPconst; + e->EV.Vllong = type_size(e->EV.sp.Vsym->Stype); + } +#endif +#ifdef DEBUG + if (e->Eoper != OPconst) + elem_print(e); +#endif + assert(e->Eoper == OPconst); + ty = tybasic(typemask(e)); +L1: + switch (ty) + { + case TYchar: + if (config.flags & CFGuchar) + goto Uchar; + /* FALL-THROUGH */ + case TYschar: + result = e->EV.Vschar; + break; + case TYuchar: + case TYbool: + Uchar: + result = e->EV.Vuchar; + break; + case TYshort: + Ishort: + result = e->EV.Vshort; + break; + case TYushort: + case TYwchar_t: + case TYchar16: + Ushort: + result = e->EV.Vushort; + break; +#if SCPP && TX86 + case TYenum: + assert(PARSER); + ty = e->ET->Tnext->Tty; + goto L1; +#endif + +#if JHANDLE + case TYjhandle: +#endif +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: +#endif + case TYnptr: + case TYnullptr: + if (NPTRSIZE == SHORTSIZE) + goto Ushort; + if (NPTRSIZE == LONGSIZE) + goto Ulong; + if (NPTRSIZE == LLONGSIZE) + goto Ullong; + assert(0); + + case TYuint: + if (intsize == SHORTSIZE) + goto Ushort; + goto Ulong; + + case TYulong: + case TYdchar: +#if TARGET_SEGMENTED + case TYfptr: + case TYhptr: + case TYvptr: +#endif + case TYvoid: /* some odd cases */ + Ulong: + result = e->EV.Vulong; + break; + + case TYint: + if (intsize == SHORTSIZE) + goto Ishort; + goto Ilong; + + case TYlong: + Ilong: + result = e->EV.Vlong; + break; + + case TYllong: + case TYullong: + Ullong: + result = e->EV.Vullong; + break; + case TYdouble_alias: + case TYldouble: + case TYdouble: + case TYfloat: + case TYildouble: + case TYidouble: + case TYifloat: + case TYcldouble: + case TYcdouble: + case TYcfloat: + result = (targ_llong)el_toldouble(e); + break; + +#if SCPP + case TYmemptr: + ty = tybasic(tym_conv(e->ET)); + goto L1; +#endif + + case TYcent: + case TYucent: + goto Ullong; // should do better than this when actually doing arithmetic on cents + + default: +#if SCPP + // Can happen as result of syntax errors + assert(errcnt); +#else +#ifdef DEBUG + elem_print(e); +#endif + assert(0); +#endif + } + return result; +} + +/*********************************** + * Determine if constant e is all ones or all zeros. + * Input: + * bit 0: all zeros + * 1: 1 + * -1: all ones + */ + +int el_allbits(elem *e,int bit) +{ targ_llong value; + + elem_debug(e); + assert(e->Eoper == OPconst); + value = e->EV.Vullong; + switch (tysize(e->Ety)) + { + case 1: value = (signed char) value; + break; + case 2: value = (short) value; + break; + case 4: value = (int) value; + break; + case 8: break; + default: + assert(0); + } + if (bit == -1) + value++; + else if (bit == 1) + value--; + return value == 0; +} + +/******************************************** + * Determine if constant e is a 32 bit or less value, or is a 32 bit value sign extended to 64 bits. + */ + +int el_signx32(elem *e) +{ + elem_debug(e); + assert(e->Eoper == OPconst); + if (tysize(e->Ety) == 8) + { + if (e->EV.Vullong != (int)e->EV.Vullong) + return FALSE; + } + return TRUE; +} + +/****************************** + * Extract long double value from constant elem. + * Silently ignore types which are not floating point values. + */ + +targ_ldouble el_toldouble(elem *e) +{ targ_ldouble result; + + elem_debug(e); + assert(cnst(e)); +#if TX86 + switch (tybasic(typemask(e))) + { + case TYfloat: + case TYifloat: + result = e->EV.Vfloat; + break; + case TYdouble: + case TYidouble: + case TYdouble_alias: + result = e->EV.Vdouble; + break; + case TYldouble: + case TYildouble: + result = e->EV.Vldouble; + break; + default: + result = 0; + break; + } +#else + switch (tysize[tybasic(typemask(e))]) + { + case FLOATSIZE: // TYfloat + result = e->EV.Vfloat; + break; + case DOUBLESIZE: // TYdouble + result = e->EV.Vdouble; + break; +#if DOUBLESIZE != LNGDBLSIZE + case LNGDBLSIZE: // TYldouble +#ifdef LNGHDBLSIZE + case LNGHDBLSIZE: +#endif + result = e->EV.Vldouble; + break; +#endif + default: + result = 0; + break; + } +#endif + return result; +} + +/******************************** + * Is elem type-dependent or value-dependent? + * Return !=0 if so. + */ + +int el_isdependent(elem *e) +{ + if (type_isdependent(e->ET)) + return 1; + while (1) + { + if (e->PEFflags & PEFdependent) + return 1; + if (OTunary(e->Eoper)) + e = e->E1; + else if (OTbinary(e->Eoper)) + { if (el_isdependent(e->E2)) + return 1; + e = e->E1; + } + else + break; + } + return 0; +} + +/**************************************** + * Return alignment size of elem. + */ + +unsigned el_alignsize(elem *e) +{ + tym_t tym = tybasic(e->Ety); + unsigned alignsize = tyalignsize(tym); + if (alignsize == (unsigned)-1) + { + assert(e->ET); + alignsize = type_alignsize(e->ET); + } + return alignsize; +} + +/******************************* + * Check for errors in a tree. + */ + +#ifdef DEBUG + +void el_check(elem *e) +{ + elem_debug(e); + while (1) + { + if (OTunary(e->Eoper)) + e = e->E1; + else if (OTbinary(e->Eoper)) + { el_check(e->E2); + e = e->E1; + } + else + break; + } +} + +#endif + +/******************************* + * Write out expression elem. + */ + +#ifdef DEBUG + +void elem_print(elem *e) +{ static int nestlevel = 0; + int i; + tym_t tym; + + nestlevel++; + for (i = nestlevel; --i;) dbg_printf(" "); + dbg_printf("el:%p ",e); + if (!e) + { dbg_printf("\n"); + goto ret; + } + elem_debug(e); + if (configv.addlinenumbers) + { + e->Esrcpos.print("elem_print"); + } + if (!PARSER) + { dbg_printf("cnt=%d ",e->Ecount); + if (!OPTIMIZER) + dbg_printf("cs=%d ",e->Ecomsub); + } + WROP(e->Eoper); + dbg_printf(" "); + if (SCPP && PARSER) + { + if (e->ET) + { type_debug(e->ET); + if (tybasic(e->ET->Tty) == TYstruct) + dbg_printf("%d ", (int)type_size(e->ET)); + WRTYxx(e->ET->Tty); + } + } + else + { + if ((e->Eoper == OPstrpar || e->Eoper == OPstrctor || e->Eoper == OPstreq) || + e->Ety == TYstruct) + if (e->ET) + dbg_printf("%d ", (int)type_size(e->ET)); + WRTYxx(e->Ety); + } + if (OTunary(e->Eoper)) + { + if (e->E2) + dbg_printf("%p %p\n",e->E1,e->E2); + else + dbg_printf("%p\n",e->E1); + elem_print(e->E1); + } + else if (OTbinary(e->Eoper)) + { + if (!PARSER && e->Eoper == OPstreq) + dbg_printf("bytes=%d ", (int)type_size(e->ET)); + dbg_printf("%p %p\n",e->E1,e->E2); + elem_print(e->E1); + elem_print(e->E2); + } + else + { + switch (e->Eoper) + { + case OPrelconst: + dbg_printf(" %lld+&",(unsigned long long)e->Eoffset); + dbg_printf(" %s",e->EV.sp.Vsym->Sident); + break; + case OPvar: + if (e->Eoffset) + dbg_printf(" %lld+",(unsigned long long)e->Eoffset); + dbg_printf(" %s",e->EV.sp.Vsym->Sident); + break; + case OPasm: + case OPstring: + case OPhstring: + dbg_printf(" '%s',%lld\n",e->EV.ss.Vstring,(unsigned long long)e->EV.ss.Voffset); + break; + case OPconst: + tym = tybasic(typemask(e)); + case_tym: + switch (tym) + { case TYbool: + case TYchar: + case TYschar: + case TYuchar: + dbg_printf("%d ",e->EV.Vuchar); + break; +#if JHANDLE + case TYjhandle: +#endif +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: +#endif + case TYnullptr: + case TYnptr: + if (NPTRSIZE == LONGSIZE) + goto L1; + if (NPTRSIZE == SHORTSIZE) + goto L3; + if (NPTRSIZE == LLONGSIZE) + goto L2; + assert(0); + break; + case TYenum: + if (PARSER) + { tym = e->ET->Tnext->Tty; + goto case_tym; + } + case TYint: + case TYuint: + case TYvoid: /* in case (void)(1) */ +#if TX86 + if (tysize[TYint] == LONGSIZE) + goto L1; +#endif + case TYshort: + case TYwchar_t: + case TYushort: + case TYchar16: + L3: + dbg_printf("%d ",e->EV.Vint); + break; + case TYlong: + case TYulong: + case TYdchar: +#if TARGET_SEGMENTED + case TYfptr: + case TYvptr: + case TYhptr: +#endif + L1: + dbg_printf("%dL ",e->EV.Vlong); + break; + + case TYllong: + L2: + dbg_printf("%lldLL ",e->EV.Vllong); + break; + + case TYullong: + dbg_printf("%lluLL ",e->EV.Vullong); + break; + + case TYcent: + case TYucent: + dbg_printf("%lluLL+%lluLL ", e->EV.Vcent.msw, e->EV.Vcent.lsw); + break; + + case TYfloat: + dbg_printf("%gf ",(double)e->EV.Vfloat); + break; + case TYdouble: + case TYdouble_alias: + dbg_printf("%g ",(double)e->EV.Vdouble); + break; + case TYldouble: + dbg_printf("%Lg ", e->EV.Vldouble); + break; + + case TYifloat: + dbg_printf("%gfi ", (double)e->EV.Vfloat); + break; + + case TYidouble: + dbg_printf("%gi ", (double)e->EV.Vdouble); + break; + + case TYildouble: + dbg_printf("%gLi ", (double)e->EV.Vldouble); + break; + + case TYcfloat: + dbg_printf("%gf+%gfi ", (double)e->EV.Vcfloat.re, (double)e->EV.Vcfloat.im); + break; + + case TYcdouble: + dbg_printf("%g+%gi ", (double)e->EV.Vcdouble.re, (double)e->EV.Vcdouble.im); + break; + + case TYcldouble: + dbg_printf("%gL+%gLi ", (double)e->EV.Vcldouble.re, (double)e->EV.Vcldouble.im); + break; + + case TYfloat4: + case TYdouble2: + case TYschar16: + case TYuchar16: + case TYshort8: + case TYushort8: + case TYlong4: + case TYulong4: + case TYllong2: + case TYullong2: + dbg_printf("%llxLL+%llxLL ", e->EV.Vcent.msw, e->EV.Vcent.lsw); + break; + +#if !MARS + case TYident: + dbg_printf("'%s' ", e->ET->Tident); + break; +#endif + + default: + dbg_printf("Invalid type "); + WRTYxx(typemask(e)); + /*assert(0);*/ + } + break; + default: + break; + } + dbg_printf("\n"); + } +ret: + nestlevel--; +} + +#endif + +/********************************** + * Hydrate an elem. + */ + +#if HYDRATE +void el_hydrate(elem **pe) +{ + elem *e; + + if (!isdehydrated(*pe)) + return; + + assert(PARSER); + e = (elem *) ph_hydrate(pe); + elem_debug(e); +#ifdef DEBUG + if (!(e->Eoper < OPMAX)) + dbg_printf("e = x%lx, e->Eoper = %d\n",e,e->Eoper); +#endif + debug_assert(e->Eoper < OPMAX); + type_hydrate(&e->ET); + if (configv.addlinenumbers) + { filename_translate(&e->Esrcpos); + srcpos_hydrate(&e->Esrcpos); + } + if (EOP(e)) + { el_hydrate(&e->E1); + if (EBIN(e)) + el_hydrate(&e->E2); +#if SCPP + else if (e->Eoper == OPctor) + { symbol_hydrate(&e->EV.eop.Edtor); + symbol_debug(e->EV.eop.Edtor); + } +#endif + } + else + { + switch (e->Eoper) + { case OPstring: + case OPasm: + ph_hydrate(&e->EV.ss.Vstring); + break; + + case OPrelconst: + //if (tybasic(e->ET->Tty) == TYmemptr) + //el_hydrate(&e->EV.sm.ethis); + case OPvar: +#if TX86 + symbol_hydrate(&e->EV.sp.Vsym); +#else + ph_hydrate(&e->EV.sp.Vsym); +#endif + symbol_debug(e->EV.sp.Vsym); + break; + } + } +} +#endif + +/********************************** + * Dehydrate an elem. + */ + +#if DEHYDRATE +void el_dehydrate(elem **pe) +{ elem *e; + + if ((e = *pe) == NULL || isdehydrated(e)) + return; + + assert(PARSER); + elem_debug(e); +#ifdef DEBUG + if (!(e->Eoper < OPMAX)) + dbg_printf("e = x%lx, e->Eoper = %d\n",e,e->Eoper); +#endif + debug_assert(e->Eoper < OPMAX); + ph_dehydrate(pe); +#if DEBUG_XSYMGEN + if (xsym_gen && ph_in_head(e)) + return; +#endif + type_dehydrate(&e->ET); +#if TX86 + if (configv.addlinenumbers) + srcpos_dehydrate(&e->Esrcpos); +#endif + if (EOP(e)) + { el_dehydrate(&e->E1); + if (EBIN(e)) + el_dehydrate(&e->E2); +#if SCPP + else if (e->Eoper == OPctor) + symbol_dehydrate(&e->EV.eop.Edtor); +#endif + } + else + { + switch (e->Eoper) + { case OPstring: + case OPasm: + ph_dehydrate(&e->EV.ss.Vstring); + break; + case OPrelconst: + //if (tybasic(e->ET->Tty) == TYmemptr) + //el_dehydrate(&e->EV.sm.ethis); + case OPvar: +#if TX86 + symbol_dehydrate(&e->EV.sp.Vsym); +#else + ph_dehydrate(&e->EV.sp.Vsym); +#endif + break; + } + } +} +#endif + +#endif /* !SPP */ diff --git a/backend/el.h b/backend/el.h new file mode 100644 index 00000000..40aa3eb2 --- /dev/null +++ b/backend/el.h @@ -0,0 +1,219 @@ +// Copyright (C) 1985-1995 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +/* Routines to handle elems. */ + +#if __SC__ +#pragma once +#endif + +#ifndef EL_H +#define EL_H 1 + +/****************************************** + * Elems: + * Elems are the basic tree element. They can be either + * terminal elems (leaves), unary elems (left subtree exists) + * or binary elems (left and right subtrees exist). + */ + +struct elem +{ +#ifdef DEBUG + unsigned short id; +#define IDelem 0x4C45 // 'EL' +#define elem_debug(e) assert((e)->id == IDelem) +#else +#define elem_debug(e) +#endif + + unsigned char Eoper; // operator (OPxxxx) + unsigned char Ecount; // # of parents of this elem - 1, + // always 0 until CSE elimination is done + unsigned char Eflags; + #define EFLAGS_variadic 1 // variadic function call + + union eve EV; // variants for each type of elem + union + { + // PARSER + struct + { + unsigned PEFflags_; + #define PEFflags _EU._EP.PEFflags_ + #define PEFnotlvalue 1 // although elem may look like + // an lvalue, it isn't + #define PEFtemplate_id 0x10 // symbol is a template-id + #define PEFparentheses 0x20 // expression was within () + #define PEFaddrmem 0x40 // address of member + #define PEFdependent 0x80 // value-dependent + #define PEFmember 0x100 // was a class member access + Symbol *Emember_; // if PEFmember, this is the member + #define Emember _EU._EP.Emember_ + }_EP; + + // OPTIMIZER + struct + { + tym_t Ety_; // data type (TYxxxx) + #define Ety _EU._EO.Ety_ + unsigned Eexp_; // index into expnod[] + #define Eexp _EU._EO.Eexp_ + + // These flags are all temporary markers, used once and then + // thrown away. + unsigned char Nflags_; // NFLxxx + #define Nflags _EU._EO.Nflags_ + #define NFLli 1 // loop invariant + #define NFLnogoal 2 // evaluate elem for side effects only + #define NFLassign 8 // unambiguous assignment elem + #define NFLaecp 0x10 // AE or CP or VBE expression + #define NFLdelcse 0x40 // this is not the generating CSE + #define NFLtouns 0x80 // relational operator was changed from signed to unsigned +#if MARS + unsigned char Ejty_; // original Jupiter/Mars type + #define Ejty _EU._EO.Ejty_ +#endif + }_EO; + + // CODGEN + struct + { + // Ety2: Must be in same position as Ety! + tym_t Ety2_; // data type (TYxxxx) + #define Ety2 _EU._EC.Ety2_ + unsigned char Ecomsub_; // number of remaining references to + // this common subexp (used to determine + // first, intermediate, and last references + // to a CSE) + #define Ecomsub _EU._EC.Ecomsub_ + }_EC; + }_EU; + + struct TYPE *ET; // pointer to type of elem if TYstruct | TYarray + Srcpos Esrcpos; // source file position +}; + +#define typemask(e) ((!MARS && PARSER) ? (e)->ET->Tty : (e)->Ety ) +#define typetym(e) ((e)->ET->Tty) +#define el_fl(e) ((enum FL)((e)->EV.sp.Vsym->Sfl)) +#define Eoffset EV.sp.Voffset +#define Esymnum EV.sp.Vsymnum + +#define list_elem(list) ((elem *) list_ptr(list)) +#define list_setelem(list,ptr) list_ptr(list) = (elem *)(ptr) +#define cnst(e) ((e)->Eoper == OPconst) /* Determine if elem is a constant */ +#define E1 EV.eop.Eleft /* left child */ +#define E2 EV.eop.Eright /* right child */ +#define Erd EV.sp.spu.Erd // reaching definition + +#define el_int(a,b) el_long(a,b) + +typedef elem *elem_p; /* try to reduce the symbol table size */ + +void el_init(void); +void el_reset(void); +void el_term(void); +elem_p el_calloc(void); +void el_free(elem_p); +elem_p el_combine(elem_p ,elem_p); +elem_p el_param(elem_p ,elem_p); +elem_p el_params(elem_p , ...); +elem *el_params(void **args, int length); +elem *el_combines(void **args, int length); +int el_nparams(elem *e); +elem_p el_pair(tym_t, elem_p, elem_p); +void el_copy(elem_p ,elem_p); +elem_p el_alloctmp(tym_t); +elem_p el_selecte1(elem_p); +elem_p el_selecte2(elem_p); +elem_p el_copytree(elem_p); +void el_replace_sym(elem *e,symbol *s1,symbol *s2); +elem_p el_scancommas(elem_p); +int el_countCommas(elem_p); +int el_sideeffect(elem_p); +int el_depends(elem *ea,elem *eb); +#if LONGLONG +targ_llong el_tolongt(elem_p); +targ_llong el_tolong(elem_p); +#else +targ_long el_tolongt(elem_p); +targ_long el_tolong(elem_p); +#endif +int el_allbits(elem_p,int); +int el_signx32(elem_p); +targ_ldouble el_toldouble(elem_p); +void el_toconst(elem_p); +elem_p el_same(elem_p *); +int el_match(elem_p ,elem_p); +int el_match2(elem_p ,elem_p); +int el_match3(elem_p ,elem_p); +int el_match4(elem_p ,elem_p); +int el_match5(elem_p ,elem_p); + +int el_appears(elem *e,symbol *s); +Symbol *el_basesym(elem *e); +int el_anydef(elem *ed, elem *e); +elem_p el_bint(unsigned,type *,elem_p ,elem_p); +elem_p el_unat(unsigned,type *,elem_p); +elem_p el_bin(unsigned,tym_t,elem_p ,elem_p); +elem_p el_una(unsigned,tym_t,elem_p); +#if LONGLONG // DJB +elem_p el_longt(type *,targ_llong); +#else +elem_p el_longt(type *,targ_long); +#endif +symbol *el_alloc_localgot(); +elem_p el_var(symbol *); +elem_p el_settype(elem_p ,type *); +elem_p el_typesize(type *); +elem_p el_ptr(symbol *); +void el_replace_sym(elem *e,symbol *s1,symbol *s2); +elem * el_ptr_offset(symbol *s,targ_size_t offset); +void el_replacesym(elem *,symbol *,symbol *); +elem_p el_nelems(type *); + +#if LONGLONG +elem_p el_long(tym_t,targ_llong); +#else +elem_p el_long(tym_t,targ_long); +#endif + +int ERTOL(elem_p); +int el_noreturn(elem_p); +elem *el_dctor(elem *e,void *decl); +elem *el_ddtor(elem *e,void *decl); +elem *el_ctor(elem *ector,elem *e,symbol *sdtor); +elem *el_dtor(elem *edtor,elem *e); +elem *el_zero(type *t); +elem_p el_const(tym_t,union eve *); +elem_p el_test(tym_t,union eve *); +elem_p * el_parent(elem_p ,elem_p *); + +#ifdef DEBUG +void el_check(elem_p); +#else +#define el_check(e) ((void)0) +#endif + +elem *el_convfloat(elem *); +elem *el_convstring(elem *); +elem *el_convert(elem *e); +int el_isdependent(elem *); +unsigned el_alignsize(elem *); + +void elem_print(elem *); +void el_hydrate(elem **); +void el_dehydrate(elem **); + +#endif + diff --git a/backend/elfobj.c b/backend/elfobj.c new file mode 100644 index 00000000..b3aa5a28 --- /dev/null +++ b/backend/elfobj.c @@ -0,0 +1,3111 @@ +// Copyright (C) ?-1998 by Symantec +// Copyright (C) 2000-2010 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +// Output to ELF object files + +#if SCPP || MARS +#include +#include +#include + +#if __sun&&__SVR4 +#include +#endif + +#include "cc.h" +#include "global.h" +#include "code.h" +#include "type.h" +#include "melf.h" +#include "outbuf.h" +#include "filespec.h" +#include "cv4.h" +#include "cgcv.h" +#include "dt.h" + +#include "aa.h" +#include "tinfo.h" + +#if ELFOBJ + +#include "dwarf.h" + +#include "aa.h" +#include "tinfo.h" + +//#define DEBSYM 0x7E + +static Outbuffer *fobjbuf; + +regm_t BYTEREGS = BYTEREGS_INIT; +regm_t ALLREGS = ALLREGS_INIT; + +static char __file__[] = __FILE__; // for tassert.h +#include "tassert.h" + +#define MATCH_SECTION 1 + +#define DEST_LEN (IDMAX + IDOHD + 1) +char *obj_mangle2(Symbol *s,char *dest); + +#if MARS +// C++ name mangling is handled by front end +#define cpp_mangle(s) ((s)->Sident) +#endif + +/*************************************************** + * Correspondence of relocation types + * 386 32 bit in 64 64 in 64 + * RI_TYPE_SYM32 R_X86_64_32 R_X86_64_64 + * RI_TYPE_GOTOFF R_X86_64_PC32 R_X86_64_ + * RI_TYPE_GOTPC R_X86_64_ R_X86_64_ + * RI_TYPE_GOT32 R_X86_64_ R_X86_64_ + * RI_TYPE_TLS_GD R_X86_64_TLSGD R_X86_64_ + * RI_TYPE_TLS_IE R_X86_64_GOTTPOFF R_X86_64_ + * RI_TYPE_TLS_LE R_X86_64_TPOFF32 R_X86_64_ + * RI_TYPE_PLT32 R_X86_64_PLT32 R_X86_64_ + * RI_TYPE_PC32 R_X86_64_PC32 R_X86_64_ + */ + +/****************************************** + */ + +symbol *GOTsym; // global offset table reference + +symbol *elfobj_getGOTsym() +{ + if (!GOTsym) + { + GOTsym = symbol_name("_GLOBAL_OFFSET_TABLE_",SCglobal,tspvoid); + } + return GOTsym; +} + +void elfobj_refGOTsym() +{ + if (!GOTsym) + { + symbol *s = elfobj_getGOTsym(); + objextern(s); + } +} + +static void objfile_write(FILE *fd, void *buffer, unsigned len); + +STATIC char * objmodtoseg (const char *modname); +STATIC void obj_browse_flush(); +STATIC void objfixupp (struct FIXUP *); +STATIC void ledata_new (int seg,targ_size_t offset); +void obj_tlssections(); + +static IDXSYM elf_addsym(IDXSTR sym, targ_size_t val, unsigned sz, + unsigned typ,unsigned bind,IDXSEC sec); +static long elf_align(FILE *fd, targ_size_t size, long offset); + +// The object file is built is several separate pieces + +// Non-repeatable section types have single output buffers +// Pre-allocated buffers are defined for: +// Section Names string table +// Section Headers table +// Symbol table +// String table +// Notes section +// Comment data + +// Section Names - String table for section names only +static Outbuffer *section_names; +#define SEC_NAMES_INIT 800 +#define SEC_NAMES_INC 400 + +// Hash table for section_names +AArray *section_names_hashtable; + +/* ====================== Cached Strings in section_names ================= */ + +struct TypeInfo_Idxstr : TypeInfo +{ + const char* toString(); + hash_t getHash(void *p); + int equals(void *p1, void *p2); + int compare(void *p1, void *p2); + size_t tsize(); + void swap(void *p1, void *p2); +}; + +TypeInfo_Idxstr ti_idxstr; + +const char* TypeInfo_Idxstr::toString() +{ + return "IDXSTR"; +} + +hash_t TypeInfo_Idxstr::getHash(void *p) +{ + IDXSTR a = *(IDXSTR *)p; + hash_t hash = 0; + for (const char *s = (char *)(section_names->buf + a); + *s; + s++) + { + hash = hash * 11 + *s; + } + return hash; +} + +int TypeInfo_Idxstr::equals(void *p1, void *p2) +{ + IDXSTR a1 = *(IDXSTR*)p1; + IDXSTR a2 = *(IDXSTR*)p2; + const char *s1 = (char *)(section_names->buf + a1); + const char *s2 = (char *)(section_names->buf + a2); + + return strcmp(s1, s2) == 0; +} + +int TypeInfo_Idxstr::compare(void *p1, void *p2) +{ + IDXSTR a1 = *(IDXSTR*)p1; + IDXSTR a2 = *(IDXSTR*)p2; + const char *s1 = (char *)(section_names->buf + a1); + const char *s2 = (char *)(section_names->buf + a2); + + return strcmp(s1, s2); +} + +size_t TypeInfo_Idxstr::tsize() +{ + return sizeof(IDXSTR); +} + +void TypeInfo_Idxstr::swap(void *p1, void *p2) +{ + assert(0); +} + + +/* ======================================================================== */ + +// String Table - String table for all other names +static Outbuffer *symtab_strings; + + +// Section Headers +Outbuffer *SECbuf; // Buffer to build section table in +#define SecHdrTab ((Elf32_Shdr *)SECbuf->buf) +#define GET_SECTION(secidx) (SecHdrTab + secidx) +#define GET_SECTION_NAME(secidx) (section_names->buf + SecHdrTab[secidx].sh_name) + +// The relocation for text and data seems to get lost. +// Try matching the order gcc output them +// This means defining the sections and then removing them if they are +// not used. +static int section_cnt; // Number of sections in table + +#define SHI_TEXT 1 +#define SHI_RELTEXT 2 +#define SHI_DATA 3 +#define SHI_RELDATA 4 +#define SHI_BSS 5 +#define SHI_RODAT 6 +#define SHI_STRINGS 7 +#define SHI_SYMTAB 8 +#define SHI_SECNAMES 9 +#define SHI_COM 10 +#define SHI_NOTE 11 + +IDXSYM *mapsec2sym; +#define S2S_INC 20 + +#define SymbolTable ((Elf32_Sym *)SYMbuf->buf) +#define SymbolTable64 ((Elf64_Sym *)SYMbuf->buf) +static int symbol_idx; // Number of symbols in symbol table +static int local_cnt; // Number of symbols with STB_LOCAL + +#define STI_FILE 1 // Where file symbol table entry is +#define STI_TEXT 2 +#define STI_DATA 3 +#define STI_BSS 4 +#define STI_GCC 5 // Where "gcc2_compiled" symbol is */ +#define STI_RODAT 6 // Symbol for readonly data +#define STI_NOTE 7 // Where note symbol table entry is +#define STI_COM 8 + +// NOTE: There seems to be a requirement that the read-only data have the +// same symbol table index and section index. Use section NOTE as a place +// holder. When a read-only string section is required, swap to NOTE. + +// Symbol Table +Outbuffer *SYMbuf; // Buffer to build symbol table in + +// Notes data (note currently used) +static Outbuffer *note_data; +static IDXSEC secidx_note; // Final table index for note data + +// Comment data for compiler version +static Outbuffer *comment_data; +static const char compiler[] = "\0Digital Mars C/C++" + VERSION + ; // compiled by ... + +// Each compiler segment is an elf section +// Predefined compiler segments CODE,DATA,CDATA,UDATA map to indexes +// into SegData[] +// An additionl index is reserved for comment data +// New compiler segments are added to end. +// +// There doesn't seem to be any way to get reserved data space in the +// same section as initialized data or code, so section offsets should +// be continuous when adding data. Fix-ups anywhere withing existing data. + +#define COMD UDATA+1 +#define OB_SEG_SIZ 10 // initial number of segments supported +#define OB_SEG_INC 10 // increment for additional segments + +#define OB_CODE_STR 100000 // initial size for code +#define OB_CODE_INC 100000 // increment for additional code +#define OB_DATA_STR 100000 // initial size for data +#define OB_DATA_INC 100000 // increment for additional data +#define OB_CDATA_STR 1024 // initial size for data +#define OB_CDATA_INC 1024 // increment for additional data +#define OB_COMD_STR 256 // initial size for comments + // increment as needed +#define OB_XTRA_STR 250 // initial size for extra segments +#define OB_XTRA_INC 10000 // increment size + +#define MAP_SEG2SECIDX(seg) (SegData[seg]->SDshtidx) +#define MAP_SEG2SYMIDX(seg) (SegData[seg]->SDsymidx) +#define MAP_SEG2SEC(seg) (&SecHdrTab[MAP_SEG2SECIDX(seg)]) +#define MAP_SEG2TYP(seg) (MAP_SEG2SEC(seg)->sh_flags & SHF_EXECINSTR ? CODE : DATA) + +seg_data **SegData; +int seg_count; +int seg_max; +int seg_tlsseg = UNKNOWN; +int seg_tlsseg_bss = UNKNOWN; + +int elf_getsegment2(IDXSEC shtidx, IDXSYM symidx, IDXSEC relidx); + + +/******************************* + * Output a string into a string table + * Input: + * strtab = string table for entry + * str = string to add + * + * Returns index into the specified string table. + */ + +IDXSTR elf_addstr(Outbuffer *strtab, const char *str) +{ + //dbg_printf("elf_addstr(strtab = x%x str = '%s')\n",strtab,str); + IDXSTR idx = strtab->size(); // remember starting offset + strtab->writeString(str); + //dbg_printf("\tidx %d, new size %d\n",idx,strtab->size()); + return idx; +} + +/******************************* + * Find a string in a string table + * Input: + * strtab = string table for entry + * str = string to find + * + * Returns index into the specified string table or 0. + */ + +static IDXSTR elf_findstr(Outbuffer *strtab, const char *str, const char *suffix) +{ + //printf("elf_findstr(strtab = %p, str = %s, suffix = %s\n", strtab, str ? str : "", suffix ? suffix : ""); + + size_t len = strlen(str); + + // Combine str~suffix and have buf point to the combination +#ifdef DEBUG + char tmpbuf[25]; // to exercise the alloca() code path +#else + char tmpbuf[1024]; // the alloca() code path is slow +#endif + const char *buf; + if (suffix) + { + size_t suffixlen = strlen(suffix); + if (len + suffixlen >= sizeof(tmpbuf)) + { + buf = (char *)alloca(len + suffixlen + 1); + assert(buf); + } + else + { + buf = tmpbuf; + } + memcpy((char *)buf, str, len); + memcpy((char *)buf + len, suffix, suffixlen + 1); + len += suffixlen; + } + else + buf = str; + + // Linear search, slow + const char *ent = (char *)strtab->buf+1; + const char *pend = ent+strtab->size() - 1; + while (ent + len < pend) + { + if (memcmp(buf, ent, len + 1) == 0) + return ent - (const char *)strtab->buf; + ent = (const char *)memchr(ent, 0, pend - ent); + ent += 1; + } + return 0; // never found match +} + +/******************************* + * Output a mangled string into the symbol string table + * Input: + * str = string to add + * + * Returns index into the table. + */ + +static IDXSTR elf_addmangled(Symbol *s) +{ + //printf("elf_addmangled(%s)\n", s->Sident); + char dest[DEST_LEN]; + char *destr; + const char *name; + int len; + IDXSTR namidx; + + namidx = symtab_strings->size(); + destr = obj_mangle2(s, dest); + name = destr; + if (CPP && name[0] == '_' && name[1] == '_') + { + if (strncmp(name,"__ct__",6) == 0) + name += 4; +#if 0 + switch(name[2]) + { + case 'c': + if (strncmp(name,"__ct__",6) == 0) + name += 4; + break; + case 'd': + if (strcmp(name,"__dl__FvP") == 0) + name = "__builtin_delete"; + break; + case 'v': + //if (strcmp(name,"__vec_delete__FvPiUIPi") == 0) + //name = "__builtin_vec_del"; + //else + //if (strcmp(name,"__vn__FPUI") == 0) + //name = "__builtin_vec_new"; + break; + case 'n': + if (strcmp(name,"__nw__FPUI") == 0) + name = "__builtin_new"; + break; + } +#endif + } + else if (tyfunc(s->ty()) && s->Sfunc && s->Sfunc->Fredirect) + name = s->Sfunc->Fredirect; + len = strlen(name); + symtab_strings->reserve(len+1); + strcpy((char *)symtab_strings->p,name); + symtab_strings->setsize(namidx+len+1); + if (destr != dest) // if we resized result + mem_free(destr); + //dbg_printf("\telf_addmagled symtab_strings %s namidx %d len %d size %d\n",name, namidx,len,symtab_strings->size()); + return namidx; +} + +/******************************* + * Output a symbol into the symbol table + * Input: + * stridx = string table index for name + * val = value associated with symbol + * sz = symbol size + * typ = symbol type + * bind = symbol binding + * segidx = segment index for segment where symbol is defined + * + * Returns the symbol table index for the symbol + */ + +static IDXSYM elf_addsym(IDXSTR nam, targ_size_t val, unsigned sz, + unsigned typ, unsigned bind, IDXSEC sec) +{ + //dbg_printf("elf_addsym(nam %d, val %d, sz %x, typ %x, bind %x, sec %d\n", + //nam,val,sz,typ,bind,sec); + + /* We want globally defined data symbols to have a size because + * zero sized symbols break copy relocations for shared libraries. + */ + assert(!(sz == 0 && (bind == STB_GLOBAL || bind == STB_WEAK) && + (typ == STT_OBJECT || typ == STT_TLS) && + sec != SHT_UNDEF)); + + if (I64) + { + if (!SYMbuf) + { SYMbuf = new Outbuffer(50 * sizeof(Elf64_Sym)); + SYMbuf->reserve(100 * sizeof(Elf64_Sym)); + } + Elf64_Sym sym; + sym.st_name = nam; + sym.st_value = val; + sym.st_size = sz; + sym.st_info = ELF_ST_INFO(bind,typ); + sym.st_other = 0; + sym.st_shndx = sec; + SYMbuf->write(&sym,sizeof(sym)); + } + else + { + if (!SYMbuf) + { SYMbuf = new Outbuffer(50 * sizeof(Elf32_Sym)); + SYMbuf->reserve(100 * sizeof(Elf32_Sym)); + } + Elf32_Sym sym; + sym.st_name = nam; + sym.st_value = val; + sym.st_size = sz; + sym.st_info = ELF_ST_INFO(bind,typ); + sym.st_other = 0; + sym.st_shndx = sec; + SYMbuf->write(&sym,sizeof(sym)); + } + if (bind == STB_LOCAL) + local_cnt++; + //dbg_printf("\treturning symbol table index %d\n",symbol_idx); + return symbol_idx++; +} + +/******************************* + * Create a new section header table entry. + * + * Input: + * name = section name + * suffix = suffix for name or NULL + * type = type of data in section sh_type + * flags = attribute flags sh_flags + * Output: + * section_cnt = assigned number for this section + * Note: Sections will be reordered on output + */ + +static IDXSEC elf_newsection2( + elf_u32_f32 name, + elf_u32_f32 type, + elf_u32_f32 flags, + elf_add_f32 addr, + elf_off_f32 offset, + elf_u32_f32 size, + elf_u32_f32 link, + elf_u32_f32 info, + elf_u32_f32 addralign, + elf_u32_f32 entsize) +{ + Elf32_Shdr sec; + + sec.sh_name = name; + sec.sh_type = type; + sec.sh_flags = flags; + sec.sh_addr = addr; + sec.sh_offset = offset; + sec.sh_size = size; + sec.sh_link = link; + sec.sh_info = info; + sec.sh_addralign = addralign; + sec.sh_entsize = entsize; + + if (!SECbuf) + { SECbuf = new Outbuffer(4 * sizeof(Elf32_Shdr)); + SECbuf->reserve(16 * sizeof(Elf32_Shdr)); + } + SECbuf->write((void *)&sec, sizeof(sec)); + return section_cnt++; +} + +static IDXSEC elf_newsection(const char *name, const char *suffix, + elf_u32_f32 type, elf_u32_f32 flags) +{ + // dbg_printf("elf_newsection(%s,%s,type %d, flags x%x)\n", + // name?name:"",suffix?suffix:"",type,flags); + + IDXSTR namidx = section_names->size(); + section_names->writeString(name); + if (suffix) + { // Append suffix string + section_names->setsize(section_names->size() - 1); // back up over terminating 0 + section_names->writeString(suffix); + } + IDXSTR *pidx = (IDXSTR *)section_names_hashtable->get(&namidx); + assert(!*pidx); // must not already exist + *pidx = namidx; + + return elf_newsection2(namidx,type,flags,0,0,0,0,0,0,0); +} + +/************************** + * Ouput read only data and generate a symbol for it. + * + */ + +symbol *elf_sym_cdata(tym_t ty,char *p,int len) +{ + symbol *s; + +#if 0 + if (OPT_IS_SET(OPTfwritable_strings)) + { + alignOffset(DATA, tysize(ty)); + s = symboldata(Doffset, ty); + SegData[DATA]->SDbuf->write(p,len); + s->Sseg = DATA; + s->Soffset = Doffset; // Remember its offset into DATA section + Doffset += len; + } + else +#endif + { + //printf("elf_sym_cdata(ty = %x, p = %x, len = %d, CDoffset = %x)\n", ty, p, len, CDoffset); + alignOffset(CDATA, tysize(ty)); + s = symboldata(CDoffset, ty); + obj_bytes(CDATA, CDoffset, len, p); + s->Sseg = CDATA; + } + + s->Sfl = /*(config.flags3 & CFG3pic) ? FLgotoff :*/ FLextern; + return s; +} + +/************************** + * Ouput read only data for data + * + */ + +int elf_data_cdata(char *p, int len, int *pseg) +{ + int oldoff; + /*if (OPT_IS_SET(OPTfwritable_strings)) + { + oldoff = Doffset; + SegData[DATA]->SDbuf->reserve(len); + SegData[DATA]->SDbuf->writen(p,len); + Doffset += len; + *pseg = DATA; + } + else*/ + { + oldoff = CDoffset; + SegData[CDATA]->SDbuf->reserve(len); + SegData[CDATA]->SDbuf->writen(p,len); + CDoffset += len; + *pseg = CDATA; + } + return oldoff; +} + +int elf_data_cdata(char *p, int len) +{ + int pseg; + + return elf_data_cdata(p, len, &pseg); +} + +/****************************** + * Perform initialization that applies to all .o output files. + * Called before any other obj_xxx routines + */ + +void obj_init(Outbuffer *objbuf, const char *filename, const char *csegname) +{ + //printf("obj_init()\n"); + + cseg = CODE; + fobjbuf = objbuf; + + mapsec2sym = NULL; + note_data = NULL; + secidx_note = 0; + comment_data = NULL; + seg_tlsseg = UNKNOWN; + seg_tlsseg_bss = UNKNOWN; + GOTsym = NULL; + + // Initialize buffers + + if (symtab_strings) + symtab_strings->setsize(1); + else + { symtab_strings = new Outbuffer(1024); + symtab_strings->reserve(2048); + symtab_strings->writeByte(0); + } + + if (SECbuf) + SECbuf->setsize(0); + section_cnt = 0; + + if (I64) + { + static char section_names_init64[] = + "\0.symtab\0.strtab\0.shstrtab\0.text\0.data\0.bss\0.note\0.comment\0.rodata\0.note.GNU-stack\0.rela.text\0.rela.data"; + #define NAMIDX_NONE 0 + #define NAMIDX_SYMTAB 1 // .symtab + #define NAMIDX_STRTAB 9 // .strtab + #define NAMIDX_SHSTRTAB 17 // .shstrtab + #define NAMIDX_TEXT 27 // .text + #define NAMIDX_DATA 33 // .data + #define NAMIDX_BSS 39 // .bss + #define NAMIDX_NOTE 44 // .note + #define NAMIDX_COMMENT 50 // .comment + #define NAMIDX_RODATA 59 // .rodata + #define NAMIDX_GNUSTACK 67 // .note.GNU-stack + #define NAMIDX_RELTEXT 83 // .rel.text and .rela.text + #define NAMIDX_RELDATA 93 // .rel.data + #define NAMIDX_RELDATA64 94 // .rela.data + + if (section_names) + section_names->setsize(sizeof(section_names_init64)); + else + { section_names = new Outbuffer(512); + section_names->reserve(1024); + section_names->writen(section_names_init64, sizeof(section_names_init64)); + } + + if (section_names_hashtable) + delete section_names_hashtable; + section_names_hashtable = new AArray(&ti_idxstr, sizeof(IDXSTR)); + + // name,type,flags,addr,offset,size,link,info,addralign,entsize + elf_newsection2(0, SHT_NULL, 0, 0,0,0,0,0, 0,0); + elf_newsection2(NAMIDX_TEXT,SHT_PROGDEF,SHF_ALLOC|SHF_EXECINSTR,0,0,0,0,0, 4,0); + elf_newsection2(NAMIDX_RELTEXT,SHT_RELA, 0,0,0,0,SHI_SYMTAB, SHI_TEXT, 8,0x18); + elf_newsection2(NAMIDX_DATA,SHT_PROGDEF,SHF_ALLOC|SHF_WRITE, 0,0,0,0,0, 8,0); + elf_newsection2(NAMIDX_RELDATA64,SHT_RELA, 0,0,0,0,SHI_SYMTAB, SHI_DATA, 8,0x18); + elf_newsection2(NAMIDX_BSS, SHT_NOBITS,SHF_ALLOC|SHF_WRITE, 0,0,0,0,0, 16,0); + elf_newsection2(NAMIDX_RODATA,SHT_PROGDEF,SHF_ALLOC, 0,0,0,0,0, 16,0); + elf_newsection2(NAMIDX_STRTAB,SHT_STRTAB, 0, 0,0,0,0,0, 1,0); + elf_newsection2(NAMIDX_SYMTAB,SHT_SYMTAB, 0, 0,0,0,0,0, 8,0); + elf_newsection2(NAMIDX_SHSTRTAB,SHT_STRTAB, 0, 0,0,0,0,0, 1,0); + elf_newsection2(NAMIDX_COMMENT, SHT_PROGDEF,0, 0,0,0,0,0, 1,0); + elf_newsection2(NAMIDX_NOTE,SHT_NOTE, 0, 0,0,0,0,0, 1,0); + elf_newsection2(NAMIDX_GNUSTACK,SHT_PROGDEF,0, 0,0,0,0,0, 1,0); + + IDXSTR namidx; + namidx = NAMIDX_TEXT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_RELTEXT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_DATA; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_RELDATA64; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_BSS; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_RODATA; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_STRTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_SYMTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_SHSTRTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_COMMENT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_NOTE; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_GNUSTACK; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + } + else + { + static char section_names_init[] = + "\0.symtab\0.strtab\0.shstrtab\0.text\0.data\0.bss\0.note\0.comment\0.rodata\0.note.GNU-stack\0.rel.text\0.rel.data"; + + if (section_names) + section_names->setsize(sizeof(section_names_init)); + else + { section_names = new Outbuffer(512); + section_names->reserve(100*1024); + section_names->writen(section_names_init, sizeof(section_names_init)); + } + + if (section_names_hashtable) + delete section_names_hashtable; + section_names_hashtable = new AArray(&ti_idxstr, sizeof(IDXSTR)); + + // name,type,flags,addr,offset,size,link,info,addralign,entsize + elf_newsection2(0, SHT_NULL, 0, 0,0,0,0,0, 0,0); + elf_newsection2(NAMIDX_TEXT,SHT_PROGDEF,SHF_ALLOC|SHF_EXECINSTR,0,0,0,0,0, 16,0); + elf_newsection2(NAMIDX_RELTEXT,SHT_REL, 0,0,0,0,SHI_SYMTAB, SHI_TEXT, 4,8); + elf_newsection2(NAMIDX_DATA,SHT_PROGDEF,SHF_ALLOC|SHF_WRITE, 0,0,0,0,0, 4,0); + elf_newsection2(NAMIDX_RELDATA,SHT_REL, 0,0,0,0,SHI_SYMTAB, SHI_DATA, 4,8); + elf_newsection2(NAMIDX_BSS, SHT_NOBITS,SHF_ALLOC|SHF_WRITE, 0,0,0,0,0, 32,0); + elf_newsection2(NAMIDX_RODATA,SHT_PROGDEF,SHF_ALLOC, 0,0,0,0,0, 1,0); + elf_newsection2(NAMIDX_STRTAB,SHT_STRTAB, 0, 0,0,0,0,0, 1,0); + elf_newsection2(NAMIDX_SYMTAB,SHT_SYMTAB, 0, 0,0,0,0,0, 4,0); + elf_newsection2(NAMIDX_SHSTRTAB,SHT_STRTAB, 0, 0,0,0,0,0, 1,0); + elf_newsection2(NAMIDX_COMMENT, SHT_PROGDEF,0, 0,0,0,0,0, 1,0); + elf_newsection2(NAMIDX_NOTE,SHT_NOTE, 0, 0,0,0,0,0, 1,0); + elf_newsection2(NAMIDX_GNUSTACK,SHT_PROGDEF,0, 0,0,0,0,0, 1,0); + + IDXSTR namidx; + namidx = NAMIDX_TEXT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_RELTEXT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_DATA; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_RELDATA; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_BSS; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_RODATA; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_STRTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_SYMTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_SHSTRTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_COMMENT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_NOTE; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + namidx = NAMIDX_GNUSTACK; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx; + } + + if (SYMbuf) + SYMbuf->setsize(0); + symbol_idx = 0; + local_cnt = 0; + // The symbols that every object file has + elf_addsym(0, 0, 0, STT_NOTYPE, STB_LOCAL, 0); + elf_addsym(0, 0, 0, STT_FILE, STB_LOCAL, SHT_ABS); // STI_FILE + elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_TEXT); // STI_TEXT + elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_DATA); // STI_DATA + elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_BSS); // STI_BSS + elf_addsym(0, 0, 0, STT_NOTYPE, STB_LOCAL, SHI_TEXT); // STI_GCC + elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_RODAT); // STI_RODAT + elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_NOTE); // STI_NOTE + elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_COM); // STI_COM + + // Initialize output buffers for CODE, DATA and COMMENTS + // (NOTE not supported, BSS not required) + + seg_count = 0; + + elf_getsegment2(SHI_TEXT, STI_TEXT, SHI_RELTEXT); + assert(SegData[CODE]->SDseg == CODE); + + elf_getsegment2(SHI_DATA, STI_DATA, SHI_RELDATA); + assert(SegData[DATA]->SDseg == DATA); + + elf_getsegment2(SHI_RODAT, STI_RODAT, 0); + assert(SegData[CDATA]->SDseg == CDATA); + + elf_getsegment2(SHI_BSS, STI_BSS, 0); + assert(SegData[UDATA]->SDseg == UDATA); + + elf_getsegment2(SHI_COM, STI_COM, 0); + assert(SegData[COMD]->SDseg == COMD); + + if (config.fulltypes) + dwarf_initfile(filename); +} + +/************************** + * Initialize the start of object output for this particular .o file. + * + * Input: + * filename: Name of source file + * csegname: User specified default code segment name + */ + +void obj_initfile(const char *filename, const char *csegname, const char *modname) +{ + //dbg_printf("obj_initfile(filename = %s, modname = %s)\n",filename,modname); + + IDXSTR name = elf_addstr(symtab_strings, filename); + if (I64) + SymbolTable64[STI_FILE].st_name = name; + else + SymbolTable[STI_FILE].st_name = name; + +#if 0 + // compiler flag for linker + if (I64) + SymbolTable64[STI_GCC].st_name = elf_addstr(symtab_strings,"gcc2_compiled."); + else + SymbolTable[STI_GCC].st_name = elf_addstr(symtab_strings,"gcc2_compiled."); +#endif + + if (csegname && *csegname && strcmp(csegname,".text")) + { // Define new section and make it the default for cseg segment + // NOTE: cseg is initialized to CODE + IDXSEC newsecidx; + Elf32_Shdr *newtextsec; + IDXSYM newsymidx; + SegData[cseg]->SDshtidx = newsecidx = + elf_newsection(csegname,0,SHT_PROGDEF,SHF_ALLOC|SHF_EXECINSTR); + newtextsec = &SecHdrTab[newsecidx]; + newtextsec->sh_addralign = 4; + SegData[cseg]->SDsymidx = + elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, newsecidx); + } + if (config.fulltypes) + dwarf_initmodule(filename, modname); +} + +/*************************** + * Renumber symbols so they are + * ordered as locals, weak and then global + * Returns: + * sorted symbol table, caller must free with util_free() + */ + +void *elf_renumbersyms() +{ void *symtab; + int nextlocal = 0; + int nextglobal = local_cnt; + + SYMIDX *sym_map = (SYMIDX *)util_malloc(sizeof(SYMIDX),symbol_idx); + + if (I64) + { + Elf64_Sym *oldsymtab = (Elf64_Sym *)SYMbuf->buf; + Elf64_Sym *symtabend = oldsymtab+symbol_idx; + + symtab = util_malloc(sizeof(Elf64_Sym),symbol_idx); + + Elf64_Sym *sl = (Elf64_Sym *)symtab; + Elf64_Sym *sg = sl + local_cnt; + + int old_idx = 0; + for(Elf64_Sym *s = oldsymtab; s != symtabend; s++) + { // reorder symbol and map new #s to old + int bind = ELF_ST_BIND(s->st_info); + if (bind == STB_LOCAL) + { + *sl++ = *s; + sym_map[old_idx] = nextlocal++; + } + else + { + *sg++ = *s; + sym_map[old_idx] = nextglobal++; + } + old_idx++; + } + } + else + { + Elf32_Sym *oldsymtab = (Elf32_Sym *)SYMbuf->buf; + Elf32_Sym *symtabend = oldsymtab+symbol_idx; + + symtab = util_malloc(sizeof(Elf32_Sym),symbol_idx); + + Elf32_Sym *sl = (Elf32_Sym *)symtab; + Elf32_Sym *sg = sl + local_cnt; + + int old_idx = 0; + for(Elf32_Sym *s = oldsymtab; s != symtabend; s++) + { // reorder symbol and map new #s to old + int bind = ELF_ST_BIND(s->st_info); + if (bind == STB_LOCAL) + { + *sl++ = *s; + sym_map[old_idx] = nextlocal++; + } + else + { + *sg++ = *s; + sym_map[old_idx] = nextglobal++; + } + old_idx++; + } + } + + // Renumber the relocations + for (int i = 1; i <= seg_count; i++) + { // Map indicies in the segment table + seg_data *pseg = SegData[i]; + pseg->SDsymidx = sym_map[pseg->SDsymidx]; + if (pseg->SDrel) + { + if (I64) + { + Elf64_Rela *rel = (Elf64_Rela *) pseg->SDrel->buf; + for (int r = 0; r < pseg->SDrelcnt; r++) + { + unsigned t = ELF64_R_TYPE(rel->r_info); + unsigned si = ELF64_R_SYM(rel->r_info); + assert(si < symbol_idx); + rel->r_info = ELF64_R_INFO(sym_map[si],t); + rel++; + } + } + else + { + Elf32_Rel *rel = (Elf32_Rel *) pseg->SDrel->buf; + assert(pseg->SDrelcnt == pseg->SDrel->size() / sizeof(Elf32_Rel)); + for (int r = 0; r < pseg->SDrelcnt; r++) + { + unsigned t = ELF32_R_TYPE(rel->r_info); + unsigned si = ELF32_R_IDX(rel->r_info); + assert(si < symbol_idx); + rel->r_info = ELF32_R_INFO(sym_map[si],t); + rel++; + } + } + } + }; + + return symtab; +} + + +/*************************** + * Fixup and terminate object file. + */ + +void obj_termfile() +{ + //dbg_printf("obj_termfile\n"); + if (configv.addlinenumbers) + { + dwarf_termmodule(); + } +} + +/********************************* + * Terminate package. + */ + +void obj_term() +{ + //printf("obj_term()\n"); +#if SCPP + if (!errcnt) +#endif + { + outfixlist(); // backpatches + } + + if (configv.addlinenumbers) + { + dwarf_termfile(); + } + +#if SCPP + if (errcnt) + return; +#endif + + // Write out the bytes for the header + static const char elf_string32[EI_NIDENT] = + { + ELFMAG0,ELFMAG1,ELFMAG2,ELFMAG3, + ELFCLASS32, // EI_CLASS + ELFDATA2LSB, // EI_DATA + EV_CURRENT, // EI_VERSION + ELFOSABI_LINUX,0, // EI_OSABI,EI_ABIVERSION + 0,0,0,0,0,0,0 + }; + static const char elf_string64[EI_NIDENT] = + { + ELFMAG0,ELFMAG1,ELFMAG2,ELFMAG3, + ELFCLASS64, // EI_CLASS + ELFDATA2LSB, // EI_DATA + EV_CURRENT, // EI_VERSION + ELFOSABI_LINUX,0, // EI_OSABI,EI_ABIVERSION + 0,0,0,0,0,0,0 + }; + fobjbuf->write(I64 ? elf_string64 : elf_string32, EI_NIDENT); + + long foffset; + Elf32_Shdr *sechdr; + seg_data *seg; + void *symtab = elf_renumbersyms(); + FILE *fd = NULL; + + // Output the ELF Header + // The section header is build in the static variable elf_header + static Elf64_Ehdr elf_header = + { + ET_REL, // e_type + EM_X86_64, // e_machine + EV_CURRENT, // e_version + 0, // e_entry + 0, // e_phoff + 0, // e_shoff + 0, // e_flags + sizeof(Elf64_Ehdr) + EI_NIDENT, // e_ehsize + sizeof(Elf64_Phdr), // e_phentsize + 0, // e_phnum + sizeof(Elf64_Shdr), // e_shentsize + 0, // e_shnum + 0 // e_shstrndx + }; + int hdrsize = I64 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Hdr); + + elf_header.e_shnum = section_cnt; + elf_header.e_shstrndx = SHI_SECNAMES; + fobjbuf->writezeros(hdrsize); + + // Walk through sections determining size and file offsets + // Sections will be output in the following order + // Null segment + // For each Code/Data Segment + // code/data to load + // relocations without addens + // .bss + // notes + // comments + // section names table + // symbol table + // strings table + + foffset = EI_NIDENT + hdrsize; // start after header + // section header table at end + + // + // First output individual section data associate with program + // code and data + // + //printf("Setup offsets and sizes foffset %d\n\tsection_cnt %d, seg_count %d\n",foffset,section_cnt,seg_count); + for (int i=1; i<= seg_count; i++) + { + seg = SegData[i]; + sechdr = MAP_SEG2SEC(i); // corresponding section + foffset = elf_align(fd,sechdr->sh_addralign,foffset); + if (i == UDATA) // 0, BSS never allocated + { // but foffset as if it has + sechdr->sh_offset = foffset; + sechdr->sh_size = seg->SDoffset; + // accumulated size + continue; + } + else if (sechdr->sh_type == SHT_NOBITS) // .tbss never allocated + { + sechdr->sh_offset = foffset; + sechdr->sh_size = seg->SDoffset; + // accumulated size + continue; + } + else if (!seg->SDbuf) + continue; // For others leave sh_offset as 0 + + sechdr->sh_offset = foffset; + //printf("\tsection name %d,",sechdr->sh_name); + if (seg->SDbuf && seg->SDbuf->size()) + { + //printf(" - size %d\n",seg->SDbuf->size()); + sechdr->sh_size = seg->SDbuf->size(); + fobjbuf->write(seg->SDbuf->buf, sechdr->sh_size); + foffset += sechdr->sh_size; + } + //printf(" assigned offset %d, size %d\n",foffset,sechdr->sh_size); + } + + // + // Next output any notes or comments + // + if (note_data) + { + sechdr = &SecHdrTab[secidx_note]; // Notes + sechdr->sh_size = note_data->size(); + sechdr->sh_offset = foffset; + fobjbuf->write(note_data->buf, sechdr->sh_size); + foffset += sechdr->sh_size; + } + + if (comment_data) + { + sechdr = &SecHdrTab[SHI_COM]; // Comments + sechdr->sh_size = comment_data->size(); + sechdr->sh_offset = foffset; + fobjbuf->write(comment_data->buf, sechdr->sh_size); + foffset += sechdr->sh_size; + } + + // + // Then output string table for section names + // + sechdr = &SecHdrTab[SHI_SECNAMES]; // Section Names + sechdr->sh_size = section_names->size(); + sechdr->sh_offset = foffset; + //dbg_printf("section names offset %d\n",foffset); + fobjbuf->write(section_names->buf, sechdr->sh_size); + foffset += sechdr->sh_size; + + // + // Symbol table and string table for symbols next + // + //dbg_printf("output symbol table size %d\n",SYMbuf->size()); + sechdr = &SecHdrTab[SHI_SYMTAB]; // Symbol Table + sechdr->sh_size = SYMbuf->size(); + sechdr->sh_entsize = I64 ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym); + sechdr->sh_link = SHI_STRINGS; + sechdr->sh_info = local_cnt; + foffset = elf_align(fd,4,foffset); + sechdr->sh_offset = foffset; + fobjbuf->write(symtab, sechdr->sh_size); + foffset += sechdr->sh_size; + util_free(symtab); + + //dbg_printf("output section strings size 0x%x,offset 0x%x\n",symtab_strings->size(),foffset); + sechdr = &SecHdrTab[SHI_STRINGS]; // Symbol Strings + sechdr->sh_size = symtab_strings->size(); + sechdr->sh_offset = foffset; + fobjbuf->write(symtab_strings->buf, sechdr->sh_size); + foffset += sechdr->sh_size; + + // + // Now the relocation data for program code and data sections + // + foffset = elf_align(fd,4,foffset); + //dbg_printf("output relocations size 0x%x, foffset 0x%x\n",section_names->size(),foffset); + for (int i=1; i<= seg_count; i++) + { + seg = SegData[i]; + if (!seg->SDbuf) + { +// sechdr = &SecHdrTab[seg->SDrelidx]; +// if (I64 && sechdr->sh_type == SHT_RELA) +// sechdr->sh_offset = foffset; + continue; // 0, BSS never allocated + } + if (seg->SDrel && seg->SDrel->size()) + { + assert(seg->SDrelidx); + sechdr = &SecHdrTab[seg->SDrelidx]; + sechdr->sh_size = seg->SDrel->size(); + sechdr->sh_offset = foffset; + if (I64) + { + assert(seg->SDrelcnt == seg->SDrel->size() / sizeof(Elf64_Rela)); +#ifdef DEBUG + for (size_t i = 0; i < seg->SDrelcnt; ++i) + { Elf64_Rela *p = ((Elf64_Rela *)seg->SDrel->buf) + i; + if (ELF64_R_TYPE(p->r_info) == R_X86_64_64) + assert(*(Elf64_Xword *)(seg->SDbuf->buf + p->r_offset) == 0); + } +#endif + } + else + assert(seg->SDrelcnt == seg->SDrel->size() / sizeof(Elf32_Rel)); + fobjbuf->write(seg->SDrel->buf, sechdr->sh_size); + foffset += sechdr->sh_size; + } + } + + // + // Finish off with the section header table + // + elf_header.e_shoff = foffset; // remember location in elf header + //dbg_printf("output section header table\n"); + + // Output the completed Section Header Table + if (I64) + { // Translate section headers to 64 bits + int sz = section_cnt * sizeof(Elf64_Shdr); + fobjbuf->reserve(sz); + for (int i = 0; i < section_cnt; i++) + { + Elf32_Shdr *p = SecHdrTab + i; + Elf64_Shdr s; + s.sh_name = p->sh_name; + s.sh_type = p->sh_type; + s.sh_flags = p->sh_flags; + s.sh_addr = p->sh_addr; + s.sh_offset = p->sh_offset; + s.sh_size = p->sh_size; + s.sh_link = p->sh_link; + s.sh_info = p->sh_info; + s.sh_addralign = p->sh_addralign; + s.sh_entsize = p->sh_entsize; + fobjbuf->write(&s, sizeof(s)); + } + foffset += sz; + } + else + { + fobjbuf->write(SecHdrTab, section_cnt * sizeof(Elf32_Shdr)); + foffset += section_cnt * sizeof(Elf32_Shdr); + } + + // + // Now that we have correct offset to section header table, e_shoff, + // go back and re-output the elf header + // + fobjbuf->position(EI_NIDENT, hdrsize); + if (I64) + { + fobjbuf->write(&elf_header, hdrsize); + } + else + { Elf32_Hdr h; + // Transfer to 32 bit header + h.e_type = elf_header.e_type; + h.e_machine = EM_386; + h.e_version = elf_header.e_version; + h.e_entry = elf_header.e_entry; + h.e_phoff = elf_header.e_phoff; + h.e_shoff = elf_header.e_shoff; + h.e_flags = elf_header.e_flags; + h.e_ehsize = sizeof(Elf32_Hdr) + EI_NIDENT; + h.e_phentsize = sizeof(elf_pht); + h.e_phnum = elf_header.e_phnum; + h.e_shentsize = sizeof(Elf32_Shdr); + h.e_shnum = elf_header.e_shnum; + h.e_shstrndx = elf_header.e_shstrndx; + fobjbuf->write(&h, hdrsize); + } + fobjbuf->position(foffset, 0); + fobjbuf->flush(); +} + +/***************************** + * Line number support. + */ + +/*************************** + * Record file and line number at segment and offset. + * The actual .debug_line segment is put out by dwarf_termfile(). + * Input: + * cseg current code segment + */ + +void objlinnum(Srcpos srcpos, targ_size_t offset) +{ + if (srcpos.Slinnum == 0) + return; + +#if 0 +#if MARS || SCPP + printf("objlinnum(cseg=%d, offset=0x%lx) ", cseg, offset); +#endif + srcpos.print(""); +#endif + +#if MARS + if (!srcpos.Sfilename) + return; +#endif +#if SCPP + if (!srcpos.Sfilptr) + return; + sfile_debug(&srcpos_sfile(srcpos)); + Sfile *sf = *srcpos.Sfilptr; +#endif + + size_t i; + seg_data *seg = SegData[cseg]; + + // Find entry i in SDlinnum_data[] that corresponds to srcpos filename + for (i = 0; 1; i++) + { + if (i == seg->SDlinnum_count) + { // Create new entry + if (seg->SDlinnum_count == seg->SDlinnum_max) + { // Enlarge array + unsigned newmax = seg->SDlinnum_max * 2 + 1; + //printf("realloc %d\n", newmax * sizeof(linnum_data)); + seg->SDlinnum_data = (linnum_data *)mem_realloc( + seg->SDlinnum_data, newmax * sizeof(linnum_data)); + memset(seg->SDlinnum_data + seg->SDlinnum_max, 0, + (newmax - seg->SDlinnum_max) * sizeof(linnum_data)); + seg->SDlinnum_max = newmax; + } + seg->SDlinnum_count++; +#if MARS + seg->SDlinnum_data[i].filename = srcpos.Sfilename; +#endif +#if SCPP + seg->SDlinnum_data[i].filptr = sf; +#endif + break; + } +#if MARS + if (seg->SDlinnum_data[i].filename == srcpos.Sfilename) +#endif +#if SCPP + if (seg->SDlinnum_data[i].filptr == sf) +#endif + break; + } + + linnum_data *ld = &seg->SDlinnum_data[i]; +// printf("i = %d, ld = x%x\n", i, ld); + if (ld->linoff_count == ld->linoff_max) + { + if (!ld->linoff_max) + ld->linoff_max = 8; + ld->linoff_max *= 2; + ld->linoff = (unsigned (*)[2])mem_realloc(ld->linoff, ld->linoff_max * sizeof(unsigned) * 2); + } + ld->linoff[ld->linoff_count][0] = srcpos.Slinnum; + ld->linoff[ld->linoff_count][1] = offset; + ld->linoff_count++; +} + + +/******************************* + * Set start address + */ + +void obj_startaddress(Symbol *s) +{ + //dbg_printf("obj_startaddress(Symbol *%s)\n",s->Sident); + //obj.startaddress = s; +} + +/******************************* + * Output library name. + * Output: + */ + +void obj_includelib(const char *name) +{ + //dbg_printf("obj_includelib(name *%s)\n",name); +} + +/************************** + * Embed string in executable. + */ + +void obj_exestr(const char *p) +{ + //dbg_printf("obj_exestr(char *%s)\n",p); +} + +/************************** + * Embed string in obj. + */ + +void obj_user(const char *p) +{ + //dbg_printf("obj_user(char *%s)\n",p); +} + +/******************************* + * Output a weak extern record. + */ + +void obj_wkext(Symbol *s1,Symbol *s2) +{ + //dbg_printf("obj_wkext(Symbol *%s,Symbol *s2)\n",s1->Sident,s2->Sident); +} + +/******************************* + * Output file name record. + * + * Currently assumes that obj_filename will not be called + * twice for the same file. + */ + +void obj_filename(const char *modname) +{ + //dbg_printf("obj_filename(char *%s)\n",modname); + unsigned strtab_idx = elf_addstr(symtab_strings,modname); + elf_addsym(strtab_idx,0,0,STT_FILE,STB_LOCAL,SHT_ABS); +} + +/******************************* + * Embed compiler version in .obj file. + */ + +void obj_compiler() +{ + //dbg_printf("obj_compiler\n"); + comment_data = new Outbuffer(); + comment_data->write(compiler,sizeof(compiler)); + //dbg_printf("Comment data size %d\n",comment_data->size()); +} + + +//#if NEWSTATICDTOR + +/************************************** + * Symbol is the function that calls the static constructors. + * Put a pointer to it into a special segment that the startup code + * looks at. + * Input: + * s static constructor function + * dtor !=0 if leave space for static destructor + * seg 1: user + * 2: lib + * 3: compiler + */ + +void obj_staticctor(Symbol *s,int dtor,int none) +{ +// Static constructors and destructors + IDXSEC seg; + Outbuffer *buf; + + //dbg_printf("obj_staticctor(%s) offset %x\n",s->Sident,s->Soffset); + //symbol_print(s); + s->Sseg = seg = + elf_getsegment(".ctors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4); + buf = SegData[seg]->SDbuf; + if (I64) + buf->write64(s->Soffset); + else + buf->write32(s->Soffset); + elf_addrel(seg,SegData[seg]->SDoffset,I64 ? R_X86_64_64 : RI_TYPE_SYM32,STI_TEXT,0); + SegData[seg]->SDoffset = buf->size(); +} + +/************************************** + * Symbol is the function that calls the static destructors. + * Put a pointer to it into a special segment that the exit code + * looks at. + * Input: + * s static destructor function + */ + +void obj_staticdtor(Symbol *s) +{ + IDXSEC seg; + Outbuffer *buf; + + //dbg_printf("obj_staticdtor(%s) offset %x\n",s->Sident,s->Soffset); + //symbol_print(s); + seg = elf_getsegment(".dtors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4); + buf = SegData[seg]->SDbuf; + if (I64) + buf->write64(s->Soffset); + else + buf->write32(s->Soffset); + elf_addrel(seg,SegData[seg]->SDoffset,I64 ? R_X86_64_64 : RI_TYPE_SYM32,s->Sxtrnnum,0); + SegData[seg]->SDoffset = buf->size(); +} + +//#else + +/*************************************** + * Stuff pointer to function in its own segment. + * Used for static ctor and dtor lists. + */ + +void obj_funcptr(Symbol *s) +{ + //dbg_printf("obj_funcptr(%s) \n",s->Sident); +} + +//#endif + +/*************************************** + * Stuff the following data in a separate segment: + * pointer to function + * pointer to ehsym + * length of function + */ + +void obj_ehtables(Symbol *sfunc,targ_size_t size,Symbol *ehsym) +{ + //dbg_printf("obj_ehtables(%s) \n",sfunc->Sident); + + symbol *ehtab_entry = symbol_generate(SCstatic,type_alloc(TYint)); + symbol_keep(ehtab_entry); + elf_getsegment(".deh_beg", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE); + int seg = elf_getsegment(".deh_eh", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE); + ehtab_entry->Sseg = seg; + Outbuffer *buf = SegData[seg]->SDbuf; + elf_getsegment(".deh_end", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE); + ehtab_entry->Stype->Tmangle = mTYman_c; + ehsym->Stype->Tmangle = mTYman_c; + + assert(sfunc->Sxtrnnum && sfunc->Sseg); + assert(ehsym->Sxtrnnum && ehsym->Sseg); + if (I64) + { + elf_addrel(seg, buf->size(), R_X86_64_64, MAP_SEG2SYMIDX(sfunc->Sseg), sfunc->Soffset); + buf->write64(0); + + elf_addrel(seg, buf->size(), R_X86_64_64, MAP_SEG2SYMIDX(ehsym->Sseg), ehsym->Soffset); + buf->write64(0); + + buf->write64(sfunc->Ssize); + } + else + { + elf_addrel(seg, buf->size(), RI_TYPE_SYM32, MAP_SEG2SYMIDX(sfunc->Sseg), 0); + buf->write32(sfunc->Soffset); + + elf_addrel(seg, buf->size(), RI_TYPE_SYM32, MAP_SEG2SYMIDX(ehsym->Sseg), 0); + buf->write32(ehsym->Soffset); + + buf->write32(sfunc->Ssize); + } +} + +/********************************************* + * Put out symbols that define the beginning/end of the .deh_eh section. + */ + +void obj_ehsections() +{ + int sec = elf_getsegment(".deh_beg", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE); + //obj_bytes(sec, 0, 4, NULL); + + IDXSTR namidx = elf_addstr(symtab_strings,"_deh_beg"); + elf_addsym(namidx, 0, 4, STT_OBJECT, STB_GLOBAL, MAP_SEG2SECIDX(sec)); + //elf_addsym(namidx, 0, 4, STT_OBJECT, STB_GLOBAL, MAP_SEG2SECIDX(sec)); + + elf_getsegment(".deh_eh", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE); + + sec = elf_getsegment(".deh_end", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE); + namidx = elf_addstr(symtab_strings,"_deh_end"); + elf_addsym(namidx, 0, 4, STT_OBJECT, STB_GLOBAL, MAP_SEG2SECIDX(sec)); + + obj_tlssections(); +} + +/********************************************* + * Put out symbols that define the beginning/end of the thread local storage sections. + */ + +void obj_tlssections() +{ + IDXSTR namidx; + int align = I64 ? 16 : 4; + + int sec = elf_getsegment(".tdata", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, align); + obj_bytes(sec, 0, align, NULL); + + namidx = elf_addstr(symtab_strings,"_tlsstart"); + elf_addsym(namidx, 0, align, STT_TLS, STB_GLOBAL, MAP_SEG2SECIDX(sec)); + + elf_getsegment(".tdata.", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, align); + + sec = elf_getsegment(".tcommon", NULL, SHT_NOBITS, SHF_ALLOC|SHF_WRITE|SHF_TLS, align); + namidx = elf_addstr(symtab_strings,"_tlsend"); + elf_addsym(namidx, 0, align, STT_TLS, STB_GLOBAL, MAP_SEG2SECIDX(sec)); +} + +/********************************* + * Setup for Symbol s to go into a COMDAT segment. + * Output (if s is a function): + * cseg segment index of new current code segment + * Coffset starting offset in cseg + * Returns: + * "segment index" of COMDAT + */ + +STATIC void setup_comdat(Symbol *s) +{ + const char *prefix; + int type; + int flags; + int align = 4; + + //printf("obj_comdat(Symbol *%s\n",s->Sident); + //symbol_print(s); + symbol_debug(s); + if (tyfunc(s->ty())) + { + //s->Sfl = FLcode; // was FLoncecode + //prefix = ".gnu.linkonce.t"; // doesn't work, despite documentation + prefix = ".text."; // undocumented, but works + type = SHT_PROGDEF; + flags = SHF_ALLOC|SHF_EXECINSTR; + } + else if ((s->ty() & mTYLINK) == mTYthread) + { + /* Ensure that ".tdata" precedes any other .tdata. section, as the ld + * linker script fails to work right. + */ + if (I64) + align = 16; + elf_getsegment(".tdata", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, align); + + s->Sfl = FLtlsdata; + prefix = ".tdata."; + type = SHT_PROGDEF; + flags = SHF_ALLOC|SHF_WRITE|SHF_TLS; + } + else + { + if (I64) + align = 16; + s->Sfl = FLdata; + //prefix = ".gnu.linkonce.d."; + prefix = ".data."; + type = SHT_PROGDEF; + flags = SHF_ALLOC|SHF_WRITE; + } + + s->Sseg = elf_getsegment(prefix, cpp_mangle(s), type, flags, align); + // find or create new segment + SegData[s->Sseg]->SDsym = s; +} + +int obj_comdat(Symbol *s) +{ + setup_comdat(s); + if (s->Sfl == FLdata || s->Sfl == FLtlsdata) + { + objpubdef(s->Sseg,s,0); + searchfixlist(s); // backpatch any refs to this symbol + } + return s->Sseg; +} + +int obj_comdatsize(Symbol *s, targ_size_t symsize) +{ + setup_comdat(s); + if (s->Sfl == FLdata || s->Sfl == FLtlsdata) + { + objpubdefsize(s->Sseg,s,0,symsize); + searchfixlist(s); // backpatch any refs to this symbol + } + return s->Sseg; +} + +/******************************** + * Get a segment for a segment name. + * Input: + * name name of segment, if NULL then revert to default name + * suffix append to name + * align alignment + * Returns: + * segment index of found or newly created segment + */ + +int elf_getsegment2(IDXSEC shtidx, IDXSYM symidx, IDXSEC relidx) +{ + //printf("SegData = %p\n", SegData); + int seg = ++seg_count; + if (seg_count >= seg_max) + { // need more room in segment table + seg_max += OB_SEG_INC; + SegData = (seg_data **)mem_realloc(SegData,seg_max * sizeof(seg_data *)); + memset(&SegData[seg_count], 0, (seg_max - seg_count) * sizeof(seg_data *)); + } + assert(seg_count < seg_max); + if (!SegData[seg]) + { SegData[seg] = (seg_data *)mem_calloc(sizeof(seg_data)); + //printf("test2: SegData[%d] = %p\n", seg, SegData[seg]); + } + + seg_data *pseg = SegData[seg]; + pseg->SDseg = seg; + pseg->SDshtidx = shtidx; + pseg->SDoffset = 0; + if (pseg->SDbuf) + pseg->SDbuf->setsize(0); + else + { if (SecHdrTab[shtidx].sh_type != SHT_NOBITS) + { pseg->SDbuf = new Outbuffer(OB_XTRA_STR); + pseg->SDbuf->reserve(1024); + } + } + if (pseg->SDrel) + pseg->SDrel->setsize(0); + pseg->SDsymidx = symidx; + pseg->SDrelidx = relidx; + pseg->SDrelmaxoff = 0; + pseg->SDrelindex = 0; + pseg->SDrelcnt = 0; + pseg->SDshtidxout = 0; + pseg->SDsym = NULL; + pseg->SDaranges_offset = 0; + pseg->SDlinnum_count = 0; + return seg; +} + +int elf_getsegment(const char *name, const char *suffix, int type, int flags, + int align) +{ + //printf("elf_getsegment(%s,%s,flags %x, align %d)\n",name,suffix,flags,align); + + // Add name~suffix to the section_names table + IDXSTR namidx = section_names->size(); + section_names->writeString(name); + if (suffix) + { // Append suffix string + section_names->setsize(section_names->size() - 1); // back up over terminating 0 + section_names->writeString(suffix); + } + IDXSTR *pidx = (IDXSTR *)section_names_hashtable->get(&namidx); + if (*pidx) + { // this section name already exists + section_names->setsize(namidx); // remove addition + namidx = *pidx; + for (int seg = CODE; seg <= seg_count; seg++) + { // should be in segment table + if (MAP_SEG2SEC(seg)->sh_name == namidx) + { + return seg; // found section for segment + } + } + assert(0); // but it's not a segment + // FIX - should be an error message conflict with section names + } + *pidx = namidx; + + //dbg_printf("\tNew segment - %d size %d\n", seg,SegData[seg]->SDbuf); + IDXSEC shtidx = elf_newsection2(namidx,type,flags,0,0,0,0,0,0,0); + SecHdrTab[shtidx].sh_addralign = align; + IDXSYM symidx = elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, shtidx); + int seg = elf_getsegment2(shtidx, symidx, 0); + //printf("-elf_getsegment() = %d\n", seg); + return seg; +} + +/******************************** + * Define a new code segment. + * Input: + * name name of segment, if NULL then revert to default + * suffix 0 use name as is + * 1 append "_TEXT" to name + * Output: + * cseg segment index of new current code segment + * Coffset starting offset in cseg + * Returns: + * segment index of newly created code segment + */ + +int obj_codeseg(char *name,int suffix) +{ + int seg; + const char *sfx; + + //dbg_printf("obj_codeseg(%s,%x)\n",name,suffix); + + sfx = (suffix) ? "_TEXT" : NULL; + + if (!name) // returning to default code segment + { + if (cseg != CODE) // not the current default + { + SegData[cseg]->SDoffset = Coffset; + Coffset = SegData[CODE]->SDoffset; + cseg = CODE; + } + return cseg; + } + + seg = elf_getsegment(name, sfx, SHT_PROGDEF, SHF_ALLOC|SHF_EXECINSTR, 4); + // find or create code segment + + cseg = seg; // new code segment index + Coffset = 0; + + return seg; +} + +/********************************* + * Define segments for Thread Local Storage. + * Here's what the elf tls spec says: + * Field .tbss .tdata + * sh_name .tbss .tdata + * sh_type SHT_NOBITS SHT_PROGBITS + * sh_flags SHF_ALLOC|SHF_WRITE| SHF_ALLOC|SHF_WRITE| + * SHF_TLS SHF_TLS + * sh_addr virtual addr of section virtual addr of section + * sh_offset 0 file offset of initialization image + * sh_size size of section size of section + * sh_link SHN_UNDEF SHN_UNDEF + * sh_info 0 0 + * sh_addralign alignment of section alignment of section + * sh_entsize 0 0 + * We want _tlsstart and _tlsend to bracket all the D tls data. + * The default linker script (ld -verbose) says: + * .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + * .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + * so if we assign names: + * _tlsstart .tdata + * symbols .tdata. + * symbols .tbss + * _tlsend .tbss. + * this should work. + * Don't care about sections emitted by other languages, as we presume they + * won't be storing D gc roots in their tls. + * Output: + * seg_tlsseg set to segment number for TLS segment. + * Returns: + * segment for TLS segment + */ + +seg_data *obj_tlsseg() +{ + /* Ensure that ".tdata" precedes any other .tdata. section, as the ld + * linker script fails to work right. + */ + elf_getsegment(".tdata", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, 4); + + static const char tlssegname[] = ".tdata."; + //dbg_printf("obj_tlsseg(\n"); + + if (seg_tlsseg == UNKNOWN) + { + seg_tlsseg = elf_getsegment(tlssegname, NULL, SHT_PROGDEF, + SHF_ALLOC|SHF_WRITE|SHF_TLS, I64 ? 16 : 4); + } + return SegData[seg_tlsseg]; +} + + +/********************************* + * Define segments for Thread Local Storage. + * Output: + * seg_tlsseg_bss set to segment number for TLS segment. + * Returns: + * segment for TLS segment + */ + +seg_data *obj_tlsseg_bss() +{ + static const char tlssegname[] = ".tbss"; + //dbg_printf("obj_tlsseg_bss(\n"); + + if (seg_tlsseg_bss == UNKNOWN) + { + seg_tlsseg_bss = elf_getsegment(tlssegname, NULL, SHT_NOBITS, + SHF_ALLOC|SHF_WRITE|SHF_TLS, I64 ? 16 : 4); + } + return SegData[seg_tlsseg_bss]; +} + + +/******************************* + * Output an alias definition record. + */ + +void obj_alias(const char *n1,const char *n2) +{ + dbg_printf("obj_alias(%s,%s)\n",n1,n2); + assert(0); +#if NOT_DONE + char *buffer = (char *) alloca(strlen(n1) + strlen(n2) + 2 * ONS_OHD); + unsigned len = obj_namestring(buffer,n1); + len += obj_namestring(buffer + len,n2); + objrecord(ALIAS,buffer,len); +#endif +} + +char *unsstr(unsigned value) +{ + static char buffer[64]; + + sprintf(buffer, "%d", value); + return buffer; +} + +/******************************* + * Mangle a name. + * Returns: + * mangled name + */ + +char *obj_mangle2(Symbol *s,char *dest) +{ + char *name; + + //dbg_printf("obj_mangle('%s'), mangle = x%x\n",s->Sident,type_mangle(s->Stype)); + symbol_debug(s); + assert(dest); +#if SCPP + name = CPP ? cpp_mangle(s) : s->Sident; +#elif MARS + name = cpp_mangle(s); +#else + name = s->Sident; +#endif + size_t len = strlen(name); // # of bytes in name + //dbg_printf("len %d\n",len); + switch (type_mangle(s->Stype)) + { + case mTYman_pas: // if upper case + case mTYman_for: + if (len >= DEST_LEN) + dest = (char *)mem_malloc(len + 1); + memcpy(dest,name,len + 1); // copy in name and ending 0 + for (int i = 0; 1; i++) + { char c = dest[i]; + if (!c) + break; + if (c >= 'a' && c <= 'z') + dest[i] = c + 'A' - 'a'; + } + break; + case mTYman_std: +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (tyfunc(s->ty()) && !variadic(s->Stype)) +#else + if (!(config.flags4 & CFG4oldstdmangle) && + config.exe == EX_NT && tyfunc(s->ty()) && + !variadic(s->Stype)) +#endif + { + char *pstr = unsstr(type_paramsize(s->Stype)); + size_t pstrlen = strlen(pstr); + size_t destlen = len + 1 + pstrlen + 1; + + if (destlen > DEST_LEN) + dest = (char *)mem_malloc(destlen); + memcpy(dest,name,len); + dest[len] = '@'; + memcpy(dest + 1 + len, pstr, pstrlen + 1); + break; + } + case mTYman_cpp: + case mTYman_c: + case mTYman_d: + case mTYman_sys: + case 0: + if (len >= DEST_LEN) + dest = (char *)mem_malloc(len + 1); + memcpy(dest,name,len+1);// copy in name and trailing 0 + break; + + default: +#ifdef DEBUG + printf("mangling %x\n",type_mangle(s->Stype)); + symbol_print(s); +#endif + printf("%d\n", type_mangle(s->Stype)); + assert(0); + } + //dbg_printf("\t %s\n",dest); + return dest; +} + +/******************************* + * Export a function name. + */ + +void obj_export(Symbol *s,unsigned argsize) +{ + //dbg_printf("obj_export(%s,%d)\n",s->Sident,argsize); +} + +/******************************* + * Update data information about symbol + * align for output and assign segment + * if not already specified. + * + * Input: + * sdata data symbol + * datasize output size + * seg default seg if not known + * Returns: + * actual seg + */ + +int elf_data_start(Symbol *sdata, targ_size_t datasize, int seg) +{ + targ_size_t alignbytes; + //printf("elf_data_start(%s,size %llx,seg %d)\n",sdata->Sident,datasize,seg); + //symbol_print(sdata); + + if (sdata->Sseg == UNKNOWN) // if we don't know then there + sdata->Sseg = seg; // wasn't any segment override + else + seg = sdata->Sseg; + targ_size_t offset = Offset(seg); + alignbytes = align(datasize, offset) - offset; + if (alignbytes) + obj_lidata(seg, offset, alignbytes); + sdata->Soffset = offset + alignbytes; + return seg; +} + +/******************************* + * Update function info before codgen + * + * If code for this function is in a different segment + * than the current default in cseg, switch cseg to new segment. + */ + +void elf_func_start(Symbol *sfunc) +{ + //dbg_printf("elf_func_start(%s)\n",sfunc->Sident); + symbol_debug(sfunc); + + if ((tybasic(sfunc->ty()) == TYmfunc) && (sfunc->Sclass == SCextern)) + { // create a new code segment + sfunc->Sseg = + elf_getsegment(".gnu.linkonce.t.", cpp_mangle(sfunc), SHT_PROGDEF, SHF_ALLOC|SHF_EXECINSTR,4); + + } + else if (sfunc->Sseg == UNKNOWN) + sfunc->Sseg = CODE; + //dbg_printf("sfunc->Sseg %d CODE %d cseg %d Coffset %d\n",sfunc->Sseg,CODE,cseg,Coffset); + cseg = sfunc->Sseg; + assert(cseg == CODE || cseg > COMD); + objpubdef(cseg, sfunc, Coffset); + sfunc->Soffset = Coffset; + + if (config.fulltypes) + dwarf_func_start(sfunc); +} + +/******************************* + * Update function info after codgen + */ + +void elf_func_term(Symbol *sfunc) +{ + //dbg_printf("elf_func_term(%s) offset %x, Coffset %x symidx %d\n", +// sfunc->Sident, sfunc->Soffset,Coffset,sfunc->Sxtrnnum); + + // fill in the function size + if (I64) + SymbolTable64[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset; + else + SymbolTable[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset; + if (config.fulltypes) + dwarf_func_term(sfunc); +} + +/******************************** + * Output a public definition. + * Input: + * seg = segment index that symbol is defined in + * s -> symbol + * offset = offset of name within segment + */ + +void objpubdef(int seg, Symbol *s, targ_size_t offset) +{ + const targ_size_t symsize= + tyfunc(s->ty()) ? Offset(s->Sseg) - offset : type_size(s->Stype); + objpubdefsize(seg, s, offset, symsize); +} + +/******************************** + * Output a public definition. + * Input: + * seg = segment index that symbol is defined in + * s -> symbol + * offset = offset of name within segment + * symsize size of symbol + */ + +void objpubdefsize(int seg, Symbol *s, targ_size_t offset, targ_size_t symsize) +{ + int bind; + switch (s->Sclass) + { + case SCglobal: + case SCinline: + bind = STB_GLOBAL; + break; + case SCcomdat: + case SCcomdef: + bind = STB_WEAK; + break; + default: + bind = STB_LOCAL; + break; + } + +#if 0 + //printf("\nobjpubdef(%d,%s,%d)\n",seg,s->Sident,offset); + //symbol_print(s); +#endif + + symbol_debug(s); + IDXSTR namidx = elf_addmangled(s); + //printf("\tnamidx %d,section %d\n",namidx,MAP_SEG2SECIDX(seg)); + if (tyfunc(s->ty())) + { + s->Sxtrnnum = elf_addsym(namidx, offset, symsize, + STT_FUNC, bind, MAP_SEG2SECIDX(seg)); + } + else + { + const unsigned typ = (s->ty() & mTYthread) ? STT_TLS : STT_OBJECT; + s->Sxtrnnum = elf_addsym(namidx, offset, symsize, + typ, bind, MAP_SEG2SECIDX(seg)); + } + fflush(NULL); +} + +/******************************* + * Output an external symbol for name. + * Input: + * name Name to do EXTDEF on + * (Not to be mangled) + * Returns: + * Symbol table index of the definition + * NOTE: Numbers will not be linear. + */ + +int objextern(const char *name) +{ + //dbg_printf("objextdef('%s')\n",name); + assert(name); + IDXSTR namidx = elf_addstr(symtab_strings,name); + IDXSYM symidx = elf_addsym(namidx, 0, 0, STT_NOTYPE, STB_GLOBAL, SHT_UNDEF); + return symidx; +} + +int objextdef(const char *name) +{ + return objextern(name); +} + +/******************************* + * Output an external for existing symbol. + * Input: + * s Symbol to do EXTDEF on + * (Name is to be mangled) + * Returns: + * Symbol table index of the definition + * NOTE: Numbers will not be linear. + */ + +int objextern(Symbol *s) +{ + int symtype,sectype; + unsigned size; + + //dbg_printf("objextern('%s') %x\n",s->Sident,s->Svalue); + symbol_debug(s); + IDXSTR namidx = elf_addmangled(s); + +#if SCPP + if (s->Sscope && !tyfunc(s->ty())) + { + symtype = STT_OBJECT; + sectype = SHT_COMMON; + size = type_size(s->Stype); + } + else +#endif + { + symtype = STT_NOTYPE; + sectype = SHT_UNDEF; + size = 0; + } + if (s->ty() & mTYthread) + { + //printf("objextern('%s') %x TLS\n",s->Sident,s->Svalue); + symtype = STT_TLS; + } + + s->Sxtrnnum = elf_addsym(namidx, size, size, symtype, + /*(s->ty() & mTYweak) ? STB_WEAK : */STB_GLOBAL, sectype); + return s->Sxtrnnum; + +} + +/******************************* + * Output a common block definition. + * Input: + * p -> external identifier + * size size in bytes of each elem + * count number of elems + * Returns: + * Symbol table index for symbol + */ + +int obj_comdef(Symbol *s,targ_size_t size,targ_size_t count) +{ + //printf("obj_comdef('%s',%d,%d)\n",s->Sident,size,count); + symbol_debug(s); + + int align = I64 ? 16 : 4; + if (s->ty() & mTYthread) + { + s->Sseg = elf_getsegment(".tbss.", cpp_mangle(s), + SHT_NOBITS, SHF_ALLOC|SHF_WRITE|SHF_TLS, align); + s->Sfl = FLtlsdata; + SegData[s->Sseg]->SDsym = s; + SegData[s->Sseg]->SDoffset += size * count; + objpubdef(s->Sseg, s, 0); + searchfixlist(s); + return s->Sseg; + } + else + { + s->Sseg = elf_getsegment(".bss.", cpp_mangle(s), + SHT_NOBITS, SHF_ALLOC|SHF_WRITE, align); + s->Sfl = FLudata; + SegData[s->Sseg]->SDsym = s; + SegData[s->Sseg]->SDoffset += size * count; + objpubdef(s->Sseg, s, 0); + searchfixlist(s); + return s->Sseg; + } +#if 0 + IDXSTR namidx = elf_addmangled(s); + alignOffset(UDATA,size); + IDXSYM symidx = elf_addsym(namidx, SegData[UDATA]->SDoffset, size*count, + (s->ty() & mTYthread) ? STT_TLS : STT_OBJECT, + STB_WEAK, SHI_BSS); + //dbg_printf("\tobj_comdef returning symidx %d\n",symidx); + s->Sseg = UDATA; + s->Sfl = FLudata; + SegData[UDATA]->SDoffset += size * count; + return symidx; +#endif +} + +int obj_comdef(Symbol *s, int flag, targ_size_t size, targ_size_t count) +{ + return obj_comdef(s, size, count); +} + +/*************************************** + * Append an iterated data block of 0s. + * (uninitialized data only) + */ + +void obj_write_zeros(seg_data *pseg, targ_size_t count) +{ + obj_lidata(pseg->SDseg, pseg->SDoffset, count); +} + +/*************************************** + * Output an iterated data block of 0s. + * + * For boundary alignment and initialization + */ + +void obj_lidata(int seg,targ_size_t offset,targ_size_t count) +{ + //printf("obj_lidata(%d,%x,%d)\n",seg,offset,count); + if (seg == UDATA || seg == UNKNOWN) + { // Use SDoffset to record size of .BSS section + SegData[UDATA]->SDoffset += count; + } + else if (MAP_SEG2SEC(seg)->sh_type == SHT_NOBITS) + { // Use SDoffset to record size of .TBSS section + SegData[seg]->SDoffset += count; + } + else + { + obj_bytes(seg, offset, count, NULL); + } +} + +/*********************************** + * Append byte to segment. + */ + +void obj_write_byte(seg_data *pseg, unsigned byte) +{ + obj_byte(pseg->SDseg, pseg->SDoffset, byte); +} + +/************************************ + * Output byte to object file. + */ + +void obj_byte(int seg,targ_size_t offset,unsigned byte) +{ + Outbuffer *buf = SegData[seg]->SDbuf; + int save = buf->size(); + //dbg_printf("obj_byte(seg=%d, offset=x%lx, byte=x%x)\n",seg,offset,byte); + buf->setsize(offset); + buf->writeByte(byte); + if (save > offset+1) + buf->setsize(save); + else + SegData[seg]->SDoffset = offset+1; + //dbg_printf("\tsize now %d\n",buf->size()); +} + +/*********************************** + * Append bytes to segment. + */ + +void obj_write_bytes(seg_data *pseg, unsigned nbytes, void *p) +{ + obj_bytes(pseg->SDseg, pseg->SDoffset, nbytes, p); +} + +/************************************ + * Output bytes to object file. + * Returns: + * nbytes + */ + +unsigned obj_bytes(int seg, targ_size_t offset, unsigned nbytes, void *p) +{ +#if 0 + if (!(seg >= 0 && seg <= seg_count)) + { printf("obj_bytes: seg = %d, seg_count = %d\n", seg, seg_count); + *(char*)0=0; + } +#endif + assert(seg >= 0 && seg <= seg_count); + Outbuffer *buf = SegData[seg]->SDbuf; + if (buf == NULL) + { + //dbg_printf("obj_bytes(seg=%d, offset=x%lx, nbytes=%d, p=x%x)\n", seg, offset, nbytes, p); + //raise(SIGSEGV); + assert(buf != NULL); + } + int save = buf->size(); + //dbg_printf("obj_bytes(seg=%d, offset=x%lx, nbytes=%d, p=x%x)\n", + //seg,offset,nbytes,p); + buf->setsize(offset); + buf->reserve(nbytes); + if (p) + { + buf->writen(p,nbytes); + } + else + { // Zero out the bytes + buf->clearn(nbytes); + } + if (save > offset+nbytes) + buf->setsize(save); + else + SegData[seg]->SDoffset = offset+nbytes; + return nbytes; +} + +/******************************* + * Output a relocation entry for a segment + * Input: + * seg = where the address is going + * offset = offset within seg + * type = ELF relocation type RI_TYPE_XXXX + * index = Related symbol table index + * val = addend or displacement from address + */ + +int relcnt=0; + +void elf_addrel(int seg, targ_size_t offset, unsigned type, + IDXSYM symidx, targ_size_t val) +{ + seg_data *segdata; + Outbuffer *buf; + IDXSEC secidx; + + //assert(val == 0); + relcnt++; + //dbg_printf("%d-elf_addrel(seg %d,offset x%x,type x%x,symidx %d,val %d)\n", + //relcnt,seg, offset, type, symidx,val); + + assert(seg >= 0 && seg <= seg_count); + segdata = SegData[seg]; + secidx = MAP_SEG2SECIDX(seg); + assert(secidx != 0); + + if (segdata->SDrel == NULL) + segdata->SDrel = new Outbuffer(); + if (segdata->SDrel->size() == 0) + { IDXSEC relidx; + + if (secidx == SHI_TEXT) + relidx = SHI_RELTEXT; + else if (secidx == SHI_DATA) + relidx = SHI_RELDATA; + else + { + // Get the section name, and make a copy because + // elf_newsection() may reallocate the string buffer. + char *section_name = (char *)GET_SECTION_NAME(secidx); + int len = strlen(section_name) + 1; + char *p = (char *)alloca(len); + memcpy(p, section_name, len); + + relidx = elf_newsection(I64 ? ".rela" : ".rel", p, I64 ? SHT_RELA : SHT_REL, 0); + segdata->SDrelidx = relidx; + } + + if (I64) + { + /* Note that we're using Elf32_Shdr here instead of Elf64_Shdr. This is to make + * the code a bit simpler. In obj_term(), we translate the Elf32_Shdr into the proper + * Elf64_Shdr. + */ + Elf32_Shdr *relsec = &SecHdrTab[relidx]; + relsec->sh_link = SHI_SYMTAB; + relsec->sh_info = secidx; + relsec->sh_entsize = sizeof(Elf64_Rela); + relsec->sh_addralign = 8; + } + else + { + Elf32_Shdr *relsec = &SecHdrTab[relidx]; + relsec->sh_link = SHI_SYMTAB; + relsec->sh_info = secidx; + relsec->sh_entsize = sizeof(Elf32_Rel); + relsec->sh_addralign = 4; + } + } + + if (I64) + { + Elf64_Rela rel; + rel.r_offset = offset; // build relocation information + rel.r_info = ELF64_R_INFO(symidx,type); + rel.r_addend = val; + buf = segdata->SDrel; + buf->write(&rel,sizeof(rel)); + segdata->SDrelcnt++; + + if (offset >= segdata->SDrelmaxoff) + segdata->SDrelmaxoff = offset; + else + { // insert numerically + Elf64_Rela *relbuf = (Elf64_Rela *)buf->buf; + int i = relbuf[segdata->SDrelindex].r_offset > offset ? 0 : segdata->SDrelindex; + while (i < segdata->SDrelcnt) + { + if (relbuf[i].r_offset > offset) + break; + i++; + } + assert(i != segdata->SDrelcnt); // slide greater offsets down + memmove(relbuf+i+1,relbuf+i,sizeof(Elf64_Rela) * (segdata->SDrelcnt - i - 1)); + *(relbuf+i) = rel; // copy to correct location + segdata->SDrelindex = i; // next entry usually greater + } + } + else + { + Elf32_Rel rel; + rel.r_offset = offset; // build relocation information + rel.r_info = ELF32_R_INFO(symidx,type); + buf = segdata->SDrel; + buf->write(&rel,sizeof(rel)); + segdata->SDrelcnt++; + + if (offset >= segdata->SDrelmaxoff) + segdata->SDrelmaxoff = offset; + else + { // insert numerically + Elf32_Rel *relbuf = (Elf32_Rel *)buf->buf; + int i = relbuf[segdata->SDrelindex].r_offset > offset ? 0 : segdata->SDrelindex; + while (i < segdata->SDrelcnt) + { + if (relbuf[i].r_offset > offset) + break; + i++; + } + assert(i != segdata->SDrelcnt); // slide greater offsets down + memmove(relbuf+i+1,relbuf+i,sizeof(Elf32_Rel) * (segdata->SDrelcnt - i - 1)); + *(relbuf+i) = rel; // copy to correct location + segdata->SDrelindex = i; // next entry usually greater + } + } +} + +/******************************* + * Refer to address that is in the data segment. + * Input: + * seg = where the address is going + * offset = offset within seg + * val = displacement from address + * targetdatum = DATA, CDATA or UDATA, depending where the address is + * flags = CFoff, CFseg, CFoffset64 + * Example: + * int *abc = &def[3]; + * to allocate storage: + * reftodatseg(DATA,offset,3 * sizeof(int *),UDATA); + */ + +void reftodatseg(int seg,targ_size_t offset,targ_size_t val, + unsigned targetdatum,int flags) +{ + Outbuffer *buf; + int save; + + buf = SegData[seg]->SDbuf; + save = buf->size(); + buf->setsize(offset); +#if 0 + printf("reftodatseg(seg=%d, offset=x%llx, val=x%llx,data %x, flags %x)\n", + seg,(unsigned long long)offset,(unsigned long long)val,targetdatum,flags); +#endif + /*if (OPT_IS_SET(OPTfwritable_strings)) + { + elf_addrel(seg,offset,RI_TYPE_SYM32,STI_DATA,0); + } + else*/ + { + int relinfo; + targ_size_t v = 0; + + if (I64) + { + if (flags & CFoffset64) + { + relinfo = R_X86_64_64; + elf_addrel(seg, offset, relinfo, STI_RODAT, val); + buf->write64(0); + if (save > offset + 8) + buf->setsize(save); + return; + } + else if (MAP_SEG2TYP(seg) == CODE && config.flags3 & CFG3pic) + { relinfo = R_X86_64_PC32; + //v = -4L; + } + else if (MAP_SEG2SEC(targetdatum)->sh_flags & SHF_TLS) + relinfo = config.flags3 & CFG3pic ? R_X86_64_TLSGD : R_X86_64_TPOFF32; + else + relinfo = R_X86_64_32; + } + else + { + if (MAP_SEG2TYP(seg) == CODE && config.flags3 & CFG3pic) + relinfo = RI_TYPE_GOTOFF; + else if (MAP_SEG2SEC(targetdatum)->sh_flags & SHF_TLS) + relinfo = config.flags3 & CFG3pic ? RI_TYPE_TLS_GD : RI_TYPE_TLS_LE; + else + relinfo = RI_TYPE_SYM32; + } + + elf_addrel(seg, offset, relinfo, STI_RODAT, v); + } + buf->write32(val); + if (save > offset + 4) + buf->setsize(save); +} + +/******************************* + * Refer to address that is in the code segment. + * Only offsets are output, regardless of the memory model. + * Used to put values in switch address tables. + * Input: + * seg = where the address is going (CODE or DATA) + * offset = offset within seg + * val = displacement from start of this module + */ + +void reftocodseg(int seg,targ_size_t offset,targ_size_t val) +{ + Outbuffer *buf; + int save; + int segtyp = MAP_SEG2TYP(seg); + + //dbg_printf("reftocodseg(seg=%d, offset=x%lx, val=x%lx )\n",seg,offset,val); + assert(seg > 0); // COMDATs not done yet + buf = SegData[seg]->SDbuf; + save = buf->size(); + buf->setsize(offset); +#if 0 + if (segtyp == CODE) + { + val = val - funcsym_p->Soffset; + elf_addrel(seg,offset,RI_TYPE_PC32,funcsym_p->Sxtrnnum,0); + } + else +#endif + { + val = val - funcsym_p->Soffset; + int relinfo; + targ_size_t v = 0; + if (I64) + relinfo = (config.flags3 & CFG3pic) ? R_X86_64_PC32 : R_X86_64_32; + else + relinfo = (config.flags3 & CFG3pic) ? RI_TYPE_GOTOFF : RI_TYPE_SYM32; + elf_addrel(seg,offset, relinfo, funcsym_p->Sxtrnnum, v); + } + buf->write32(val); + if (save > offset + 4) + buf->setsize(save); +} + +/******************************* + * Refer to an identifier. + * Input: + * segtyp = where the address is going (CODE or DATA) + * offset = offset within seg + * s -> Symbol table entry for identifier + * val = displacement from identifier + * flags = CFselfrel: self-relative + * CFseg: get segment + * CFoff: get offset + * CFoffset64: 64 bit fixup + * CFpc32: I64: PC relative 32 bit fixup + * Returns: + * number of bytes in reference (4 or 8) + */ + +int reftoident(int seg, targ_size_t offset, Symbol *s, targ_size_t val, + int flags) +{ + bool external = TRUE; + Outbuffer *buf; + elf_u32_f32 relinfo,refseg; + int segtyp = MAP_SEG2TYP(seg); + //assert(val == 0); + int retsize = (flags & CFoffset64) ? 8 : 4; + targ_size_t v = 0; + +#if 0 + printf("\nreftoident('%s' seg %d, offset x%llx, val x%llx, flags x%x)\n", + s->Sident,seg,offset,val,flags); + dbg_printf("Sseg = %d, Sxtrnnum = %d, retsize = %d\n",s->Sseg,s->Sxtrnnum,retsize); + symbol_print(s); +#endif + + tym_t ty = s->ty(); + if (s->Sxtrnnum) + { // identifier is defined somewhere else + if (I64) + { + if (SymbolTable64[s->Sxtrnnum].st_shndx != SHT_UNDEF) + external = FALSE; + } + else + { + if (SymbolTable[s->Sxtrnnum].st_shndx != SHT_UNDEF) + external = FALSE; + } + } + + switch (s->Sclass) + { + case SClocstat: + buf = SegData[seg]->SDbuf; + if (I64) + { + if (s->Sfl == FLtlsdata) + relinfo = config.flags3 & CFG3pic ? R_X86_64_TLSGD : R_X86_64_TPOFF32; + else + { relinfo = config.flags3 & CFG3pic ? R_X86_64_PC32 : R_X86_64_32; + if (flags & CFpc32) + relinfo = R_X86_64_PC32; + if (relinfo == R_X86_64_PC32) + { + assert(retsize == 4); + if (val > 0xFFFFFFFF) + { /* The value to be added is bigger than 32 bits, so we + * transfer it to the 64 bit addend of the fixup record + */ + v = val; + val = 0; + } + } + } + } + else + { + if (s->Sfl == FLtlsdata) + relinfo = config.flags3 & CFG3pic ? RI_TYPE_TLS_GD : RI_TYPE_TLS_LE; + else + relinfo = config.flags3 & CFG3pic ? RI_TYPE_GOTOFF : RI_TYPE_SYM32; + } + if (flags & CFoffset64 && relinfo == R_X86_64_32) + { + relinfo = R_X86_64_64; + elf_addrel(seg,offset,relinfo,STI_RODAT,val + s->Soffset); + buf->write64(0); + } + else + { + elf_addrel(seg,offset,relinfo,STI_RODAT,v); + if (retsize == 8) + buf->write64(val + s->Soffset); + else + buf->write32(val + s->Soffset); + } + break; + + case SCcomdat: + case_SCcomdat: + case SCstatic: +#if 0 + if ((s->Sflags & SFLthunk) && s->Soffset) + { // A thunk symbol that has be defined + assert(s->Sseg == seg); + val = (s->Soffset+val) - (offset+4); + goto outaddrval; + } + // FALL_THROUGH +#endif + + case SCextern: + case SCcomdef: + case_extern: + case SCglobal: + if (!s->Sxtrnnum) + { // not in symbol table yet - class might change + //dbg_printf("\tadding %s to fixlist\n",s->Sident); + addtofixlist(s,offset,seg,val,flags); + return retsize; + } + else + { + int save; + buf = SegData[seg]->SDbuf; + save = buf->size(); + buf->setsize(offset); + if (flags & CFselfrel) + { // only for function references within code segments + if (!external && // local definition found + s->Sseg == seg && // within same code segment + (!(config.flags3 & CFG3pic) || // not position indp code + s->Sclass == SCstatic)) // or is pic, but declared static + { // Can use PC relative + //dbg_printf("\tdoing PC relative\n"); + val = (s->Soffset+val) - (offset+4); + } + else + { + //dbg_printf("\tadding relocation\n"); + if (I64) + { relinfo = config.flags3 & CFG3pic ? R_X86_64_PLT32 : R_X86_64_PC32; + elf_addrel(seg,offset, relinfo, s->Sxtrnnum, -4); + val = 0; + } + else + { relinfo = config.flags3 & CFG3pic ? RI_TYPE_PLT32 : RI_TYPE_PC32; + elf_addrel(seg,offset, relinfo, s->Sxtrnnum, 0); + val = (targ_size_t)-4; + } + } + } + else + { // code to code code to data, data to code, data to data refs + refseg = s->Sxtrnnum; // default to name symbol table entry + if (s->Sclass == SCstatic) + { // offset into .data or .bss seg + refseg = MAP_SEG2SYMIDX(s->Sseg); + // use segment symbol table entry + val += s->Soffset; + if (!(config.flags3 & CFG3pic) || // all static refs from normal code + segtyp == DATA) // or refs from data from posi indp + { + if (I64) + relinfo = (flags & CFpc32) ? R_X86_64_PC32 : R_X86_64_32; + else + relinfo = RI_TYPE_SYM32; + } + else + { + relinfo = I64 ? R_X86_64_PC32 : RI_TYPE_GOTOFF; + } + } + else if (config.flags3 & CFG3pic && s == GOTsym) + { // relocation for Gbl Offset Tab + relinfo = I64 ? R_X86_64_NONE : RI_TYPE_GOTPC; + } + else if (segtyp == DATA) + { // relocation from within DATA seg + relinfo = I64 ? R_X86_64_32 : RI_TYPE_SYM32; + } + else + { // relocation from within CODE seg + if (I64) + { if (config.flags3 & CFG3pic) + relinfo = R_X86_64_GOTPCREL; + else + relinfo = (flags & CFpc32) ? R_X86_64_PC32 : R_X86_64_32; + } + else + relinfo = config.flags3 & CFG3pic ? RI_TYPE_GOT32 : RI_TYPE_SYM32; + } + if ((s->ty() & mTYLINK) & mTYthread) + { + if (I64) + { + if (config.flags3 & CFG3pic) + { + if (s->Sclass == SCstatic || s->Sclass == SClocstat) + relinfo = R_X86_64_TLSGD; // TLS_GD? + else + relinfo = R_X86_64_TLSGD; + } + else + { + if (s->Sclass == SCstatic || s->Sclass == SClocstat) + relinfo = R_X86_64_TPOFF32; + else + relinfo = R_X86_64_GOTTPOFF; + } + } + else + { + if (config.flags3 & CFG3pic) + { + if (s->Sclass == SCstatic) + relinfo = RI_TYPE_TLS_LE; // TLS_GD? + else + relinfo = RI_TYPE_TLS_IE; + } + else + { + if (s->Sclass == SCstatic) + relinfo = RI_TYPE_TLS_LE; + else + relinfo = RI_TYPE_TLS_IE; + } + } + } + targ_size_t v = 0; + if (flags & CFoffset64 && relinfo == R_X86_64_32) + { + // The value to be added must only reside in the 64 bit addend. + relinfo = R_X86_64_64; + v = val; + val = 0; + } + //printf("\t\t************* adding relocation\n"); + if (I64 && retsize == 4) + { + assert(retsize == 4); + if (val > 0xFFFFFFFF) + { /* The value to be added is bigger than 32 bits, so we + * transfer it to the 64 bit addend of the fixup record + */ + v = val; + val = 0; + } + } +#if 0 + targ_size_t v = (relinfo == R_X86_64_PC32) ? -4 : 0; + if (relinfo == R_X86_64_PC32 && flags & CFaddend8) + v = -8; +#endif + assert(!(I64 && relinfo == R_X86_64_64) || val == 0); + elf_addrel(seg,offset,relinfo,refseg,v); + } +outaddrval: + if (retsize == 8) + buf->write64(val); + else + buf->write32(val); + if (save > offset + retsize) + buf->setsize(save); + } + break; + + case SCsinline: + case SCeinline: + printf ("Undefined inline value <>\n"); + //warerr(WM_undefined_inline,s->Sident); + case SCinline: + if (tyfunc(ty)) + { + s->Sclass = SCextern; + goto case_extern; + } + else if (config.flags2 & CFG2comdat) + goto case_SCcomdat; // treat as initialized common block + + default: +#ifdef DEBUG + //symbol_print(s); +#endif + assert(0); + } + return retsize; +} + +/***************************************** + * Generate far16 thunk. + * Input: + * s Symbol to generate a thunk for + */ + +void obj_far16thunk(Symbol *s) +{ + //dbg_printf("obj_far16thunk('%s')\n", s->Sident); + assert(0); +} + +/************************************** + * Mark object file as using floating point. + */ + +void obj_fltused() +{ + //dbg_printf("obj_fltused()\n"); +} + +/************************************ + * Close and delete .OBJ file. + */ + +void objfile_delete() +{ + //remove(fobjname); // delete corrupt output file +} + +/********************************** + * Terminate. + */ + +void objfile_term() +{ +#if TERMCODE + mem_free(fobjname); + fobjname = NULL; +#endif +} + +/********************************** + * Write to the object file + */ +void objfile_write(FILE *fd, void *buffer, unsigned len) +{ + fobjbuf->write(buffer, len); +} + +long elf_align(FILE *fd, targ_size_t size,long foffset) +{ + long offset; + switch (size) + { + case 0: + case 1: + return foffset; + case 4: + offset = (foffset + 3) & ~3; + break; + case 8: + offset = (foffset + 7) & ~7; + break; + case 16: + offset = (foffset + 15) & ~15; + break; + case 32: + offset = (foffset + 31) & ~31; + break; + default: + dbg_printf("size was %d\n",(int)size); + assert(0); + break; + } + if (offset > foffset) + fobjbuf->writezeros(offset - foffset); + return offset; +} + +/*************************************** + * Stuff pointer to ModuleInfo in its own segment. + */ + +#if MARS + +void obj_moduleinfo(Symbol *scc) +{ +// if (I64) return; // for now, until Phobos64 works + + int codeOffset, refOffset; + + /* Put in the ModuleReference. */ + { + /* struct ModuleReference + * { + * void* next; + * ModuleReference* module; + * } + */ + const int seg = DATA; + alignOffset(seg, NPTRSIZE); + SegData[seg]->SDoffset = SegData[seg]->SDbuf->size(); + refOffset = SegData[seg]->SDoffset; + SegData[seg]->SDbuf->writezeros(NPTRSIZE); + SegData[seg]->SDoffset += NPTRSIZE; + SegData[seg]->SDoffset += reftoident(seg, SegData[seg]->SDoffset, scc, 0, CFoffset64 | CFoff); + } + + /* Constructor that links the ModuleReference to the head of + * the list pointed to by _Dmoduleref + */ + { + /* ret + * codeOffset: + * pushad + * mov EAX,&ModuleReference + * mov ECX,_DmoduleRef + * mov EDX,[ECX] + * mov [EAX],EDX + * mov [ECX],EAX + * popad + * ret + */ + + const int seg = CODE; + Outbuffer *buf = SegData[seg]->SDbuf; + SegData[seg]->SDoffset = buf->size(); + codeOffset = SegData[seg]->SDoffset; + + if (I64 && config.flags3 & CFG3pic) + { // LEA RAX,ModuleReference[RIP] + buf->writeByte(REX | REX_W); + buf->writeByte(0x8D); + buf->writeByte(modregrm(0,AX,5)); + buf->write32(refOffset); + elf_addrel(seg, codeOffset + 3, R_X86_64_PC32, STI_DATA, -4); + + // MOV RCX,_DmoduleRef@GOTPCREL[RIP] + buf->writeByte(REX | REX_W); + buf->writeByte(0x8B); + buf->writeByte(modregrm(0,CX,5)); + buf->write32(0); + elf_addrel(seg, codeOffset + 10, R_X86_64_GOTPCREL, objextern("_Dmodule_ref"), -4); + } + else + { + const int reltype = I64 ? R_X86_64_32 : RI_TYPE_SYM32; + + /* movl ModuleReference*, %eax */ + buf->writeByte(0xB8); + buf->write32(refOffset); + elf_addrel(seg, codeOffset + 1, reltype, STI_DATA, 0); + + /* movl _Dmodule_ref, %ecx */ + buf->writeByte(0xB9); + buf->write32(0); + elf_addrel(seg, codeOffset + 6, reltype, objextern("_Dmodule_ref"), 0); + } + + if (I64) + buf->writeByte(REX | REX_W); + buf->writeByte(0x8B); buf->writeByte(0x11); /* movl (%ecx), %edx */ + if (I64) + buf->writeByte(REX | REX_W); + buf->writeByte(0x89); buf->writeByte(0x10); /* movl %edx, (%eax) */ + if (I64) + buf->writeByte(REX | REX_W); + buf->writeByte(0x89); buf->writeByte(0x01); /* movl %eax, (%ecx) */ + + buf->writeByte(0xC3); /* ret */ + SegData[seg]->SDoffset = buf->size(); + } + + /* Add reference to constructor into ".ctors" segment + */ + const int seg = elf_getsegment(".ctors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, NPTRSIZE); + + Outbuffer *buf = SegData[seg]->SDbuf; + if (I64) + { + elf_addrel(seg, SegData[seg]->SDoffset, R_X86_64_64, STI_TEXT, codeOffset); + buf->write64(0); + } + else + { + elf_addrel(seg, SegData[seg]->SDoffset, RI_TYPE_SYM32, STI_TEXT, 0); + buf->write32(codeOffset); + } + SegData[seg]->SDoffset += NPTRSIZE; +} + +#endif + +/************************************* + */ + +void elfobj_gotref(symbol *s) +{ + //printf("elfobj_gotref(%x '%s', %d)\n",s,s->Sident, s->Sclass); + switch(s->Sclass) + { + case SCstatic: + case SClocstat: + s->Sfl = FLgotoff; + break; + + case SCextern: + case SCglobal: + case SCcomdat: + case SCcomdef: + s->Sfl = FLgot; + break; + + default: + break; + } +} + +#endif +#endif diff --git a/backend/evalu8.c b/backend/evalu8.c new file mode 100644 index 00000000..59600236 --- /dev/null +++ b/backend/evalu8.c @@ -0,0 +1,2089 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include +#include +#include + +#if !defined(__OpenBSD__) +// Mysteriously missing from OpenBSD +#include +#endif + +#if __DMC__ +#include +#endif + +#if __FreeBSD__ || __OpenBSD__ +#define fmodl fmod +#endif + +#include "cc.h" +#include "oper.h" /* OPxxxx definitions */ +#include "global.h" +#include "el.h" +#include "type.h" + +#if SCPP +#include "parser.h" +#include "cpp.h" +#endif + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +extern void error(const char *filename, unsigned linnum, const char *format, ...); + +#if linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4 +int _status87() +{ + return fetestexcept(FE_ALL_EXCEPT); +} + +void _clear87() +{ + feclearexcept(FE_ALL_EXCEPT); +} +#endif + +CEXTERN elem * evalu8(elem *); + +/* When this !=0, we do constant folding on floating point constants + * even if they raise overflow, underflow, invalid, etc. exceptions. + */ + +static int ignore_exceptions; + +/* When this is !=0, we try to fold out OPsizeof expressions. + */ + +static int resolve_sizeof; + +/************************************ + * Helper to do % for long doubles. + */ + +#if __DMC__ +long double _modulo(long double x, long double y) +{ short sw; + + __asm + { + fld tbyte ptr y + fld tbyte ptr x // ST = x, ST1 = y +FM1: // We don't use fprem1 because for some inexplicable + // reason we get -5 when we do _modulo(15, 10) + fprem // ST = ST % ST1 + fstsw word ptr sw + fwait + mov AH,byte ptr sw+1 // get msb of status word in AH + sahf // transfer to flags + jp FM1 // continue till ST < ST1 + fstp ST(1) // leave remainder on stack + } +} +#endif + +/********************** + * Return boolean result of constant elem. + */ + +HINT boolres(elem *e) +{ int b; + + //printf("boolres()\n"); + //elem_print(e); + elem_debug(e); +// assert((_status87() & 0x3800) == 0); + switch (e->Eoper) + { + case OPrelconst: + case OPstring: + return TRUE; + +#if SCPP + case OPvar: + assert(CPP && PARSER); + el_toconst(e); + assert(e->Eoper == OPconst); +#endif + case OPconst: + switch (tybasic(typemask(e))) + { case TYchar: + case TYuchar: + case TYschar: + case TYchar16: + case TYshort: + case TYushort: + case TYint: + case TYuint: + case TYbool: + case TYwchar_t: + case TYenum: +#if !MARS + case TYmemptr: +#endif + case TYlong: + case TYulong: + case TYdchar: + case TYllong: + case TYullong: +#if JHANDLE + case TYjhandle: +#endif +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: + case TYhptr: + case TYfptr: + case TYvptr: +#endif + case TYnptr: + b = el_tolong(e) != 0; + break; + case TYfloat: + case TYifloat: + case TYdouble: + case TYidouble: + case TYdouble_alias: + case TYildouble: + case TYldouble: + { targ_ldouble ld = el_toldouble(e); + + if (isnan((double)ld)) + b = 1; + else + b = (ld != 0); + break; + } + case TYcfloat: + if (isnan(e->EV.Vcfloat.re) || isnan(e->EV.Vcfloat.im)) + b = 1; + else + b = e->EV.Vcfloat.re != 0 || e->EV.Vcfloat.im != 0; + break; + case TYcdouble: + case TYdouble2: + if (isnan(e->EV.Vcdouble.re) || isnan(e->EV.Vcdouble.im)) + b = 1; + else + b = e->EV.Vcdouble.re != 0 || e->EV.Vcdouble.im != 0; + break; + case TYcldouble: + if (isnan(e->EV.Vcldouble.re) || isnan(e->EV.Vcldouble.im)) + b = 1; + else + b = e->EV.Vcldouble.re != 0 || e->EV.Vcldouble.im != 0; + break; + case TYstruct: // happens on syntax error of (struct x)0 +#if SCPP + assert(errcnt); +#else + assert(0); +#endif + case TYvoid: /* happens if we get syntax errors or + on RHS of && || expressions */ + b = 0; + break; + + case TYcent: + case TYucent: + case TYschar16: + case TYuchar16: + case TYshort8: + case TYushort8: + case TYlong4: + case TYulong4: + case TYllong2: + case TYullong2: + b = e->EV.Vcent.lsw || e->EV.Vcent.msw; + break; + + case TYfloat4: + { b = 0; + targ_float *pf = (targ_float *)&e->EV.Vcent; + for (size_t i = 0; i < 4; i++) + { + if (isnan(pf[i]) || pf[i] != 0) + { b = 1; + break; + } + } + break; + } + + default: +#ifdef DEBUG + WRTYxx(typemask(e)); +#endif + assert(0); + } + break; + default: + assert(0); + } + return b; +} + +/*************************** + * Return TRUE if expression will always evaluate to TRUE. + */ + +HINT iftrue(elem *e) +{ + while (1) + { + assert(e); + elem_debug(e); + switch (e->Eoper) + { case OPcomma: + case OPinfo: + e = e->E2; + break; + case OPrelconst: + case OPconst: + case OPstring: + return boolres(e); + default: + return FALSE; + } + } +} + +/*************************** + * Return TRUE if expression will always evaluate to FALSE. + */ + +HINT iffalse(elem *e) +{ + while (1) + { assert(e); + elem_debug(e); + switch (e->Eoper) + { case OPcomma: + case OPinfo: + e = e->E2; + break; + case OPconst: + return !boolres(e); + //case OPstring: + //case OPrelconst: + default: + return FALSE; + } + } +} + +/****************************** + * Constant fold expression tree. + * Calculate &symbol and &*e1 if we can. + */ + +#if SCPP + +elem *poptelem2(elem *e) +{ + // Same as poptelem(), but we ignore floating point exceptions + ignore_exceptions++; + e = poptelem(e); + ignore_exceptions--; + return e; +} + +elem *poptelem3(elem *e) +{ + resolve_sizeof++; + e = poptelem(e); + resolve_sizeof--; + return e; +} + +elem *poptelem4(elem *e) +{ + resolve_sizeof++; + ignore_exceptions++; + e = poptelem(e); + ignore_exceptions--; + resolve_sizeof--; + return e; +} + +elem *poptelem(elem *e) +{ + elem *e1,*e2; + unsigned op; + + //dbg_printf("poptelem(e = %p)\n", e); elem_print(e); +#ifdef DEBUG + assert(PARSER); + assert(e && e->ET); + + if (controlc_saw) + exit(1); +// static int xxx; if (++xxx == 1000) *(char *)0 = 0; +#endif + elem_debug(e); + type_debug(e->ET); + + op = e->Eoper; + +#ifdef DEBUG + if (OTunary(op)) + assert(!e->E2 || op == OPinfo); +#endif + + switch (op) + { + case OPvar: + if (CPP && e->EV.sp.Vsym->Sflags & SFLvalue) + el_toconst(e); + break; + + case OPsizeof: + if (resolve_sizeof) + { + e->Eoper = OPconst; + e->EV.Vlong = type_size(e->EV.sp.Vsym->Stype); + } + break; + + case OPconst: + case OPrelconst: + case OPstring: + break; + + case OPaddr: + e1 = e->E1; + if (e1->Eoper == OPvar) + goto L3; + e->E1 = e1 = poptelem(e1); + if (e1->Eoper == OPind) /* if &*exp */ + { type *t; + + L6: + t = e->ET; + e1->E1 = cast(e1->E1,t); + e = selecte1(selecte1(e,t),t); + } + else if (e1->Eoper == OPvar) + { /* convert &var to relconst */ + L3: + e = selecte1(e,e->ET); + e->Eoper = OPrelconst; +#if 1 + // If this is an address of a function template, + // try to expand the template if it's got an explicit + // parameter list. + if (e->PEFflags & PEFtemplate_id) + { symbol *s; + + s = e->EV.sp.Vsym; + s = cpp_lookformatch(s, NULL, NULL,NULL,NULL,NULL, + e->EV.sp.spu.Vtal, 1|8, NULL, NULL); + if (s) + { + e->EV.sp.Vsym = s; + param_free(&e->EV.sp.spu.Vtal); + e->PEFflags &= ~PEFtemplate_id; + type_settype(&e->ET, newpointer(s->Stype)); + } + } +#endif + } + break; + case OPind: + e->E1 = e1 = poptelem(e->E1); +#if TX86 + if (e1->Eoper == OPrelconst) + { /* convert *(&var) to var */ + + e = selecte1(e,e->ET); + e->Eoper = OPvar; + } +#else + if (e1->Eoper == OPrelconst) + { + unsigned to_sz = tysize(tym_conv(e->ET)); + unsigned frm_sz = tysize(tym_conv(e1->ET)); + + if (tyfunc(tybasic(e->ET->Tty))) + to_sz = LONGSIZE; + else if (tybasic(e->ET->Tty) == TYstruct || tybasic(e->ET->Tty) == TYarray) + { + to_sz = LONGSIZE; + e1->ET = e->ET; + } + if(to_sz == frm_sz) + { /* convert *(&var) to var */ +doit: + e = selecte1(e,e->ET); + e->Eoper = OPvar; + } + else /* handle the most common cases for now */ + { unsigned offset = e1->Eoffset; + switch(to_sz) + { + case SHORTSIZE: + if (frm_sz == LONGSIZE && (offset%LONGSIZE) == SHORTSIZE) + goto doit; + break; + case CHARSIZE: + if (frm_sz == LONGSIZE && + offset%(LONGSIZE-CHARSIZE) == CHARSIZE) + goto doit; + if (frm_sz == SHORTSIZE && offset&1) + goto doit; + break; + } + } + } +#endif + break; +#if TARGET_SEGMENTED + case OPnp_fp: + e->E1 = e1 = poptelem(e->E1); + // If casting a non-NULL constant pointer + if (e1->Eoper == OPconst && el_tolong(e1) != 0) + break; + goto L5; + case OPoffset: + e->E1 = e1 = poptelem(e->E1); + if (e1->Eoper == OPnp_fp) + goto L6; + goto L5; +#endif + case OP32_16: + e->E1 = e1 = poptelem(e->E1); + L5: + if (e1->Eoper == OPrelconst || e1->Eoper == OPstring) + e = selecte1(e,e->ET); + else + goto eval; + break; + case OPandand: + e->E1 = e1 = poptelem(e->E1); + if (iffalse(e1)) + goto L2; + else + goto def; + case OPoror: + e->E1 = e1 = poptelem(e->E1); + if (iftrue(e1)) + { + L2: el_free(e->E2); + e->E2 = NULL; + e->Eoper = OPbool; + e = poptelem(e); + } + else + goto def; + break; + case OPcond: + e->E1 = e1 = poptelem(e->E1); + if (e1->Eoper == OPconst) + { + e2 = e->E2; + type_free(e->ET); + if (boolres(e1)) + { el_copy(e,e2->E1); + e2->E1->Eoper = OPunde; + e2->E1->ET = NULL; + e2->E1->E1 = NULL; + e2->E1->E2 = NULL; + el_free(e2->E1); + e2->E1 = NULL; + } + else + { el_copy(e,e2->E2); + e2->E2->Eoper = OPunde; + e2->E2->ET = NULL; + e2->E2->E1 = NULL; + e2->E2->E2 = NULL; + el_free(e2->E2); + e2->E2 = NULL; + } + el_free(e2); + el_free(e1); + e = poptelem(e); + } + else + goto def; + break; + case OPadd: + e->E1 = e1 = poptelem(e->E1); + e->E2 = e2 = poptelem(e->E2); + if (e1->Eoper == OPconst) + { /* swap leaves */ + e->E1 = e2; + e2 = e->E2 = e1; + e1 = e->E1; + } + goto L4; + case OPmin: + e->E1 = e1 = poptelem(e->E1); + e->E2 = e2 = poptelem(e->E2); + L4: + if (e1->Eoper == OPrelconst || e1->Eoper == OPstring) + { + if (e2->Eoper == OPconst) + { targ_int i = e2->EV.Vint; + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (i && e1->EV.sp.Vsym->Sfl == FLgot) + break; +#endif + if (e->Eoper == OPmin) + i = -i; + e1->EV.sp.Voffset += i; + e = selecte1(e,e->ET); + break; + } + } + goto eval; + default: + if (OTleaf(op)) + goto ret; + e->E1 = poptelem(e->E1); + def: + if (OTbinary(op)) // if binary node + { + e->E2 = poptelem(e->E2); + } + eval: + e = evalu8(e); + break; + } +ret: + return e; +} + +/******************** + * Select E1 leaf of e, and give it type t. + */ + +elem *selecte1(elem *e,type *t) +{ elem *e1; + + elem_debug(e); + assert(EOP(e)); + e1 = e->E1; + el_settype(e1,t); + e->E1 = NULL; + el_free(e); + return e1; +} + +#endif + +/****************************** + * Evaluate a node with only constants as leaves. + * Return with the result. + */ + +elem * evalu8(elem *e) +{ elem *e1,*e2; + tym_t tym,tym2,uns; + unsigned op; + targ_int i1,i2; + int i; + targ_llong l1,l2; + targ_ldouble d1,d2; + elem esave; + +// assert((_status87() & 0x3800) == 0); + assert(e && EOP(e)); + op = e->Eoper; + elem_debug(e); + e1 = e->E1; + + //printf("evalu8(): "); elem_print(e); + elem_debug(e1); + if (e1->Eoper == OPconst && !tyvector(e1->Ety)) + { + tym2 = 0; + e2 = NULL; + if (EBIN(e)) + { e2 = e->E2; + elem_debug(e2); + if (e2->Eoper == OPconst && !tyvector(e2->Ety)) + { + i2 = l2 = el_tolong(e2); + d2 = el_toldouble(e2); + } + else + return e; + tym2 = tybasic(typemask(e2)); + } + else + { + tym2 = 0; + e2 = NULL; + i2 = 0; // not used, but static analyzer complains + l2 = 0; // " + d2 = 0; // " + } + i1 = l1 = el_tolong(e1); + d1 = el_toldouble(e1); + tym = tybasic(typemask(e1)); /* type of op is type of left child */ + +#if TX86 && SCPP + // Huge pointers are always evaluated at runtime + if (tym == TYhptr && (l1 != 0 || l2 != 0)) + return e; +#endif + esave = *e; +#if !__OpenBSD__ + _clear87(); +#endif + } + else + return e; + + /* if left or right leaf is unsigned, this is an unsigned operation */ + uns = tyuns(tym) | tyuns(tym2); + + /*elem_print(e);*/ + /*dbg_printf("x%lx ",l1); WROP(op); dbg_printf("x%lx = ",l2);*/ +#if 0 + if (0 && e2) + { + dbg_printf("d1 = %Lg, d2 = %Lg, op = %d, OPne = %d, tym = x%lx\n",d1,d2,op,OPne,tym); + dbg_printf("tym1 = x%lx, tym2 = x%lx, e2 = %g\n",tym,tym2,e2->EV.Vdouble); + + union eve u; + dbg_printf("d1 = x%16llx\n", (u.Vldouble = d1, u.Vullong)); + dbg_printf("d2 = x%16llx\n", (u.Vldouble = d2, u.Vullong)); + } +#endif + i = 0; + switch (op) + { + case OPadd: + switch (tym) + { + case TYfloat: + switch (tym2) + { + case TYfloat: + e->EV.Vfloat = e1->EV.Vfloat + e2->EV.Vfloat; + break; + case TYifloat: + e->EV.Vcfloat.re = e1->EV.Vfloat; + e->EV.Vcfloat.im = e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat.re = e1->EV.Vfloat + e2->EV.Vcfloat.re; + e->EV.Vcfloat.im = 0 + e2->EV.Vcfloat.im; + break; + default: + assert(0); + } + break; + case TYdouble: + case TYdouble_alias: + switch (tym2) + { + case TYdouble: + case TYdouble_alias: + e->EV.Vdouble = e1->EV.Vdouble + e2->EV.Vdouble; + break; + case TYidouble: + e->EV.Vcdouble.re = e1->EV.Vdouble; + e->EV.Vcdouble.im = e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble.re = e1->EV.Vdouble + e2->EV.Vcdouble.re; + e->EV.Vcdouble.im = 0 + e2->EV.Vcdouble.im; + break; + default: + assert(0); + } + break; + case TYldouble: + switch (tym2) + { + case TYldouble: + e->EV.Vldouble = d1 + d2; + break; + case TYildouble: + e->EV.Vcldouble.re = d1; + e->EV.Vcldouble.im = d2; + break; + case TYcldouble: + e->EV.Vcldouble.re = d1 + e2->EV.Vcldouble.re; + e->EV.Vcldouble.im = 0 + e2->EV.Vcldouble.im; + break; + default: + assert(0); + } + break; + case TYifloat: + switch (tym2) + { + case TYfloat: + e->EV.Vcfloat.re = e2->EV.Vfloat; + e->EV.Vcfloat.im = e1->EV.Vfloat; + break; + case TYifloat: + e->EV.Vfloat = e1->EV.Vfloat + e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat.re = 0 + e2->EV.Vcfloat.re; + e->EV.Vcfloat.im = e1->EV.Vfloat + e2->EV.Vcfloat.im; + break; + default: + assert(0); + } + break; + case TYidouble: + switch (tym2) + { + case TYdouble: + e->EV.Vcdouble.re = e2->EV.Vdouble; + e->EV.Vcdouble.im = e1->EV.Vdouble; + break; + case TYidouble: + e->EV.Vdouble = e1->EV.Vdouble + e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble.re = 0 + e2->EV.Vcdouble.re; + e->EV.Vcdouble.im = e1->EV.Vdouble + e2->EV.Vcdouble.im; + break; + default: + assert(0); + } + break; + case TYildouble: + switch (tym2) + { + case TYldouble: + e->EV.Vcldouble.re = d2; + e->EV.Vcldouble.im = d1; + break; + case TYildouble: + e->EV.Vldouble = d1 + d2; + break; + case TYcldouble: + e->EV.Vcldouble.re = 0 + e2->EV.Vcldouble.re; + e->EV.Vcldouble.im = d1 + e2->EV.Vcldouble.im; + break; + default: + assert(0); + } + break; + case TYcfloat: + switch (tym2) + { + case TYfloat: + e->EV.Vcfloat.re = e1->EV.Vcfloat.re + e2->EV.Vfloat; + e->EV.Vcfloat.im = e1->EV.Vcfloat.im; + break; + case TYifloat: + e->EV.Vcfloat.re = e1->EV.Vcfloat.re; + e->EV.Vcfloat.im = e1->EV.Vcfloat.im + e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat.re = e1->EV.Vcfloat.re + e2->EV.Vcfloat.re; + e->EV.Vcfloat.im = e1->EV.Vcfloat.im + e2->EV.Vcfloat.im; + break; + default: + assert(0); + } + break; + case TYcdouble: + switch (tym2) + { + case TYdouble: + e->EV.Vcdouble.re = e1->EV.Vcdouble.re + e2->EV.Vdouble; + e->EV.Vcdouble.im = e1->EV.Vcdouble.im; + break; + case TYidouble: + e->EV.Vcdouble.re = e1->EV.Vcdouble.re; + e->EV.Vcdouble.im = e1->EV.Vcdouble.im + e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble.re = e1->EV.Vcdouble.re + e2->EV.Vcdouble.re; + e->EV.Vcdouble.im = e1->EV.Vcdouble.im + e2->EV.Vcdouble.im; + break; + default: + assert(0); + } + break; + case TYcldouble: + switch (tym2) + { + case TYldouble: + e->EV.Vcldouble.re = e1->EV.Vcldouble.re + d2; + e->EV.Vcldouble.im = e1->EV.Vcldouble.im; + break; + case TYildouble: + e->EV.Vcldouble.re = e1->EV.Vcldouble.re; + e->EV.Vcldouble.im = e1->EV.Vcldouble.im + d2; + break; + case TYcldouble: + e->EV.Vcldouble.re = e1->EV.Vcldouble.re + e2->EV.Vcldouble.re; + e->EV.Vcldouble.im = e1->EV.Vcldouble.im + e2->EV.Vcldouble.im; + break; + default: + assert(0); + } + break; + + default: +#if TX86 + if (intsize == 2) + { if (tyfv(tym)) + e->EV.Vlong = (l1 & 0xFFFF0000) | + (targ_ushort) ((targ_ushort) l1 + i2); + else if (tyfv(tym2)) + e->EV.Vlong = (l2 & 0xFFFF0000) | + (targ_ushort) (i1 + (targ_ushort) l2); + else if (tyintegral(tym) || typtr(tym)) + e->EV.Vllong = l1 + l2; + else + assert(0); + } + else +#endif + if (tyintegral(tym) || typtr(tym)) + e->EV.Vllong = l1 + l2; + else + assert(0); + break; + } + break; + + case OPmin: + switch (tym) + { + case TYfloat: + switch (tym2) + { + case TYfloat: + e->EV.Vfloat = e1->EV.Vfloat - e2->EV.Vfloat; + break; + case TYifloat: + e->EV.Vcfloat.re = e1->EV.Vfloat; + e->EV.Vcfloat.im = -e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat.re = e1->EV.Vfloat - e2->EV.Vcfloat.re; + e->EV.Vcfloat.im = 0 - e2->EV.Vcfloat.im; + break; + default: + assert(0); + } + break; + case TYdouble: + case TYdouble_alias: + switch (tym2) + { + case TYdouble: + case TYdouble_alias: + e->EV.Vdouble = e1->EV.Vdouble - e2->EV.Vdouble; + break; + case TYidouble: + e->EV.Vcdouble.re = e1->EV.Vdouble; + e->EV.Vcdouble.im = -e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble.re = e1->EV.Vdouble - e2->EV.Vcdouble.re; + e->EV.Vcdouble.im = 0 - e2->EV.Vcdouble.im; + break; + default: + assert(0); + } + break; + case TYldouble: + switch (tym2) + { + case TYldouble: + e->EV.Vldouble = d1 - d2; + break; + case TYildouble: + e->EV.Vcldouble.re = d1; + e->EV.Vcldouble.im = -d2; + break; + case TYcldouble: + e->EV.Vcldouble.re = d1 - e2->EV.Vcldouble.re; + e->EV.Vcldouble.im = 0 - e2->EV.Vcldouble.im; + break; + default: + assert(0); + } + break; + case TYifloat: + switch (tym2) + { + case TYfloat: + e->EV.Vcfloat.re = -e2->EV.Vfloat; + e->EV.Vcfloat.im = e1->EV.Vfloat; + break; + case TYifloat: + e->EV.Vfloat = e1->EV.Vfloat - e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat.re = 0 - e2->EV.Vcfloat.re; + e->EV.Vcfloat.im = e1->EV.Vfloat - e2->EV.Vcfloat.im; + break; + default: + assert(0); + } + break; + case TYidouble: + switch (tym2) + { + case TYdouble: + e->EV.Vcdouble.re = -e2->EV.Vdouble; + e->EV.Vcdouble.im = e1->EV.Vdouble; + break; + case TYidouble: + e->EV.Vdouble = e1->EV.Vdouble - e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble.re = 0 - e2->EV.Vcdouble.re; + e->EV.Vcdouble.im = e1->EV.Vdouble - e2->EV.Vcdouble.im; + break; + default: + assert(0); + } + break; + case TYildouble: + switch (tym2) + { + case TYldouble: + e->EV.Vcldouble.re = -d2; + e->EV.Vcldouble.im = d1; + break; + case TYildouble: + e->EV.Vldouble = d1 - d2; + break; + case TYcldouble: + e->EV.Vcldouble.re = 0 - e2->EV.Vcldouble.re; + e->EV.Vcldouble.im = d1 - e2->EV.Vcldouble.im; + break; + default: + assert(0); + } + break; + case TYcfloat: + switch (tym2) + { + case TYfloat: + e->EV.Vcfloat.re = e1->EV.Vcfloat.re - e2->EV.Vfloat; + e->EV.Vcfloat.im = e1->EV.Vcfloat.im; + break; + case TYifloat: + e->EV.Vcfloat.re = e1->EV.Vcfloat.re; + e->EV.Vcfloat.im = e1->EV.Vcfloat.im - e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat.re = e1->EV.Vcfloat.re - e2->EV.Vcfloat.re; + e->EV.Vcfloat.im = e1->EV.Vcfloat.im - e2->EV.Vcfloat.im; + break; + default: + assert(0); + } + break; + case TYcdouble: + switch (tym2) + { + case TYdouble: + e->EV.Vcdouble.re = e1->EV.Vcdouble.re - e2->EV.Vdouble; + e->EV.Vcdouble.im = e1->EV.Vcdouble.im; + break; + case TYidouble: + e->EV.Vcdouble.re = e1->EV.Vcdouble.re; + e->EV.Vcdouble.im = e1->EV.Vcdouble.im - e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble.re = e1->EV.Vcdouble.re - e2->EV.Vcdouble.re; + e->EV.Vcdouble.im = e1->EV.Vcdouble.im - e2->EV.Vcdouble.im; + break; + default: + assert(0); + } + break; + case TYcldouble: + switch (tym2) + { + case TYldouble: + e->EV.Vcldouble.re = e1->EV.Vcldouble.re - d2; + e->EV.Vcldouble.im = e1->EV.Vcldouble.im; + break; + case TYildouble: + e->EV.Vcldouble.re = e1->EV.Vcldouble.re; + e->EV.Vcldouble.im = e1->EV.Vcldouble.im - d2; + break; + case TYcldouble: + e->EV.Vcldouble.re = e1->EV.Vcldouble.re - e2->EV.Vcldouble.re; + e->EV.Vcldouble.im = e1->EV.Vcldouble.im - e2->EV.Vcldouble.im; + break; + default: + assert(0); + } + break; + + default: +#if TX86 + if (intsize == 2 && + tyfv(tym) && tysize[tym2] == 2) + e->EV.Vllong = (l1 & 0xFFFF0000) | + (targ_ushort) ((targ_ushort) l1 - i2); + else +#endif + if (tyintegral(tym) || typtr(tym)) + e->EV.Vllong = l1 - l2; + else + assert(0); + break; + } + break; + case OPmul: + if (tyintegral(tym) || typtr(tym)) + e->EV.Vllong = l1 * l2; + else + { switch (tym) + { + case TYfloat: + switch (tym2) + { + case TYfloat: + case TYifloat: + e->EV.Vfloat = e1->EV.Vfloat * e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat.re = e1->EV.Vfloat * e2->EV.Vcfloat.re; + e->EV.Vcfloat.im = e1->EV.Vfloat * e2->EV.Vcfloat.im; + break; + default: + assert(0); + } + break; + case TYdouble: + case TYdouble_alias: + switch (tym2) + { + case TYdouble: + case TYdouble_alias: + case TYidouble: + e->EV.Vdouble = e1->EV.Vdouble * e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble.re = e1->EV.Vdouble * e2->EV.Vcdouble.re; + e->EV.Vcdouble.im = e1->EV.Vdouble * e2->EV.Vcdouble.im; + break; + default: + assert(0); + } + break; + case TYldouble: + switch (tym2) + { + case TYldouble: + case TYildouble: + e->EV.Vldouble = d1 * d2; + break; + case TYcldouble: + e->EV.Vcldouble.re = d1 * e2->EV.Vcldouble.re; + e->EV.Vcldouble.im = d1 * e2->EV.Vcldouble.im; + break; + default: + assert(0); + } + break; + case TYifloat: + switch (tym2) + { + case TYfloat: + e->EV.Vfloat = e1->EV.Vfloat * e2->EV.Vfloat; + break; + case TYifloat: + e->EV.Vfloat = -e1->EV.Vfloat * e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat.re = -e1->EV.Vfloat * e2->EV.Vcfloat.im; + e->EV.Vcfloat.im = e1->EV.Vfloat * e2->EV.Vcfloat.re; + break; + default: + assert(0); + } + break; + case TYidouble: + switch (tym2) + { + case TYdouble: + e->EV.Vdouble = e1->EV.Vdouble * e2->EV.Vdouble; + break; + case TYidouble: + e->EV.Vdouble = -e1->EV.Vdouble * e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble.re = -e1->EV.Vdouble * e2->EV.Vcdouble.im; + e->EV.Vcdouble.im = e1->EV.Vdouble * e2->EV.Vcdouble.re; + break; + default: + assert(0); + } + break; + case TYildouble: + switch (tym2) + { + case TYldouble: + e->EV.Vldouble = d1 * d2; + break; + case TYildouble: + e->EV.Vldouble = -d1 * d2; + break; + case TYcldouble: + e->EV.Vcldouble.re = -d1 * e2->EV.Vcldouble.im; + e->EV.Vcldouble.im = d1 * e2->EV.Vcldouble.re; + break; + default: + assert(0); + } + break; + case TYcfloat: + switch (tym2) + { + case TYfloat: + e->EV.Vcfloat.re = e1->EV.Vcfloat.re * e2->EV.Vfloat; + e->EV.Vcfloat.im = e1->EV.Vcfloat.im * e2->EV.Vfloat; + break; + case TYifloat: + e->EV.Vcfloat.re = -e1->EV.Vcfloat.im * e2->EV.Vfloat; + e->EV.Vcfloat.im = e1->EV.Vcfloat.re * e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat = Complex_f::mul(e1->EV.Vcfloat, e2->EV.Vcfloat); + break; + default: + assert(0); + } + break; + case TYcdouble: + switch (tym2) + { + case TYdouble: + e->EV.Vcdouble.re = e1->EV.Vcdouble.re * e2->EV.Vdouble; + e->EV.Vcdouble.im = e1->EV.Vcdouble.im * e2->EV.Vdouble; + break; + case TYidouble: + e->EV.Vcdouble.re = -e1->EV.Vcdouble.im * e2->EV.Vdouble; + e->EV.Vcdouble.im = e1->EV.Vcdouble.re * e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble = Complex_d::mul(e1->EV.Vcdouble, e2->EV.Vcdouble); + break; + default: + assert(0); + } + break; + case TYcldouble: + switch (tym2) + { + case TYldouble: + e->EV.Vcldouble.re = e1->EV.Vcldouble.re * d2; + e->EV.Vcldouble.im = e1->EV.Vcldouble.im * d2; + break; + case TYildouble: + e->EV.Vcldouble.re = -e1->EV.Vcldouble.im * d2; + e->EV.Vcldouble.im = e1->EV.Vcldouble.re * d2; + break; + case TYcldouble: + e->EV.Vcldouble = Complex_ld::mul(e1->EV.Vcldouble, e2->EV.Vcldouble); + break; + default: + assert(0); + } + break; + default: +#ifdef DEBUG + dbg_printf("tym = x%x\n",tym); + elem_print(e); +#endif + assert(0); + } + } + break; + case OPdiv: + if (!boolres(e2)) // divide by 0 + { +#if SCPP + if (!tyfloating(tym)) +#endif + goto div0; + } + if (uns) + e->EV.Vullong = ((targ_ullong) l1) / ((targ_ullong) l2); + else + { switch (tym) + { + case TYfloat: + switch (tym2) + { + case TYfloat: + e->EV.Vfloat = e1->EV.Vfloat / e2->EV.Vfloat; + break; + case TYifloat: + e->EV.Vfloat = -e1->EV.Vfloat / e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat.re = d1; + e->EV.Vcfloat.im = 0; + e->EV.Vcfloat = Complex_f::div(e->EV.Vcfloat, e2->EV.Vcfloat); + break; + default: + assert(0); + } + break; + case TYdouble: + case TYdouble_alias: + switch (tym2) + { + case TYdouble: + case TYdouble_alias: + e->EV.Vdouble = e1->EV.Vdouble / e2->EV.Vdouble; + break; + case TYidouble: + e->EV.Vdouble = -e1->EV.Vdouble / e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble.re = d1; + e->EV.Vcdouble.im = 0; + e->EV.Vcdouble = Complex_d::div(e->EV.Vcdouble, e2->EV.Vcdouble); + break; + default: + assert(0); + } + break; + case TYldouble: + switch (tym2) + { + case TYldouble: + e->EV.Vldouble = d1 / d2; + break; + case TYildouble: + e->EV.Vldouble = -d1 / d2; + break; + case TYcldouble: + e->EV.Vcldouble.re = d1; + e->EV.Vcldouble.im = 0; + e->EV.Vcldouble = Complex_ld::div(e->EV.Vcldouble, e2->EV.Vcldouble); + break; + default: + assert(0); + } + break; + case TYifloat: + switch (tym2) + { + case TYfloat: + case TYifloat: + e->EV.Vfloat = e1->EV.Vfloat / e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat.re = 0; + e->EV.Vcfloat.im = e1->EV.Vfloat; + e->EV.Vcfloat = Complex_f::div(e->EV.Vcfloat, e2->EV.Vcfloat); + break; + default: + assert(0); + } + break; + case TYidouble: + switch (tym2) + { + case TYdouble: + case TYidouble: + e->EV.Vdouble = e1->EV.Vdouble / e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble.re = 0; + e->EV.Vcdouble.im = e1->EV.Vdouble; + e->EV.Vcdouble = Complex_d::div(e->EV.Vcdouble, e2->EV.Vcdouble); + break; + default: + assert(0); + } + break; + case TYildouble: + switch (tym2) + { + case TYldouble: + case TYildouble: + e->EV.Vldouble = d1 / d2; + break; + case TYcldouble: + e->EV.Vcldouble.re = 0; + e->EV.Vcldouble.im = d1; + e->EV.Vcldouble = Complex_ld::div(e->EV.Vcldouble, e2->EV.Vcldouble); + break; + default: + assert(0); + } + break; + case TYcfloat: + switch (tym2) + { + case TYfloat: + e->EV.Vcfloat.re = e1->EV.Vcfloat.re / e2->EV.Vfloat; + e->EV.Vcfloat.im = e1->EV.Vcfloat.im / e2->EV.Vfloat; + break; + case TYifloat: + e->EV.Vcfloat.re = e1->EV.Vcfloat.im / e2->EV.Vfloat; + e->EV.Vcfloat.im = -e1->EV.Vcfloat.re / e2->EV.Vfloat; + break; + case TYcfloat: + e->EV.Vcfloat = Complex_f::div(e1->EV.Vcfloat, e2->EV.Vcfloat); + break; + default: + assert(0); + } + break; + case TYcdouble: + switch (tym2) + { + case TYdouble: + e->EV.Vcdouble.re = e1->EV.Vcdouble.re / e2->EV.Vdouble; + e->EV.Vcdouble.im = e1->EV.Vcdouble.im / e2->EV.Vdouble; + break; + case TYidouble: + e->EV.Vcdouble.re = e1->EV.Vcdouble.im / e2->EV.Vdouble; + e->EV.Vcdouble.im = -e1->EV.Vcdouble.re / e2->EV.Vdouble; + break; + case TYcdouble: + e->EV.Vcdouble = Complex_d::div(e1->EV.Vcdouble, e2->EV.Vcdouble); + break; + default: + assert(0); + } + break; + case TYcldouble: + switch (tym2) + { + case TYldouble: + e->EV.Vcldouble.re = e1->EV.Vcldouble.re / d2; + e->EV.Vcldouble.im = e1->EV.Vcldouble.im / d2; + break; + case TYildouble: + e->EV.Vcldouble.re = e1->EV.Vcldouble.im / d2; + e->EV.Vcldouble.im = -e1->EV.Vcldouble.re / d2; + break; + case TYcldouble: + e->EV.Vcldouble = Complex_ld::div(e1->EV.Vcldouble, e2->EV.Vcldouble); + break; + default: + assert(0); + } + break; + default: + e->EV.Vllong = l1 / l2; + break; + } + } + break; + case OPmod: + if (!boolres(e2)) + { + div0: +#if SCPP + synerr(EM_divby0); +#else // MARS + //error(e->Esrcpos.Sfilename, e->Esrcpos.Slinnum, "divide by zero"); +#endif + break; + } + if (uns) + e->EV.Vullong = ((targ_ullong) l1) % ((targ_ullong) l2); + else + { + // BUG: what do we do for imaginary, complex? + switch (tym) + { case TYdouble: + case TYidouble: + case TYdouble_alias: + e->EV.Vdouble = fmod(e1->EV.Vdouble,e2->EV.Vdouble); + break; + case TYfloat: + case TYifloat: + e->EV.Vfloat = fmodf(e1->EV.Vfloat,e2->EV.Vfloat); + break; + case TYldouble: + case TYildouble: +#if __DMC__ + e->EV.Vldouble = _modulo(d1, d2); +#else + e->EV.Vldouble = fmodl(d1, d2); +#endif + break; + case TYcfloat: + switch (tym2) + { + case TYfloat: + case TYifloat: + e->EV.Vcfloat.re = fmodf(e1->EV.Vcfloat.re, e2->EV.Vfloat); + e->EV.Vcfloat.im = fmodf(e1->EV.Vcfloat.im, e2->EV.Vfloat); + break; + default: + assert(0); + } + break; + case TYcdouble: + switch (tym2) + { + case TYdouble: + case TYidouble: + e->EV.Vcdouble.re = fmod(e1->EV.Vcdouble.re, e2->EV.Vdouble); + e->EV.Vcdouble.im = fmod(e1->EV.Vcdouble.im, e2->EV.Vdouble); + break; + default: + assert(0); + } + break; + case TYcldouble: + switch (tym2) + { + case TYldouble: + case TYildouble: +#if __DMC__ + e->EV.Vcldouble.re = _modulo(e1->EV.Vcldouble.re, d2); + e->EV.Vcldouble.im = _modulo(e1->EV.Vcldouble.im, d2); +#else + e->EV.Vcldouble.re = fmodl(e1->EV.Vcldouble.re, d2); + e->EV.Vcldouble.im = fmodl(e1->EV.Vcldouble.im, d2); +#endif + break; + default: + assert(0); + } + break; + default: + e->EV.Vllong = l1 % l2; + break; + } + } + break; + case OPremquo: + { + targ_llong rem, quo; + + if (!boolres(e2)) + goto div0; + if (uns) + { + rem = ((targ_ullong) l1) % ((targ_ullong) l2); + quo = ((targ_ullong) l1) / ((targ_ullong) l2); + } + else + { + rem = l1 % l2; + quo = l1 / l2; + } + switch (tysize(tym)) + { + case 2: + e->EV.Vllong = (rem << 16) | (quo & 0xFFFF); + break; + case 4: + e->EV.Vllong = (rem << 32) | (quo & 0xFFFFFFFF); + break; + case 8: + e->EV.Vcent.lsw = quo; + e->EV.Vcent.msw = rem; + break; + default: + assert(0); + break; + } + break; + } + case OPand: + e->EV.Vllong = l1 & l2; + break; + case OPor: + e->EV.Vllong = l1 | l2; + break; + case OPxor: + e->EV.Vllong = l1 ^ l2; + break; + case OPnot: + e->EV.Vint = boolres(e1) ^ TRUE; + break; + case OPcom: + e->EV.Vllong = ~l1; + break; + case OPcomma: + e->EV = e2->EV; + break; + case OPoror: + e->EV.Vint = boolres(e1) || boolres(e2); + break; + case OPandand: + e->EV.Vint = boolres(e1) && boolres(e2); + break; + case OPshl: + if ((targ_ullong) i2 < sizeof(targ_ullong) * 8) + e->EV.Vllong = l1 << i2; + else + e->EV.Vllong = 0; + break; + case OPshr: + if ((targ_ullong) i2 > sizeof(targ_ullong) * 8) + i2 = sizeof(targ_ullong) * 8; +#if SCPP + if (tyuns(tym)) + { //printf("unsigned\n"); + e->EV.Vullong = ((targ_ullong) l1) >> i2; + } + else + { //printf("signed\n"); + e->EV.Vllong = l1 >> i2; + } +#endif +#if MARS + // Always unsigned + e->EV.Vullong = ((targ_ullong) l1) >> i2; +#endif + break; + +#if MARS + case OPashr: + if ((targ_ullong) i2 > sizeof(targ_ullong) * 8) + i2 = sizeof(targ_ullong) * 8; + // Always signed + e->EV.Vllong = l1 >> i2; + break; +#endif + + case OPpair: + switch (tysize[tym]) + { + case 2: + e->EV.Vlong = (i2 << 16) | (i1 & 0xFFFF); + break; + case 4: + e->EV.Vllong = (l2 << 32) | (l1 & 0xFFFFFFFF); + break; + case 8: + e->EV.Vcent.lsw = l1; + e->EV.Vcent.msw = l2; + break; + default: + assert(0); + } + break; + + case OPneg: +#if TX86 + // Avoid converting NANS to NAN + memcpy(&e->EV.Vcldouble,&e1->EV.Vcldouble,sizeof(e->EV.Vcldouble)); + switch (tym) + { case TYdouble: + case TYidouble: + case TYdouble_alias: + e->EV.Vdouble = -e->EV.Vdouble; + break; + case TYfloat: + case TYifloat: + e->EV.Vfloat = -e->EV.Vfloat; + break; + case TYldouble: + case TYildouble: + e->EV.Vldouble = -e->EV.Vldouble; + break; + case TYcfloat: + e->EV.Vcfloat.re = -e->EV.Vcfloat.re; + e->EV.Vcfloat.im = -e->EV.Vcfloat.im; + break; + case TYcdouble: + e->EV.Vcdouble.re = -e->EV.Vcdouble.re; + e->EV.Vcdouble.im = -e->EV.Vcdouble.im; + break; + case TYcldouble: + e->EV.Vcldouble.re = -e->EV.Vcldouble.re; + e->EV.Vcldouble.im = -e->EV.Vcldouble.im; + break; + default: + e->EV.Vllong = -l1; + break; + } +#else + switch (tym) + { case TYdouble: + e->EV.Vdouble = -e1->EV.Vdouble; + break; + case TYfloat: + e->EV.Vfloat = -e1->EV.Vfloat; + break; + case TYldouble: + e->EV.Vldouble = -d1; + break; + default: + e->EV.Vllong = -l1; + break; + } +#endif + break; + case OPabs: +#if 1 + switch (tym) + { + case TYdouble: + case TYidouble: + case TYdouble_alias: + e->EV.Vdouble = fabs(e1->EV.Vdouble); + break; + case TYfloat: + case TYifloat: +#if __SC__ + e->EV.Vfloat = fabsf(e1->EV.Vfloat); +#else + e->EV.Vfloat = fabs(e1->EV.Vfloat); +#endif + break; + case TYldouble: + case TYildouble: + e->EV.Vldouble = fabsl(d1); + break; + case TYcfloat: + e->EV.Vfloat = Complex_f::abs(e1->EV.Vcfloat); + break; + case TYcdouble: + e->EV.Vdouble = Complex_d::abs(e1->EV.Vcdouble); + break; + case TYcldouble: + e->EV.Vldouble = Complex_ld::abs(e1->EV.Vcldouble); + break; + default: + e->EV.Vllong = labs(l1); + break; + } + break; +#endif + case OPsqrt: + case OPrndtol: + case OPsin: + case OPcos: + case OPrint: + return e; + case OPngt: + i++; + case OPgt: + if (!tyfloating(tym)) + goto Lnle; + i ^= (int)(d1 > d2); + e->EV.Vint = i; + break; + + case OPnle: + Lnle: + i++; + case OPle: + if (uns) + { + i ^= (int)(((targ_ullong) l1) <= ((targ_ullong) l2)); + } + else + { + if (tyfloating(tym)) + i ^= (int)(d1 <= d2); + else + i ^= (int)(l1 <= l2); + } + e->EV.Vint = i; + break; + + case OPnge: + i++; + case OPge: + if (!tyfloating(tym)) + goto Lnlt; + i ^= (int)(d1 >= d2); + e->EV.Vint = i; + break; + + case OPnlt: + Lnlt: + i++; + case OPlt: + if (uns) + { + i ^= (int)(((targ_ullong) l1) < ((targ_ullong) l2)); + } + else + { + if (tyfloating(tym)) + i ^= (int)(d1 < d2); + else + i ^= (int)(l1 < l2); + } + e->EV.Vint = i; + break; + + case OPne: + i++; + case OPeqeq: + if (tyfloating(tym)) + { + switch (tybasic(tym)) + { + case TYcfloat: + if (isnan(e1->EV.Vcfloat.re) || isnan(e1->EV.Vcfloat.im) || + isnan(e2->EV.Vcfloat.re) || isnan(e2->EV.Vcfloat.im)) + i ^= 1; + else + i ^= (int)((e1->EV.Vcfloat.re == e2->EV.Vcfloat.re) && + (e1->EV.Vcfloat.im == e2->EV.Vcfloat.im)); + break; + case TYcdouble: + if (isnan(e1->EV.Vcdouble.re) || isnan(e1->EV.Vcdouble.im) || + isnan(e2->EV.Vcdouble.re) || isnan(e2->EV.Vcdouble.im)) + i ^= 1; + else + i ^= (int)((e1->EV.Vcdouble.re == e2->EV.Vcdouble.re) && + (e1->EV.Vcdouble.im == e2->EV.Vcdouble.im)); + break; + case TYcldouble: + if (isnan(e1->EV.Vcldouble.re) || isnan(e1->EV.Vcldouble.im) || + isnan(e2->EV.Vcldouble.re) || isnan(e2->EV.Vcldouble.im)) + i ^= 1; + else + i ^= (int)((e1->EV.Vcldouble.re == e2->EV.Vcldouble.re) && + (e1->EV.Vcldouble.im == e2->EV.Vcldouble.im)); + break; + default: + i ^= (int)(d1 == d2); + break; + } + //printf("%Lg + %Lgi, %Lg + %Lgi\n", e1->EV.Vcldouble.re, e1->EV.Vcldouble.im, e2->EV.Vcldouble.re, e2->EV.Vcldouble.im); + } + else + i ^= (int)(l1 == l2); + e->EV.Vint = i; + break; + +#if __DMC__ + case OPord: + i++; + case OPunord: + // BUG: complex numbers + i ^= d1 !<>= d2; + e->EV.Vint = i; + break; + + case OPnlg: + i++; + case OPlg: + // BUG: complex numbers + i ^= d1 <> d2; + e->EV.Vint = i; + break; + + case OPnleg: + i++; + case OPleg: + // BUG: complex numbers + i ^= d1 <>= d2; + e->EV.Vint = i; + break; + + case OPnule: + i++; + case OPule: + // BUG: complex numbers + i ^= d1 !> d2; + e->EV.Vint = i; + break; + + case OPnul: + i++; + case OPul: + // BUG: complex numbers + i ^= d1 !>= d2; + e->EV.Vint = i; + break; + + case OPnuge: + i++; + case OPuge: + // BUG: complex numbers + i ^= d1 !< d2; + e->EV.Vint = i; + break; + + case OPnug: + i++; + case OPug: + // BUG: complex numbers + i ^= d1 !<= d2; + e->EV.Vint = i; + break; + + case OPnue: + i++; + case OPue: + // BUG: complex numbers + i ^= d1 !<> d2; + e->EV.Vint = i; + break; + +#endif + case OPs16_32: + e->EV.Vlong = (targ_short) i1; + break; +#if TARGET_SEGMENTED + case OPnp_fp: +#endif + case OPu16_32: + e->EV.Vulong = (targ_ushort) i1; + break; + case OPd_u32: + e->EV.Vulong = (targ_ulong)d1; + //printf("OPd_u32: dbl = %g, ulng = x%lx\n",d1,e->EV.Vulong); + break; + case OPd_s32: + e->EV.Vlong = (targ_long)d1; + break; + case OPu32_d: + e->EV.Vdouble = (unsigned) l1; + break; + case OPs32_d: + e->EV.Vdouble = (int) l1; + break; + case OPd_s16: + e->EV.Vint = (targ_int)d1; + break; + case OPs16_d: + e->EV.Vdouble = (targ_short) i1; + break; + case OPd_u16: + e->EV.Vushort = (targ_ushort)d1; + break; + case OPu16_d: + e->EV.Vdouble = (targ_ushort) i1; + break; + case OPd_s64: + e->EV.Vllong = (targ_llong)d1; + break; + case OPd_u64: + case OPld_u64: + e->EV.Vullong = (targ_ullong)d1; + break; + case OPs64_d: + e->EV.Vdouble = l1; + break; + case OPu64_d: + e->EV.Vdouble = (targ_ullong) l1; + break; + case OPd_f: + //assert((_status87() & 0x3800) == 0); + e->EV.Vfloat = e1->EV.Vdouble; + if (tycomplex(tym)) + e->EV.Vcfloat.im = e1->EV.Vcdouble.im; + //assert((_status87() & 0x3800) == 0); + break; + case OPf_d: + e->EV.Vdouble = e1->EV.Vfloat; + if (tycomplex(tym)) + e->EV.Vcdouble.im = e1->EV.Vcfloat.im; + break; + case OPd_ld: + e->EV.Vldouble = e1->EV.Vdouble; + if (tycomplex(tym)) + e->EV.Vcldouble.im = e1->EV.Vcdouble.im; + break; + case OPld_d: + e->EV.Vdouble = e1->EV.Vldouble; + if (tycomplex(tym)) + e->EV.Vcdouble.im = e1->EV.Vcldouble.im; + break; + case OPc_r: + e->EV = e1->EV; + break; + case OPc_i: + switch (tym) + { + case TYcfloat: + e->EV.Vfloat = e1->EV.Vcfloat.im; + break; + case TYcdouble: + e->EV.Vdouble = e1->EV.Vcdouble.im; + break; + case TYcldouble: + e->EV.Vldouble = e1->EV.Vcldouble.im; + break; + default: + assert(0); + } + break; + case OPs8_16: + e->EV.Vint = (targ_schar) i1; + break; + case OPu8_16: + e->EV.Vint = i1 & 0xFF; + break; + case OP16_8: + e->EV.Vint = i1; + break; + case OPbool: + e->EV.Vint = boolres(e1); + break; + case OP32_16: +#if TARGET_SEGMENTED + case OPoffset: +#endif + e->EV.Vint = l1; + break; + + case OP64_32: + e->EV.Vlong = l1; + break; + case OPs32_64: + e->EV.Vllong = (targ_long) l1; + break; + case OPu32_64: + e->EV.Vllong = (targ_ulong) l1; + break; + + case OP128_64: + e->EV.Vllong = e1->EV.Vcent.lsw; + break; + case OPs64_128: + e->EV.Vcent.lsw = e1->EV.Vllong; + e->EV.Vcent.msw = 0; + if ((targ_llong)e->EV.Vcent.lsw < 0) + e->EV.Vcent.msw = ~(targ_ullong)0; + break; + case OPu64_128: + e->EV.Vcent.lsw = e1->EV.Vullong; + e->EV.Vcent.msw = 0; + break; + + case OPmsw: + switch (tysize(tym)) + { + case 4: + e->EV.Vllong = (l1 >> 16) & 0xFFFF; + break; + case 8: + e->EV.Vllong = (l1 >> 32) & 0xFFFFFFFF; + break; + case 16: + e->EV.Vllong = e1->EV.Vcent.msw; + break; + default: + assert(0); + } + break; + case OPb_8: + e->EV.Vlong = i1 & 1; + break; + case OPbswap: + e->EV.Vint = ((i1 >> 24) & 0x000000FF) | + ((i1 >> 8) & 0x0000FF00) | + ((i1 << 8) & 0x00FF0000) | + ((i1 << 24) & 0xFF000000); + break; + case OProl: + case OPror: + { unsigned n = i2; + if (op == OPror) + n = -n; + switch (tysize(tym)) + { + case 1: + n &= 7; + e->EV.Vuchar = (unsigned char)((i1 << n) | ((i1 & 0xFF) >> (8 - n))); + break; + case 2: + n &= 0xF; + e->EV.Vushort = (targ_ushort)((i1 << n) | ((i1 & 0xFFFF) >> (16 - n))); + break; + case 4: + n &= 0x1F; + e->EV.Vulong = (targ_ulong)((i1 << n) | ((i1 & 0xFFFFFFFF) >> (32 - n))); + break; + case 8: + n &= 0x3F; + e->EV.Vullong = (targ_ullong)((l1 << n) | ((l1 & 0xFFFFFFFFFFFFFFFFLL) >> (64 - n))); + break; + //case 16: + default: + assert(0); + } + break; + } + case OPind: +#if 0 && MARS + /* The problem with this is that although the only reaching definition + * of the variable is null, it still may never get executed, as in: + * int* p = null; if (p) *p = 3; + * and the error will be spurious. + */ + if (l1 >= 0 && l1 < 4096) + { + error(e->Esrcpos.Sfilename, e->Esrcpos.Slinnum, "dereference of null pointer"); + e->E1->EV.Vlong = 4096; // suppress redundant messages + } +#endif + return e; + default: + return e; + } +#if TX86 + int flags; + + if (!ignore_exceptions && + (config.flags4 & CFG4fastfloat) == 0 && +#if __OpenBSD__ + 1 // until OpenBSD supports C standard fenv.h +#else + _status87() & 0x3F +#endif + ) + { + // Exceptions happened. Do not fold the constants. + *e = esave; + return e; + } +#if SCPP + else if ((flags = _status87()) & 0x3F) + { // Should also give diagnostic warning for: + // overflow, underflow, denormal, invalid + if (flags & 0x04) + warerr(WM_divby0); +// else if (flags & 0x08) // overflow +// warerr(WM_badnumber); + } +#endif +#endif + + /*dbg_printf("result = x%lx\n",e->EV.Vlong);*/ + e->Eoper = OPconst; + el_free(e1); + if (e2) + el_free(e2); +#if !__GNUC__ + //printf("2: %x\n", _status87()); + assert((_status87() & 0x3800) == 0); +#endif + //printf("evalu8() returns: "); elem_print(e); + return e; +} + +#endif /* !SPP */ diff --git a/backend/exh.h b/backend/exh.h new file mode 100644 index 00000000..12a15c7c --- /dev/null +++ b/backend/exh.h @@ -0,0 +1,47 @@ +// Copyright (C) 1993-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +//#pragma once +#ifndef EXCEPT_H +#define EXCEPT_H 1 + +struct Aobject +{ + symbol *AOsym; // symbol for active object + targ_size_t AOoffset; // offset from that object + symbol *AOfunc; // cleanup function +}; + + +/* except.c */ +void except_init(void); +void except_term(void); +elem *except_obj_ctor(elem *e,symbol *s,targ_size_t offset,symbol *sdtor); +elem *except_obj_dtor(elem *e,symbol *s,targ_size_t offset); +elem *except_throw_expression(void); +type *except_declaration(symbol *cv); +void except_exception_spec(type *t); +void except_index_set(int index); +int except_index_get(void); +void except_pair_setoffset(void *p,targ_size_t offset); +void except_pair_append(void *p, int index); +void except_push(void *p,elem *e,block *b); +void except_pop(void *p,elem *e,block *b); +void except_mark(); +void except_release(); +symbol *except_gensym(); +symbol *except_gentables(); +void except_fillInEHTable(symbol *s); +void except_reset(); + +#endif + diff --git a/backend/gdag.c b/backend/gdag.c new file mode 100644 index 00000000..9cd9af4c --- /dev/null +++ b/backend/gdag.c @@ -0,0 +1,852 @@ +// Copyright (C) 1986-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +#if (SCPP || MARS) && !HTOD + +#include +#include + +#include "cc.h" +#include "global.h" +#include "el.h" +#include "go.h" +#include "ty.h" +#include "oper.h" +#include "vec.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +STATIC void aewalk(elem **pn , vec_t ae); +STATIC elem * delcse(elem **pe); +STATIC void removecses(elem **pe); +STATIC void boundscheck(elem *e, vec_t ae); + +enum Aetype { AEcse, AEarraybounds }; +static Aetype aetype; + +/************************************* + * Determine if floating point should be cse'd. + */ + +inline int cse_float(elem *e) +{ +#if TX86 + // Don't CSE floating stuff if generating + // inline 8087 code, the code generator + // can't handle it yet + return !(tyfloating(e->Ety) && config.inline8087 && + e->Eoper != OPvar && e->Eoper != OPconst); +#else + return 1; +#endif +} + +/************************************ + * Build DAGs (basically find all the common subexpressions). + * Must be done after all other optimizations, because most + * of them depend on the trees being trees, not DAGs. + * The general strategy is: + * Compute available expressions (AEs) + * For each block + * stick together nodes that match, keeping AEs up to date + * For each block + * unstick unprofitable common subexpressions + * (this is generally target-dependent) + */ + +void builddags() +{ register unsigned i; + register vec_t aevec; + + cmes("builddags()\n"); + assert(dfo); + flowae(); /* compute available expressions */ + if (exptop <= 1) /* if no AEs */ + return; + aetype = AEcse; +#ifdef DEBUG + for (i = 0; i < exptop; i++) + { + //dbg_printf("expnod[%d] = %p\n",i,expnod[i]); + if (expnod[i]) + elem_debug(expnod[i]); + } +#endif +#if 0 + dbg_printf("defkill "); vec_println(defkill,exptop); + dbg_printf("starkill "); vec_println(starkill,exptop); + dbg_printf("vptrkill "); vec_println(vptrkill,exptop); +#endif + +#if 0 + /* This is the 'correct' algorithm for CSEs. We can't use it */ + /* till we fix the code generator. */ + for (i = 0; i < dfotop; i++) + { register block *b; + + b = dfo[i]; + if (b->Belem) + { +#if 0 + dbg_printf("dfo[%d] = %p\n",i,b); + dbg_printf("b->Bin "); vec_println(b->Bin,exptop); + dbg_printf("b->Bout "); vec_println(b->Bout,exptop); + aewalk(&(b->Belem),b->Bin); + dbg_printf("b->Bin "); vec_println(b->Bin,exptop); + dbg_printf("b->Bout "); vec_println(b->Bout,exptop); +#else + aewalk(&(b->Belem),b->Bin); +#endif + /* Bin and Bout would be equal at this point */ + /* except that we deleted some elems from */ + /* expnod[] and so it's a subset of Bout */ + /* assert(veceq(b->Bin,b->Bout)); */ + } + } +#else + /* Do CSEs across extended basic blocks only. This is because */ + /* the code generator can only track register contents */ + /* properly across extended basic blocks. */ + aevec = vec_calloc(exptop); + for (i = 0; i < dfotop; i++) + { register block *b; + + b = dfo[i]; + /* if not first block and (there are more than one */ + /* predecessor or the only predecessor is not the */ + /* previous block), then zero out the available */ + /* expressions. */ + if ((i != 0 && + (list_block(b->Bpred) != dfo[i - 1] || + list_next(b->Bpred) != NULL)) + || b->BC == BCasm + || b->BC == BC_finally +#if SCPP + || b->BC == BCcatch +#endif +#if MARS + || b->BC == BCjcatch +#endif + ) + vec_clear(aevec); + if (b->Belem) /* if there is an expression */ + aewalk(&(b->Belem),aevec); + + } + vec_free(aevec); +#endif + // Need 2 passes to converge on solution + for (int j = 0; j < 2; j++) + for (i = 0; i < dfotop; i++) + { register block *b; + + b = dfo[i]; + if (b->Belem) + { +#if 0 + dbg_printf("b = 0x%x\n",b); +#endif + removecses(&(b->Belem)); + } + } +} + +/********************************** + */ + +STATIC void aeclear(elem *n,vec_t ae) +{ int i; + + i = n->Eexp; + assert(i); + if (n->Ecount == 0) + { + expnod[i] = 0; + vec_clearbit(i,ae); + if (EUNA(n)) + aeclear(n->E1,ae); + else if (EBIN(n)) + { aeclear(n->E1,ae); + aeclear(n->E2,ae); + } + } +} + +/**************************** + * Walk tree, building DAG as we go. + * ae = vector of available expressions + */ + +STATIC void aewalk(register elem **pn,register vec_t ae) +{ register vec_t aer; + register unsigned i,op; + register elem *n,*t; + + n = *pn; + assert(n && ae); + //printf("visiting %d: (",n->Eexp); WReqn(*pn); dbg_printf(")\n"); + //chkvecdim(exptop); + op = n->Eoper; + if (n->Eexp) // if an AE + { // Try to find an equivalent AE, and point to it instead + assert(expnod[n->Eexp] == n); + if (aetype == AEcse) + { + foreach (i,exptop,ae) + { elem *e = expnod[i]; + + // Attempt to replace n with e + if (e == NULL) // if elem no longer exists + vec_clearbit(i,ae); // it's not available + else if (n != e && + el_match(n,e) && + e->Ecount < 0xFF-1 && // must fit in unsigned char + cse_float(n) + ) + { + *pn = e; // replace n with e + //dbg_printf("cse: %p (",n); WReqn(*pn); dbg_printf(")\n"); + e->Ecount++; +#ifdef DEBUG + assert(e->Ecount != 0); +#endif + aeclear(n,ae); + el_free(n); + return; + } + } + } + } + switch (op) + { case OPcolon: + case OPcolon2: + // ae = ae & ael & aer + // AEs gened by ael and aer are mutually exclusive + aer = vec_clone(ae); + aewalk(&(n->E1),ae); + aewalk(&(n->E2),aer); + vec_andass(ae,aer); + vec_free(aer); + break; + case OPandand: + case OPoror: + aewalk(&(n->E1),ae); + /* ae &= aer */ + aer = vec_clone(ae); + aewalk(&(n->E2),aer); + if (!el_noreturn(n->E2)) + vec_andass(ae,aer); + vec_free(aer); + break; + case OPnegass: + t = Elvalue(n); + if (t->Eoper == OPind) + aewalk(&(t->E1),ae); + break; + case OPctor: + case OPdtor: + case OPdctor: + break; + case OPasm: + vec_clear(ae); // kill everything + return; + + default: + if (OTbinary(op)) + { if (ERTOL(n)) + { + // Don't CSE constants that will turn into + // an INC or DEC anyway + if (n->E2->Eoper == OPconst && + n->E2->EV.Vint == 1 && + (op == OPaddass || op == OPminass || + op == OPpostinc || op == OPpostdec) + ) + ; + else + aewalk(&(n->E2),ae); + } + if (OTassign(op)) + { t = Elvalue(n); + if (t->Eoper == OPind) + aewalk(&(t->E1),ae); + } + else + aewalk(&(n->E1),ae); + if (!ERTOL(n)) + aewalk(&(n->E2),ae); + } + else if (OTunary(op)) + { assert(op != OPnegass); + aewalk(&(n->E1),ae); + } + } + + if (OTdef(op)) + { + assert(n->Eexp == 0); // should not be an AE + /* remove all AEs that could be affected by this def */ + if (Eunambig(n)) // if unambiguous definition + { symbol *s; + + assert(t->Eoper == OPvar); + s = t->EV.sp.Vsym; + if (!(s->Sflags & SFLunambig)) + vec_subass(ae,starkill); + foreach (i,exptop,ae) /* for each ae elem */ + { register elem *e = expnod[i]; + + if (!e) continue; + if (OTunary(e->Eoper)) + { + if (vec_testbit(e->E1->Eexp,ae)) + continue; + } + else if (OTbinary(e->Eoper)) + { + if (vec_testbit(e->E1->Eexp,ae) && + vec_testbit(e->E2->Eexp,ae)) + continue; + } + else if (e->Eoper == OPvar) + { if (e->EV.sp.Vsym != s) + continue; + } + else + continue; + vec_clearbit(i,ae); + } + } + else /* else ambiguous definition */ + { + vec_subass(ae,defkill); + if (OTcalldef(op)) + vec_subass(ae,vptrkill); + } + + // GEN the lvalue of an assignment operator + if (OTassign(op) && !OTpost(op) && t->Eexp) + vec_setbit(t->Eexp,ae); + } + if (n->Eexp) // if an AE + { +#if TARGET_SEGMENTED + if (op == OPvp_fp || op == OPcvp_fp) + /* Invalidate all other OPvp_fps */ + vec_subass(ae,vptrkill); +#endif + + /*dbg_printf("available: ("); WReqn(n); dbg_printf(")\n"); + elem_print(n);*/ + vec_setbit(n->Eexp,ae); /* mark this elem as available */ + } +} + +/************************** + * Remove a CSE. + * Input: + * pe pointer to pointer to CSE + * Output: + * *pe new elem to replace the old + * Returns: + * *pe + */ + +STATIC elem * delcse(elem **pe) +{ elem *e; + + e = el_calloc(); + el_copy(e,*pe); +#ifdef DEBUG + if (debugc) + { dbg_printf("deleting unprofitable CSE ("); + WReqn(e); + dbg_printf(")\n"); + } +#endif + assert(e->Ecount != 0); + if (EOP(e)) + { + if (e->E1->Ecount == 0xFF-1) + { elem *ereplace; + ereplace = el_calloc(); + el_copy(ereplace,e->E1); + e->E1 = ereplace; + ereplace->Ecount = 0; + } + else + { + e->E1->Ecount++; +#ifdef DEBUG + assert(e->E1->Ecount != 0); +#endif + } + if (EBIN(e)) + { + if (e->E2->Ecount == 0xFF-1) + { elem *ereplace; + ereplace = el_calloc(); + el_copy(ereplace,e->E2); + e->E2 = ereplace; + ereplace->Ecount = 0; + } + else + { e->E2->Ecount++; +#ifdef DEBUG + assert(e->E2->Ecount != 0); +#endif + } + + } + } + --(*pe)->Ecount; +#ifdef DEBUG + assert((*pe)->Ecount != 0xFF); +#endif + (*pe)->Nflags |= NFLdelcse; // not generating node + e->Ecount = 0; +#if FLOATS_IN_CODE + if (FLT_CODESEG_CELEM(e)) + flt_record_const(e); +#endif + *pe = e; + return *pe; +} + +/****************************** + * 'Unstick' CSEs that would be unprofitable to do. These are usually + * things like addressing modes, and are usually target-dependent. + */ + +STATIC void removecses(elem **pe) +{ unsigned op; + elem *e; + +L1: e = *pe; + assert(e); + elem_debug(e); + if (e->Nflags & NFLdelcse && e->Ecount) + { + delcse(pe); + goto L1; + } + op = e->Eoper; + if (OTunary(op)) + { + if (op == OPind) + { elem *e1; + + e1 = e->E1; + if (e1->Eoper == OPadd && + e1->Ecount // == 1 + ) + { + if (I32) + { + e1 = delcse(&e->E1); + if (e1->E1->Ecount) // == 1) + delcse(&e1->E1); + if (e1->E2->Ecount && e1->E2->Eoper != OPind) + delcse(&e1->E2); + } + // Look for *(var + const). The + and the const + // shouldn't be CSEs. + else if (e1->E2->Eoper == OPconst && + (e1->E1->Eoper == OPvar || (e1->E1->Eoper == OPind && e1->E1->Ety & (mTYconst | mTYimmutable))) + ) + { + e1 = delcse(&e->E1); + } + } + + if (I32 && e1->Eoper == OPadd && + e1->E1->Eoper == OPadd && + e1->E1->E1->Ecount && + e1->E1->E1->Eoper == OPshl && + e1->E1->E1->E2->Eoper == OPconst && + e1->E1->E1->E2->EV.Vuns <= 3 + ) + { + delcse(&e1->E1->E1); + } + + if (I32 && e1->Eoper == OPadd && + e1->E1->Eoper == OPadd && + e1->E1->Ecount && + e1->E1->E1->Eoper == OPshl && + e1->E1->E1->E2->Eoper == OPconst && + e1->E1->E1->E2->EV.Vuns <= 3 + ) + { + delcse(&e1->E1); + } + + else if (I32 && e1->Eoper == OPadd && + e1->E1->Ecount && + e1->E1->Eoper == OPshl && + e1->E1->E2->Eoper == OPconst && + e1->E1->E2->EV.Vuns <= 3 + ) + { + delcse(&e1->E1); + } + + // Remove *e1 where it's a double + if (e->Ecount && tyfloating(e->Ety)) + e = delcse(pe); + } + // This CSE is too easy to regenerate + else if (op == OPu16_32 && !I32 && e->Ecount) + e = delcse(pe); + } + else if (OTbinary(op)) + { + if (e->Ecount > 0 && OTrel(op) && e->Ecount < 4 +#if TX86 + /* Don't CSE floating stuff if generating */ + /* inline 8087 code, the code generator */ + /* can't handle it yet */ + && !(tyfloating(e->E1->Ety) && config.inline8087) +#endif + ) + e = delcse(pe); + removecses(&(e->E2)); + } + else /* leaf node */ + { + return; + } + pe = &(e->E1); + goto L1; +} + +/***************************************** + * Do optimizations based on if we know an expression is + * 0 or !=0, even though we don't know anything else. + */ + +STATIC void abewalk(elem *n,vec_t ae,vec_t aeval); +STATIC void abeboolres(elem *n,vec_t abe,vec_t abeval); +STATIC void abefree(elem *e,vec_t abe); +STATIC void abeset(elem *n,vec_t abe,vec_t abeval,int flag); + +void boolopt() +{ unsigned i; + vec_t aevec; + vec_t aevecval; + + cmes("boolopt()\n"); + if (!dfo) + compdfo(); + flowae(); /* compute available expressions */ + if (exptop <= 1) /* if no AEs */ + return; +#if 0 + for (i = 0; i < exptop; i++) + dbg_printf("expnod[%d] = 0x%x\n",i,expnod[i]); + dbg_printf("defkill "); vec_println(defkill,exptop); + dbg_printf("starkill "); vec_println(starkill,exptop); + dbg_printf("vptrkill "); vec_println(vptrkill,exptop); +#endif + + /* Do CSEs across extended basic blocks only. This is because */ + /* the code generator can only track register contents */ + /* properly across extended basic blocks. */ + aevec = vec_calloc(exptop); + aevecval = vec_calloc(exptop); + + // Mark each expression that we know starts off with a non-zero value + for (i = 0; i < exptop; i++) + { elem *e = expnod[i]; + + if (e) + { elem_debug(e); + if (e->Eoper == OPvar && e->EV.sp.Vsym->Sflags & SFLtrue) + { vec_setbit(i,aevec); + vec_setbit(i,aevecval); + } + } + } + + for (i = 0; i < dfotop; i++) + { register block *b; + + b = dfo[i]; + /* if not first block and (there are more than one */ + /* predecessor or the only predecessor is not the */ + /* previous block), then zero out the available */ + /* expressions. */ + if ((i != 0 && + (list_block(b->Bpred) != dfo[i - 1] || + list_next(b->Bpred) != NULL)) + || b->BC == BCasm + ) + vec_clear(aevec); + if (b->Belem) /* if there is an expression */ + abewalk(b->Belem,aevec,aevecval); + + } + vec_free(aevec); + vec_free(aevecval); +} + +/**************************** + * Walk tree, replacing bool expressions that we know + * ae = vector of available boolean expressions + * aeval = parallel vector of values corresponding to whether bool + * value is 1 or 0 + * n = elem tree to look at + */ + +STATIC void abewalk(elem *n,vec_t ae,vec_t aeval) +{ vec_t aer,aerval; + unsigned i,op; + unsigned i1,i2; + elem *t; + + assert(n && ae); + elem_debug(n); + /*dbg_printf("visiting: ("); WReqn(*pn); dbg_printf("), Eexp = %d\n",n->Eexp);*/ + /*chkvecdim(exptop);*/ + op = n->Eoper; + switch (op) + { case OPcolon: + case OPcolon2: + /* ae = ae & ael & aer */ + /* AEs gened by ael and aer are mutually exclusive */ + aer = vec_clone(ae); + aerval = vec_clone(aeval); + abewalk(n->E1,ae,aeval); + goto L1; + case OPandand: + case OPoror: + abewalk(n->E1,ae,aeval); + abeboolres(n->E1,ae,aeval); + /* ae &= aer */ + aer = vec_clone(ae); + aerval = vec_clone(aeval); + abeset(n->E1,aer,aerval,(op == OPandand)); + L1: + abewalk(n->E2,aer,aerval); + if (!el_noreturn(n->E2)) + { vec_xorass(aerval,aeval); + vec_subass(aer,aerval); + vec_andass(ae,aer); + } + vec_free(aer); + vec_free(aerval); + break; + case OPbool: + case OPnot: + abewalk(n->E1,ae,aeval); + abeboolres(n->E1,ae,aeval); + break; + case OPnegass: + t = Elvalue(n); + if (t->Eoper == OPind) + abewalk(t->E1,ae,aeval); + break; + + case OPasm: + vec_clear(ae); // kill everything + return; + + default: + if (OTbinary(op)) + { if (ERTOL(n)) + abewalk(n->E2,ae,aeval); + if (OTassign(op)) + { t = Elvalue(n); + if (t->Eoper == OPind) + abewalk(t->E1,ae,aeval); + } + else + abewalk(n->E1,ae,aeval); + if (!ERTOL(n)) + abewalk(n->E2,ae,aeval); + } + else if (OTunary(op)) + abewalk(n->E1,ae,aeval); + break; + } + + if (OTdef(op)) + { assert(n->Eexp == 0); // should not be an AE + /* remove all AEs that could be affected by this def */ + if (Eunambig(n)) /* if unambiguous definition */ + { symbol *s; + + assert(t->Eoper == OPvar); + s = t->EV.sp.Vsym; + if (!(s->Sflags & SFLunambig)) + vec_subass(ae,starkill); + foreach (i,exptop,ae) /* for each ae elem */ + { register elem *e = expnod[i]; + + if (!e) continue; + if (OTunary(e->Eoper)) + { + if (vec_testbit(e->E1->Eexp,ae)) + continue; + } + else if (OTbinary(e->Eoper)) + { + if (vec_testbit(e->E1->Eexp,ae) && + vec_testbit(e->E2->Eexp,ae)) + continue; + } + else if (e->Eoper == OPvar) + { if (e->EV.sp.Vsym != s) + continue; + } + else + continue; + vec_clearbit(i,ae); + } + } + else /* else ambiguous definition */ + { + vec_subass(ae,defkill); + if (OTcalldef(op)) + vec_subass(ae,vptrkill); + } + /* GEN the lvalue of an assignment operator */ +#if 1 + if (op == OPeq && (i1 = t->Eexp) != 0 && (i2 = n->E2->Eexp) != 0) + { + if (vec_testbit(i2,ae)) + { + vec_setbit(i1,ae); + if (vec_testbit(i2,aeval)) + vec_setbit(i1,aeval); + else + vec_clearbit(i1,aeval); + } + } +#else + if ((OTopeq(op) || op == OPeq || op == OPnegass) && n->E1->Eexp) + { vec_setbit(t->Eexp,ae); + if (n->E1->Eoper == OPbit) + vec_setbit(n->E1->Eexp,ae); + } +#endif + } + else if (n->Eexp) /* if an AE */ + { +#if TARGET_SEGMENTED + if (op == OPvp_fp || op == OPcvp_fp) + /* Invalidate all other OPvp_fps */ + vec_subass(ae,vptrkill); +#endif + + /*dbg_printf("available: ("); WReqn(n); dbg_printf(")\n"); + elem_print(n);*/ +// vec_setbit(n->Eexp,ae); /* mark this elem as available */ + } +} + +/************************************ + * Elem e is to be evaluated for a boolean result. + * See if we already know its value. + */ + +STATIC void abeboolres(elem *n,vec_t ae,vec_t aeval) +{ unsigned i; + + elem_debug(n); + if (n->Eexp) + { /* Try to find an equivalent AE, and point to it instead */ + assert(expnod[n->Eexp] == n); + foreach (i,exptop,ae) + { elem *e = expnod[i]; + + // Attempt to replace n with e + //dbg_printf("Looking at expnod[%d] = %p\n",i,e); + assert(e); + elem_debug(e); + if (n != e && el_match(n,e)) + { +#ifdef DEBUG + if (debugc) + { dbg_printf("Elem %p: ",n); + WReqn(n); + dbg_printf(" is replaced by %d\n",vec_testbit(i,aeval) != 0); + } +#endif + abefree(n,ae); + n->EV.Vlong = vec_testbit(i,aeval) != 0; + n->Eoper = OPconst; + n->Ety = TYint; + changes++; + break; + } + } + } +} + +/**************************** + * Remove e from available expressions, and its children. + */ + +STATIC void abefree(elem *e,vec_t ae) +{ + //dbg_printf("abefree %p: "); WReqn(e); dbg_printf("\n"); + assert(e->Eexp); + vec_clearbit(e->Eexp,ae); + expnod[e->Eexp] = NULL; + if (EOP(e)) + { if (EBIN(e)) + { abefree(e->E2,ae); + el_free(e->E2); + e->E2 = NULL; + } + abefree(e->E1,ae); + el_free(e->E1); + e->E1 = NULL; + } +} + +/************************************ + * Elem e is to be evaluated for a boolean result. + * Set its result according to flag. + */ + +STATIC void abeset(elem *e,vec_t ae,vec_t aeval,int flag) +{ unsigned i; + + while (1) + { + i = e->Eexp; + if (i && expnod[i]) + { + //dbg_printf("abeset for expnod[%d] = %p: ",i,e); WReqn(e); dbg_printf("\n"); + vec_setbit(i,ae); + if (flag) + vec_setbit(i,aeval); + else + vec_clearbit(i,aeval); + } + switch (e->Eoper) + { case OPnot: + flag ^= 1; + case OPbool: + case OPeq: + e = e->E1; + continue; + } + break; + } +} + +#endif diff --git a/backend/gflow.c b/backend/gflow.c new file mode 100644 index 00000000..5a92125a --- /dev/null +++ b/backend/gflow.c @@ -0,0 +1,1721 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if (SCPP || MARS) && !HTOD + +#include +#include + +#include "cc.h" +#include "global.h" +#include "el.h" +#include "go.h" +#include "type.h" +#include "oper.h" +#include "vec.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +/* Since many routines are nearly identical, we can combine them with */ +/* this flag: */ + +#define AE 1 +#define CP 2 +#define VBE 3 + +static int flowxx; /* one of the above values */ + +static vec_t ambigsym = NULL; + +STATIC void rdgenkill(void); +STATIC void numdefelems(elem *n); +STATIC void asgdefelems(block *b , elem *n); +STATIC void aecpgenkill(void); +STATIC int numaeelems(elem *n); +STATIC void numcpelems(elem *n); +STATIC void asgexpelems(elem *n); +STATIC void defstarkill(void); +STATIC void rdelem(vec_t *pgen , vec_t *pkill , elem *n); +STATIC void aecpelem(vec_t *pgen , vec_t *pkill , elem *n); +STATIC void accumaecp(vec_t GEN , vec_t KILL , elem *n); +STATIC void accumaecpx(elem *n); +STATIC void lvgenkill(void); +STATIC void lvelem(vec_t *pgen , vec_t *pkill , elem *n); +STATIC void accumlv(vec_t GEN , vec_t KILL , elem *n); +STATIC void accumvbe(vec_t GEN , vec_t KILL , elem *n); +STATIC void accumrd(vec_t GEN , vec_t KILL , elem *n); +STATIC void flowaecp(void); + +/***************** REACHING DEFINITIONS *********************/ + +/************************************ + * Compute reaching definitions (RDs). + * That is, for each block B and each program variable X + * find all elems that could be the last elem that defines + * X along some path to B. + * Binrd = the set of defs reaching the beginning of B. + * Boutrd = the set of defs reaching the end of B. + * Bkillrd = set of defs that are killed by some def in B. + * Bgenrd = set of defs in B that reach the end of B. + */ + +void flowrd() +{ register vec_t tmp; + register unsigned i; + register bool anychng; + + rdgenkill(); /* Compute Bgen and Bkill for RDs */ + if (deftop == 0) /* if no definition elems */ + return; /* no analysis to be done */ + + /* The transfer equation is: */ + /* Bin = union of Bouts of all predecessors of B. */ + /* Bout = (Bin - Bkill) | Bgen */ + /* Using Ullman's algorithm: */ + + for (i = 0; i < dfotop; i++) + vec_copy(dfo[i]->Boutrd,dfo[i]->Bgen); + + tmp = vec_calloc(deftop); + do + { anychng = FALSE; + for (i = 0; i < dfotop; i++) /* for each block */ + { register block *b; + register list_t bp; + + b = dfo[i]; + + /* Binrd = union of Boutrds of all predecessors of b */ + vec_clear(b->Binrd); + if (b->BC != BCcatch /*&& b->BC != BCjcatch*/) + { + /* Set Binrd to 0 to account for: + * i = 0; + * try { i = 1; throw; } catch () { x = i; } + */ + for (bp = b->Bpred; bp; bp = list_next(bp)) + vec_orass(b->Binrd,list_block(bp)->Boutrd); + } + /* Bout = (Bin - Bkill) | Bgen */ + vec_sub(tmp,b->Binrd,b->Bkill); + vec_orass(tmp,b->Bgen); + if (!anychng) + anychng = !vec_equal(tmp,b->Boutrd); + vec_copy(b->Boutrd,tmp); + } + } while (anychng); /* while any changes to Boutrd */ + vec_free(tmp); + +#if 0 + dbg_printf("Reaching definitions\n"); + for (i = 0; i < dfotop; i++) + { register block *b = dfo[i]; + + assert(vec_numbits(b->Binrd) == deftop); + dbg_printf("B%d Bin ",i); vec_println(b->Binrd); + dbg_printf(" Bgen "); vec_println(b->Bgen); + dbg_printf(" Bkill "); vec_println(b->Bkill); + dbg_printf(" Bout "); vec_println(b->Boutrd); + } +#endif +} + +/*************************** + * Compute Bgen and Bkill for RDs. + */ + +STATIC void rdgenkill() +{ register unsigned i,deftopsave; + + util_free(defnod); /* free existing junk */ + + defnod = NULL; + + /* Compute number of definition elems. */ + deftop = 0; + for (i = 0; i < dfotop; i++) + if (dfo[i]->Belem) + { + numdefelems(dfo[i]->Belem); + } + if (deftop == 0) + return; + + /* Allocate array of pointers to all definition elems */ + /* The elems are in dfo order. */ + /* defnod[]s consist of a elem pointer and a pointer */ + /* to the enclosing block. */ + defnod = (dn *) util_calloc(sizeof(dn),deftop); + deftopsave = deftop; + deftop = 0; + for (i = 0; i < dfotop; i++) + if (dfo[i]->Belem) + asgdefelems(dfo[i],dfo[i]->Belem); + assert(deftop == deftopsave); + + for (i = 0; i < dfotop; i++) /* for each block */ + { register block *b = dfo[i]; + + /* dump any existing vectors */ + vec_free(b->Bgen); + vec_free(b->Bkill); + vec_free(b->Binrd); + vec_free(b->Boutrd); + + /* calculate and create new vectors */ + rdelem(&(b->Bgen),&(b->Bkill),b->Belem); + if (b->BC == BCasm) + { vec_clear(b->Bkill); // KILL nothing + vec_set(b->Bgen); // GEN everything + } + b->Binrd = vec_calloc(deftop); + b->Boutrd = vec_calloc(deftop); + } +} + +/********************** + * Compute # of definition elems (deftop). + */ + +STATIC void numdefelems(register elem *n) +{ + while (1) + { assert(n); + if (OTdef(n->Eoper)) + deftop++; + if (OTbinary(n->Eoper)) + { + numdefelems(n->E1); + n = n->E2; + } + else if (OTunary(n->Eoper)) + { + n = n->E1; + } + else + break; + } +} + +/************************** + * Load defnod[] array. + * Loaded in order of execution of the elems. Not sure if this is + * necessary. + */ + +STATIC void asgdefelems(block *b,register elem *n) +{ register unsigned op; + + assert(b && n); + op = n->Eoper; + if (ERTOL(n)) + { asgdefelems(b,n->E2); + asgdefelems(b,n->E1); + } + else if (OTbinary(op)) + { asgdefelems(b,n->E1); + asgdefelems(b,n->E2); + } + else if (OTunary(op)) + asgdefelems(b,n->E1); + if (OTdef(op)) + { assert(defnod); + defnod[deftop].DNblock = b; + defnod[deftop].DNelem = n; + deftop++; + } +} + +/************************************* + * Allocate and compute rd GEN and KILL. + */ + +STATIC void rdelem(vec_t *pgen,vec_t *pkill, /* where to put result */ + elem *n ) /* tree to evaluate for GEN and KILL */ +{ + *pgen = vec_calloc(deftop); + *pkill = vec_calloc(deftop); + if (n) + accumrd(*pgen,*pkill,n); +} + +/************************************** + * Accumulate GEN and KILL vectors for this elem. + */ + +STATIC void accumrd(vec_t GEN,vec_t KILL,elem *n) +{ vec_t Gl,Kl,Gr,Kr; + unsigned op; + + assert(GEN && KILL && n); + op = n->Eoper; + if (OTunary(op)) + accumrd(GEN,KILL,n->E1); + else if (OTbinary(op)) + { + if (op == OPcolon || op == OPcolon2) + { + rdelem(&Gl,&Kl,n->E1); + rdelem(&Gr,&Kr,n->E2); + + /* GEN = (GEN - Kl) | Gl | */ + /* (GEN - Kr) | Gr */ + /* KILL |= Kl & Kr */ + + vec_orass(Gl,Gr); + vec_sub(Gr,GEN,Kl); + vec_orass(Gl,Gr); + vec_sub(Gr,GEN,Kr); + vec_or(GEN,Gl,Gr); + + vec_andass(Kl,Kr); + vec_orass(KILL,Kl); + + vec_free(Gl); + vec_free(Kl); + vec_free(Gr); + vec_free(Kr); + } + else if (op == OPandand || op == OPoror) + { + accumrd(GEN,KILL,n->E1); + rdelem(&Gr,&Kr,n->E2); + vec_orass(GEN,Gr); /* GEN |= Gr */ + + vec_free(Gr); + vec_free(Kr); + } + else if (OTrtol(op) && ERTOL(n)) + { accumrd(GEN,KILL,n->E2); + accumrd(GEN,KILL,n->E1); + } + else + { accumrd(GEN,KILL,n->E1); + accumrd(GEN,KILL,n->E2); + } + } + + if (OTdef(op)) /* if definition elem */ + updaterd(n,GEN,KILL); +} + +/******************** AVAILABLE EXPRESSIONS ***********************/ + +/************************************ + * Compute available expressions (AEs). + * That is, expressions whose result is still current. + * Bin = the set of AEs reaching the beginning of B. + * Bout = the set of AEs reaching the end of B. + */ + +void flowae() +{ + flowxx = AE; + flowaecp(); +} + +/**************************** COPY PROPAGATION ************************/ + +/*************************************** + * Compute copy propagation info (CPs). + * Very similar to AEs (the same code is used). + * Using RDs for copy propagation is WRONG! + * That is, set of copy statements still valid. + * Bin = the set of CPs reaching the beginning of B. + * Bout = the set of CPs reaching the end of B. + */ + +void flowcp() +{ + flowxx = CP; + flowaecp(); +} + +/***************************************** + * Common flow analysis routines for Available Expressions and + * Copy Propagation. + * Input: + * flowxx + */ + +STATIC void flowaecp() +{ vec_t tmp; + register unsigned i; + bool anychng; + + aecpgenkill(); /* Compute Bgen and Bkill for AEs or CPs */ + if (exptop <= 1) /* if no expressions */ + return; + + /* The transfer equation is: */ + /* Bin = & Bout(all predecessors P of B) */ + /* Bout = (Bin - Bkill) | Bgen */ + /* Using Ullman's algorithm: */ + + vec_clear(startblock->Bin); + vec_copy(startblock->Bout,startblock->Bgen); /* these never change */ + if (startblock->BC == BCiftrue) + vec_copy(startblock->Bout2,startblock->Bgen2); // these never change + + /* For all blocks except startblock */ + for (i = 1; i < dfotop; i++) + { register block *b = dfo[i]; + + vec_set(b->Bin); /* Bin = all expressions */ + + /* Bout = (Bin - Bkill) | Bgen */ + vec_sub(b->Bout,b->Bin,b->Bkill); + vec_orass(b->Bout,b->Bgen); + if (b->BC == BCiftrue) + { vec_sub(b->Bout2,b->Bin,b->Bkill2); + vec_orass(b->Bout2,b->Bgen2); + } + } + + tmp = vec_calloc(exptop); + do + { anychng = FALSE; + + // For all blocks except startblock + for (i = 1; i < dfotop; i++) + { block *b = dfo[i]; + list_t bl = b->Bpred; + block *bp; + + // Bin = & of Bout of all predecessors + // Bout = (Bin - Bkill) | Bgen + + assert(bl); // it must have predecessors + bp = list_block(bl); + if (bp->BC == BCiftrue && list_block(bp->Bsucc) != b) + vec_copy(b->Bin,bp->Bout2); + else + vec_copy(b->Bin,bp->Bout); + while (TRUE) + { bl = list_next(bl); + if (!bl) + break; + bp = list_block(bl); + if (bp->BC == BCiftrue && list_block(bp->Bsucc) != b) + vec_andass(b->Bin,bp->Bout2); + else + vec_andass(b->Bin,bp->Bout); + } + + if (anychng) + { vec_sub(b->Bout,b->Bin,b->Bkill); + vec_orass(b->Bout,b->Bgen); + } + else + { vec_sub(tmp,b->Bin,b->Bkill); + vec_orass(tmp,b->Bgen); + if (!vec_equal(tmp,b->Bout)) + { // Swap Bout and tmp instead of + // copying tmp over Bout + vec_t v; + + v = tmp; + tmp = b->Bout; + b->Bout = v; + anychng = TRUE; + } + } + + if (b->BC == BCiftrue) + { // Bout2 = (Bin - Bkill2) | Bgen2 + if (anychng) + { vec_sub(b->Bout2,b->Bin,b->Bkill2); + vec_orass(b->Bout2,b->Bgen2); + } + else + { vec_sub(tmp,b->Bin,b->Bkill2); + vec_orass(tmp,b->Bgen2); + if (!vec_equal(tmp,b->Bout2)) + { // Swap Bout and tmp instead of + // copying tmp over Bout2 + vec_t v; + + v = tmp; + tmp = b->Bout2; + b->Bout2 = v; + anychng = TRUE; + } + } + } + } + } while (anychng); + vec_free(tmp); +} + +/****************************** + * A variable to avoid parameter overhead to asgexpelems(). + */ + +static block *this_block; + +/*********************************** + * Compute Bgen and Bkill for AEs, CPs, and VBEs. + */ + +STATIC void aecpgenkill() +{ register unsigned i; + unsigned exptopsave; + + util_free(expnod); /* dump any existing one */ + + expnod = NULL; + + /* Compute number of expressions */ + exptop = 1; /* start at 1 */ + for (i = 0; i < dfotop; i++) + if (dfo[i]->Belem) + { if (flowxx == CP) + numcpelems(dfo[i]->Belem); + else // AE || VBE + numaeelems(dfo[i]->Belem); + } + if (exptop <= 1) /* if no expressions */ + return; + + /* Allocate array of pointers to all expression elems. */ + /* (The elems are in order. Also, these expressions must not */ + /* have any side effects, and possibly should not be machine */ + /* dependent primitive addressing modes.) */ + expnod = (elem **) util_calloc(sizeof(elem *),exptop); + util_free(expblk); + expblk = (flowxx == VBE) + ? (block **) util_calloc(sizeof(block *),exptop) : NULL; + + exptopsave = exptop; + exptop = 1; + for (i = 0; i < dfotop; i++) + { this_block = dfo[i]; /* so asgexpelems knows about this */ + if (this_block->Belem) + asgexpelems(this_block->Belem); + } + assert(exptop == exptopsave); + + defstarkill(); /* compute defkill and starkill */ + +#if 0 + assert(vec_numbits(defkill) == exptop); + assert(vec_numbits(starkill) == exptop); + assert(vec_numbits(vptrkill) == exptop); + dbg_printf("defkill "); vec_println(defkill); + if (starkill) + { dbg_printf("starkill "); vec_println(starkill);} + if (vptrkill) + { dbg_printf("vptrkill "); vec_println(vptrkill); } +#endif + + for (i = 0; i < dfotop; i++) /* for each block */ + { register block *b = dfo[i]; + elem *e; + + /* dump any existing vectors */ + vec_free(b->Bin); + vec_free(b->Bout); + vec_free(b->Bgen); + vec_free(b->Bkill); + b->Bgen = vec_calloc(exptop); + b->Bkill = vec_calloc(exptop); + switch (b->BC) + { + case BCiftrue: + vec_free(b->Bout2); + vec_free(b->Bgen2); + vec_free(b->Bkill2); + for (e = b->Belem; e->Eoper == OPcomma; e = e->E2) + accumaecp(b->Bgen,b->Bkill,e); + if (e->Eoper == OPandand || e->Eoper == OPoror) + { vec_t Kr,Gr; + + accumaecp(b->Bgen,b->Bkill,e->E1); + Kr = vec_calloc(exptop); + Gr = vec_calloc(exptop); + accumaecp(Gr,Kr,e->E2); + + // We might or might not have executed E2 + // KILL1 = KILL | Kr + // GEN1 = GEN & ((GEN - Kr) | Gr) + + // We definitely executed E2 + // KILL2 = (KILL - Gr) | Kr + // GEN2 = (GEN - Kr) | Gr + + unsigned j,dim; + dim = vec_dim(Kr); + vec_t KILL = b->Bkill; + vec_t GEN = b->Bgen; + + for (j = 0; j < dim; j++) + { vec_base_t KILL1,KILL2,GEN1,GEN2; + + KILL1 = KILL[j] | Kr[j]; + GEN1 = GEN[j] & ((GEN[j] & ~Kr[j]) | Gr[j]); + + KILL2 = (KILL[j] & ~Gr[j]) | Kr[j]; + GEN2 = (GEN[j] & ~Kr[j]) | Gr[j]; + + KILL[j] = KILL1; + GEN[j] = GEN1; + Kr[j] = KILL2; + Gr[j] = GEN2; + } + + if (e->Eoper == OPandand) + { b->Bkill = Kr; + b->Bgen = Gr; + b->Bkill2 = KILL; + b->Bgen2 = GEN; + } + else + { b->Bkill = KILL; + b->Bgen = GEN; + b->Bkill2 = Kr; + b->Bgen2 = Gr; + } + } + else + { + accumaecp(b->Bgen,b->Bkill,e); + b->Bgen2 = vec_clone(b->Bgen); + b->Bkill2 = vec_clone(b->Bkill); + } + b->Bout2 = vec_calloc(exptop); + break; + + case BCasm: + vec_set(b->Bkill); // KILL everything + vec_clear(b->Bgen); // GEN nothing + break; + + default: + // calculate GEN & KILL vectors + if (b->Belem) + accumaecp(b->Bgen,b->Bkill,b->Belem); + break; + } +#if 0 + dbg_printf("block %d Bgen ",i); vec_println(b->Bgen); + dbg_printf(" Bkill "); vec_println(b->Bkill); +#endif + b->Bin = vec_calloc(exptop); + b->Bout = vec_calloc(exptop); + } +} + +/***************************** + * Accumulate number of expressions in exptop. + * Set NFLaecp as a flag indicating an AE elem. + * Returns: + * TRUE if this elem is a possible AE elem. + */ + +STATIC int numaeelems(register elem *n) +{ register unsigned op; + unsigned ae; + + assert(n); + op = n->Eoper; + if (OTunary(op)) + { ae = numaeelems(n->E1); + // Disallow starred references to avoid problems with VBE's + // being hoisted before tests of an invalid pointer. + if (flowxx == VBE && op == OPind) + goto L1; + } + else if (OTbinary(op)) + ae = numaeelems(n->E1) & numaeelems(n->E2); + else + ae = TRUE; + + if (ae && OTae(op) && !(n->Ety & mTYvolatile) && + // Disallow struct AEs, because we can't handle CSEs that are structs + tybasic(n->Ety) != TYstruct) + { n->Nflags |= NFLaecp; /* remember for asgexpelems() */ + exptop++; + } + else + L1: + n->Nflags &= ~NFLaecp; + return n->Nflags & NFLaecp; +} + + +/**************************** + * Compute number of cp elems into exptop. + * Mark cp elems by setting NFLaecp flag. + */ + +STATIC void numcpelems(elem *n) +{ unsigned op; + + op = n->Eoper; + if (OTunary(op)) + numcpelems(n->E1); + else if (OTbinary(op)) + { numcpelems(n->E1); + numcpelems(n->E2); + + /* look for elem of the form OPvar=OPvar, where they aren't the */ + /* same variable. */ + if (op == OPeq && + n->E1->Eoper == OPvar && + n->E2->Eoper == OPvar && + !((n->E1->Ety | n->E2->Ety) & mTYvolatile) && + n->E1->EV.sp.Vsym != n->E2->EV.sp.Vsym) + { exptop++; + n->Nflags |= NFLaecp; + return; + } + } + n->Nflags &= ~NFLaecp; +} + +/******************************** + * Assign ae (or cp) elems to expnod[] (in order of evaluation). + */ + +STATIC void asgexpelems(elem *n) +{ + assert(n); + if (OTunary(n->Eoper)) + asgexpelems(n->E1); + else if (ERTOL(n)) + { asgexpelems(n->E2); + asgexpelems(n->E1); + } + else if (OTbinary(n->Eoper)) + { asgexpelems(n->E1); + asgexpelems(n->E2); + } + + if (n->Nflags & NFLaecp) /* if an ae, cp or vbe elem */ + { n->Eexp = exptop; /* remember index into expnod[] */ + expnod[exptop] = n; + if (expblk) + expblk[exptop] = this_block; + exptop++; + } + else + n->Eexp = 0; +} + +/******************************** + * Compute defkill, starkill and vptrkill vectors. + * starkill: set of expressions killed when a variable is + * changed that somebody could be pointing to. + * (not needed for cp) + * starkill is a subset of defkill. + * defkill: set of expressions killed by an ambiguous + * definition. + * vptrkill: set of expressions killed by an access to a vptr. + */ + +STATIC void defstarkill() +{ register unsigned i,op; + register elem *n; + + vec_free(vptrkill); + vec_free(defkill); + vec_free(starkill); /* dump any existing ones */ + defkill = vec_calloc(exptop); + if (flowxx != CP) + { starkill = vec_calloc(exptop); /* and create new ones */ + vptrkill = vec_calloc(exptop); /* and create new ones */ + } + else /* CP */ + { starkill = NULL; + vptrkill = NULL; + } + + if (flowxx == CP) + { + for (i = 1; i < exptop; i++) + { n = expnod[i]; + op = n->Eoper; + assert(op == OPeq); + assert(n->E1->Eoper==OPvar && n->E2->Eoper==OPvar); + + // Set bit in defkill if either the left or the + // right variable is killed by an ambiguous def. + + Symbol *s1 = n->E1->EV.sp.Vsym; + if (!(s1->Sflags & SFLunambig) || + !(n->E2->EV.sp.Vsym->Sflags & SFLunambig)) + { + vec_setbit(i,defkill); + } + } + } + else + { + for (i = 1; i < exptop; i++) + { n = expnod[i]; + op = n->Eoper; + switch (op) + { + case OPvar: + if (!(n->EV.sp.Vsym->Sflags & SFLunambig)) + vec_setbit(i,defkill); + break; + + case OPind: // if a 'starred' ref + +#if 1 +/* The following program fails for this: +import std.c.stdio; + +class Foo +{ + string foo = "abc"; + size_t i = 0; + + void bar() + { + printf("%c\n", foo[i]); + i++; + printf("%c\n", foo[i]); + } +} + +void main() +{ + auto f = new Foo(); + f.bar(); +} +*/ + // For C/C++, casting to 'const' doesn't mean it + // actually is const, + // but immutable really doesn't change + if ((n->Ety & (mTYimmutable | mTYvolatile)) == mTYimmutable && + n->E1->Eoper == OPvar && + n->E1->EV.sp.Vsym->Sflags & SFLunambig + ) + break; +#endif +#if TX86 + case OPstrlen: + case OPstrcmp: + case OPmemcmp: + case OPbt: // OPbt is like OPind +#endif + vec_setbit(i,defkill); + vec_setbit(i,starkill); + break; + +#if TARGET_SEGMENTED + case OPvp_fp: + case OPcvp_fp: + vec_setbit(i,vptrkill); + goto Lunary; +#endif + + default: + if (OTunary(op)) + { + Lunary: + if (vec_testbit(n->E1->Eexp,defkill)) + vec_setbit(i,defkill); + if (vec_testbit(n->E1->Eexp,starkill)) + vec_setbit(i,starkill); + } + else if (OTbinary(op)) + { + if (vec_testbit(n->E1->Eexp,defkill) || + vec_testbit(n->E2->Eexp,defkill)) + vec_setbit(i,defkill); + if (vec_testbit(n->E1->Eexp,starkill) || + vec_testbit(n->E2->Eexp,starkill)) + vec_setbit(i,starkill); + } + break; + } + } + } +} + +/******************************** + * Compute GEN and KILL vectors only for AEs. + * defkill and starkill are assumed to be already set up correctly. + * expnod[] is assumed to be set up correctly. + */ + +void genkillae() +{ register unsigned i; + + flowxx = AE; + assert(exptop > 1); + for (i = 0; i < dfotop; i++) + { register block *b = dfo[i]; + + assert(b); + vec_clear(b->Bgen); + vec_clear(b->Bkill); + if (b->Belem) + accumaecp(b->Bgen,b->Bkill,b->Belem); + else if (b->BC == BCasm) + { vec_set(b->Bkill); // KILL everything + vec_clear(b->Bgen); // GEN nothing + } + } +} + +/************************************ + * Allocate and compute KILL and GEN vectors for a elem. + */ + +STATIC void aecpelem(vec_t *pgen,vec_t *pkill, elem *n) +{ *pgen = vec_calloc(exptop); + *pkill = vec_calloc(exptop); + if (n) + { if (flowxx == VBE) + accumvbe(*pgen,*pkill,n); + else + accumaecp(*pgen,*pkill,n); + } +} + +/************************************* + * Accumulate GEN and KILL sets for AEs and CPs for this elem. + */ + +static vec_t GEN; // use static copies to save on parameter passing +static vec_t KILL; + +STATIC void accumaecp(vec_t g,vec_t k,elem *n) +{ vec_t GENsave,KILLsave; + + assert(g && k); + GENsave = GEN; + KILLsave = KILL; + GEN = g; + KILL = k; + accumaecpx(n); + GEN = GENsave; + KILL = KILLsave; +} + +STATIC void accumaecpx(elem *n) +{ unsigned i,op; + elem *t; + + assert(n); + elem_debug(n); + op = n->Eoper; + + switch (op) + { + case OPvar: + case OPconst: + case OPrelconst: + if ((flowxx == AE) && n->Eexp) + { unsigned b; +#ifdef DEBUG + assert(expnod[n->Eexp] == n); +#endif + b = n->Eexp; + vec_setclear(b,GEN,KILL); + } + return; + case OPcolon: + case OPcolon2: + { vec_t Gl,Kl,Gr,Kr; + + aecpelem(&Gl,&Kl,n->E1); + aecpelem(&Gr,&Kr,n->E2); + + /* KILL |= Kl | Kr */ + /* GEN =((GEN - Kl) | Gl) & */ + /* ((GEN - Kr) | Gr) */ + + vec_orass(KILL,Kl); + vec_orass(KILL,Kr); + + vec_sub(Kl,GEN,Kl); + vec_sub(Kr,GEN,Kr); + vec_orass(Kl,Gl); + vec_orass(Kr,Gr); + vec_and(GEN,Kl,Kr); + + vec_free(Gl); + vec_free(Gr); + vec_free(Kl); + vec_free(Kr); + break; + } + case OPandand: + case OPoror: + { vec_t Gr,Kr; + + accumaecpx(n->E1); + aecpelem(&Gr,&Kr,n->E2); + + if (!el_noreturn(n->E2)) + { + // KILL |= Kr + // GEN &= (GEN - Kr) | Gr + + vec_orass(KILL,Kr); + vec_sub(Kr,GEN,Kr); + vec_orass(Kr,Gr); + vec_andass(GEN,Kr); + } + + vec_free(Gr); + vec_free(Kr); + break; + } + case OPasm: + assert(!n->Eexp); // no ASM available expressions + vec_set(KILL); // KILL everything + vec_clear(GEN); // GEN nothing + return; + + case OPeq: + case OPstreq: + accumaecpx(n->E2); + case OPnegass: + accumaecpx(n->E1); + t = Elvalue(n); + break; + +#if TARGET_SEGMENTED + case OPvp_fp: + case OPcvp_fp: // if vptr access + if ((flowxx == AE) && n->Eexp) + vec_orass(KILL,vptrkill); // kill all other vptr accesses + break; +#endif + + default: + if (OTunary(op)) + { + case OPind: // most common unary operator + accumaecpx(n->E1); +#ifdef DEBUG + assert(!OTassign(op)); +#endif + } + else if (OTbinary(op)) + { + if (OTrtol(op) && ERTOL(n)) + { accumaecpx(n->E2); + accumaecpx(n->E1); + } + else + { accumaecpx(n->E1); + accumaecpx(n->E2); + } + if (OTassign(op)) // if assignment operator + t = Elvalue(n); + } + break; + } + + + /* Do copy propagation stuff first */ + + if (flowxx == CP) + { + if (!OTdef(op)) /* if not def elem */ + return; + if (!Eunambig(n)) /* if ambiguous def elem */ + { vec_orass(KILL,defkill); + vec_subass(GEN,defkill); + } + else /* unambiguous def elem */ + { symbol *s; + + assert(t->Eoper == OPvar); + s = t->EV.sp.Vsym; // ptr to var being def'd + for (i = 1; i < exptop; i++) /* for each ae elem */ + { register elem *e = expnod[i]; + + /* If it could be changed by the definition, */ + /* set bit in KILL. */ + + if (e->E1->EV.sp.Vsym == s || e->E2->EV.sp.Vsym == s) + vec_setclear(i,KILL,GEN); + } + } + + /* GEN CP elems */ + if (n->Eexp) + { unsigned b = n->Eexp; + + vec_setclear(b,GEN,KILL); + } + + return; + } + + /* Else Available Expression stuff */ + + if (n->Eexp) + { unsigned b = n->Eexp; // add elem to GEN + + assert(expnod[b] == n); + vec_setclear(b,GEN,KILL); + } + else if (OTdef(op)) /* else if definition elem */ + { + if (!Eunambig(n)) /* if ambiguous def elem */ + { vec_orass(KILL,defkill); + vec_subass(GEN,defkill); + if (OTcalldef(op)) + { vec_orass(KILL,vptrkill); + vec_subass(GEN,vptrkill); + } + } + else /* unambiguous def elem */ + { symbol *s; + + assert(t->Eoper == OPvar); + s = t->EV.sp.Vsym; /* idx of var being def'd */ + if (!(s->Sflags & SFLunambig)) + { vec_orass(KILL,starkill); /* kill all 'starred' refs */ + vec_subass(GEN,starkill); + } + for (i = 1; i < exptop; i++) /* for each ae elem */ + { elem *e = expnod[i]; + int eop = e->Eoper; + + /* If it could be changed by the definition, */ + /* set bit in KILL. */ + if (eop == OPvar) + { if (e->EV.sp.Vsym != s) + continue; + } + else if (OTunary(eop)) + { if (!vec_testbit(e->E1->Eexp,KILL)) + continue; + } + else if (OTbinary(eop)) + { if (!vec_testbit(e->E1->Eexp,KILL) && + !vec_testbit(e->E2->Eexp,KILL)) + continue; + } + else + continue; + + vec_setclear(i,KILL,GEN); + } + } + + /* GEN the lvalue of an assignment operator */ + if (OTassign(op) && !OTpost(op) && t->Eexp) + { unsigned b = t->Eexp; + + vec_setclear(b,GEN,KILL); + } + } +} + +/************************* LIVE VARIABLES **********************/ + +/********************************* + * Do live variable analysis (LVs). + * A variable is 'live' at some point if there is a + * subsequent use of it before a redefinition. + * Binlv = the set of variables live at the beginning of B. + * Boutlv = the set of variables live at the end of B. + * Bgen = set of variables used before any definition in B. + * Bkill = set of variables unambiguously defined before + * any use in B. + * Note that Bgen & Bkill = 0. + */ + +void flowlv() +{ vec_t tmp,livexit; + register unsigned i; + bool anychng; + unsigned cnt; + + lvgenkill(); /* compute Bgen and Bkill for LVs. */ + //assert(globsym.top); /* should be at least some symbols */ + + /* Create a vector of all the variables that are live on exit */ + /* from the function. */ + + livexit = vec_calloc(globsym.top); + for (i = 0; i < globsym.top; i++) + { if (globsym.tab[i]->Sflags & SFLlivexit) + vec_setbit(i,livexit); + } + + /* The transfer equation is: */ + /* Bin = (Bout - Bkill) | Bgen */ + /* Bout = union of Bin of all successors to B. */ + /* Using Ullman's algorithm: */ + + for (i = 0; i < dfotop; i++) /* for each block B */ + { + vec_copy(dfo[i]->Binlv,dfo[i]->Bgen); /* Binlv = Bgen */ + } + + tmp = vec_calloc(globsym.top); + cnt = 0; + do + { anychng = FALSE; + + /* For each block B in reverse DFO order */ + for (i = dfotop; i--;) + { register block *b = dfo[i]; + register list_t bl = b->Bsucc; + + /* Bout = union of Bins of all successors to B. */ + if (bl) + { vec_copy(b->Boutlv,list_block(bl)->Binlv); + while ((bl = list_next(bl)) != NULL) + { vec_orass(b->Boutlv,list_block(bl)->Binlv); + } + } + else /* no successors, Boutlv = livexit */ + { //assert(b->BC==BCret||b->BC==BCretexp||b->BC==BCexit); + vec_copy(b->Boutlv,livexit); + } + + /* Bin = (Bout - Bkill) | Bgen */ + vec_sub(tmp,b->Boutlv,b->Bkill); + vec_orass(tmp,b->Bgen); + if (!anychng) + anychng = !vec_equal(tmp,b->Binlv); + vec_copy(b->Binlv,tmp); + } + cnt++; + assert(cnt < 50); + } while (anychng); + vec_free(tmp); + vec_free(livexit); +#if 0 + dbg_printf("Live variables\n"); + for (i = 0; i < dfotop; i++) + { dbg_printf("B%d IN\t",i); + vec_println(dfo[i]->Binlv); + dbg_printf("B%d GEN\t",i); + vec_println(dfo[i]->Bgen); + dbg_printf(" KILL\t"); + vec_println(dfo[i]->Bkill); + dbg_printf(" OUT\t"); + vec_println(dfo[i]->Boutlv); + } +#endif +} + +/*********************************** + * Compute Bgen and Bkill for LVs. + * Allocate Binlv and Boutlv vectors. + */ + +STATIC void lvgenkill() +{ unsigned i; + + /* Compute ambigsym, a vector of all variables that could be */ + /* referenced by a *e or a call. */ + + assert(ambigsym == NULL); + ambigsym = vec_calloc(globsym.top); + for (i = 0; i < globsym.top; i++) + if (!(globsym.tab[i]->Sflags & SFLunambig)) + vec_setbit(i,ambigsym); + + for (i = 0; i < dfotop; i++) + { block *b = dfo[i]; + + vec_free(b->Bgen); + vec_free(b->Bkill); + lvelem(&(b->Bgen),&(b->Bkill),b->Belem); + if (b->BC == BCasm) + { vec_set(b->Bgen); + vec_clear(b->Bkill); + } + + vec_free(b->Binlv); + vec_free(b->Boutlv); + b->Binlv = vec_calloc(globsym.top); + b->Boutlv = vec_calloc(globsym.top); + } + + vec_free(ambigsym); /* dump any existing one */ + ambigsym = NULL; +} + +/***************************** + * Allocate and compute KILL and GEN for live variables. + */ + +STATIC void lvelem(vec_t *pgen,vec_t *pkill,elem *n) +{ + *pgen = vec_calloc(globsym.top); + *pkill = vec_calloc(globsym.top); + if (n && globsym.top) + accumlv(*pgen,*pkill,n); +} + +/********************************************** + * Accumulate GEN and KILL sets for LVs for this elem. + */ + +STATIC void accumlv(vec_t GEN,vec_t KILL,elem *n) +{ vec_t Gl,Kl,Gr,Kr; + register unsigned op; + elem *t; + + assert(GEN && KILL && n); + + while (1) + { elem_debug(n); + op = n->Eoper; + switch (op) + { + case OPvar: + if (symbol_isintab(n->EV.sp.Vsym)) + { SYMIDX si = n->EV.sp.Vsym->Ssymnum; + + assert((unsigned)si < globsym.top); + if (!vec_testbit(si,KILL)) // if not in KILL + vec_setbit(si,GEN); // put in GEN + } + break; + + case OPcolon: + case OPcolon2: + lvelem(&Gl,&Kl,n->E1); + lvelem(&Gr,&Kr,n->E2); + + /* GEN |= (Gl | Gr) - KILL */ + /* KILL |= (Kl & Kr) - GEN */ + + vec_orass(Gl,Gr); + vec_subass(Gl,KILL); + vec_orass(GEN,Gl); + vec_andass(Kl,Kr); + vec_subass(Kl,GEN); + vec_orass(KILL,Kl); + + vec_free(Gl); + vec_free(Gr); + vec_free(Kl); + vec_free(Kr); + break; + + case OPandand: + case OPoror: + accumlv(GEN,KILL,n->E1); + lvelem(&Gr,&Kr,n->E2); + + /* GEN |= Gr - KILL */ + /* KILL |= 0 */ + + vec_subass(Gr,KILL); + vec_orass(GEN,Gr); + + vec_free(Gr); + vec_free(Kr); + break; + + case OPasm: + vec_set(GEN); /* GEN everything not already KILLed */ + vec_subass(GEN,KILL); + break; + + case OPnewarray: + case OPmultinewarray: + accumlv(GEN,KILL,n->E1); + accumlv(GEN,KILL,n->E2); + goto L1; + + case OPcall: + case OPcallns: +#if TX86 + case OPstrcpy: + case OPmemcpy: + case OPmemset: +#endif +#ifdef DEBUG + assert(OTrtol(op)); +#endif + accumlv(GEN,KILL,n->E2); + accumlv(GEN,KILL,n->E1); + goto L1; + +#if TX86 + case OPstrcat: +#ifdef DEBUG + assert(!OTrtol(op)); +#endif + accumlv(GEN,KILL,n->E1); + accumlv(GEN,KILL,n->E2); +#endif + L1: + vec_orass(GEN,ambigsym); + vec_subass(GEN,KILL); + break; + + case OPeq: + /* Avoid GENing the lvalue of an = */ + accumlv(GEN,KILL,n->E2); + t = Elvalue(n); + if (t->Eoper != OPvar) + accumlv(GEN,KILL,t->E1); + else /* unambiguous assignment */ + { symbol *s; + + s = t->EV.sp.Vsym; + symbol_debug(s); + + /* if not GENed already, KILL it */ + if (symbol_isintab(s) && + !vec_testbit(s->Ssymnum,GEN) && + /* assignments to aggregates are */ + /* not unambiguous */ + !tyaggregate(s->ty()) && + t->EV.sp.Voffset == 0 && + tysize(t->Ety) == tysize(s->ty()) + ) + { assert((unsigned)s->Ssymnum < globsym.top); + vec_setbit(s->Ssymnum,KILL); + } + } + break; + + case OPind: + case OPucall: + case OPucallns: + case OPstrlen: + accumlv(GEN,KILL,n->E1); + + /* If it was a *p elem, set bits in GEN for all symbols */ + /* it could have referenced, but only if bits in KILL */ + /* are not already set. */ + + vec_orass(GEN,ambigsym); + vec_subass(GEN,KILL); + break; + + default: + if (OTunary(op)) + { n = n->E1; + continue; + } + else if (OTrtol(op) && ERTOL(n)) + { + accumlv(GEN,KILL,n->E2); + + /* Note that lvalues of op=,i++,i-- elems */ + /* are GENed. */ + n = n->E1; + continue; + } + else if (OTbinary(op)) + { + accumlv(GEN,KILL,n->E1); + n = n->E2; + continue; + } + break; + } + break; + } +} + +/********************* VERY BUSY EXPRESSIONS ********************/ + +/********************************************** + * Compute very busy expressions(VBEs). + * That is,expressions that are evaluated along + * separate paths. + * Bin = the set of VBEs at the beginning of B. + * Bout = the set of VBEs at the end of B. + * Bgen = set of expressions X+Y such that X+Y is + * evaluated before any def of X or Y. + * Bkill = set of expressions X+Y such that X or Y could + * be defined before X+Y is computed. + * Note that gen and kill are mutually exclusive. + */ + +void flowvbe() +{ vec_t tmp; + unsigned i; + bool anychng; + + flowxx = VBE; + aecpgenkill(); /* compute Bgen and Bkill for VBEs */ + if (exptop <= 1) /* if no candidates for VBEs */ + return; + + /*for (i = 0; i < exptop; i++) + dbg_printf("expnod[%d] = 0x%x\n",i,expnod[i]);*/ + + /* The transfer equation is: */ + /* Bout = & Bin(all successors S of B) */ + /* Bin =(Bout - Bkill) | Bgen */ + /* Using Ullman's algorithm: */ + + /*dbg_printf("defkill = "); vec_println(defkill); + dbg_printf("starkill = "); vec_println(starkill);*/ + + for (i = 0; i < dfotop; i++) + { block *b = dfo[i]; + + /*dbg_printf("block 0x%x\n",b); + dbg_printf("Bgen = "); vec_println(b->Bgen); + dbg_printf("Bkill = "); vec_println(b->Bkill);*/ + + if (b->BC == BCret || b->BC == BCretexp || b->BC == BCexit) + vec_clear(b->Bout); + else + vec_set(b->Bout); + + /* Bin = (Bout - Bkill) | Bgen */ + vec_sub(b->Bin,b->Bout,b->Bkill); + vec_orass(b->Bin,b->Bgen); + } + + tmp = vec_calloc(exptop); + do + { anychng = FALSE; + + /* for all blocks except return blocks in reverse dfo order */ + for (i = dfotop; i--;) + { block *b = dfo[i]; + list_t bl; + + if (b->BC == BCret || b->BC == BCretexp || b->BC == BCexit) + continue; + + /* Bout = & of Bin of all successors */ + bl = b->Bsucc; + assert(bl); /* must have successors */ + vec_copy(b->Bout,list_block(bl)->Bin); + while (TRUE) + { bl = list_next(bl); + if (!bl) + break; + vec_andass(b->Bout,list_block(bl)->Bin); + } + + /* Bin = (Bout - Bkill) | Bgen */ + vec_sub(tmp,b->Bout,b->Bkill); + vec_orass(tmp,b->Bgen); + if (!anychng) + anychng = !vec_equal(tmp,b->Bin); + vec_copy(b->Bin,tmp); + } + } while (anychng); /* while any changes occurred to any Bin */ + vec_free(tmp); +} + +/************************************* + * Accumulate GEN and KILL sets for VBEs for this elem. + */ + +STATIC void accumvbe(vec_t GEN,vec_t KILL,elem *n) +{ register unsigned op,i; + register elem *t; + + assert(GEN && KILL && n); + op = n->Eoper; + + switch (op) + { + case OPcolon: + case OPcolon2: + { vec_t Gl,Gr,Kl,Kr; + + aecpelem(&Gl,&Kl,n->E1); + aecpelem(&Gr,&Kr,n->E2); + + /* GEN |=((Gr - Kl) | (Gl - Kr)) - KILL */ + vec_subass(Gr,Kl); + vec_subass(Gl,Kr); + vec_orass(Gr,Gl); + vec_subass(Gr,KILL); + vec_orass(GEN,Gr); + + /* KILL |=(Kl | Kr) - GEN */ + vec_orass(Kl,Kr); + vec_subass(Kl,GEN); + vec_orass(KILL,Kl); + + vec_free(Gl); + vec_free(Kl); + vec_free(Gr); + vec_free(Kr); + break; + } + + case OPandand: + case OPoror: + accumvbe(GEN,KILL,n->E1); + /* WARNING: just so happens that it works this way. */ + /* Be careful about (b+c)||(b+c) being VBEs, only the */ + /* first should be GENed. Doing things this way instead */ + /* of (GEN |= Gr - KILL) and (KILL |= Kr - GEN) will */ + /* ensure this. */ + accumvbe(GEN,KILL,n->E2); + break; + + case OPnegass: + t = n->E1; + if (t->Eoper != OPvar) + { accumvbe(GEN,KILL,t->E1); + if (OTbinary(t->Eoper)) + accumvbe(GEN,KILL,t->E2); + } + break; + + case OPnewarray: + case OPmultinewarray: + accumvbe(GEN,KILL,n->E1); + accumvbe(GEN,KILL,n->E2); + break; + + case OPcall: + case OPcallns: + accumvbe(GEN,KILL,n->E2); + case OPucall: + case OPucallns: + t = n->E1; + // Do not VBE indirect function calls + if (t->Eoper == OPind) + t = t->E1; + accumvbe(GEN,KILL,t); + break; + + case OPasm: // if the dreaded OPasm elem + vec_set(KILL); // KILL everything + vec_subass(KILL,GEN); // except for GENed stuff + return; + + default: + if (OTunary(op)) + { + t = n->E1; + accumvbe(GEN,KILL,t); + } + else if (ERTOL(n)) + { accumvbe(GEN,KILL,n->E2); + t = n->E1; + // do not GEN the lvalue of an assignment op + if (OTassign(op)) + { t = Elvalue(n); + if (t->Eoper != OPvar) + { accumvbe(GEN,KILL,t->E1); + if (OTbinary(t->Eoper)) + accumvbe(GEN,KILL,t->E2); + } + } + else + accumvbe(GEN,KILL,t); + } + else if (OTbinary(op)) + { + /* do not GEN the lvalue of an assignment op */ + if (OTassign(op)) + { t = Elvalue(n); + if (t->Eoper != OPvar) + { accumvbe(GEN,KILL,t->E1); + if (OTbinary(t->Eoper)) + accumvbe(GEN,KILL,t->E2); + } + } + else + accumvbe(GEN,KILL,n->E1); + accumvbe(GEN,KILL,n->E2); + } + break; + } + + if (n->Eexp) /* if a vbe elem */ + { int ne = n->Eexp; + + assert(expnod[ne] == n); + if (!vec_testbit(ne,KILL)) /* if not already KILLed */ + { + /* GEN this expression only if it hasn't */ + /* already been GENed in this block. */ + /* (Don't GEN common subexpressions.) */ + if (vec_testbit(ne,GEN)) + vec_clearbit(ne,GEN); + else + { vec_setbit(ne,GEN); /* GEN this expression */ + /* GEN all identical expressions */ + /* (operators only, as there is no point */ + /* to hoisting out variables and constants) */ + if (!OTleaf(op)) + { for (i = 1; i < exptop; i++) + { if (op == expnod[i]->Eoper && + i != ne && + el_match(n,expnod[i])) + { vec_setbit(i,GEN); + assert(!vec_testbit(i,KILL)); + } + } + } + } + } +#if TARGET_SEGMENTED + if (op == OPvp_fp || op == OPcvp_fp) + { + vec_orass(KILL,vptrkill); /* KILL all vptr accesses */ + vec_subass(KILL,GEN); /* except for GENed stuff */ + } +#endif + } + else if (OTdef(op)) /* if definition elem */ + { + if (!Eunambig(n)) /* if ambiguous definition */ + { vec_orass(KILL,defkill); + if (OTcalldef(op)) + vec_orass(KILL,vptrkill); + } + else /* unambiguous definition */ + { symbol *s; + + assert(t->Eoper == OPvar); + s = t->EV.sp.Vsym; // ptr to var being def'd + if (!(s->Sflags & SFLunambig)) + vec_orass(KILL,starkill);/* kill all 'starred' refs */ + for (i = 1; i < exptop; i++) /* for each vbe elem */ + { elem *e = expnod[i]; + unsigned eop = e->Eoper; + + /* If it could be changed by the definition, */ + /* set bit in KILL. */ + if (eop == OPvar) + { if (e->EV.sp.Vsym != s) + continue; + } + else if (OTbinary(eop)) + { if (!vec_testbit(e->E1->Eexp,KILL) && + !vec_testbit(e->E2->Eexp,KILL)) + continue; + } + else if (OTunary(eop)) + { if (!vec_testbit(e->E1->Eexp,KILL)) + continue; + } + else /* OPconst or OPrelconst or OPstring or OPhstring */ + continue; + + vec_setbit(i,KILL); // KILL it + } /* for */ + } /* if */ + vec_subass(KILL,GEN); + } /* if */ +} + +#endif diff --git a/backend/global.h b/backend/global.h new file mode 100644 index 00000000..c0f361f9 --- /dev/null +++ b/backend/global.h @@ -0,0 +1,589 @@ +// Copyright (C) 1984-1996 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +// Globals + +//#pragma once +#ifndef GLOBAL_H +#define GLOBAL_H 1 + +#ifndef EL_H +#include "el.h" +#endif + +#ifdef DEBUG +CEXTERN char debuga; /* cg - watch assignaddr() */ +CEXTERN char debugb; /* watch block optimization */ +CEXTERN char debugc; /* watch code generated */ +CEXTERN char debugd; /* watch debug information generated */ +CEXTERN char debuge; // dump eh info +CEXTERN char debugf; /* trees after dooptim */ +CEXTERN char debugg; /* trees for code generator */ +CEXTERN char debugo; // watch optimizer +CEXTERN char debugr; // watch register allocation +CEXTERN char debugs; /* watch common subexp eliminator */ +CEXTERN char debugt; /* do test points */ +CEXTERN char debugu; +CEXTERN char debugw; /* watch progress */ +CEXTERN char debugx; /* suppress predefined CPP stuff */ +CEXTERN char debugy; /* watch output to il buffer */ +#endif /* DEBUG */ + +#define CR '\r' // Used because the MPW version of the compiler warps +#define LF '\n' // \n into \r and \r into \n. The translator version + // does not and this causes problems with the compilation + // with the translator +#define CR_STR "\r" +#define LF_STR "\n" + +struct seg_data; + +/************************ + * Bit masks + */ + +CEXTERN const unsigned mask[32]; +CEXTERN const unsigned long maskl[32]; + +extern char *argv0; +CEXTERN char *finname,*foutname,*foutdir; + +CEXTERN char OPTIMIZER,PARSER; +CEXTERN symtab_t globsym; +#if AUTONEST +CEXTERN int pushcount; +#endif + +CEXTERN Config config; // precompiled part of configuration +CEXTERN Configv configv; // non-ph part of configuration +CEXTERN char sytab[]; + +CEXTERN volatile int controlc_saw; /* a control C was seen */ +CEXTERN unsigned maxblks; /* array max for all block stuff */ +CEXTERN unsigned numblks; /* number of basic blocks (if optimized) */ +CEXTERN block *startblock; /* beginning block of function */ + +CEXTERN block **dfo; /* array of depth first order */ +CEXTERN unsigned dfotop; /* # of items in dfo[] */ +CEXTERN block **labelarr; /* dynamically allocated array, index is label #*/ +CEXTERN unsigned labelmax; /* size of labelarr[] */ +CEXTERN unsigned labeltop; /* # of used entries in labelarr[] */ +CEXTERN block *curblock; /* current block being read in */ +CEXTERN block *block_last; + +CEXTERN int errcnt; +CEXTERN regm_t fregsaved; + +#if SCPP +CEXTERN targ_size_t dsout; /* # of bytes actually output to data */ +#endif +CEXTERN tym_t pointertype; /* default data pointer type */ + +// cg.c +extern symbol *localgot; +extern symbol *tls_get_addr_sym; + +// iasm.c +Symbol *asm_define_label(const char *id); + +// cpp.c +#if SCPP || MARS +char *cpp_mangle(Symbol *s); +#else +#define cpp_mangle(s) ((s)->Sident) +#endif + +// ee.c +void eecontext_convs(unsigned marksi); +void eecontext_parse(); + +// exp2.c +#define REP_THRESHOLD (REGSIZE * (6+ (REGSIZE == 4))) + /* doesn't belong here, but func to OPxxx is in exp2 */ +void exp2_setstrthis(elem *e,Symbol *s,targ_size_t offset,type *t); +symbol *exp2_qualified_lookup(Classsym *sclass, int flags, int *pflags); +elem *exp2_copytotemp(elem *e); + +/* util.c */ +#if __clang__ +void util_exit(int) __attribute__((analyzer_noreturn)); +void util_assert(char *, int) __attribute__((analyzer_noreturn)); +#else +void util_exit(int); +void util_assert(char *, int); +#if __DMC__ +#pragma ZTC noreturn(util_exit) +#pragma ZTC noreturn(util_assert) +#endif +#endif + +void util_progress(); +void util_set16(void); +void util_set32(void); +void util_set64(void); +int ispow2(targ_ullong); + +#if TX86 +#if __GNUC__ +#define util_malloc(n,size) mem_malloc((n)*(size)) +#define util_calloc(n,size) mem_calloc((n)*(size)) +#define util_free mem_free +#define util_realloc(oldp,n,size) mem_realloc(oldp,(n)*(size)) +#define parc_malloc mem_malloc +#define parc_calloc mem_calloc +#define parc_realloc mem_realloc +#define parc_strdup mem_strdup +#define parc_free mem_free +#else +void *util_malloc(unsigned n,unsigned size); +void *util_calloc(unsigned n,unsigned size); +void util_free(void *p); +void *util_realloc(void *oldp,unsigned n,unsigned size); +void *parc_malloc(size_t len); +void *parc_calloc(size_t len); +void *parc_realloc(void *oldp,size_t len); +char *parc_strdup(const char *s); +void parc_free(void *p); +#endif +#endif + +void swap(int *,int *); +void crlf(FILE *); +char *unsstr(unsigned); +int isignore(int); +int isillegal(int); + +#if !defined(__SC__) && !defined(_MSC_VER) +int ishex(int); +#endif + +/* from cgcs.c */ +extern void comsubs(void); +void cgcs_term(); + +/* errmsgs.c */ +CEXTERN char *dlcmsgs(int); +extern void errmsgs_term(); + +/* from evalu8.c */ +int boolres(elem *); +int iftrue(elem *); +int iffalse(elem *); +elem *poptelem(elem *); +elem *poptelem2(elem *); +elem *poptelem3(elem *); +elem *poptelem4(elem *); +elem *selecte1(elem *,type *); + +//CEXTERN type *declar(type *,char *,int); + +/* from err.c */ +void err_message(const char *format,...); +void dll_printf(const char *format,...); +void cmderr(unsigned,...); +int synerr(unsigned,...); +void preerr(unsigned,...); + +#if __clang__ +void err_exit(void) __attribute__((analyzer_noreturn)); +void err_nomem(void) __attribute__((analyzer_noreturn)); +void err_fatal(unsigned,...) __attribute__((analyzer_noreturn)); +#else +void err_exit(void); +void err_nomem(void); +void err_fatal(unsigned,...); +#if __DMC__ +#pragma ZTC noreturn(err_exit) +#pragma ZTC noreturn(err_nomem) +#pragma ZTC noreturn(err_fatal) +#endif +#endif + +int cpperr(unsigned,...); +#if TX86 +int tx86err(unsigned,...); +extern int errmsgs_tx86idx; +#endif +void warerr(unsigned,...); +void err_warning_enable(unsigned warnum, int on); +CEXTERN void lexerr(unsigned,...); + +int typerr(int,type *,type *,...); +void err_noctor(Classsym *stag,list_t arglist); +void err_nomatch(const char *, list_t); +void err_ambiguous(Symbol *,Symbol *); +void err_noinstance(Symbol *s1,Symbol *s2); +void err_redeclar(Symbol *s,type *t1,type *t2); +void err_override(Symbol *sfbase,Symbol *sfder); +void err_notamember(const char *id, Classsym *s, symbol *alternate = NULL); + +/* exp.c */ +elem *expression(void),*const_exp(void),*assign_exp(void); +elem *exp_simplecast(type *); + +/* file.c */ +char *file_getsource(const char *iname); +int file_isdir(const char *fname); +void file_progress(void); +void file_remove(char *fname); +int file_stat(const char *fname,struct stat *pbuf); +int file_exists(const char *fname); +long file_size(const char *fname); +void file_term(void); +#if __NT__ && _WINDLL +char *file_nettranslate(const char *filename,const char *mode); +#else +#define file_nettranslate(f,m) ((char *)(f)) +#endif +char *file_unique(); + +/* from msc.c */ +type *newpointer(type *), + *newpointer_share(type *), + *reftoptr(type *t), + *newref(type *), + *topointer(type *), + *type_ptr(elem *, type *); +int type_chksize(unsigned long); +tym_t tym_conv(type *); +type * type_arrayroot(type *); +void chklvalue(elem *); +int tolvalue(elem **); +void chkassign(elem *); +void chknosu(elem *); +void chkunass(elem *); +void chknoabstract(type *); +CEXTERN targ_llong msc_getnum(void); +CEXTERN targ_size_t alignmember(type *,targ_size_t,targ_size_t); +CEXTERN targ_size_t align(targ_size_t,targ_size_t); + +/* nteh.c */ +unsigned char *nteh_context_string(); +void nteh_declarvars(Blockx *bx); +elem *nteh_setScopeTableIndex(Blockx *blx, int scope_index); +Symbol *nteh_contextsym(); +unsigned nteh_contextsym_size(); +Symbol *nteh_ecodesym(); +code *nteh_unwind(regm_t retregs,unsigned index); +code *linux_unwind(regm_t retregs,unsigned index); +int nteh_offset_sindex(); +int nteh_offset_sindex_seh(); +int nteh_offset_info(); + +/* os.c */ +void *globalrealloc(void *oldp,size_t nbytes); +void *vmem_baseaddr(); +void vmem_reservesize(unsigned long *psize); +unsigned long vmem_physmem(); +void *vmem_reserve(void *ptr,unsigned long size); +int vmem_commit(void *ptr, unsigned long size); +void vmem_decommit(void *ptr,unsigned long size); +void vmem_release(void *ptr,unsigned long size); +void *vmem_mapfile(const char *filename,void *ptr,unsigned long size,int flag); +void vmem_setfilesize(unsigned long size); +void vmem_unmapfile(); +void os_loadlibrary(const char *dllname); +void os_freelibrary(); +void *os_getprocaddress(const char *funcname); +void os_heapinit(); +void os_heapterm(); +void os_term(); +unsigned long os_unique(); +int os_file_exists(const char *name); +long os_file_size(int fd); +char *file_8dot3name(const char *filename); +int file_write(char *name, void *buffer, unsigned len); +int file_createdirs(char *name); +int os_critsecsize32(); +int os_critsecsize64(); + +#ifdef PSEUDO_REGS +/* pseudo.c */ +Symbol *pseudo_declar(char *); + +CEXTERN unsigned char pseudoreg[]; +CEXTERN regm_t pseudomask[]; +#endif /* PSEUDO_REGS */ + +/* Symbol.c */ +symbol **symtab_realloc(symbol **tab, size_t symmax); +symbol **symtab_malloc(size_t symmax); +symbol **symtab_calloc(size_t symmax); +void symtab_free(symbol **tab); +#if TERMCODE +void symbol_keep(Symbol *s); +#else +#define symbol_keep(s) ((void)(s)) +#endif +void symbol_print(Symbol *s); +void symbol_term(void); +char *symbol_ident(symbol *s); +Symbol *symbol_calloc(const char *id); +Symbol *symbol_name(const char *name, int sclass, type *t); +Symbol *symbol_generate(int sclass, type *t); +Symbol *symbol_genauto(type *t); +Symbol *symbol_genauto(elem *e); +Symbol *symbol_genauto(tym_t ty); +void symbol_func(Symbol *); +Funcsym *symbol_funcalias(Funcsym *sf); +Symbol *defsy(const char *p, Symbol **parent); +void symbol_addtotree(Symbol **parent,Symbol *s); +//Symbol *lookupsym(const char *p); +Symbol *findsy(const char *p, Symbol *rover); +void createglobalsymtab(void); +void createlocalsymtab(void); +void deletesymtab(void); +void meminit_free(meminit_t *m); +baseclass_t *baseclass_find(baseclass_t *bm,Classsym *sbase); +baseclass_t *baseclass_find_nest(baseclass_t *bm,Classsym *sbase); +int baseclass_nitems(baseclass_t *b); +void symbol_free(Symbol *s); +SYMIDX symbol_add(Symbol *s); +void freesymtab(Symbol **stab, SYMIDX n1, SYMIDX n2); +Symbol * symbol_copy(Symbol *s); +Symbol * symbol_searchlist(symlist_t sl, const char *vident); + + +#if TX86 +// cg87.c +void cg87_reset(); +#endif + +unsigned char loadconst(elem *e, int im); + +/* From cgopt.c */ +CEXTERN void opt(void); + +/* cgobj.c */ +void obj_init(Outbuffer *, const char *filename, const char *csegname); +void obj_initfile(const char *filename, const char *csegname, const char *modname); +size_t obj_mangle(Symbol *s,char *dest); +void obj_termfile(void); +void obj_term(void); +void obj_import(elem *e); +void objlinnum(Srcpos srcpos, targ_size_t offset); +void obj_dosseg(void); +void obj_startaddress(Symbol *); +void obj_includelib(const char *); +void obj_exestr(const char *p); +void obj_user(const char *p); +void obj_compiler(); +void obj_wkext(Symbol *,Symbol *); +void obj_lzext(Symbol *,Symbol *); +void obj_alias(const char *n1,const char *n2); +void obj_theadr(const char *modname); +void objseggrp(targ_size_t codesize, targ_size_t datasize, targ_size_t cdatasize, targ_size_t udatasize); +void obj_staticctor(Symbol *s,int dtor,int seg); +void obj_staticdtor(Symbol *s); +void obj_funcptr(Symbol *s); +void obj_ehtables(Symbol *sfunc,targ_size_t size,Symbol *ehsym); +void obj_ehsections(); +void obj_moduleinfo(Symbol *scc); +int obj_comdat(Symbol *); +int obj_comdatsize(Symbol *, targ_size_t symsize); +void obj_setcodeseg(int seg,targ_size_t offset); +int obj_codeseg(char *name,int suffix); +seg_data *obj_tlsseg(); +seg_data *obj_tlsseg_bss(); +int obj_fardata(char *name, targ_size_t size, targ_size_t *poffset); +void obj_browse(char *, unsigned); +void objend(void); +void obj_export(Symbol *s, unsigned argsize); +void objpubdef(int seg, Symbol *s, targ_size_t offset); +#if ELFOBJ +void objpubdefsize(int seg, Symbol *s, targ_size_t offset, targ_size_t symsize); +#elif MACHOBJ + #define objpubdefsize(seg, s, offset, symsize) objpubdef(seg, s, offset) +#endif +int objextdef(const char *); +int elf_data_start(Symbol *sdata, targ_size_t datasize, int seg); +int objextern(Symbol *); +int obj_comdef(Symbol *s, int flag, targ_size_t size, targ_size_t count); +void obj_lidata(int seg, targ_size_t offset, targ_size_t count); +void obj_write_zeros(seg_data *pseg, targ_size_t count); +void obj_write_byte(seg_data *pseg, unsigned byte); +void obj_write_bytes(seg_data *pseg, unsigned nbytes, void *p); +void obj_byte(int seg, targ_size_t offset, unsigned byte); +unsigned obj_bytes(int seg, targ_size_t offset, unsigned nbytes, void *p); +void objledata(int seg, targ_size_t offset, targ_size_t data, unsigned lcfd, unsigned idx1, unsigned idx2); +void obj_long(int seg, targ_size_t offset, unsigned long data, unsigned lcfd, unsigned idx1, unsigned idx2); +void reftodatseg(int seg, targ_size_t offset, targ_size_t val, unsigned targetdatum, int flags); +void reftofarseg(int seg, targ_size_t offset, targ_size_t val, int farseg, int flags); +void reftocodseg(int seg, targ_size_t offset, targ_size_t val); +int reftoident(int seg, targ_size_t offset, Symbol *s, targ_size_t val, int flags); +void obj_far16thunk(Symbol *s); +void obj_fltused(); + +// objrecor.c +void objfile_open(const char *); +void objfile_close(void *data, unsigned len); +void objfile_delete(); +void objfile_term(); + +/* cod3.c */ +void cod3_thunk(Symbol *sthunk,Symbol *sfunc,unsigned p,tym_t thisty, + targ_size_t d,int i,targ_size_t d2); + +/* out.c */ +void outfilename(char *name,int linnum); +void outcsegname(char *csegname); +void outthunk(Symbol *sthunk, Symbol *sfunc, unsigned p, tym_t thisty, targ_size_t d, int i, targ_size_t d2); +void outdata(Symbol *s); +void outcommon(Symbol *s, targ_size_t n); +void out_regcand(symtab_t *); +void writefunc(Symbol *sfunc); +void alignOffset(int seg,targ_size_t datasize); +void out_reset(); +symbol *out_readonly_sym(tym_t ty, void *p, int len); + +/* blockopt.c */ +extern unsigned bc_goal[BCMAX]; + +block *block_calloc(); +void block_init(); +void block_term(); +#if MARS +void block_next(Blockx *bctx,enum BC bc,block *bn); +block *block_goto(Blockx *bctx,enum BC bc,block *bn); +#else +void block_next(enum BC,block *); +#endif +void block_setlabel(unsigned lbl); +void block_goto(); +void block_goto(block *); +void block_goto(block *bgoto, block *bnew); +void block_ptr(void); +void block_pred(void); +void block_clearvisit(); +void block_visit(block *b); +void block_compbcount(void); +void blocklist_free(block **pb); +void block_optimizer_free(block *b); +void block_free(block *b); +void blocklist_hydrate(block **pb); +void blocklist_dehydrate(block **pb); +void block_appendexp(block *b, elem *e); +void block_initvar(Symbol *s); +void block_endfunc(int flag); +void brcombine(void); +void blockopt(int); +void compdfo(void); + +#define block_initvar(s) (curblock->Binitvar = (s)) + +#ifdef DEBUG + +/* debug.c */ +CEXTERN const char *regstring[]; + +void WRclass(enum SC c); +void WRTYxx(tym_t t); +void WROP(unsigned oper); +void WRBC(unsigned bc); +void WRarglst(list_t a); +void WRblock(block *b); +void WRblocklist(list_t bl); +void WReqn(elem *e); +void WRfunc(void); +void WRdefnod(void); +void WRFL(enum FL); +char *sym_ident(SYMIDX si); + +#endif + +/* cgelem.c */ +elem *doptelem(elem *,int); +void postoptelem(elem *); +unsigned swaprel(unsigned); +int elemisone(elem *); + +/* msc.c */ +targ_size_t size(tym_t); +Symbol *symboldata(targ_size_t offset,tym_t ty); +int dom(block *A , block *B); +unsigned revop(unsigned op); +unsigned invrel(unsigned op); +int binary(const char *p, const char __near * __near *tab, int high); + +/* go.c */ +void go_term(void); +int go_flag(char *cp); +void optfunc(void); + +/* filename.c */ +#if !MARS +extern Srcfiles srcfiles; +Sfile **filename_indirect(Sfile *sf); +Sfile *filename_search( const char *name ); +Sfile *filename_add( const char *name ); +void filename_hydrate( Srcfiles *fn ); +void filename_dehydrate( Srcfiles *fn ); +void filename_merge( Srcfiles *fn ); +void filename_mergefl(Sfile *sf); +void filename_translate(Srcpos *); +void filename_free( void ); +int filename_cmp(const char *f1,const char *f2); +void srcpos_hydrate(Srcpos *); +void srcpos_dehydrate(Srcpos *); +#endif + +// tdb.c +unsigned long tdb_gettimestamp(); +void tdb_write(void *buf,unsigned size,unsigned numindices); +unsigned long tdb_typidx(void *buf); +//unsigned long tdb_typidx(unsigned char *buf,unsigned length); +void tdb_term(); + +// rtlsym.c +void rtlsym_init(); +void rtlsym_reset(); +void rtlsym_term(); + +#if ELFOBJ || MACHOBJ +void elf_add_cdata(); +symbol * elf_sym_cdata(tym_t, char *, int ); +int elf_data_cdata(char *str,int len,int *pseg); +#if ELFOBJ +int elf_getsegment(const char *name, const char *suffix, + int type, int flags, int align); +void elf_addrel(int seg, targ_size_t offset, unsigned type, + unsigned symidx, targ_size_t val); +#endif +#if MACHOBJ +int mach_getsegment(const char *sectname, const char *segname, + int align, int flags, int flags2 = 0); +void mach_addrel(int seg, targ_size_t offset, symbol *targsym, + unsigned targseg, int rtype, int val = 0); +#endif +void elf_func_start(Symbol *sfunc); +void elf_func_term(Symbol *sfunc); +unsigned elf_addstr(Outbuffer *strtab, const char *); + +void dwarf_CFA_set_loc(size_t location); +void dwarf_CFA_set_reg_offset(int reg, int offset); +void dwarf_CFA_offset(int reg, int offset); +void dwarf_CFA_args_size(size_t sz); +#endif + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +void elfobj_gotref(symbol *s); +symbol *elfobj_getGOTsym(); +void elfobj_refGOTsym(); +elem * exp_isconst(); +elem *lnx_builtin_next_arg(elem *efunc,list_t arglist); +char *lnx_redirect_funcname(const char *); +void lnx_funcdecl(symbol *,enum SC,enum_SC,int); +int lnx_attributes(int hinttype,const void *hint, type **ptyp, tym_t *ptym,int *pattrtype); +#endif + +#endif /* GLOBAL_H */ + diff --git a/backend/glocal.c b/backend/glocal.c new file mode 100644 index 00000000..10fd0373 --- /dev/null +++ b/backend/glocal.c @@ -0,0 +1,741 @@ +// Copyright (C) 1993-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !DEMO && !SPP + +#include +#include +#include + +#if __sun&&__SVR4 +#include +#endif + +#include "cc.h" +#include "global.h" +#include "el.h" +#include "go.h" +#include "type.h" +#include "oper.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +typedef struct loc_t +{ + elem *e; + int flags; +# define LFvolatile 1 // contains volatile refs or defs +# define LFambigref 2 // references ambiguous data +# define LFambigdef 4 // defines ambiguous data +# define LFsymref 8 // reference to symbol s +# define LFsymdef 0x10 // definition of symbol s +# define LFunambigref 0x20 // references unambiguous data other than s +# define LFunambigdef 0x40 // defines unambiguous data other than s +#if TX86 +# define LFinp 0x80 // input from I/O port +# define LFoutp 0x100 // output to I/O port +#endif +# define LFfloat 0x200 // sets float flags and/or depends on + // floating point settings +} loc_t; + +static loc_t *loctab; +static unsigned locmax; +static unsigned loctop; + +STATIC void local_exp(elem *e,int goal); +STATIC int local_chkrem(elem *e,elem *eu); +STATIC void local_ins(elem *e); +STATIC void local_rem(unsigned u); +STATIC int local_getflags(elem *e,symbol *s); +STATIC void local_remove(int flags); +STATIC void local_ambigref(void); +STATIC void local_ambigdef(void); +STATIC void local_symref(symbol *s); +STATIC void local_symdef(symbol *s); + +/////////////////////////////// +// This optimization attempts to replace sequences like: +// x = func(); +// y = 3; +// z = x + 5; +// with: +// y = 3; +// z = (x = func()) + 5; +// In other words, we attempt to localize expressions by moving them +// as near as we can to where they are used. This should minimize +// temporary generation and register usage. + +void localize() +{ block *b; + + cmes("localize()\n"); + + // Table should not get any larger than the symbol table + locmax = globsym.symmax; + loctab = (loc_t *) alloca(locmax * sizeof(*loctab)); + + for (b = startblock; b; b = b->Bnext) /* for each block */ + { + loctop = 0; // start over for each block + if (b->Belem && + /* Overly broad way to account for the case: + * try + * { i++; + * foo(); // throws exception + * i++; // shouldn't combine previous i++ with this one + * } + */ + !b->Btry) + { + local_exp(b->Belem,0); + } + } +} + +////////////////////////////////////// +// Input: +// goal !=0 if we want the result of the expression +// + +STATIC void local_exp(elem *e,int goal) +{ symbol *s; + unsigned u; + elem *e1; + int op1; + +Loop: + elem_debug(e); + int op = e->Eoper; + switch (op) + { case OPcomma: + local_exp(e->E1,0); + e = e->E2; + goto Loop; + + case OPandand: + case OPoror: + local_exp(e->E1,1); + loctop = 0; // we can do better than this, fix later + break; + + case OPcolon: + case OPcolon2: + loctop = 0; // we can do better than this, fix later + break; + + case OPinfo: + if (e->E1->Eoper == OPmark) + { loctop = 0; + e = e->E2; + goto Loop; + } + goto case_bin; + + case OPdtor: + case OPctor: + case OPdctor: + loctop = 0; // don't move expressions across ctor/dtor + break; // boundaries, it would goof up EH cleanup + + case OPddtor: + loctop = 0; // don't move expressions across ctor/dtor + // boundaries, it would goof up EH cleanup + local_exp(e->E1,0); + loctop = 0; + break; + + case OPeq: + case OPstreq: + e1 = e->E1; + local_exp(e->E2,1); + if (e1->Eoper == OPvar) + { s = e1->EV.sp.Vsym; + if (s->Sflags & SFLunambig) + { local_symdef(s); + if (!goal) + local_ins(e); + } + else + local_ambigdef(); + } + else + { + assert(!OTleaf(e1->Eoper)); + local_exp(e1->E1,1); + if (OTbinary(e1->Eoper)) + local_exp(e1->E2,1); + local_ambigdef(); + } + break; + + case OPpostinc: + case OPpostdec: + case OPaddass: + case OPminass: + case OPmulass: + case OPdivass: + case OPmodass: + case OPashrass: + case OPshrass: + case OPshlass: + case OPandass: + case OPxorass: + case OPorass: + if (ERTOL(e)) + { local_exp(e->E2,1); + case OPnegass: + e1 = e->E1; + op1 = e1->Eoper; + if (op1 != OPvar) + { + local_exp(e1->E1,1); + if (OTbinary(op1)) + local_exp(e1->E2,1); + } + else if (loctop && (op == OPaddass || op == OPxorass)) + { unsigned u; + + s = e1->EV.sp.Vsym; + for (u = 0; u < loctop; u++) + { elem *em; + + em = loctab[u].e; + if (em->Eoper == op && + em->E1->EV.sp.Vsym == s && + tysize(em->Ety) == tysize(e1->Ety) && + em->E1->EV.sp.Voffset == e1->EV.sp.Voffset && + !el_sideeffect(em->E2) + ) + { // Change (x += a),(x += b) to + // (x + a),(x += a + b) + changes++; + e->E2 = el_bin(opeqtoop(op),e->E2->Ety,em->E2,e->E2); + em->Eoper = opeqtoop(op); + em->E2 = el_copytree(em->E2); + local_rem(u); +#ifdef DEBUG + if (debugc) + { dbg_printf("Combined equation "); + WReqn(e); + dbg_printf(";\n"); + e = doptelem(e,GOALvalue); + } +#endif + + break; + } + } + } + } + else + { + e1 = e->E1; + op1 = e1->Eoper; + if (op1 != OPvar) + { + local_exp(e1->E1,1); + if (OTbinary(op1)) + local_exp(e1->E2,1); + } + if (loctop) + { if (op1 == OPvar && + ((s = e1->EV.sp.Vsym)->Sflags & SFLunambig)) + local_symref(s); + else + local_ambigref(); + } + local_exp(e->E2,1); + } + if (op1 == OPvar && + ((s = e1->EV.sp.Vsym)->Sflags & SFLunambig)) + { local_symref(s); + local_symdef(s); + if (op == OPaddass || op == OPxorass) + local_ins(e); + } + else if (loctop) + { + local_remove(LFambigdef | LFambigref); + } + break; +#if TX86 + case OPstrlen: +#endif + case OPind: + local_exp(e->E1,1); + local_ambigref(); + break; + + case OPnewarray: + case OPmultinewarray: + local_exp(e->E1,1); + local_exp(e->E2,1); + goto Lrd; + + case OPstrcmp: + case OPmemcmp: + case OPbt: + case OParray: + case OPfield: + local_exp(e->E1,1); + local_exp(e->E2,1); + local_ambigref(); + break; + + case OPstrcpy: + case OPmemcpy: + case OPstrcat: + case OPcall: + case OPcallns: + local_exp(e->E2,1); + case OPstrctor: + case OPucall: + case OPucallns: + local_exp(e->E1,1); + goto Lrd; + + case OPbtc: + case OPbtr: + case OPbts: + local_exp(e->E1,1); + local_exp(e->E2,1); + goto Lrd; + case OPasm: + Lrd: + local_remove(LFfloat | LFambigref | LFambigdef); + break; + +#if TX86 + case OPmemset: + local_exp(e->E2,1); + if (e->E1->Eoper == OPvar) + { + /* Don't want to rearrange (p = get(); p memset 0;) + * as elemxxx() will rearrange it back. + */ + s = e->E1->EV.sp.Vsym; + if (s->Sflags & SFLunambig) + local_symref(s); + else + local_ambigref(); // ambiguous reference + } + else + local_exp(e->E1,1); + local_ambigdef(); + break; +#endif + + case OPvar: + s = e->EV.sp.Vsym; + if (loctop) + { unsigned u; + + // If potential candidate for replacement + if (s->Sflags & SFLunambig) + { + for (u = 0; u < loctop; u++) + { elem *em; + + em = loctab[u].e; + if (em->E1->EV.sp.Vsym == s && + (em->Eoper == OPeq || em->Eoper == OPstreq)) + { + if (tysize(em->Ety) == tysize(e->Ety) && + em->E1->EV.sp.Voffset == e->EV.sp.Voffset && + (tyfloating(em->Ety) != 0) == (tyfloating(e->Ety) != 0)) + { +#ifdef DEBUG + if (debugc) + { dbg_printf("Moved equation "); + WReqn(em); + dbg_printf(";\n"); + } +#endif + changes++; + em->Ety = e->Ety; + el_copy(e,em); + em->E1 = em->E2 = NULL; + em->Eoper = OPconst; + } + local_rem(u); + break; + } + } + local_symref(s); + } + else + local_ambigref(); // ambiguous reference + } + break; + + case OPremquo: + if (e->E1->Eoper != OPvar) + goto case_bin; + s = e->E1->EV.sp.Vsym; + if (loctop) + { + if (s->Sflags & SFLunambig) + local_symref(s); + else + local_ambigref(); // ambiguous reference + } + goal = 1; + e = e->E2; + goto Loop; + + default: + if (OTcommut(e->Eoper)) + { // Since commutative operators may get their leaves + // swapped, we eliminate any that may be affected by that. + + for (u = 0; u < loctop;) + { + int f1,f2,f; + elem *eu; + + f = loctab[u].flags; + eu = loctab[u].e; + s = eu->E1->EV.sp.Vsym; + f1 = local_getflags(e->E1,s); + f2 = local_getflags(e->E2,s); + if (f1 & f2 & LFsymref || // if both reference or + (f1 | f2) & LFsymdef || // either define + f & LFambigref && (f1 | f2) & LFambigdef || + f & LFambigdef && (f1 | f2) & (LFambigref | LFambigdef) + ) + local_rem(u); + else if (f & LFunambigdef && local_chkrem(e,eu->E2)) + local_rem(u); + else + u++; + } + } + if (EUNA(e)) + { goal = 1; + e = e->E1; + goto Loop; + } + case_bin: + if (EBIN(e)) + { local_exp(e->E1,1); + goal = 1; + e = e->E2; + goto Loop; + } + break; + } // end of switch (e->Eoper) +} + +/////////////////////////////////// +// Examine expression tree eu to see if it defines any variables +// that e refs or defs. +// Note that e is a binary operator. +// Returns: +// !=0 if it does + +STATIC int local_chkrem(elem *e,elem *eu) +{ int f1,f2; + int op; + symbol *s; + int result = 0; + + while (1) + { elem_debug(eu); + op = eu->Eoper; + if (OTassign(op) && eu->E1->Eoper == OPvar) + { s = eu->E1->EV.sp.Vsym; + f1 = local_getflags(e->E1,s); + f2 = local_getflags(e->E2,s); + if ((f1 | f2) & (LFsymref | LFsymdef)) // if either reference or define + { result = 1; + break; + } + } + if (OTbinary(op)) + { if (local_chkrem(e,eu->E2)) + { result = 1; + break; + } + } + else if (!OTunary(op)) + break; // leaf node + eu = eu->E1; + } + return result; +} + +////////////////////////////////////// +// Add entry e to loctab[] + +STATIC void local_ins(elem *e) +{ + elem_debug(e); + if (e->E1->Eoper == OPvar) + { symbol *s; + + s = e->E1->EV.sp.Vsym; + symbol_debug(s); + if (s->Sflags & SFLunambig) // if can only be referenced directly + { int flags; + + flags = local_getflags(e->E2,NULL); +#if TX86 + if (!(flags & (LFvolatile | LFinp | LFoutp)) && +#else + if (!(flags & LFvolatile) && +#endif + !(e->E1->Ety & mTYvolatile)) + { + // Add e to the candidate array + //dbg_printf("local_ins('%s'), loctop = %d, locmax = %d\n",s->Sident,loctop,locmax); + assert(loctop < locmax); + loctab[loctop].e = e; + loctab[loctop].flags = flags; + loctop++; + } + } + } +} + +////////////////////////////////////// +// Remove entry i from loctab[], and then compress the table. +// + +STATIC void local_rem(unsigned u) +{ + //dbg_printf("local_rem(%u)\n",u); + assert(u < loctop); + if (u + 1 != loctop) + { assert(u < loctop); + loctab[u] = loctab[loctop - 1]; + } + loctop--; +} + +////////////////////////////////////// +// Analyze and gather LFxxxx flags about expression e and symbol s. + +STATIC int local_getflags(elem *e,symbol *s) +{ int flags; + + elem_debug(e); + if (s) + symbol_debug(s); + flags = 0; + while (1) + { + if (e->Ety & mTYvolatile) + flags |= LFvolatile; + switch (e->Eoper) + { + case OPeq: + case OPstreq: + if (e->E1->Eoper == OPvar) + { symbol *s1; + + s1 = e->E1->EV.sp.Vsym; + if (s1->Sflags & SFLunambig) + flags |= (s1 == s) ? LFsymdef : LFunambigdef; + else + flags |= LFambigdef; + } + else + flags |= LFambigdef; + goto L1; + + case OPpostinc: + case OPpostdec: + case OPaddass: + case OPminass: + case OPmulass: + case OPdivass: + case OPmodass: + case OPashrass: + case OPshrass: + case OPshlass: + case OPandass: + case OPxorass: + case OPorass: + if (e->E1->Eoper == OPvar) + { symbol *s1; + + s1 = e->E1->EV.sp.Vsym; + if (s1->Sflags & SFLunambig) + flags |= (s1 == s) ? LFsymdef | LFsymref + : LFunambigdef | LFunambigref; + else + flags |= LFambigdef | LFambigref; + } + else + flags |= LFambigdef | LFambigref; + L1: + flags |= local_getflags(e->E2,s); + e = e->E1; + break; + + case OPucall: + case OPucallns: + case OPcall: + case OPcallns: + case OPnewarray: + case OPmultinewarray: +#if TX86 + case OPstrcat: + case OPstrcpy: + case OPmemcpy: + case OPbtc: + case OPbtr: + case OPbts: +#endif + case OPstrctor: + flags |= LFambigref | LFambigdef; + break; + +#if TX86 + case OPmemset: + flags |= LFambigdef; + break; +#endif + + case OPvar: + if (e->EV.sp.Vsym == s) + flags |= LFsymref; + else if (!(e->EV.sp.Vsym->Sflags & SFLunambig)) + flags |= LFambigref; + break; + + case OPind: + case OParray: + case OPfield: +#if TX86 + case OPstrlen: + case OPstrcmp: + case OPmemcmp: + case OPbt: +#endif + flags |= LFambigref; + break; + +#if TX86 + case OPinp: + flags |= LFinp; + break; + + case OPoutp: + flags |= LFoutp; + break; +#endif + } + if (EUNA(e)) + { + if (tyfloating(e->Ety)) + flags |= LFfloat; + e = e->E1; + } + else if (EBIN(e)) + { + if (tyfloating(e->Ety)) + flags |= LFfloat; + flags |= local_getflags(e->E2,s); + e = e->E1; + } + else + break; + } + return flags; +} + +////////////////////////////////////// +// Remove all entries with flags set. +// + +STATIC void local_remove(int flags) +{ unsigned u; + + for (u = 0; u < loctop;) + { + if (loctab[u].flags & flags) + local_rem(u); + else + u++; + } +} + +////////////////////////////////////// +// Ambiguous reference. Remove all with ambiguous defs +// + +STATIC void local_ambigref() +{ unsigned u; + + for (u = 0; u < loctop;) + { + if (loctab[u].flags & LFambigdef) + local_rem(u); + else + u++; + } +} + +////////////////////////////////////// +// Ambigous definition. Remove all with ambiguous refs. +// + +STATIC void local_ambigdef() +{ unsigned u; + + for (u = 0; u < loctop;) + { + if (loctab[u].flags & (LFambigref | LFambigdef)) + local_rem(u); + else + u++; + } +} + +////////////////////////////////////// +// Reference to symbol. +// Remove any that define that symbol. + +STATIC void local_symref(symbol *s) +{ unsigned u; + + symbol_debug(s); + for (u = 0; u < loctop;) + { + if (local_getflags(loctab[u].e,s) & LFsymdef) + local_rem(u); + else + u++; + } +} + +////////////////////////////////////// +// Definition of symbol. +// Remove any that reference that symbol. + +STATIC void local_symdef(symbol *s) +{ unsigned u; + + symbol_debug(s); + for (u = 0; u < loctop;) + { + if (local_getflags(loctab[u].e,s) & (LFsymref | LFsymdef)) + local_rem(u); + else + u++; + } +} + +#endif diff --git a/backend/gloop.c b/backend/gloop.c new file mode 100644 index 00000000..d8600ac9 --- /dev/null +++ b/backend/gloop.c @@ -0,0 +1,3628 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +#if (SCPP || MARS) && !HTOD + +#include +#include +#include + +#include "cc.h" +#include "el.h" +#include "go.h" +#include "oper.h" +#include "global.h" +#include "type.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +/*#define vec_copy(t,f) (dbg_printf("line %d\n",__LINE__),vec_copy((t),(f)))*/ + +extern mftype mfoptim; + +struct Iv; + +/********************************* + * Loop data structure. + */ + +struct loop +{ loop *Lnext; // Next loop in list (startloop -> start of list) + vec_t Lloop; // Vector of blocks in this loop + vec_t Lexit; // Vector of exit blocks of loop + block *Lhead; // Pointer to header of loop + block *Ltail; // Pointer to tail + block *Lpreheader; // Pointer to preheader (if any) + list_t Llis; // loop invariant elems moved to Lpreheader, so + // redundant temporaries aren't created + Iv *Livlist; // basic induction variables + Iv *Lopeqlist; // list of other op= variables + void print(); + static loop *mycalloc(); + + static loop *freelist; +}; + +struct famlist +{ elem **FLpelem; /* parent of elem in the family */ + elem *c1,*c2; /* c1*(basic IV) + c2 */ +#define FLELIM ((symbol *)-1) + symbol *FLtemp; // symbol index of temporary (FLELIM if */ + /* this entry has no temporary) */ + tym_t FLty; /* type of this induction variable */ + tym_t FLivty; /* type of the basic IV elem (which is */ + /* not necessarilly the type of the IV */ + /* elem!) */ + famlist *FLnext; // next in list + void print(); + + static famlist *mycalloc(); + + static famlist *freelist; +}; + +struct Iv +{ + symbol *IVbasic; // symbol of basic IV + elem **IVincr; // pointer to parent of IV increment elem + famlist *IVfamily; // variables in this family + Iv *IVnext; // next iv in list + void print(); + + static Iv *mycalloc(); + + static Iv *freelist; +}; + +STATIC void freeloop(loop **pl); +STATIC void buildloop(loop **pl, block *head, block *tail); +STATIC void insert(block *b , vec_t lv); +STATIC void movelis(elem *n,block *b,loop *l,int *pdomexit); +STATIC int looprotate(loop *l); +STATIC void markinvar(elem *n , vec_t rd); +STATIC bool refs(symbol *v , elem *n , elem *nstop); +STATIC void appendelem(elem *n , elem **pn); +STATIC void freeivlist(Iv *biv); +STATIC void unmarkall(elem *e); +void filterrd(vec_t f,vec_t rd,symbol *s); +STATIC void filterrdind(vec_t f,vec_t rd,elem *e); +STATIC famlist * simfl(famlist *fl , tym_t tym); +STATIC famlist * newfamlist(tym_t ty); +STATIC void loopiv(loop *l); +STATIC void findbasivs(loop *l); +STATIC void findopeqs(loop *l); +STATIC void findivfams(loop *l); +STATIC void ivfamelems(Iv *biv , elem **pn); +STATIC void elimfrivivs(loop *l); +STATIC void intronvars(loop *l); +STATIC bool funcprev(Iv *biv , famlist *fl); +STATIC void elimbasivs(loop *l); +STATIC void elimopeqs(loop *l); +STATIC famlist * flcmp(famlist *f1 , famlist *f2); +STATIC elem ** onlyref(symbol *x , loop *l , elem *incn , int *prefcount); +STATIC void countrefs(elem **pn , bool flag); +STATIC int countrefs2(elem *e); +STATIC void elimspec(loop *l); +STATIC void elimspecwalk(elem **pn); + +static bool addblk; /* if TRUE, then we added a block */ + +/* is elem loop invariant? */ +#define isLI(n) ((n)->Nflags & NFLli) + +/* make elem loop invariant */ +#define makeLI(n) ((n)->Nflags |= NFLli) + +/****************************** + * UNAMBIG being defined means that: + * Only variables that could only be unambiguously defined + * are candidates for loop invariant removal and induction + * variables. + * This means only variables that have the SFLunambig flag + * set for them. + * Doing this will still cover 90% (I hope) of the cases, and + * is a lot faster to compute. + */ + +#define UNAMBIG 1 + +/**************************** + */ + +void famlist::print() +{ +#ifdef DEBUG + dbg_printf("famlist:\n"); + dbg_printf("*FLpelem:\n"); + elem_print(*FLpelem); + dbg_printf("c1:"); + elem_print(c1); + dbg_printf("c2:"); + elem_print(c2); + dbg_printf("FLty = "); WRTYxx(FLty); + dbg_printf("\nFLivty = "); WRTYxx(FLivty); + dbg_printf("\n"); +#endif +} + + +/**************************** + */ + +void Iv::print() +{ +#ifdef DEBUG + dbg_printf("IV: '%s'\n",IVbasic->Sident); + dbg_printf("*IVincr:\n"); + elem_print(*IVincr); +#endif +} + +/*********************** + * Write loop. + */ + +void loop::print() +{ +#ifdef DEBUG + loop *l = this; + dbg_printf("loop %p, next = %p\n",l,(l) ? l->Lnext : (loop *) NULL); + if (!l) + return; + dbg_printf("\thead: B%d, tail: B%d, prehead: B%d\n",l->Lhead->Bdfoidx, + l->Ltail->Bdfoidx,(l->Lpreheader ) ? l->Lpreheader->Bdfoidx : + (unsigned)-1); + dbg_printf("\tLloop "); vec_println(l->Lloop); + dbg_printf("\tLexit "); vec_println(l->Lexit); +#endif +} + +/*************************** + * Allocate loop. + */ + +loop *loop::freelist = NULL; + +loop *loop::mycalloc() +{ loop *l; + + if (freelist) + { + l = freelist; + freelist = l->Lnext; + memset(l,0,sizeof(loop)); + } + else + l = (loop *) mem_calloc(sizeof(loop)); + return l; +} + +/************* + * Free loops. + */ + +STATIC void freeloop(loop **pl) +{ loop *ln; + loop *l; + + for (l = *pl; l; l = ln) + { ln = l->Lnext; + vec_free(l->Lloop); + vec_free(l->Lexit); + list_free(&l->Llis); + l->Lnext = loop::freelist; + loop::freelist = l; + } + *pl = NULL; +} + +/********************************** + * Initialize block information. + * Returns: + * !=0 contains BCasm block + */ + +int blockinit() +{ register unsigned i; + register block *b; + int hasasm = 0; + + assert(dfo); + for (i = 0, b = startblock; b; i++, b = b->Bnext) + { +#ifdef DEBUG /* check integrity of Bpred and Bsucc */ + register list_t blp; + + for (blp = b->Bpred; blp; blp = list_next(blp)) + { register list_t bls; + + for (bls = list_block(blp)->Bsucc; bls; bls = list_next(bls)) + if (list_block(bls) == b) + goto L1; + assert(0); + L1: ; + } +#endif + if (b->BC == BCasm) + hasasm = 1; + ; /* compute number of blocks */ + } + assert(numblks == i && maxblks); + assert(i <= maxblks); + for (i = 0; i < dfotop; i++) + { assert(dfo[i]->Bdfoidx == i); + if (!dfo[i]->Bdom) + dfo[i]->Bdom = vec_calloc(maxblks); /* alloc Bdom vectors */ + } + return hasasm; +} + +/**************************************** + * Compute dominators (Bdom) for each block. + * See Aho & Ullman Fig. 13.5. + * Note that flow graph is reducible if there is only one + * pass through the loop. + * Input: + * dfo[] + * Output: + * fills in the Bdom vector for each block + */ + +void compdom() +{ unsigned i; + unsigned cntr; + vec_t t1; + list_t bl; + bool chgs; + block *sb; + + assert(dfo); + sb = dfo[0]; // starting block + t1 = vec_calloc(vec_numbits(sb->Bdom)); // allocate a temporary + vec_clear(sb->Bdom); + vec_setbit(0,sb->Bdom); // starting block only doms itself + for (i = 1; i < dfotop; i++) // for all except startblock + vec_set(dfo[i]->Bdom); // dominate all blocks + cntr = 0; // # of times thru loop + do + { chgs = FALSE; + for (i = 1; i < dfotop; ++i) // for each block in dfo[] + { // except startblock + bl = dfo[i]->Bpred; + if (bl) // if there are predecessors + { vec_copy(t1,list_block(bl)->Bdom); + while ((bl = list_next(bl)) != NULL) + vec_andass(t1,list_block(bl)->Bdom); + } + else + vec_clear(t1); // no predecessors to dominate + vec_setbit(i,t1); // each block doms itself + if (chgs) + vec_copy(dfo[i]->Bdom,t1); + else if (!vec_equal(dfo[i]->Bdom,t1)) // if any changes + { vec_copy(dfo[i]->Bdom,t1); + chgs = TRUE; + } + } + cntr++; + assert(cntr < 50); // should have converged by now + } while (chgs); + vec_free(t1); + if (cntr <= 2) + cmes("Flow graph is reducible\n"); + else + cmes("Flow graph is not reducible\n"); +} + +/*************************** + * Return !=0 if block A dominates block B. + */ + +HINT dom(block *A,block *B) +{ + assert(A && B && dfo && dfo[A->Bdfoidx] == A); + return vec_testbit(A->Bdfoidx,B->Bdom); +} + +/********************** + * Find all the loops. + */ + +STATIC void findloops(loop **ploops) +{ unsigned i; + list_t bl; + block *b,*s; + + freeloop(ploops); + + //printf("findloops()\n"); + for (i = 0; i < dfotop; i++) + dfo[i]->Bweight = 1; /* reset Bweights */ + for (i = dfotop; i--;) /* for each block (note reverse */ + /* dfo order, so most nested */ + /* loops are found first) */ + { b = dfo[i]; + assert(b); + for (bl = b->Bsucc; bl; bl = list_next(bl)) + { s = list_block(bl); /* each successor s to b */ + assert(s); + if (dom(s,b)) /* if s dominates b */ + buildloop(ploops,s,b); // we found a loop + } + } + +#ifdef DEBUG + if (debugc) + { loop *l; + + for (l = *ploops; l; l = l->Lnext) + { + l->print(); + } + } +#endif +} + +/******************************** + */ + +STATIC void loop_weight(block *b,int factor) +{ + // Be careful not to overflow + if (b->Bweight < 0x10000) + b->Bweight *= 10 * factor; + else if (b->Bweight < 0x100000) + b->Bweight *= 2 * factor; + else + b->Bweight += factor; +} + +/***************************** + * Construct natural loop. + * Algorithm 13.1 from Aho & Ullman. + * Note that head dom tail. + */ + +STATIC void buildloop(loop **ploops,block *head,block *tail) +{ loop *l; + unsigned i; + list_t bl; + + //printf("buildloop()\n"); + /* See if this is part of an existing loop. If so, merge the two. */ + for (l = *ploops; l; l = l->Lnext) + if (l->Lhead == head) /* two loops with same header */ + { + vec_t v; + + // Calculate loop contents separately so we get the Bweights + // done accurately. + + v = vec_calloc(maxblks); + vec_setbit(head->Bdfoidx,v); + loop_weight(head,1); + insert(tail,v); + + vec_orass(l->Lloop,v); // merge into existing loop + vec_free(v); + + vec_clear(l->Lexit); // recompute exit blocks + goto L1; + } + + /* Allocate loop entry */ + l = loop::mycalloc(); + l->Lnext = *ploops; + *ploops = l; // put l at beginning of list + + l->Lloop = vec_calloc(maxblks); /* allocate loop bit vector */ + l->Lexit = vec_calloc(maxblks); /* bit vector for exit blocks */ + l->Lhead = head; + l->Ltail = tail; + + vec_setbit(head->Bdfoidx,l->Lloop); /* add head to the loop */ + loop_weight(head,2); // *20 usage for loop header + + insert(tail,l->Lloop); /* insert tail in loop */ + +L1: + /* Find all the exit blocks (those blocks with + * successors outside the loop). + */ + + foreach (i,dfotop,l->Lloop) /* for each block in this loop */ + { if (dfo[i]->BC == BCret || dfo[i]->BC == BCretexp || dfo[i]->BC == BCexit) + vec_setbit(i,l->Lexit); /* ret blocks are exit blocks */ + else + { for (bl = dfo[i]->Bsucc; bl; bl = list_next(bl)) + if (!vec_testbit(list_block(bl)->Bdfoidx,l->Lloop)) + { vec_setbit(i,l->Lexit); + break; + } + } + } + + /* Find preheader, if any, to the loop. + The preheader is a block that has only the head as a successor. + All other predecessors of head must be inside the loop. + */ + l->Lpreheader = NULL; + for (bl = head->Bpred; bl; bl = list_next(bl)) + { block *b = list_block(bl); + + if (!vec_testbit(b->Bdfoidx,l->Lloop)) /* if not in loop */ + { if (l->Lpreheader) /* if already one */ + { l->Lpreheader = NULL; /* can only be one */ + break; + } + else + { if (list_next(b->Bsucc)) // if more than 1 successor + break; // b can't be a preheader + l->Lpreheader = b; + } + } + } +} + +/******************************** + * Support routine for buildloop(). + * Add a block b and all its predecessors to loop lv. + */ + +STATIC void insert(register block *b, register vec_t lv) +{ register list_t bl; + + assert(b && lv); + if (!vec_testbit(b->Bdfoidx,lv)) /* if block is not in loop */ + { vec_setbit(b->Bdfoidx,lv); /* add block to loop */ + loop_weight(b,1); // *10 usage count + for (bl = b->Bpred; bl; bl = list_next(bl)) + insert(list_block(bl),lv); /* insert all its predecessors */ + } +} + +/************************************** + * Perform loop rotations. + * Loop starts as: + * + * prehead + * | + * v + * +->head----> + * | | + * | v + * | body + * | | + * | v + * +--tail + * + * Two types are done: + * 1) Header is moved to be past the tail. + * + * prehead + * | + * +---+ + * | + * | body<-+ + * | | | + * | v | + * | tail | + * | | | + * | v | + * +->head--+ + * | + * v + * + * 2) Header is copied past the tail (done only if MFtime is set). + * + * prehead + * | + * v + * head1-----+ + * | | + * v | + * body<--+ | + * | | | + * v | | + * tail | | + * | | | + * v | | + * head2--+ | + * | | + * +--------+ + * v + * + * Input: + * Loop information (do not depend on the preheader information) + * Output: + * Revised list of blocks, a new dfo and new loop information + * Returns: + * TRUE need to recompute loop data + */ + +STATIC int looprotate(loop *l) +{ + register block *tail = l->Ltail; + register block *head = l->Lhead; + register block *b; + + //printf("looprotate(%p)\n",l); + + // Do not rotate loop if: + if (head == tail || // loop is only one block big + !vec_testbit(head->Bdfoidx,l->Lexit)) // header is not an exit block + goto Lret; + + if (//iter != 1 && + vec_testbit(tail->Bdfoidx,l->Lexit)) // tail is an exit block + goto Lret; + + // Do not rotate if already rotated + for (b = tail->Bnext; b; b = b->Bnext) + if (b == head) // if loop already rotated + goto Lret; + +#if SCPP + if (head->BC == BCtry) + goto Lret; +#endif + if (head->BC == BC_try) + goto Lret; +#ifdef DEBUG + //if (debugc) { dbg_printf("looprotate: "); l->print(); } +#endif + + if ((mfoptim & MFtime) && head->BC != BCswitch && head->BC != BCasm) + { // Duplicate the header past the tail (but doing + // switches would be too expensive in terms of code + // generated). + register block *head2; + register list_t bl, *pbl2, *pbl, *pbln; + + head2 = block_calloc(); // create new head block + numblks++; // number of blocks in existence + head2->Btry = head->Btry; + head2->Bflags = head->Bflags; + head->Bflags = BFLnomerg; // move flags over to head2 + head2->Bflags |= BFLnomerg; + head2->BC = head->BC; + assert(head2->BC != BCswitch); + if (head->Belem) // copy expression tree + head2->Belem = el_copytree(head->Belem); + head2->Bnext = tail->Bnext; + tail->Bnext = head2; + + // pred(head1) = pred(head) outside loop + // pred(head2) = pred(head) inside loop + pbl2 = &(head2->Bpred); + for (pbl = &(head->Bpred); *pbl; pbl = pbln) + { + if (vec_testbit(list_block(*pbl)->Bdfoidx, l->Lloop)) + { // if this predecessor is inside the loop + + *pbl2 = *pbl; + *pbl = list_next(*pbl); + pbln = pbl; // don't skip this next one + list_next(*pbl2) = NULL; + bl = list_block(*pbl2)->Bsucc; + pbl2 = &(list_next(*pbl2)); + for (; bl; bl = list_next(bl)) + if (list_block(bl) == head) + { + list_ptr(bl) = (void *)head2; + goto L2; + } + assert(0); + L2: ; + } + else + pbln = &(list_next(*pbl)); // next predecessor in list + } // for each pred(head) + + // succ(head2) = succ(head) + for (bl = head->Bsucc; bl; bl = list_next(bl)) + { + list_append(&(head2->Bsucc),list_block(bl)); + list_append(&(list_block(bl)->Bpred),head2); + } + changes++; + return TRUE; + } + else if (startblock != head + /* This screws up the OPctor/OPdtor sequence for: + * struct CString + * { CString(); + * ~CString(); + * int GetLength(); + * }; + * + * void f(void) + * { for(;;) + * { CString s ; + * if(s.GetLength()!=0) + * break ; + * } + * } + */ + && !(config.flags3 & CFG3eh) + ) + { // optimize for space + // Simply position the header past the tail + for (b = startblock; b; b = b->Bnext) + if (b->Bnext == head) + goto L1; // found parent b of head + assert(0); + + L1: + b->Bnext = head->Bnext; + head->Bnext = tail->Bnext; + tail->Bnext = head; + cmes2( "Rotated loop %p\n", l); + changes++; + } +Lret: + return FALSE; +} + +static int gref; // parameter for markinvar() +static block *gblock; // parameter for markinvar() +static vec_t lv; // parameter for markinvar() +static vec_t gin; // parameter for markinvar() +static bool doflow; // TRUE if flow analysis has to be redone + +/********************************* + * Loop invariant and induction variable elimination. + * Input: + * iter which optimization iteration we are on + */ + +void loopopt() +{ + list_t bl; + loop *l; + loop *ln; + vec_t rd; + loop *startloop; + + cmes("loopopt()\n"); + startloop = NULL; +restart: + file_progress(); + if (blockinit()) // init block data + { + findloops(&startloop); // Compute Bweights + freeloop(&startloop); // free existing loops + return; // can't handle ASM blocks + } + compdom(); // compute dominators + findloops(&startloop); // find the loops + + for (l = startloop; l; l = ln) + { + ln = l->Lnext; + if (looprotate(l)) // rotate the loop + { + compdfo(); + blockinit(); + compdom(); + findloops(&startloop); // may trash l->Lnext + if (ln) + { ln = startloop; // start over + file_progress(); + } + } + } + // Make sure there is a preheader for each loop. + + addblk = FALSE; /* assume no blocks added */ + for (l = startloop; l; l = l->Lnext)/* for each loop */ + { +#ifdef DEBUG + //if (debugc) l->print(); +#endif + if (!l->Lpreheader) /* if no preheader */ + { register block *h, *p; + + cmes("Generating preheader for loop\n"); + addblk = TRUE; /* add one */ + p = block_calloc(); // the preheader + numblks++; + assert (numblks <= maxblks); + h = l->Lhead; /* loop header */ + + /* Find parent of h */ + if (h == startblock) + startblock = p; + else + { register block *ph; + + for (ph = startblock; 1; ph = ph->Bnext) + { assert(ph); /* should have found it */ + if (ph->Bnext == h) + break; + } + /* Link p into block list between ph and h */ + ph->Bnext = p; + } + p->Bnext = h; + + l->Lpreheader = p; + p->BC = BCgoto; + assert(p->Bsucc == NULL); + list_append(&(p->Bsucc),h); /* only successor is h */ + p->Btry = h->Btry; + + cmes3("Adding preheader %p to loop %p\n",p,l); + + // Move preds of h that aren't in the loop to preds of p + for (bl = h->Bpred; bl;) + { register block *b = list_block(bl); + + if (!vec_testbit (b->Bdfoidx, l->Lloop)) + { register list_t bls; + + list_append(&(p->Bpred), b); + list_subtract(&(h->Bpred), b); + bl = h->Bpred; /* dunno what subtract did */ + + /* Fix up successors of predecessors */ + for (bls = b->Bsucc; bls; bls = list_next(bls)) + if (list_block(bls) == h) + list_ptr(bls) = (void *)p; + } + else + bl = list_next(bl); + } + list_append(&(h->Bpred),p); /* p is a predecessor to h */ + } + } /* for */ + if (addblk) /* if any blocks were added */ + { + compdfo(); /* compute depth-first order */ + blockinit(); + compdom(); + findloops(&startloop); // recompute block info + addblk = FALSE; + } + + /* Do the loop optimizations. Note that accessing the loops */ + /* starting from startloop will access them in least nested */ + /* one first, thus moving LIs out as far as possible. */ + + doflow = TRUE; /* do flow analysis */ + cmes("Starting loop invariants\n"); + + for (l = startloop; l; l = l->Lnext) + { register unsigned i,j; + +#ifdef DEBUG + //if (debugc) l->print(); +#endif + file_progress(); + assert(l->Lpreheader); + if (doflow) + { + flowrd(); /* compute reaching definitions */ + flowlv(); /* compute live variables */ + flowae(); // compute available expressions + doflow = FALSE; /* no need to redo it */ + if (deftop == 0) /* if no definition elems */ + break; /* no need to optimize */ + } + lv = l->Lloop; + cmes2("...Loop %p start...\n",l); + + /* Unmark all elems in this loop */ + foreach (i,dfotop,lv) + if (dfo[i]->Belem) + unmarkall(dfo[i]->Belem); /* unmark all elems */ + + /* Find & mark all LIs */ + gin = vec_clone(l->Lpreheader->Bout); + rd = vec_calloc(deftop); /* allocate our running RD vector */ + foreach (i,dfotop,lv) /* for each block in loop */ + { block *b = dfo[i]; + + cmes2("B%d\n",i); + if (b->Belem) + { + vec_copy(rd, b->Binrd); // IN reaching defs +#if 0 + dbg_printf("i = %d\n",i); + { int j; + for (j = 0; j < deftop; j++) + elem_print(defnod[j].DNelem); + } + dbg_printf("rd : "); vec_println(rd); +#endif + gblock = b; + gref = 0; + if (b != l->Lhead) + gref = 1; + markinvar(b->Belem, rd); +#if 0 + dbg_printf("i = %d\n",i); + { int j; + for (j = 0; j < deftop; j++) + elem_print(defnod[j].DNelem); + } + dbg_printf("rd : "); vec_println(rd); + dbg_printf("Boutrd: "); vec_println(b->Boutrd); +#endif + assert(vec_equal(rd, b->Boutrd)); + } + else + assert(vec_equal(b->Binrd, b->Boutrd)); + } + vec_free(rd); + vec_free(gin); + + /* Move loop invariants */ + foreach (i,dfotop,lv) + { + int domexit; // TRUE if this block dominates all + // exit blocks of the loop + + foreach (j,dfotop,l->Lexit) /* for each exit block */ + { + if (!vec_testbit (i, dfo[j]->Bdom)) + { domexit = 0; + goto L1; // break if !(i dom j) + } + } + // if i dom (all exit blocks) + domexit = 1; + L1: ; + if (dfo[i]->Belem) + { // If there is any hope of making an improvement + if (domexit || l->Llis) + { if (dfo[i] != l->Lhead) + ; //domexit |= 2; + movelis(dfo[i]->Belem, dfo[i], l, &domexit); + } + } + } + //list_free(&l->Llis,FPNULL); + cmes2("...Loop %p done...\n",l); + + if (mfoptim & MFliv) + { loopiv(l); /* induction variables */ + if (addblk) /* if we added a block */ + { compdfo(); + goto restart; /* play it safe and start over */ + } + } + } /* for */ + freeloop(&startloop); +} + +/***************************** + * If elem is loop invariant, mark it. + * Input: + * lv = vector of all the blocks in this loop. + * rd = vector of loop invariants for this elem. This must be + * continually updated. + * Note that we do not iterate until no more LIs are found. The only + * thing this would buy us is stuff that depends on LI assignments. + */ + +STATIC void markinvar(elem *n,vec_t rd) +{ vec_t tmp; + unsigned i; + symbol *v; + elem *n1; + + assert(n && rd); + assert(vec_numbits(rd) == deftop); + switch (n->Eoper) + { + case OPaddass: case OPminass: case OPmulass: case OPandass: + case OPorass: case OPxorass: case OPdivass: case OPmodass: + case OPshlass: case OPshrass: case OPashrass: + case OPpostinc: case OPpostdec: + case OPcall: + markinvar(n->E2,rd); + case OPnegass: + n1 = n->E1; + if (n1->Eoper == OPind) + markinvar(n1->E1,rd); + else if (OTbinary(n1->Eoper)) + { markinvar(n1->E1,rd); + markinvar(n1->E2,rd); + } + L2: + if (n->Eoper == OPcall || + gblock->Btry || + !(n1->Eoper == OPvar && + symbol_isintab(n1->EV.sp.Vsym))) + { + gref = 1; + } + + updaterd(n,rd,NULL); + break; + + case OPcallns: + markinvar(n->E2,rd); + markinvar(n->E1,rd); + break; + +#if TX86 + case OPstrcpy: + case OPstrcat: + case OPmemcpy: + case OPmemset: + markinvar(n->E2,rd); + markinvar(n->E1,rd); + updaterd(n,rd,NULL); + break; + case OPbtc: + case OPbtr: + case OPbts: + markinvar(n->E1,rd); + markinvar(n->E2,rd); + updaterd(n,rd,NULL); + break; +#endif + case OPucall: + markinvar(n->E1,rd); + /* FALL-THROUGH */ + case OPasm: + gref = 1; + updaterd(n,rd,NULL); + break; + + case OPucallns: + case OPstrpar: + case OPstrctor: + case OPvector: +#if TX86 + case OPvoid: + case OPstrlen: + case OPinp: +#endif + markinvar(n->E1,rd); + break; + case OPcond: + case OPparam: +#if TX86 + case OPoutp: + case OPstrcmp: + case OPmemcmp: + case OPbt: // OPbt is like OPind, assume not LI +#endif + markinvar(n->E1,rd); + markinvar(n->E2,rd); + break; + case OPandand: + case OPoror: + markinvar(n->E1,rd); + tmp = vec_clone(rd); + markinvar(n->E2,tmp); + vec_orass(rd,tmp); /* rd |= tmp */ + vec_free(tmp); + break; + case OPcolon: + case OPcolon2: + tmp = vec_clone(rd); + markinvar(n->E1,rd); + markinvar(n->E2,tmp); + vec_orass(rd,tmp); /* rd |= tmp */ + vec_free(tmp); + break; + case OPaddr: // mark addresses of OPvars as LI + markinvar(n->E1,rd); + if (n->E1->Eoper == OPvar || isLI(n->E1)) + makeLI(n); + break; + case OPmsw: + case OPneg: case OPbool: case OPnot: case OPcom: + case OPs16_32: case OPd_s32: case OPs32_d: + case OPd_s16: case OPs16_d: case OPd_f: case OPf_d: + case OP32_16: case OPu8_16: + case OPld_d: case OPd_ld: + case OPld_u64: + case OPc_r: case OPc_i: + case OParraylength: + case OPnullcheck: + case OPu16_32: + case OPu16_d: case OPd_u16: + case OPs8_16: case OP16_8: + case OPd_u32: case OPu32_d: + +#if LONGLONG + case OPs32_64: case OPu32_64: + case OP64_32: + case OPd_s64: case OPd_u64: + case OPs64_d: + case OPu64_d: + case OP128_64: + case OPs64_128: + case OPu64_128: +#endif + case OPabs: + case OPsqrt: + case OPrndtol: + case OPsin: + case OPcos: + case OPrint: +#if TX86 + case OPsetjmp: + case OPbsf: + case OPbsr: + case OPbswap: +#endif +#if TARGET_SEGMENTED + case OPvp_fp: /* BUG for MacHandles */ + case OPnp_f16p: case OPf16p_np: case OPoffset: case OPnp_fp: + case OPcvp_fp: +#endif + markinvar(n->E1,rd); + if (isLI(n->E1)) /* if child is LI */ + makeLI(n); + break; + case OPeq: + case OPstreq: + markinvar(n->E2,rd); + n1 = n->E1; + markinvar(n1,rd); + + /* Determine if assignment is LI. Conditions are: */ + /* 1) Rvalue is LI */ + /* 2) Lvalue is a variable (simplifies things a lot) */ + /* 3) Lvalue can only be affected by unambiguous defs */ + /* 4) No rd's of lvalue that are within the loop (other */ + /* than the current def) */ + if (isLI(n->E2) && n1->Eoper == OPvar) /* 1 & 2 */ + { v = n1->EV.sp.Vsym; +#if UNAMBIG + if (v->Sflags & SFLunambig) +#endif + { + tmp = vec_calloc(deftop); + //filterrd(tmp,rd,v); + listrds(rd,n1,tmp); + foreach (i,deftop,tmp) + if (defnod[i].DNelem != n && + vec_testbit(defnod[i].DNblock->Bdfoidx,lv)) + goto L3; + makeLI(n); // then the def is LI + L3: vec_free(tmp); + } + } + goto L2; + + case OPadd: case OPmin: case OPmul: case OPand: + case OPor: case OPxor: case OPdiv: case OPmod: + case OPshl: case OPshr: case OPeqeq: case OPne: + case OPlt: case OPle: case OPgt: case OPge: + case OPashr: + case OPror: case OProl: + + case OPunord: case OPlg: case OPleg: case OPule: + case OPul: case OPuge: case OPug: case OPue: + case OPngt: case OPnge: case OPnlt: case OPnle: + case OPord: case OPnlg: case OPnleg: case OPnule: + case OPnul: case OPnuge: case OPnug: case OPnue: + + case OPinstanceof: + case OPfinalinstanceof: + case OPcheckcast: + case OPcomma: + case OPpair: + case OPrpair: + case OPscale: + case OPremquo: + case OPyl2x: + case OPyl2xp1: + markinvar(n->E1,rd); + markinvar(n->E2,rd); + if (isLI(n->E2) && isLI(n->E1)) + makeLI(n); + break; + + case OPind: /* must assume this is not LI */ + markinvar(n->E1,rd); + if (isLI(n->E1)) + { +#if 0 + // This doesn't work with C++, because exp2_ptrtocomtype() will + // transfer const to where it doesn't belong. + if (n->Ety & mTYconst) + { + makeLI(n); + } +#endif +#if 0 + // This was disabled because it was marking as LI + // the loop dimension for the [i] array if + // a[j][i] was in a loop. This meant the a[j] array bounds + // check for the a[j].length was skipped. + else if (n->Ejty) + { + tmp = vec_calloc(deftop); + filterrdind(tmp,rd,n); // only the RDs pertaining to n + + // if (no RDs within loop) + // then it's loop invariant + + foreach (i,deftop,tmp) // for each RD + if (vec_testbit(defnod[i].DNblock->Bdfoidx,lv)) + goto L10; // found a RD in the loop + + // If gref has occurred, this can still be LI + // if n is an AE that was also an AE at the + // point of gref. + // We can catch a subset of these cases by looking + // at the AEs at the start of the loop. + if (gref) + { int j; + + //printf("\tn is: "); WReqn(n); printf("\n"); + foreach (j,exptop,gin) + { elem *e = expnod[j]; + + //printf("\t\texpnod[%d] = %p\n",j,e); + //printf("\t\tAE is: "); WReqn(e); printf("\n"); + if (el_match2(n,e)) + { + makeLI(n); + //printf("Ind LI: "); WReqn(n); printf("\n"); + break; + } + } + } + else + makeLI(n); + L10: vec_free(tmp); + break; + } +#endif + } + break; + case OPvar: + v = n->EV.sp.Vsym; +#if UNAMBIG + if (v->Sflags & SFLunambig) // must be unambiguous to be LI +#endif + { + tmp = vec_calloc(deftop); + //filterrd(tmp,rd,v); // only the RDs pertaining to v + listrds(rd,n,tmp); // only the RDs pertaining to v + + // if (no RDs within loop) + // then it's loop invariant + + foreach (i,deftop,tmp) // for each RD + if (vec_testbit(defnod[i].DNblock->Bdfoidx,lv)) + goto L1; // found a RD in the loop + makeLI(n); + + L1: vec_free(tmp); + } + break; + case OPstring: + case OPrelconst: + case OPconst: /* constants are always LI */ + case OPhstring: + case OPframeptr: + makeLI(n); + break; + case OPinfo: + markinvar(n->E2,rd); + break; + + case OPstrthis: + case OPmark: + case OPctor: + case OPdtor: + case OPdctor: + case OPddtor: + case OPhalt: + case OPgot: // shouldn't OPgot be makeLI ? + break; + + default: +#ifdef DEBUG + WROP(n->Eoper); +#endif + //printf("n->Eoper = %d, OPconst = %d\n", n->Eoper, OPconst); + assert(0); + } +#ifdef DEBUG + if (debugc && isLI(n)) + { dbg_printf(" LI elem: "); + WReqn(n); + dbg_printf("\n"); + } +#endif +} + +/******************** + * Update rd vector. + * Input: + * n assignment elem or function call elem or OPasm elem + * rd reaching def vector to update + * (clear bits for defs we kill, set bit for n (which is the + * def we are genning)) + * vecdim deftop + */ + +void updaterd(elem *n,vec_t GEN,vec_t KILL) +{ unsigned op = n->Eoper; + unsigned i; + unsigned ni; + elem *t; + + assert(OTdef(op)); + assert(GEN); + elem_debug(n); + + // If unambiguous def + if (OTassign(op) && (t = n->E1)->Eoper == OPvar) + { symbol *d = t->EV.sp.Vsym; + targ_size_t toff = t->EV.sp.Voffset; + targ_size_t tsize; + targ_size_t ttop; + + tsize = (op == OPstreq) ? type_size(n->ET) : tysize(t->Ety); + ttop = toff + tsize; + + //printf("updaterd: "); WReqn(n); printf(" toff=%d, tsize=%d\n", toff, tsize); + + ni = (unsigned)-1; + + /* for all unambig defs in defnod[] */ + for (i = 0; i < deftop; i++) + { elem *tn = defnod[i].DNelem; + elem *tn1; + targ_size_t tn1size; + + if (tn == n) + ni = i; + + if (!OTassign(tn->Eoper)) + continue; + + // If def of same variable, kill that def + tn1 = tn->E1; + if (tn1->Eoper != OPvar || d != tn1->EV.sp.Vsym) + continue; + + // If t completely overlaps tn1 + tn1size = (tn->Eoper == OPstreq) + ? type_size(tn->ET) : tysize(tn1->Ety); + if (toff <= tn1->EV.sp.Voffset && + tn1->EV.sp.Voffset + tn1size <= ttop) + { + if (KILL) + vec_setbit(i,KILL); + vec_clearbit(i,GEN); + } + } + assert(ni != -1); + } +#if 0 + else if (OTassign(op) && t->Eoper != OPvar && t->Ejty) + { + ni = -1; + + // for all unambig defs in defnod[] + for (i = 0; i < deftop; i++) + { elem *tn = defnod[i].DNelem; + elem *tn1; + + if (tn == n) + ni = i; + + if (!OTassign(tn->Eoper)) + continue; + + // If def of same variable, kill that def + tn1 = tn->E1; + if (tn1->Eoper != OPind || t->Ejty != tn1->Ejty) + continue; + + if (KILL) + vec_setbit(i,KILL); + vec_clearbit(i,GEN); + } + assert(ni != -1); + } +#endif + else + { + /* Set bit in GEN for this def */ + for (i = 0; 1; i++) + { assert(i < deftop); // should find n in defnod[] + if (defnod[i].DNelem == n) + { ni = i; + break; + } + } + } + + vec_setbit(ni,GEN); // set bit in GEN for this def +} + +/*************************** + * Mark all elems as not being loop invariant. + */ + +STATIC void unmarkall(elem *e) +{ + for (; 1; e = e->E1) + { + assert(e); + e->Nflags &= ~NFLli; /* unmark this elem */ + if (OTunary(e->Eoper)) + continue; + else if (OTbinary(e->Eoper)) + { unmarkall(e->E2); + continue; + } + return; + } +} + +/******************************* + * Take a RD vector and filter out all RDs but + * ones that are defs of symbol s. + * Output: + * f + */ + +#if 0 // replaced by listrds() +void filterrd(vec_t f,vec_t rd,symbol *s) +{ + register unsigned i; + register elem *n; + + vec_copy(f,rd); +#if UNAMBIG + foreach (i,deftop,f) /* for each def in f */ + { n = defnod[i].DNelem; /* the definition elem */ + elem_debug(n); + if (n->Eoper == OPasm) // OPasm defs always reach (sigh) + continue; + /* Clear bit if it's not an unambiguous def of si */ + if (OTassign(n->Eoper)) /* if assignment elem */ + { if (!(n->E1->Eoper == OPvar && n->E1->EV.sp.Vsym == s + )) + vec_clearbit(i,f); + } + else /* else ambiguous def */ + vec_clearbit(i,f); // and couldn't def this var + } +#else + assert(0); /* not implemented */ +#endif +} +#endif + +/******************************* + * Take a RD vector and filter out all RDs but + * ones that are possible defs of OPind elem e. + * Output: + * f + */ + +#if 0 + +STATIC void filterrdind(vec_t f,vec_t rd,elem *e) +{ + unsigned i; + elem *n; + tym_t jty = e->Ejty; + + vec_copy(f,rd); +#if UNAMBIG + foreach (i,deftop,f) // for each def in f + { n = defnod[i].DNelem; // the definition elem + elem_debug(n); + if (n->Eoper == OPasm) // OPasm defs always reach (sigh) + continue; + // Clear bit if it's not an unambiguous def of si + if (OTassign(n->Eoper)) // if assignment elem + { elem *n1 = n->E1; + + if (n1->Eoper == OPind) + { + if (jty && n1->Ejty && jty != n1->Ejty) + vec_clearbit(i,f); + } + else if (n1->Eoper == OPvar) + { + if (jty || n1->EV.sp.Vsym->Sflags & SFLunambig) + vec_clearbit(i,f); + } + } + else if (OTcall(n->Eoper) && el_noreturn(n)) + vec_clearbit(i,f); + } +#else + assert(0); // not implemented +#endif +} + +#endif + +/******************************** + * Return TRUE if there are any refs of v in n before nstop is encountered. + * Input: + * refstop = -1 + */ + +static int refstop; /* flag to stop refs() */ + +STATIC bool refs(symbol *v,elem *n,elem *nstop) +{ register bool f; + register unsigned op; + + symbol_debug(v); + elem_debug(n); + assert(symbol_isintab(v)); + assert(v->Ssymnum < globsym.top); + assert(n); + + op = n->Eoper; +#if UNAMBIG + if (refstop == 0) + return FALSE; + f = FALSE; + if (OTunary(op)) + f = refs(v,n->E1,nstop); + else if (OTbinary(op)) + { if (ERTOL(n)) /* watch order of evaluation */ + { + /* Note that (OPvar = e) is not a ref of OPvar, whereas */ + /* ((OPbit OPvar) = e) is a ref of OPvar, and (OPvar op= e) is */ + /* a ref of OPvar, etc. */ + f = refs(v,n->E2,nstop); + if (!f) + { if (op == OPeq) + { if (n->E1->Eoper != OPvar) + f = refs(v,n->E1->E1,nstop); + } + else + f = refs(v,n->E1,nstop); + } + } + else + f = refs(v,n->E1,nstop) || refs(v,n->E2,nstop); + } + + if (n == nstop) + refstop = 0; + else if (n->Eoper == OPvar) /* if variable reference */ + return v == n->EV.sp.Vsym; + else if (op == OPasm) /* everything is referenced */ + return TRUE; + return f; +#else + assert(0); +#endif +} + +/************************* + * Move LIs to preheader. + * Conditions to be satisfied for code motion are: + * 1) All exit blocks are dominated (TRUE before this is called). + * -- OR -- + * 2) Variable assigned by a statement is not live on entering + * any successor outside the loop of any exit block of the + * loop. + * + * 3) Cannot move assignment to variable if there are any other + * assignments to that variable within the loop (TRUE or + * assignment would not have been marked LI). + * 4) Cannot move assignments to a variable if there is a use + * of that variable in this loop that is reached by any other + * def of it. + * 5) Cannot move expressions that have side effects. + * 6) Do not move assignments to variables that could be affected + * by ambiguous defs. + * 7) It is not worth it to move expressions of the form: + * (var == const) + * Input: + * n the elem we're considering moving + * b the block this elem is in + * l the loop we're in + * domexit flags + * bit 0: 1 this branch is always executed + * 0 this branch is only sometimes executed + * bit 1: 1 do not move LIs that could throw exceptions + * or cannot be moved past possibly thrown exceptions + * Returns: + * revised domexit + */ + +STATIC void movelis(elem *n,block *b,loop *l,int *pdomexit) +{ register unsigned i,j,op; + register vec_t tmp; + register elem *ne,*t,*n2; + register list_t nl; + symbol *v; + tym_t ty; + +Lnextlis: + //if (isLI(n)) { printf("movelis("); WReqn(n); printf(")\n"); } + assert(l && n); + elem_debug(n); + op = n->Eoper; + switch (op) + { + case OPvar: + case OPconst: + case OPrelconst: + goto Lret; + + case OPandand: + case OPoror: + case OPcond: + { int domexit; + + movelis(n->E1,b,l,pdomexit); // always executed + domexit = *pdomexit & ~1; // sometimes executed + movelis(n->E2,b,l,&domexit); + *pdomexit |= domexit & 2; + goto Lret; + } + + case OPeq: + // Do loop invariant assignments + if (isLI(n) && n->E1->Eoper == OPvar) + { v = n->E1->EV.sp.Vsym; // variable index number + + #ifdef UNAMBIG + if (!(v->Sflags & SFLunambig)) goto L3; // case 6 + #endif + + // If case 4 is not satisfied, return + + // Function parameters have an implied definition prior to the + // first block of the function. Unfortunately, the rd vector + // does not take this into account. Therefore, we assume the + // worst and reject assignments to function parameters. + if (v->Sclass == SCparameter || v->Sclass == SCregpar || v->Sclass == SCfastpar) + goto L3; + + if (el_sideeffect(n->E2)) goto L3; // case 5 + + // If case 1 or case 2 is not satisfied, return + + if (!(*pdomexit & 1)) // if not case 1 + { + foreach (i,dfotop,l->Lexit) // for each exit block + { register list_t bl; + + for (bl = dfo[i]->Bsucc; bl; bl = list_next(bl)) + { block *s; // successor to exit block + + s = list_block(bl); + if (!vec_testbit(s->Bdfoidx,l->Lloop) && + (!symbol_isintab(v) || + vec_testbit(v->Ssymnum,s->Binlv))) // if v is live on exit + goto L3; + } + } + } + + tmp = vec_calloc(deftop); + foreach (i,dfotop,l->Lloop) // for each block in loop + { + if (dfo[i] == b) // except this one + continue; + + // + // + // return; + + //filterrd(tmp,dfo[i]->Binrd,v); + listrds(dfo[i]->Binrd,n->E1,tmp); + foreach (j,deftop,tmp) // for each RD of v in Binrd + { if (defnod[j].DNelem == n) + continue; + refstop = -1; + if (dfo[i]->Belem && + refs(v,dfo[i]->Belem,(elem *)NULL)) //if refs of v + { vec_free(tmp); + goto L3; + } + break; + } + } // foreach + + // Binrd other than n> + // + // + + //filterrd(tmp,b->Binrd,v); + listrds(b->Binrd,n->E1,tmp); + foreach (j,deftop,tmp) // for each RD of v in Binrd + { if (defnod[j].DNelem == n) + continue; + refstop = -1; + if (b->Belem && refs(v,b->Belem,n)) + { vec_free(tmp); + goto L3; // can't move it + } + break; // avoid redundant looping + } + vec_free(tmp); + + // We have an LI assignment, n. + // Check to see if the rvalue is already in the preheader. + for (nl = l->Llis; nl; nl = list_next(nl)) + { + if (el_match(n->E2,list_elem(nl)->E2)) + { + el_free(n->E2); + n->E2 = el_calloc(); + el_copy(n->E2,list_elem(nl)->E1); + cmes("LI assignment rvalue was replaced\n"); + doflow = TRUE; + changes++; + break; + } + } + + // move assignment elem to preheader + cmes("Moved LI assignment "); + #ifdef DEBUG + if (debugc) + { WReqn(n); + dbg_printf(";\n"); + } + #endif + changes++; + doflow = TRUE; // redo flow analysis + ne = el_calloc(); + el_copy(ne,n); // create assignment elem + assert(l->Lpreheader); // make sure there is one + appendelem(ne,&(l->Lpreheader->Belem)); // append ne to preheader + list_prepend(&l->Llis,ne); + + el_copy(n,ne->E1); // replace n with just a reference to v + goto Lret; + } // if + break; + + case OPcall: + case OPucall: + *pdomexit |= 2; + break; + } + +L3: + // Do leaves of non-LI expressions, leaves of = elems that didn't + // meet the invariant assignment removal criteria, and don't do leaves + if (OTleaf(op)) + goto Lret; + if (!isLI(n) || op == OPeq || op == OPcomma || OTrel(op) || op == OPnot || + // These are usually addressing modes, so moving them is a net loss + (I32 && op == OPshl && n->E2->Eoper == OPconst && el_tolong(n->E2) <= 3ull) + ) + { + if (OTassign(op)) + { elem *n1 = n->E1; + elem *n11; + + if (OTbinary(op)) + movelis(n->E2,b,l,pdomexit); + + // Do lvalue only if it is an expression + if (n1->Eoper == OPvar) + goto Lret; + n11 = n1->E1; + if (OTbinary(n1->Eoper)) + { + movelis(n11,b,l,pdomexit); + n = n1->E2; + } + // If *(x + c), just make x the LI, not the (x + c). + // The +c comes free with the addressing mode. + else if (n1->Eoper == OPind && + isLI(n11) && + n11->Eoper == OPadd && + n11->E2->Eoper == OPconst + ) + { + n = n11->E1; + } + else + n = n11; + movelis(n,b,l,pdomexit); + if (b->Btry || !(n1->Eoper == OPvar && symbol_isintab(n1->EV.sp.Vsym))) + { + //printf("assign to global => domexit |= 2\n"); + *pdomexit |= 2; + } + } + else if (OTunary(op)) + { elem *e1 = n->E1; + + // If *(x + c), just make x the LI, not the (x + c). + // The +c comes free with the addressing mode. + if (op == OPind && + isLI(e1) && + e1->Eoper == OPadd && + e1->E2->Eoper == OPconst + ) + { + n = e1->E1; + } + else + n = e1; + } + else if (OTbinary(op)) + { movelis(n->E1,b,l,pdomexit); + n = n->E2; + } + goto Lnextlis; + } + + if (el_sideeffect(n)) + goto Lret; + +#if 0 +printf("*pdomexit = %d\n",*pdomexit); + if (*pdomexit & 2) + { + // If any indirections, can't LI it + + // If this operand has already been indirected, we can let + // it pass. + Symbol *s; + +printf("looking at:\n"); +elem_print(n); + s = el_basesym(n->E1); + if (s) + { + for (nl = l->Llis; nl; nl = list_next(nl)) + { elem *el; + tym_t ty2; + + el = list_elem(nl); + el = el->E2; +elem_print(el); + if (el->Eoper == OPind && el_basesym(el->E1) == s) + { +printf(" pass!\n"); + goto Lpass; + } + } + } +printf(" skip!\n"); + goto Lret; + + Lpass: + ; + } +#endif + + // Move the LI expression to the preheader + cmes("Moved LI expression "); +#ifdef DEBUG + if (debugc) + { WReqn(n); + dbg_printf(";\n"); + } +#endif + + // See if it's already been moved + ty = n->Ety; + for (nl = l->Llis; nl; nl = list_next(nl)) + { elem *el; + tym_t ty2; + + el = list_elem(nl); + //printf("existing LI: "); WReqn(el); printf("\n"); + ty2 = el->E2->Ety; + if (tysize(ty) == tysize(ty2)) + { el->E2->Ety = ty; + if (el_match(n,el->E2)) + { + el->E2->Ety = ty2; + if (!OTleaf(n->Eoper)) + { el_free(n->E1); + if (OTbinary(n->Eoper)) + el_free(n->E2); + } + el_copy(n,el->E1); // make copy of temp + n->Ety = ty; +#ifdef DEBUG + if (debugc) + { dbg_printf("Already moved: LI expression replaced with "); + WReqn(n); + dbg_printf("\nPreheader %d expression %p ", + l->Lpreheader->Bdfoidx,l->Lpreheader->Belem); + WReqn(l->Lpreheader->Belem); + dbg_printf("\n"); + } +#endif + changes++; + doflow = TRUE; // redo flow analysis + goto Lret; + } + el->E2->Ety = ty2; + } + } + + if (!(*pdomexit & 1)) // if only sometimes executed + { cmes(" doesn't dominate exit\n"); + goto Lret; // don't move LI + } + + if (tyaggregate(n->Ety)) + goto Lret; + + changes++; + doflow = TRUE; // redo flow analysis + + t = el_alloctmp(n->Ety); /* allocate temporary t */ +#if DEBUG + cmes2("movelis() introduced new variable '%s' of type ",t->EV.sp.Vsym->Sident); + if (debugc) WRTYxx(t->Ety); + cmes("\n"); +#endif + n2 = el_calloc(); + el_copy(n2,n); /* create copy n2 of n */ + ne = el_bin(OPeq,t->Ety,t,n2); /* create elem t=n2 */ + assert(l->Lpreheader); /* make sure there is one */ + appendelem(ne,&(l->Lpreheader->Belem)); /* append ne to preheader */ +#ifdef DEBUG + if (debugc) + { dbg_printf("Preheader %d expression %p\n\t", + l->Lpreheader->Bdfoidx,l->Lpreheader->Belem); + WReqn(l->Lpreheader->Belem); + dbg_printf("\nLI expression replaced with "); WReqn(t); + dbg_printf("\n"); + } +#endif + el_copy(n,t); /* replace this elem with t */ + + // Remember LI expression in elem list + list_prepend(&l->Llis,ne); + +Lret: + ; +} + +/*************************** + * Append elem to existing elem using an OPcomma elem. + * Input: + * n elem to append + * *pn elem to append to + */ + +STATIC void appendelem(register elem *n,elem **pn) +{ + assert(n && pn); + if (*pn) /* if this elem exists */ + { while ((*pn)->Eoper == OPcomma) /* while we see OPcomma elems */ + { (*pn)->Ety = n->Ety; + pn = &((*pn)->E2); /* cruise down right side */ + } + *pn = el_bin(OPcomma,n->Ety,*pn,n); + } + else + *pn = n; /* else create a elem */ +} + +/************** LOOP INDUCTION VARIABLES **********************/ + +/*************************** + * Allocate famlist. + */ + +famlist *famlist::freelist = NULL; + +famlist *famlist::mycalloc() +{ famlist *fl; + + if (freelist) + { + fl = freelist; + freelist = fl->FLnext; + memset(fl,0,sizeof(famlist)); + } + else + fl = (famlist *) mem_calloc(sizeof(famlist)); + return fl; +} + +/*************************** + * Allocate Iv. + */ + +Iv *Iv::freelist = NULL; + +Iv *Iv::mycalloc() +{ Iv *iv; + + if (freelist) + { + iv = freelist; + freelist = iv->IVnext; + memset(iv,0,sizeof(Iv)); + } + else + iv = (Iv *) mem_calloc(sizeof(Iv)); + return iv; +} + +/********************* + * Free iv list. + */ + +STATIC void freeivlist(register Iv *biv) +{ register Iv *bivnext; + + while (biv) + { register famlist *fl,*fln; + + for (fl = biv->IVfamily; fl; fl = fln) + { el_free(fl->c1); + el_free(fl->c2); + fln = fl->FLnext; + + fl->FLnext = famlist::freelist; + famlist::freelist = fl; + } + bivnext = biv->IVnext; + + biv->IVnext = Iv::freelist; + Iv::freelist = biv; + + biv = bivnext; + } +} + +/**************************** + * Create a new famlist entry. + */ + +STATIC famlist * newfamlist(tym_t ty) +{ register famlist *fl; + union eve c; + + memset(&c,0,sizeof(c)); + fl = famlist::mycalloc(); + fl->FLty = ty; + switch (tybasic(ty)) + { case TYfloat: + c.Vfloat = 1; + break; + case TYdouble: + case TYdouble_alias: + c.Vdouble = 1; + break; + case TYldouble: + c.Vldouble = 1; + break; +#if _MSDOS || __OS2__ || _WIN32 // if no byte ordering problems +#if JHANDLE + case TYjhandle: +#endif +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: + case TYnptr: + case TYfptr: + case TYvptr: +#endif + /* Convert pointers to integrals to avoid things like */ + /* multiplying pointers */ + ty = TYptrdiff; + /* FALL-THROUGH */ + default: + c.Vlong = 1; + break; +#if TARGET_SEGMENTED + case TYhptr: + ty = TYlong; + c.Vlong = 1; + break; +#endif +#else + case TYbool: + case TYchar: + case TYschar: + case TYuchar: + c.Vchar = 1; + break; + case TYshort: + case TYushort: + case TYchar16: + case TYwchar_t: // BUG: what about 4 byte wchar_t's? + c.Vshort = 1; + break; +#if JHANDLE + case TYjhandle: +#endif +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: + case TYfptr: + case TYvptr: +#endif + case TYnptr: + case TYnullptr: + ty = TYint; + if (I64) + ty = TYllong; + /* FALL-THROUGH */ + case TYint: + case TYuint: + c.Vint = 1; + break; +#if TARGET_SEGMENTED + case TYhptr: + ty = TYlong; +#endif + case TYlong: + case TYulong: + case TYdchar: + default: + c.Vlong = 1; + break; +#if 0 + default: + printf("ty = x%x\n", tybasic(ty)); + assert(0); +#endif +#endif + } + fl->c1 = el_const(ty,&c); /* c1 = 1 */ + c.Vldouble = 0; + if (typtr(ty)) + { + ty = TYint; +#if TARGET_SEGMENTED + if (tybasic(ty) == TYhptr) + ty = TYlong; +#endif + if (I64) + ty = TYllong; + } + fl->c2 = el_const(ty,&c); /* c2 = 0 */ + return fl; +} + +/*************************** + * Remove induction variables from loop l. + * Loop invariant removal should have been done just previously. + */ + +STATIC void loopiv(register loop *l) +{ + cmes2("loopiv(%p)\n",l); + assert(l->Livlist == NULL && l->Lopeqlist == NULL); + elimspec(l); + if (doflow) + { flowrd(); /* compute reaching defs */ + flowlv(); /* compute live variables */ + flowae(); // compute available expressions + doflow = FALSE; + } + findbasivs(l); /* find basic induction variables */ + findopeqs(l); // find op= variables + findivfams(l); /* find IV families */ + elimfrivivs(l); /* eliminate less useful family IVs */ + intronvars(l); /* introduce new variables */ + elimbasivs(l); /* eliminate basic IVs */ + if (!addblk) // adding a block changes the Binlv + elimopeqs(l); // eliminate op= variables + + freeivlist(l->Livlist); // free up IV list + l->Livlist = NULL; + freeivlist(l->Lopeqlist); // free up list + l->Lopeqlist = NULL; + + /* Do copy propagation and dead assignment elimination */ + /* upon return to optfunc() */ +} + +/************************************* + * Find basic IVs of loop l. + * A basic IV x of loop l is a variable x which has + * exactly one assignment within l of the form: + * x += c or x -= c, where c is either a constant + * or a LI. + * Input: + * defnod[] loaded with all the definition elems of the loop + */ + +STATIC void findbasivs(loop *l) +{ vec_t poss,notposs; + elem *n; + unsigned i,j; + bool ambdone; + + assert(l); + ambdone = FALSE; + poss = vec_calloc(globsym.top); + notposs = vec_calloc(globsym.top); /* vector of all variables */ + /* (initially all unmarked) */ + + /* for each def in defnod[] that is within loop l */ + + for (i = 0; i < deftop; i++) + { if (!vec_testbit(defnod[i].DNblock->Bdfoidx,l->Lloop)) + continue; /* def is not in the loop */ + + n = defnod[i].DNelem; + elem_debug(n); + if (OTassign(n->Eoper) && n->E1->Eoper == OPvar) + { symbol *s; /* if unambiguous def */ + + s = n->E1->EV.sp.Vsym; + if (symbol_isintab(s)) + { + SYMIDX v; + + v = n->E1->EV.sp.Vsym->Ssymnum; + if ((n->Eoper == OPaddass || n->Eoper == OPminass || + n->Eoper == OPpostinc || n->Eoper == OPpostdec) && + (cnst(n->E2) || /* if x += c or x -= c */ + n->E2->Eoper == OPvar && isLI(n->E2))) + { if (vec_testbit(v,poss)) + /* We've already seen this def elem, */ + /* therefore there is more than one */ + /* def of v within the loop, therefore */ + /* v is not a basic IV. */ + vec_setbit(v,notposs); + else + vec_setbit(v,poss); + } + else /* else mark as not possible */ + vec_setbit(v,notposs); + } + } + else /* else ambiguous def */ + { /* mark any vars that could be affected by */ + /* this def as not possible */ + + if (!ambdone) /* avoid redundant loops */ + { for (j = 0; j < globsym.top; j++) + { if (!(globsym.tab[j]->Sflags & SFLunambig)) + vec_setbit(j,notposs); + } + ambdone = TRUE; + } + } + } +#if 0 + dbg_printf("poss "); vec_println(poss); + dbg_printf("notposs "); vec_println(notposs); +#endif + vec_subass(poss,notposs); /* poss = poss - notposs */ + + /* create list of IVs */ + foreach (i,globsym.top,poss) /* for each basic IV */ + { register Iv *biv; + symbol *s; + + /* Skip if we don't want it to be a basic IV (see funcprev()) */ + s = globsym.tab[i]; + assert(symbol_isintab(s)); + if (s->Sflags & SFLnotbasiciv) + continue; + + // Do not use aggregates as basic IVs. This is because the other loop + // code doesn't check offsets into symbols, (assuming everything + // is at offset 0). We could perhaps amend this by allowing basic IVs + // if the struct consists of only one data member. + if (tyaggregate(s->ty())) + continue; + + biv = Iv::mycalloc(); + biv->IVnext = l->Livlist; + l->Livlist = biv; // link into list of IVs + + biv->IVbasic = s; // symbol of basic IV + + cmes3("Symbol '%s' (%d) is a basic IV, ",s->Sident + ? (char *)s->Sident : "",i); + + /* We have the sym idx of the basic IV. We need to find */ + /* the parent of the increment elem for it. */ + + /* First find the defnod[] */ + for (j = 0; j < deftop; j++) + { /* If defnod is a def of i and it is in the loop */ + if (defnod[j].DNelem->E1 && /* OPasm are def nodes */ + defnod[j].DNelem->E1->EV.sp.Vsym == s && + vec_testbit(defnod[j].DNblock->Bdfoidx,l->Lloop)) + goto L1; + } + assert(0); /* should have found it */ + /* NOTREACHED */ + + L1: biv->IVincr = el_parent(defnod[j].DNelem,&(defnod[j].DNblock->Belem)); + assert(s == (*biv->IVincr)->E1->EV.sp.Vsym); +#ifdef DEBUG + if (debugc) + { dbg_printf("Increment elem is: "); WReqn(*biv->IVincr); dbg_printf("\n"); } +#endif + } + + vec_free(poss); + vec_free(notposs); +} + +/************************************* + * Find op= elems of loop l. + * Analogous to findbasivs(). + * Used to eliminate useless loop code normally found in benchmark programs. + * Input: + * defnod[] loaded with all the definition elems of the loop + */ + +STATIC void findopeqs(loop *l) +{ vec_t poss,notposs; + elem *n; + unsigned i,j; + bool ambdone; + + assert(l); + ambdone = FALSE; + poss = vec_calloc(globsym.top); + notposs = vec_calloc(globsym.top); // vector of all variables + // (initially all unmarked) + + // for each def in defnod[] that is within loop l + + for (i = 0; i < deftop; i++) + { if (!vec_testbit(defnod[i].DNblock->Bdfoidx,l->Lloop)) + continue; // def is not in the loop + + n = defnod[i].DNelem; + elem_debug(n); + if (OTopeq(n->Eoper) && n->E1->Eoper == OPvar) + { symbol *s; // if unambiguous def + + s = n->E1->EV.sp.Vsym; + if (symbol_isintab(s)) + { + SYMIDX v; + + v = n->E1->EV.sp.Vsym->Ssymnum; + { if (vec_testbit(v,poss)) + // We've already seen this def elem, + // therefore there is more than one + // def of v within the loop, therefore + // v is not a basic IV. + vec_setbit(v,notposs); + else + vec_setbit(v,poss); + } + } + } + else // else ambiguous def + { // mark any vars that could be affected by + // this def as not possible + + if (!ambdone) // avoid redundant loops + { for (j = 0; j < globsym.top; j++) + { if (!(globsym.tab[j]->Sflags & SFLunambig)) + vec_setbit(j,notposs); + } + ambdone = TRUE; + } + } + } + + // Don't use symbols already in Livlist + for (Iv *iv = l->Livlist; iv; iv = iv->IVnext) + { symbol *s; + + s = iv->IVbasic; + vec_setbit(s->Ssymnum,notposs); + } + + +#if 0 + dbg_printf("poss "); vec_println(poss); + dbg_printf("notposs "); vec_println(notposs); +#endif + vec_subass(poss,notposs); // poss = poss - notposs + + // create list of IVs + foreach (i,globsym.top,poss) // for each opeq IV + { register Iv *biv; + symbol *s; + + s = globsym.tab[i]; + assert(symbol_isintab(s)); + + // Do not use aggregates as basic IVs. This is because the other loop + // code doesn't check offsets into symbols, (assuming everything + // is at offset 0). We could perhaps amend this by allowing basic IVs + // if the struct consists of only one data member. + if (tyaggregate(s->ty())) + continue; + + biv = Iv::mycalloc(); + biv->IVnext = l->Lopeqlist; + l->Lopeqlist = biv; // link into list of IVs + + biv->IVbasic = s; // symbol of basic IV + + cmes3("Symbol '%s' (%d) is an opeq IV, ",s->Sident + ? (char *)s->Sident : "",i); + + // We have the sym idx of the basic IV. We need to find + // the parent of the increment elem for it. + + // First find the defnod[] + for (j = 0; j < deftop; j++) + { // If defnod is a def of i and it is in the loop + if (defnod[j].DNelem->E1 && // OPasm are def nodes + defnod[j].DNelem->E1->EV.sp.Vsym == s && + vec_testbit(defnod[j].DNblock->Bdfoidx,l->Lloop)) + goto L1; + } + assert(0); // should have found it + // NOTREACHED + + L1: biv->IVincr = el_parent(defnod[j].DNelem,&(defnod[j].DNblock->Belem)); + assert(s == (*biv->IVincr)->E1->EV.sp.Vsym); +#ifdef DEBUG + if (debugc) + { dbg_printf("Opeq elem is: "); WReqn(*biv->IVincr); dbg_printf("\n"); } +#endif + Lcont: + ; + } + + vec_free(poss); + vec_free(notposs); +} + +/***************************** + * Find families for each basic IV. + * An IV family is a list of elems of the form + * c1*X+c2, where X is a basic induction variable. + * Note that we do not do divides, because of roundoff error problems. + */ + +STATIC void findivfams(register loop *l) +{ register Iv *biv; + register unsigned i; + register famlist *fl; + + cmes2("findivfams(%p)\n",l); + for (biv = l->Livlist; biv; biv = biv->IVnext) + { foreach (i,dfotop,l->Lloop) /* for each block in loop */ + if (dfo[i]->Belem) + ivfamelems(biv,&(dfo[i]->Belem)); + /* Fold all the constant expressions in c1 and c2. */ + for (fl = biv->IVfamily; fl; fl = fl->FLnext) + { fl->c1 = doptelem(fl->c1,GOALvalue | GOALagain); + fl->c2 = doptelem(fl->c2,GOALvalue | GOALagain); + } + } +} + +/************************* + * Tree walking support routine for findivfams(). + * biv = basic induction variable pointer + * pn pointer to elem + */ + +STATIC void ivfamelems(register Iv *biv,register elem **pn) +{ register unsigned op; + register tym_t ty,c2ty; + register famlist *f; + register elem *n,*n1,*n2; + + assert(pn); + n = *pn; + assert(biv && n); + op = n->Eoper; + if (OTunary(op)) + { ivfamelems(biv,&n->E1); + n1 = n->E1; + n2 = NULL; + } + else if (OTbinary(op)) + { ivfamelems(biv,&n->E1); + ivfamelems(biv,&n->E2); /* LTOR or RTOL order is unimportant */ + n1 = n->E1; + n2 = n->E2; + } + else /* else leaf elem */ + return; /* which can't be in the family */ + + if (op == OPmul || op == OPadd || op == OPmin || + op == OPneg || op == OPshl) + { /* Note that we are wimping out and not considering */ + /* LI variables as part of c1 and c2, but only constants. */ + + ty = n->Ety; + + /* Since trees are canonicalized, basic induction variables */ + /* will only appear on the left. */ + + /* Improvement: */ + /* We wish to pick up the cases (biv + li), (biv - li) and */ + /* (li + biv). OPmul and LS with bivs are out, since if we */ + /* try to eliminate the biv, and the loop test is a >, >=, */ + /* <, <=, we have a problem since we don't know if the li */ + /* is negative. (Would have to call swaprel() on it.) */ + + /* If we have (li + var), swap the leaves. */ + if (op == OPadd && isLI(n1) && n1->Eoper == OPvar && n2->Eoper == OPvar) + { n->E1 = n2; + n2 = n->E2 = n1; + n1 = n->E1; + } + +#if TARGET_SEGMENTED + // Get rid of case where we painted a far pointer to a long + if (op == OPadd || op == OPmin) + { int sz; + + sz = tysize(ty); + if (sz == tysize[TYfptr] && !tyfv(ty) && + (sz != tysize(n1->Ety) || sz != tysize(n2->Ety))) + return; + } +#endif + + /* Look for function of basic IV (-biv or biv op const) */ + if (n1->Eoper == OPvar && n1->EV.sp.Vsym == biv->IVbasic) + { if (op == OPneg) + { register famlist *fl; + + cmes2("found (-biv), elem %p\n",n); + fl = newfamlist(ty); + fl->FLivty = n1->Ety; + fl->FLpelem = pn; + fl->FLnext = biv->IVfamily; + biv->IVfamily = fl; + fl->c1 = el_una(op,ty,fl->c1); /* c1 = -1 */ + } + else if (n2->Eoper == OPconst || + isLI(n2) && (op == OPadd || op == OPmin)) + { register famlist *fl; + +#ifdef DEBUG + if (debugc) + { dbg_printf("found (biv op const), elem ("); + WReqn(n); + dbg_printf(");\n"); + dbg_printf("Types: n1="); WRTYxx(n1->Ety); + dbg_printf(" ty="); WRTYxx(ty); + dbg_printf(" n2="); WRTYxx(n2->Ety); + dbg_printf("\n"); + } +#endif + fl = newfamlist(ty); + fl->FLivty = n1->Ety; + fl->FLpelem = pn; + fl->FLnext = biv->IVfamily; + biv->IVfamily = fl; + switch (op) + { case OPadd: /* c2 = right */ + c2ty = n2->Ety; + if (typtr(fl->c2->Ety)) + c2ty = fl->c2->Ety; + goto L1; + case OPmin: /* c2 = -right */ + c2ty = fl->c2->Ety; + /* Check for subtracting two pointers */ + if (typtr(c2ty) && typtr(n2->Ety)) + { +#if TARGET_SEGMENTED + if (tybasic(c2ty) == TYhptr) + c2ty = TYlong; + else +#endif + c2ty = I64 ? TYllong : TYint; + } + L1: + fl->c2 = el_bin(op,c2ty,fl->c2,el_copytree(n2)); + break; + case OPmul: /* c1 = right */ + case OPshl: /* c1 = 1 << right */ + fl->c1 = el_bin(op,ty,fl->c1,el_copytree(n2)); + break; + default: + assert(0); + } + } + } + + /* Look for function of existing IV */ + + for (f = biv->IVfamily; f; f = f->FLnext) + { if (*f->FLpelem != n1) /* not it */ + continue; + + /* Look for (f op constant) */ + if (op == OPneg) + { + cmes2("found (-f), elem %p\n",n); + /* c1 = -c1; c2 = -c2; */ + f->c1 = el_una(OPneg,ty,f->c1); + f->c2 = el_una(OPneg,ty,f->c2); + f->FLty = ty; + f->FLpelem = pn; /* replace with new IV */ + + } + else if (n2->Eoper == OPconst || + isLI(n2) && (op == OPadd || op == OPmin)) + { +#ifdef DEBUG + if (debugc) + { dbg_printf("found (f op const), elem ("); + WReqn(n); + assert(*pn == n); + dbg_printf(");\n"); + elem_print(n); + } +#endif + switch (op) + { case OPmul: + case OPshl: + f->c1 = el_bin(op,ty,f->c1,el_copytree(n2)); + break; + case OPadd: + case OPmin: + break; + default: + assert(0); + } + f->c2 = el_bin(op,ty,f->c2,el_copytree(n2)); + f->FLty = ty; + f->FLpelem = pn; /* replace with new IV */ + } /* else if */ + } /* for */ + } /* if */ +} + +/********************************* + * Eliminate frivolous family ivs, that is, + * if we can't eliminate the BIV, then eliminate family ivs that + * differ from it only by a constant. + */ + +STATIC void elimfrivivs(loop *l) +{ Iv *biv; + + for (biv = l->Livlist; biv; biv = biv->IVnext) + { int nfams; + famlist *fl; + int nrefs; + +cmes("elimfrivivs()\n"); + /* Compute number of family ivs for biv */ + nfams = 0; + for (fl = biv->IVfamily; fl; fl = fl->FLnext) + nfams++; +cmes2("nfams = %d\n",nfams); + + /* Compute number of references to biv */ + if (onlyref(biv->IVbasic,l,*biv->IVincr,&nrefs)) + nrefs--; +cmes2("nrefs = %d\n",nrefs); + assert(nrefs + 1 >= nfams); + if (nrefs > nfams || // if we won't eliminate the biv + (!I16 && nrefs == nfams)) + { /* Eliminate any family ivs that only differ by a constant */ + /* from biv */ + for (fl = biv->IVfamily; fl; fl = fl->FLnext) + { elem *ec1 = fl->c1; + targ_llong c; + + if (elemisone(ec1) || + // Eliminate fl's that can be represented by + // an addressing mode + (!I16 && ec1->Eoper == OPconst && tyintegral(ec1->Ety) && + ((c = el_tolong(ec1)) == 2 || c == 4 || c == 8) + ) + ) + { fl->FLtemp = FLELIM; +#ifdef DEBUG + if (debugc) + { dbg_printf("Eliminating frivolous IV "); + WReqn(*fl->FLpelem); + dbg_printf("\n"); + } +#endif + } + } + } + } +} + + +/****************************** + * Introduce new variables. + */ + +STATIC void intronvars(loop *l) +{ + famlist *fl; + Iv *biv; + elem *T, *ne, *t2, *C2, *cmul; + tym_t ty,tyr; + + cmes2("intronvars(%p)\n",l); + for (biv = l->Livlist; biv; biv = biv->IVnext) // for each basic IV + { register elem *bivinc = *biv->IVincr; /* ptr to increment elem */ + + for (fl = biv->IVfamily; fl; fl = fl->FLnext) + { /* for each IV in family of biv */ + if (fl->FLtemp == FLELIM) /* if already eliminated */ + continue; + + /* If induction variable can be written as a simple function */ + /* of a previous induction variable, skip it. */ + if (funcprev(biv,fl)) + continue; + + ty = fl->FLty; + T = el_alloctmp(ty); /* allocate temporary T */ + fl->FLtemp = T->EV.sp.Vsym; +#if DEBUG + cmes2("intronvars() introduced new variable '%s' of type ",T->EV.sp.Vsym->Sident); + if (debugc) WRTYxx(ty); + cmes("\n"); +#endif + + /* append elem T=biv*C1+C2 to preheader */ + /* ne = biv*C1 */ + tyr = fl->FLivty; /* type of biv */ + ne = el_var(biv->IVbasic); + ne->Ety = tyr; + if (!elemisone(fl->c1)) /* don't multiply ptrs by 1 */ + ne = el_bin(OPmul,tyr,ne,el_copytree(fl->c1)); + if (tyfv(tyr) && tysize(ty) == SHORTSIZE) + ne = el_una(OP32_16,ty,ne); + C2 = el_copytree(fl->c2); + t2 = el_bin(OPadd,ty,ne,C2); /* t2 = ne + C2 */ + ne = el_bin(OPeq,ty,el_copytree(T),t2); + appendelem(ne, &(l->Lpreheader->Belem)); + + /* prefix T+=C1*C to elem biv+=C */ + /* Must prefix in case the value of the expression (biv+=C) */ + /* is used by somebody up the tree. */ + cmul = el_bin(OPmul,fl->c1->Ety,el_copytree(fl->c1), + el_copytree(bivinc->E2)); + t2 = el_bin(bivinc->Eoper,ty,el_copytree(T),cmul); + t2 = doptelem(t2,GOALvalue | GOALagain); + *biv->IVincr = el_bin(OPcomma,bivinc->Ety,t2,bivinc); + biv->IVincr = &((*biv->IVincr)->E2); +#ifdef DEBUG + if (debugc) + { dbg_printf("Replacing elem ("); + WReqn(*fl->FLpelem); + dbg_printf(") with '%s'\n",T->EV.sp.Vsym->Sident); + dbg_printf("The init elem is ("); + WReqn(ne); + dbg_printf(");\nThe increment elem is ("); + WReqn(t2); + dbg_printf(")\n"); + } +#endif + el_free(*fl->FLpelem); + *fl->FLpelem = T; /* replace elem n with ref to T */ + doflow = TRUE; /* redo flow analysis */ + changes++; + } /* for */ + } /* for */ +} + +/******************************* + * Determine if induction variable can be rewritten as a simple + * function of a previously generated temporary. + * This can frequently + * generate less code than that of an all-new temporary (especially + * if it is the same as a previous temporary!). + * Input: + * biv Basic induction variable + * fl Item in biv's family list we are looking at + * Returns: + * FALSE Caller should create a new induction variable. + * TRUE *FLpelem is replaced with function of a previous + * induction variable. FLtemp is set to FLELIM to + * indicate this. + */ + +STATIC bool funcprev(Iv *biv,famlist *fl) +{ tym_t tymin; + int sz; + famlist *fls; + elem *e1,*e2,*flse1; + +#ifdef DEBUG + if (debugc) + dbg_printf("funcprev\n"); +#endif + for (fls = biv->IVfamily; fls != fl; fls = fls->FLnext) + { assert(fls); /* fl must be in list */ + if (fls->FLtemp == FLELIM) /* no iv we can use here */ + continue; + + /* The multipliers must match */ + if (!el_match(fls->c1,fl->c1)) + continue; + + /* If the c2's match also, we got it easy */ + if (el_match(fls->c2,fl->c2)) + { + if (tysize(fl->FLty) > tysize(fls->FLtemp->ty())) + continue; /* can't increase size of var */ + flse1 = el_var(fls->FLtemp); + flse1->Ety = fl->FLty; + goto L2; + } + + /* The difference is only in the addition. Therefore, replace + *fl->FLpelem with: + case 1: (fl->c2 + (fls->FLtemp - fls->c2)) + case 2: (fls->FLtemp + (fl->c2 - fls->c2)) + */ + e1 = fl->c2; + /* Subtracting relocatables usually generates slow code for */ + /* linkers that can't handle arithmetic on relocatables. */ + if (typtr(fls->c2->Ety)) + { if (fls->c2->Eoper == OPrelconst && + !(fl->c2->Eoper == OPrelconst && + fl->c2->EV.sp.Vsym == fls->c2->EV.sp.Vsym) + ) + continue; + } + flse1 = el_var(fls->FLtemp); + e2 = flse1; /* assume case 1 */ + tymin = e2->Ety; + if (typtr(fls->c2->Ety)) + { if (!typtr(tymin)) + { if (typtr(e1->Ety)) + { e1 = e2; + e2 = fl->c2; /* case 2 */ + } + else /* can't subtract fptr */ + goto L1; + } +#if TARGET_SEGMENTED + if (tybasic(fls->c2->Ety) == TYhptr) + tymin = TYlong; + else +#endif + tymin = I64 ? TYllong : TYint; /* type of (ptr - ptr) */ + } + +#if TARGET_SEGMENTED + /* If e1 and fls->c2 are fptrs, and are not from the same */ + /* segment, we cannot subtract them. */ + if (tyfv(e1->Ety) && tyfv(fls->c2->Ety)) + { if (e1->Eoper != OPrelconst || fls->c2->Eoper != OPrelconst) + goto L1; /* assume expressions have diff segs */ + if (e1->EV.sp.Vsym->Sclass != fls->c2->EV.sp.Vsym->Sclass) + { L1: + el_free(flse1); + continue; + } + } +#else +L1: + el_free(flse1); + continue; + +#endif + /* Some more type checking... */ + sz = tysize(fl->FLty); + if (sz != tysize(e1->Ety) && + sz != tysize(tymin)) + goto L1; + + /* Do some type checking (can't add pointers and get a pointer!) */ + //if (typtr(fl->FLty) && typtr(e1->Ety) && typtr(tymin)) + //goto L1; + /* Construct (e1 + (e2 - fls->c2)) */ + flse1 = el_bin(OPadd,fl->FLty, + e1, + el_bin(OPmin,tymin, + e2, + el_copytree(fls->c2))); + if (sz < tysize(tymin) && sz == tysize(e1->Ety)) +#if TARGET_SEGMENTED + flse1->E2 = el_una(OPoffset,fl->FLty,flse1->E2); +#else + assert(0); +#endif + + flse1 = doptelem(flse1,GOALvalue | GOALagain); + fl->c2 = NULL; + L2: +#ifdef DEBUG + if (debugc) + { dbg_printf("Replacing "); + WReqn(*fl->FLpelem); + dbg_printf(" with "); + WReqn(flse1); + dbg_printf("\n"); + } +#endif + el_free(*fl->FLpelem); + *fl->FLpelem = flse1; + + /* Fix the iv so when we do loops again, we won't create */ + /* yet another iv, which is just what funcprev() is supposed */ + /* to prevent. */ + fls->FLtemp->Sflags |= SFLnotbasiciv; + + fl->FLtemp = FLELIM; /* mark iv as being gone */ + changes++; + doflow = TRUE; + return TRUE; /* it was replaced */ + } + return FALSE; /* need to create a new variable */ +} + +/*********************** + * Eliminate basic IVs. + */ + +STATIC void elimbasivs(register loop *l) +{ famlist *fl; + register Iv *biv; + register unsigned i; + register tym_t ty; + register elem **pref,*fofe,*C2; + symbol *X; + int refcount; + + cmes2("elimbasivs(%p)\n",l); + for (biv = l->Livlist; biv; biv = biv->IVnext) // for each basic IV + { + + /* Can't eliminate this basic IV if we have a goal for the */ + /* increment elem. */ + // Be careful about Nflags being in a union... + if (!((*biv->IVincr)->Nflags & NFLnogoal)) + continue; + + X = biv->IVbasic; + assert(symbol_isintab(X)); + ty = X->ty(); + pref = onlyref(X,l,*biv->IVincr,&refcount); + + /* if only ref of X is of the form (X) or (X relop e) or (e relop X) */ + if (pref != NULL && refcount <= 1) + { elem *ref; + tym_t flty; + + fl = biv->IVfamily; + if (!fl) // if no elems in family of biv + continue; + + ref = *pref; + + /* Replace (X) with (X != 0) */ + if (ref->Eoper == OPvar) + ref = *pref = el_bin(OPne,TYint,ref,el_long(ref->Ety,0L)); + + fl = simfl(fl,ty); /* find simplest elem in family */ + if (!fl) + continue; + + // Don't do the replacement if we would replace a + // signed comparison with an unsigned one + flty = fl->FLty; + if (tyuns(ref->E1->Ety) | tyuns(ref->E2->Ety)) + flty = touns(flty); + + if (ref->Eoper >= OPle && ref->Eoper <= OPge && +#if 1 + !(tyuns(ref->E1->Ety) | tyuns(ref->E2->Ety)) && + tyuns(flty)) +#else + (tyuns(ref->E1->Ety) | tyuns(ref->E2->Ety)) != + tyuns(flty)) +#endif + continue; + + /* if we have (e relop X), replace it with (X relop e) */ + if (ref->E2->Eoper == OPvar && ref->E2->EV.sp.Vsym == X) + { register elem *tmp; + + tmp = ref->E2; + ref->E2 = ref->E1; + ref->E1 = tmp; + ref->Eoper = swaprel(ref->Eoper); + } + + // If e*c1+c2 would result in a sign change or an overflow + // then we can't do it + if (fl->c1->Eoper == OPconst) + { +#if LONGLONG + targ_llong c1; +#else + targ_long c1; +#endif + int sz; + + c1 = el_tolong(fl->c1); + sz = tysize(ty); + if (sz == SHORTSIZE && + ((ref->E2->Eoper == OPconst && + c1 * el_tolong(ref->E2) & ~0x7FFFL) || + c1 & ~0x7FFFL) + ) + continue; + + if (sz == LONGSIZE && + ((ref->E2->Eoper == OPconst && + c1 * el_tolong(ref->E2) & ~0x7FFFFFFFL) || + c1 & ~0x7FFFFFFFL) + ) + continue; +#if LONGLONG && __INTSIZE >= 4 + if (sz == LLONGSIZE && + ((ref->E2->Eoper == OPconst && + c1 * el_tolong(ref->E2) & ~0x7FFFFFFFFFFFFFFFLL) || + c1 & ~0x7FFFFFFFFFFFFFFFLL) + ) + continue; +#endif + } + + /* If loop started out with a signed conditional that was + * replaced with an unsigned one, don't do it if c2 + * is less than 0. + */ + if (ref->Nflags & NFLtouns && fl->c2->Eoper == OPconst) + { + targ_llong c2 = el_tolong(fl->c2); + if (c2 < 0) + continue; + } + + elem *refE2 = el_copytree(ref->E2); + int refEoper = ref->Eoper; + + /* if c1 < 0 and relop is < <= > >= + then adjust relop as if both sides were multiplied + by -1 + */ + if (!tyuns(ty) && + (tyintegral(ty) && el_tolong(fl->c1) < 0 || + tyfloating(ty) && el_toldouble(fl->c1) < 0.0)) + refEoper = swaprel(refEoper); + + /* Replace (X relop e) with (X relop (short)e) + if T is 1 word but e is 2 + */ + if (tysize(flty) == SHORTSIZE && + tysize(refE2->Ety) == LONGSIZE) + refE2 = el_una(OP32_16,flty,refE2); + + /* replace e with e*c1 + c2 */ + C2 = el_copytree(fl->c2); + fofe = el_bin(OPadd,flty, + el_bin(OPmul,refE2->Ety, + refE2, + el_copytree(fl->c1)), + C2); + fofe = doptelem(fofe,GOALvalue | GOALagain); // fold any constants + + if (tyuns(flty) && refEoper == OPge && + fofe->Eoper == OPconst && el_allbits(fofe, 0) && + fl->c2->Eoper == OPconst && !el_allbits(fl->c2, 0)) + { + /* Don't do it if replacement will result in + * an unsigned T>=0 which will be an infinite loop. + */ + el_free(fofe); + continue; + } + + cmes2("Eliminating basic IV '%s'\n",X->Sident); + +#ifdef DEBUG + if (debugc) + { dbg_printf("Comparison replaced: "); + WReqn(ref); + dbg_printf(" with "); + } +#endif + + el_free(ref->E2); + ref->E2 = refE2; + ref->Eoper = refEoper; + + elimass(*biv->IVincr); // dump the increment elem + + // replace X with T + assert(ref->E1->EV.sp.Voffset == 0); + ref->E1->EV.sp.Vsym = fl->FLtemp; + ref->E1->Ety = flty; + ref->E2 = fofe; + + /* If sizes of expression worked out wrong... + Which can happen if we have (int)ptr==e + */ + if (EBIN(fofe)) /* if didn't optimize it away */ + { int sz; + tym_t ty,ty1,ty2; + + ty = fofe->Ety; + sz = tysize(ty); + ty1 = fofe->E1->Ety; + ty2 = fofe->E2->Ety; + /* Sizes of + expression must all be the same */ + if (sz != tysize(ty1) && + sz != tysize(ty2) + ) + { + if (tyuns(ty)) /* if unsigned comparison */ + ty1 = touns(ty1); /* to unsigned type */ + fofe->Ety = ty1; + ref->E1->Ety = ty1; + } + } + +#if TARGET_SEGMENTED + /* Fix if leaves of compare are TYfptrs and the compare */ + /* operator is < <= > >=. */ + if (ref->Eoper >= OPle && ref->Eoper <= OPge && tyfv(ref->E1->Ety)) + { assert(tyfv(ref->E2->Ety)); + ref->E1 = el_una(OPoffset,TYuint,ref->E1); + ref->E2 = el_una(OPoffset,TYuint,fofe); + } +#endif +#ifdef DEBUG + if (debugc) + { WReqn(ref); + dbg_printf("\n"); + } +#endif + + changes++; + doflow = TRUE; /* redo flow analysis */ + + /* if X is live on entry to any successor S outside loop */ + /* prepend elem X=(T-c2)/c1 to S.Belem */ + + foreach (i,dfotop,l->Lexit) /* for each exit block */ + { register elem *ne; + register block *b; + register list_t bl; + + for (bl = dfo[i]->Bsucc; bl; bl = list_next(bl)) + { /* for each successor */ + b = list_block(bl); + if (vec_testbit(b->Bdfoidx,l->Lloop)) + continue; /* inside loop */ + if (!vec_testbit(X->Ssymnum,b->Binlv)) + continue; /* not live */ + + C2 = el_copytree(fl->c2); + ne = el_bin(OPmin,ty, + el_var(fl->FLtemp), + C2); +#if TARGET_SEGMENTED + if (tybasic(ne->E1->Ety) == TYfptr && + tybasic(ne->E2->Ety) == TYfptr) + { ne->Ety = I64 ? TYllong : TYint; + if (tylong(ty) && intsize == 2) + ne = el_una(OPs16_32,ty,ne); + } +#endif + + ne = el_bin(OPeq,X->ty(), + el_var(X), + el_bin(OPdiv,ne->Ety, + ne, + el_copytree(fl->c1))); +#ifdef DEBUG + if (debugc) + { dbg_printf("Adding ("); + WReqn(ne); + dbg_printf(") to exit block B%d\n",b->Bdfoidx); + //elem_print(ne); + } +#endif + /* We have to add a new block if there is */ + /* more than one predecessor to b. */ + if (list_next(b->Bpred)) + { block *bn; + register list_t bl2; + + bn = block_calloc(); + bn->Btry = b->Btry; + numblks++; + assert(numblks <= maxblks); + bn->BC = BCgoto; + bn->Bnext = dfo[i]->Bnext; + dfo[i]->Bnext = bn; + list_append(&(bn->Bsucc),b); + list_append(&(bn->Bpred),dfo[i]); + list_ptr(bl) = (void *)bn; + for (bl2 = b->Bpred; bl2; + bl2 = list_next(bl2)) + if (list_block(bl2) == dfo[i]) + { list_ptr(bl2) = (void *)bn; + goto L2; + } + assert(0); + L2: + b = bn; + addblk = TRUE; + } + + if (b->Belem) + b->Belem = + el_bin(OPcomma,b->Belem->Ety, + ne,b->Belem); + else + b->Belem = ne; + changes++; + doflow = TRUE; /* redo flow analysis */ + } /* for each successor */ + } /* foreach exit block */ + if (addblk) + return; + } + else if (refcount == 0) /* if no uses of IV in loop */ + { /* Eliminate the basic IV if it is not live on any successor */ + foreach (i,dfotop,l->Lexit) /* for each exit block */ + { register block *b; + register list_t bl; + + for (bl = dfo[i]->Bsucc; bl; bl = list_next(bl)) + { /* for each successor */ + b = list_block(bl); + if (vec_testbit(b->Bdfoidx,l->Lloop)) + continue; /* inside loop */ + if (vec_testbit(X->Ssymnum,b->Binlv)) + goto L1; /* live */ + } + } + + cmes3("No uses, eliminating basic IV '%s' (%p)\n",(X->Sident) + ? (char *)X->Sident : "",X); + + /* Dump the increment elem */ + /* (Replace it with an OPconst that only serves as a */ + /* placeholder in the tree) */ + *(biv->IVincr) = el_selecte2(*(biv->IVincr)); + + changes++; + doflow = TRUE; /* redo flow analysis */ + L1: ; + } + } /* for */ +} + + +/*********************** + * Eliminate opeq IVs that are not used outside the loop. + */ + +STATIC void elimopeqs(register loop *l) +{ + Iv *biv; + unsigned i; + elem **pref; + symbol *X; + int refcount; + + cmes2("elimopeqs(%p)\n",l); + for (biv = l->Lopeqlist; biv; biv = biv->IVnext) // for each opeq IV + { + + // Can't eliminate this basic IV if we have a goal for the + // increment elem. + // Be careful about Nflags being in a union... + if (!((*biv->IVincr)->Nflags & NFLnogoal)) + continue; + + X = biv->IVbasic; + assert(symbol_isintab(X)); + pref = onlyref(X,l,*biv->IVincr,&refcount); + + // if only ref of X is of the form (X) or (X relop e) or (e relop X) + if (pref != NULL && refcount <= 1) + ; + else if (refcount == 0) // if no uses of IV in loop + { // Eliminate the basic IV if it is not live on any successor + foreach (i,dfotop,l->Lexit) // for each exit block + { block *b; + list_t bl; + + for (bl = dfo[i]->Bsucc; bl; bl = list_next(bl)) + { // for each successor + b = list_block(bl); + if (vec_testbit(b->Bdfoidx,l->Lloop)) + continue; // inside loop + if (vec_testbit(X->Ssymnum,b->Binlv)) + goto L1; // live + } + } + + cmes3("No uses, eliminating opeq IV '%s' (%p)\n",(X->Sident) + ? (char *)X->Sident : "",X); + + // Dump the increment elem + // (Replace it with an OPconst that only serves as a + // placeholder in the tree) + *(biv->IVincr) = el_selecte2(*(biv->IVincr)); + + changes++; + doflow = TRUE; // redo flow analysis + L1: ; + } + } +} + +/************************** + * Find simplest elem in family. + * Input: + * tym type of basic IV + * Return NULL if none found. + */ + +STATIC famlist * simfl(famlist *fl,tym_t tym) +{ famlist *sofar; + + assert(fl); + sofar = NULL; + for (; fl; fl = fl->FLnext) + { + if (fl->FLtemp == FLELIM) /* no variable, so skip it */ + continue; + /* If size of replacement is less than size of biv, we could */ + /* be in trouble due to loss of precision. */ + if (size(fl->FLtemp->ty()) < size(tym)) + continue; + sofar = flcmp(sofar,fl); /* pick simplest */ + } + return sofar; +} + +/************************** + * Return simpler of two family elems. + * There is room for improvement, namely if + * f1.c1 = 2, f2.c1 = 27 + * then pick f1 because it is a shift. + */ + +STATIC famlist * flcmp(famlist *f1,famlist *f2) +{ tym_t ty; + union eve *t1,*t2; + + assert(f2); + if (!f1) + goto Lf2; + t1 = &(f1->c1->EV); + t2 = &(f2->c1->EV); + ty = (*f1->FLpelem)->Ety; /* type of elem */ +#if 0 + printf("f1: c1 = %d, c2 = %d\n",t1->Vshort,f1->c2->EV.Vshort); + printf("f2: c1 = %d, c2 = %d\n",t2->Vshort,f2->c2->EV.Vshort); + WRTYxx((*f1->FLpelem)->Ety); + WRTYxx((*f2->FLpelem)->Ety); +#endif + /* Wimp out and just pick f1 if the types don't match */ + if (tysize(ty) == tysize((*f2->FLpelem)->Ety)) + { + switch (tybasic(ty)) + { case TYbool: + case TYchar: + case TYschar: + case TYuchar: + if (t2->Vuchar == 1 || + t1->Vuchar != 1 && f2->c2->EV.Vuchar == 0) + goto Lf2; + break; + case TYshort: + case TYushort: + case TYchar16: + case TYwchar_t: // BUG: what about 4 byte wchar_t's? + case_short: + if (t2->Vshort == 1 || + t1->Vshort != 1 && f2->c2->EV.Vshort == 0) + goto Lf2; + break; + +#if JHANDLE + case TYjhandle: +#endif +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: +#endif + case TYnptr: // BUG: 64 bit pointers? + case TYnullptr: + case TYint: + case TYuint: + if (intsize == SHORTSIZE) + goto case_short; + else + goto case_long; + case TYlong: + case TYulong: + case TYdchar: +#if TARGET_SEGMENTED + case TYfptr: + case TYvptr: + case TYhptr: +#endif + case_long: + if (t2->Vlong == 1 || + t1->Vlong != 1 && f2->c2->EV.Vlong == 0) + goto Lf2; + break; + case TYfloat: + if (t2->Vfloat == 1 || + t1->Vfloat != 1 && f2->c2->EV.Vfloat == 0) + goto Lf2; + break; + case TYdouble: + case TYdouble_alias: + if (t2->Vdouble == 1.0 || + t1->Vdouble != 1.0 && f2->c2->EV.Vdouble == 0) + goto Lf2; + break; + case TYldouble: + if (t2->Vldouble == 1.0 || + t1->Vldouble != 1.0 && f2->c2->EV.Vldouble == 0) + goto Lf2; + break; + case TYllong: + case TYullong: + if (t2->Vllong == 1 || + t1->Vllong != 1 && f2->c2->EV.Vllong == 0) + goto Lf2; + break; + default: + assert(0); + } + } + //printf("picking f1\n"); + return f1; + +Lf2: + //printf("picking f2\n"); + return f2; +} + +/************************************ + * Input: + * x basic IV symbol + * incn increment elem for basic IV X. + * Output: + * *prefcount # of references to X other than the increment elem + * Returns: + * If ref of X in loop l is of the form (X relop e) or (e relop X) + * Return the relop elem + * Else + * Return NULL + */ + +static int count; +static elem **nd,*sincn; +static symbol *X; + +STATIC elem ** onlyref(symbol *x,loop *l,elem *incn,int *prefcount) +{ register unsigned i; + + //printf("onlyref('%s')\n", x->Sident); + X = x; /* save some parameter passing */ + assert(symbol_isintab(x)); + sincn = incn; +#ifdef DEBUG + if (!(X->Ssymnum < globsym.top && l && incn)) + dbg_printf("X = %d, globsym.top = %d, l = %p, incn = %p\n",X->Ssymnum,globsym.top,l,incn); +#endif + assert(X->Ssymnum < globsym.top && l && incn); + count = 0; + nd = NULL; + foreach (i,dfotop,l->Lloop) /* for each block in loop */ + { block *b; + + b = dfo[i]; + if (b->Belem) + { + countrefs(&b->Belem,b->BC == BCiftrue); + } + } +#if 0 + dbg_printf("count = %d, nd = ("); + if (nd) WReqn(*nd); + dbg_printf(")\n"); +#endif + *prefcount = count; + return nd; +} + +/****************************** + * Count elems of the form (X relop e) or (e relop X). + * Do not count the node if it is the increment node (sincn). + * Input: + * flag: TRUE if block wants to test the elem + */ + +STATIC void countrefs(register elem **pn,bool flag) +{ elem *n = *pn; + + assert(n); + if (n == sincn) /* if it is the increment elem */ + { + if (OTbinary(n->Eoper)) + countrefs(&n->E2, FALSE); + return; // don't count lvalue + } + if (OTunary(n->Eoper)) + countrefs(&n->E1,FALSE); + else if (OTbinary(n->Eoper)) + { + if (OTrel(n->Eoper)) + { elem *e1 = n->E1; + + assert(e1->Eoper != OPcomma); + if (e1 == sincn && + (e1->Eoper == OPeq || OTopeq(e1->Eoper))) + goto L1; + + /* Check both subtrees to see if n is the comparison node, + * that is, if X is a leaf of the comparison. + */ + if (e1->Eoper == OPvar && e1->EV.sp.Vsym == X && !countrefs2(n->E2) || + n->E2->Eoper == OPvar && n->E2->EV.sp.Vsym == X && !countrefs2(e1)) + nd = pn; /* found the relop node */ + } + L1: + countrefs(&n->E1,FALSE); + countrefs(&n->E2,(flag && n->Eoper == OPcomma)); + } + else if ((n->Eoper == OPvar || n->Eoper == OPrelconst) && n->EV.sp.Vsym == X) + { if (flag) + nd = pn; /* comparing it with 0 */ + count++; /* found another reference */ + } +} + +/******************************* + * Count number of times symbol X appears in elem tree e. + */ + +STATIC int countrefs2(elem *e) +{ + elem_debug(e); + while (OTunary(e->Eoper)) + e = e->E1; + if (OTbinary(e->Eoper)) + return countrefs2(e->E1) + countrefs2(e->E2); + return ((e->Eoper == OPvar || e->Eoper == OPrelconst) && + e->EV.sp.Vsym == X); +} + +/**************************** + * Eliminate some special cases. + */ + +STATIC void elimspec(loop *l) +{ register unsigned i; + + foreach (i,dfotop,l->Lloop) /* for each block in loop */ + { block *b; + + b = dfo[i]; + if (b->Belem) + elimspecwalk(&b->Belem); + } +} + +/****************************** + */ + +STATIC void elimspecwalk(elem **pn) +{ elem *n; + + n = *pn; + assert(n); + if (OTunary(n->Eoper)) + elimspecwalk(&n->E1); + else if (OTbinary(n->Eoper)) + { + elimspecwalk(&n->E1); + elimspecwalk(&n->E2); + if (OTrel(n->Eoper)) + { elem *e1 = n->E1; + + /* Replace ((e1,e2) rel e3) with (e1,(e2 rel e3). + * This will reduce the number of cases for elimbasivs(). + * Don't do equivalent with (e1 rel (e2,e3)) because + * of potential side effects in e1. + */ + if (e1->Eoper == OPcomma) + { elem *e; + +#ifdef DEBUG + if (debugc) + { dbg_printf("3rewriting ("); WReqn(n); dbg_printf(")\n"); } +#endif + e = n->E2; + n->E2 = e1; + n->E1 = n->E2->E1; + n->E2->E1 = n->E2->E2; + n->E2->E2 = e; + n->E2->Eoper = n->Eoper; + n->E2->Ety = n->Ety; + n->Eoper = OPcomma; + + changes++; + doflow = TRUE; + + elimspecwalk(&n->E1); + elimspecwalk(&n->E2); + } + + /* Rewrite ((X op= e2) rel e3) into ((X op= e2),(X rel e3)) + * Rewrite ((X ++ e2) rel e3) into ((X += e2),(X-e2 rel e3)) + * so that the op= will not have a goal, so elimbasivs() + * will work on it. + */ + if ((OTopeq(e1->Eoper) + || OTpost(e1->Eoper) + ) && + !el_sideeffect(e1->E1)) + { elem *e; + int op; +#ifdef DEBUG + if (debugc) + { dbg_printf("4rewriting ("); WReqn(n); dbg_printf(")\n"); } +#endif + e = el_calloc(); + el_copy(e,n); + e->E1 = el_copytree(e1->E1); + e->E1->Ety = n->E1->Ety; + n->E2 = e; + switch (e1->Eoper) + { case OPpostinc: + e1->Eoper = OPaddass; + op = OPmin; + goto L3; + case OPpostdec: + e1->Eoper = OPminass; + op = OPadd; + L3: e->E1 = el_bin(op,e->E1->Ety,e->E1,el_copytree(e1->E2)); + break; + + } + /* increment node is now guaranteed to have no goal */ + e1->Nflags |= NFLnogoal; + n->Eoper = OPcomma; + //changes++; + doflow = TRUE; + + elimspecwalk(&n->E1); + elimspecwalk(&n->E2); + } + } + } +} + +#endif diff --git a/backend/go.c b/backend/go.c new file mode 100644 index 00000000..74100f70 --- /dev/null +++ b/backend/go.c @@ -0,0 +1,368 @@ +// Copyright (C) 1986-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include + +#include "cc.h" +#include "global.h" +#include "oper.h" +#include "el.h" +#include "go.h" +#include "type.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +/**************************** + * Terminate use of globals. + */ + +void go_term() +{ + vec_free(defkill); + vec_free(starkill); + vec_free(vptrkill); + util_free(expnod); + util_free(expblk); + util_free(defnod); +} + +#if DEBUG + // to print progress message and current trees set to + // DEBUG_TREES to 1 and uncomment next 2 lines +#define DEBUG_TREES 0 +#if DEBUG_TREES +void dbg_optprint(char *); +#else + // to print progress message, undo comment +#define dbg_optprint(c) // dbg_printf(c) +#endif +#else +#define dbg_optprint(c) +#endif + +/************************************** + * Parse optimizer command line flag. + * Input: + * cp flag string + * Returns: + * 0 not recognized + * !=0 recognized + */ + +int go_flag(char *cp) +{ unsigned i; + unsigned flag; + + enum GL // indices of various flags in flagtab[] + { + GLO,GLall,GLcnp,GLcp,GLcse,GLda,GLdc,GLdv,GLli,GLliv,GLlocal,GLloop, + GLnone,GLo,GLreg,GLspace,GLspeed,GLtime,GLtree,GLvbe,GLMAX + }; + static const char *flagtab[] = + { "O","all","cnp","cp","cse","da","dc","dv","li","liv","local","loop", + "none","o","reg","space","speed","time","tree","vbe" + }; + static mftype flagmftab[] = + { 0,MFall,MFcnp,MFcp,MFcse,MFda,MFdc,MFdv,MFli,MFliv,MFlocal,MFloop, + 0,0,MFreg,0,MFtime,MFtime,MFtree,MFvbe + }; + + i = GLMAX; + assert(i == arraysize(flagtab)); + assert(i == arraysize(flagmftab)); + + //printf("go_flag('%s')\n", cp); + flag = binary(cp + 1,flagtab,GLMAX); + if (mfoptim == 0 && flag != -1) + mfoptim = MFall & ~MFvbe; + + if (*cp == '-') /* a regular -whatever flag */ + { /* cp -> flag string */ + switch (flag) + { + case GLall: + case GLcnp: + case GLcp: + case GLdc: + case GLda: + case GLdv: + case GLcse: + case GLli: + case GLliv: + case GLlocal: + case GLloop: + case GLreg: + case GLspeed: + case GLtime: + case GLtree: + case GLvbe: + mfoptim &= ~flagmftab[flag]; /* clear bits */ + break; + case GLo: + case GLO: + case GLnone: + mfoptim |= MFall & ~MFvbe; // inverse of -all + break; + case GLspace: + mfoptim |= MFtime; /* inverse of -time */ + break; + case -1: /* not in flagtab[] */ + goto badflag; + default: + assert(0); + } + } + else if (*cp == '+') /* a regular +whatever flag */ + { /* cp -> flag string */ + switch (flag) + { + case GLall: + case GLcnp: + case GLcp: + case GLdc: + case GLda: + case GLdv: + case GLcse: + case GLli: + case GLliv: + case GLlocal: + case GLloop: + case GLreg: + case GLspeed: + case GLtime: + case GLtree: + case GLvbe: + mfoptim |= flagmftab[flag]; /* set bits */ + break; + case GLnone: + mfoptim &= ~MFall; /* inverse of +all */ + break; + case GLspace: + mfoptim &= ~MFtime; /* inverse of +time */ + break; + case -1: /* not in flagtab[] */ + goto badflag; + default: + assert(0); + } + } + if (mfoptim) + { + mfoptim |= MFtree | MFdc; // always do at least this much + config.flags4 |= (mfoptim & MFtime) ? CFG4speed : CFG4space; + } + else + { + config.flags4 &= ~CFG4optimized; + } + return 1; // recognized + +badflag: + return 0; +} + +#if DEBUG_TREES +void dbg_optprint(char *title) +{ + block *b; + for (b = startblock; b; b = b->Bnext) + if (b->Belem) + { + dbg_printf("%s\n",title); + elem_print(b->Belem); + } +} +#endif + +/**************************** + * Optimize function. + */ + +void optfunc() +{ +#if !HTOD + block *b; + int iter; // iteration count + clock_t starttime; + + cmes ("optfunc()\n"); + dbg_optprint("optfunc\n"); +#ifdef DEBUG + if (debugb) + { + dbg_printf("................Before optimization.........\n"); + WRfunc(); + } +#endif + iter = 0; + + if (localgot) + { // Initialize with: + // localgot = OPgot; + elem *e = el_long(TYnptr, 0); + e->Eoper = OPgot; + e = el_bin(OPeq, TYnptr, el_var(localgot), e); + startblock->Belem = el_combine(e, startblock->Belem); + } + + // Each pass through the loop can reduce only one level of comma expression. + // The infinite loop check needs to take this into account. + int iterationLimit = 0; + for (b = startblock; b; b = b->Bnext) + { + if (!b->Belem) + continue; + int d = el_countCommas(b->Belem); + if (d > iterationLimit) + iterationLimit = d; + } + + // Some functions can take enormous amounts of time to optimize. + // We try to put a lid on it. + starttime = clock(); + do + { + //printf("iter = %d\n", iter); + if (++iter > 200) + { assert(iter < iterationLimit); // infinite loop check + break; + } +#if MARS + util_progress(); +#else + file_progress(); +#endif + + //printf("optelem\n"); + /* canonicalize the trees */ + for (b = startblock; b; b = b->Bnext) + if (b->Belem) + { +#if DEBUG + if(debuge) + { + dbg_printf("before\n"); + elem_print(b->Belem); + //el_check(b->Belem); + } +#endif + b->Belem = doptelem(b->Belem,bc_goal[b->BC] | GOALagain); +#if DEBUG + if(0 && debugf) + { + dbg_printf("after\n"); + elem_print(b->Belem); + } +#endif + } + //printf("blockopt\n"); + if (mfoptim & MFdc) + blockopt(0); // do block optimization + out_regcand(&globsym); // recompute register candidates + changes = 0; /* no changes yet */ + if (mfoptim & MFcnp) + constprop(); /* make relationals unsigned */ + if (mfoptim & (MFli | MFliv)) + loopopt(); /* remove loop invariants and */ + /* induction vars */ + /* do loop rotation */ + else + for (b = startblock; b; b = b->Bnext) + b->Bweight = 1; + dbg_optprint("boolopt\n"); + + if (mfoptim & MFcnp) + boolopt(); // optimize boolean values + if (changes && mfoptim & MFloop && (clock() - starttime) < 30 * CLOCKS_PER_SEC) + continue; + + if (mfoptim & MFcnp) + constprop(); /* constant propagation */ + if (mfoptim & MFcp) + copyprop(); /* do copy propagation */ + + /* Floating point constants and string literals need to be + * replaced with loads from variables in read-only data. + * This can result in localgot getting needed. + */ + symbol *localgotsave = localgot; + for (b = startblock; b; b = b->Bnext) + { + if (b->Belem) + { + b->Belem = doptelem(b->Belem,bc_goal[b->BC] | GOALstruct); + if (b->Belem) + b->Belem = el_convert(b->Belem); + } + } + if (localgot != localgotsave) + { /* Looks like we did need localgot, initialize with: + * localgot = OPgot; + */ + elem *e = el_long(TYnptr, 0); + e->Eoper = OPgot; + e = el_bin(OPeq, TYnptr, el_var(localgot), e); + startblock->Belem = el_combine(e, startblock->Belem); + } + + /* localize() is after localgot, otherwise we wind up with + * more than one OPgot in a function, which mucks up OSX + * code generation which assumes at most one (localgotoffset). + */ + if (mfoptim & MFlocal) + localize(); // improve expression locality + if (mfoptim & MFda) + rmdeadass(); /* remove dead assignments */ + + cmes2 ("changes = %d\n", changes); + if (!(changes && mfoptim & MFloop && (clock() - starttime) < 30 * CLOCKS_PER_SEC)) + break; + } while (1); + cmes2("%d iterations\n",iter); + if (mfoptim & MFdc) + blockopt(1); // do block optimization + + for (b = startblock; b; b = b->Bnext) + { + if (b->Belem) + postoptelem(b->Belem); + } + if (mfoptim & MFvbe) + verybusyexp(); /* very busy expressions */ + if (mfoptim & MFcse) + builddags(); /* common subexpressions */ + if (mfoptim & MFdv) + deadvar(); /* eliminate dead variables */ + +#ifdef DEBUG + if (debugb) + { + dbg_printf(".............After optimization...........\n"); + WRfunc(); + } +#endif + + // Prepare for code generator + for (b = startblock; b; b = b->Bnext) + { + block_optimizer_free(b); + } +#endif +} + +#endif // !SPP diff --git a/backend/go.h b/backend/go.h new file mode 100644 index 00000000..e483b3b0 --- /dev/null +++ b/backend/go.h @@ -0,0 +1,98 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if __SC__ +#pragma once +#endif + +#ifndef GO_H +#define GO_H 1 + +/*************************************** + * Bit masks for various optimizations. + */ + +typedef unsigned mftype; /* a type big enough for all the flags */ + +#define MFdc 1 // dead code +#define MFda 2 // dead assignments +#define MFdv 4 // dead variables +#define MFreg 8 // register variables +#define MFcse 0x10 // global common subexpressions +#define MFvbe 0x20 // very busy expressions +#define MFtime 0x40 // favor time (speed) over space +#define MFli 0x80 // loop invariants +#define MFliv 0x100 // loop induction variables +#define MFcp 0x200 // copy propagation +#define MFcnp 0x400 // constant propagation +#define MFloop 0x800 // loop till no more changes +#define MFtree 0x1000 // optelem (tree optimization) +#define MFlocal 0x2000 // localize expressions +#define MFall (~0) // do everything + +/********************************** + * Definition elem vector, used for reaching definitions. + */ + +typedef struct DN + { + elem *DNelem; // pointer to definition elem + block *DNblock; // pointer to block that the elem is in + } dn; + +/* Global Variables */ +extern unsigned optab[]; +extern mftype mfoptim; +extern unsigned changes; /* # of optimizations performed */ +extern struct DN *defnod; /* array of definition elems */ +extern unsigned deftop; /* # of entries in defnod[] */ +extern elem **expnod; /* array of expression elems */ +extern unsigned exptop; /* top of expnod[] */ +extern block **expblk; /* parallel array of block pointers */ +extern vec_t defkill; /* vector of AEs killed by an ambiguous */ + /* definition */ +extern vec_t starkill; /* vector of AEs killed by a definition */ + /* of something that somebody could be */ + /* pointing to */ +extern vec_t vptrkill; /* vector of AEs killed by an access */ + +/* gdag.c */ +void builddags(void); +void boolopt(void); +void opt_arraybounds(); + +/* gflow.c */ +void flowrd(),flowlv(),flowae(),flowvbe(), + flowcp(),flowae(),genkillae(),flowarraybounds(); +int ae_field_affect(elem *lvalue,elem *e); + +/* glocal.c */ +void localize(void); + +/* gloop.c */ +int blockinit(void); +void compdom(void); +void loopopt(void); +void updaterd(elem *n,vec_t GEN,vec_t KILL); + +/* gother.c */ +void rd_arraybounds(void); +void rd_free(); +void constprop(void); +void copyprop(void); +void rmdeadass(void); +void elimass(elem *); +void deadvar(void); +void verybusyexp(void); +list_t listrds(vec_t, elem *, vec_t); + +#endif /* GO_H */ diff --git a/backend/gother.c b/backend/gother.c new file mode 100644 index 00000000..b0f36925 --- /dev/null +++ b/backend/gother.c @@ -0,0 +1,1781 @@ +// Copyright (C) 1986-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if (SCPP || MARS) && !HTOD + +#include +#include + +#include "cc.h" +#include "global.h" +#include "el.h" +#include "go.h" +#include "oper.h" +#include "list.h" +#include "type.h" + +#if SCPP +#include "parser.h" +#endif + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +extern void error(const char *filename, unsigned linnum, const char *format, ...); + +STATIC void rd_free_elem(elem *e); +STATIC void rd_compute(); +STATIC void conpropwalk(elem *n , vec_t IN); +STATIC void chkrd(elem *n , list_t rdlist); +STATIC elem * chkprop(elem *n , list_t rdlist); +STATIC void arrayboundsrds(void); +STATIC void eqeqranges(void); +STATIC void intranges(void); +STATIC int loopcheck(block *start , block *inc , block *rel); +STATIC void cpwalk(elem *n , vec_t IN); +STATIC unsigned numasg(elem *e); +STATIC void accumda(elem *n , vec_t DEAD , vec_t POSS); +STATIC void dvwalk(elem *n , unsigned i); +STATIC int killed(unsigned j , block *bp , block *b); +STATIC int ispath(unsigned j , block *bp , block *b); + +/**********************************************************************/ + +// Lists to help identify ranges of variables +struct Elemdata +{ Elemdata *next; // linked list + elem *pelem; // the elem in question + block *pblock; // which block it's in + list_t rdlist; // list of definition elems for *pelem + + static Elemdata *ctor(elem *e,block *b,list_t rd); +}; + +Elemdata *Elemdata::ctor(elem *e,block *b,list_t rd) +{ Elemdata *ed; + + ed = (Elemdata *) MEM_BEF_CALLOC(sizeof(Elemdata)); + ed->pelem = e; + ed->pblock = b; + ed->rdlist = rd; + return ed; +} + + +/***************** + * Free list of Elemdata's. + */ + +STATIC void elemdatafree(Elemdata **plist) +{ Elemdata *el; + Elemdata *eln; + + for (el = *plist; el; el = eln) + { eln = el->next; + list_free(&el->rdlist); + MEM_BEF_FREE(el); + } + *plist = NULL; +} + +static Elemdata *arraylist = NULL; // list of Elemdata's of OParray elems +static Elemdata *eqeqlist = NULL; // list of Elemdata's of OPeqeq & OPne elems +static Elemdata *rellist = NULL; // list of Elemdata's of relop elems +static Elemdata *inclist = NULL; // list of Elemdata's of increment elems + +enum Rdtype { RDarraybounds, RDconstprop }; +static Rdtype rdtype; + +/*************************** Constant Propagation ***************************/ + + +/************************** + * Constant propagation. + * Also detects use of variable before any possible def. + */ + +void constprop() +{ + rdtype = RDconstprop; + rd_compute(); + intranges(); // compute integer ranges +#if 0 + eqeqranges(); // see if we can eliminate some relationals +#endif + elemdatafree(&eqeqlist); + elemdatafree(&rellist); + elemdatafree(&inclist); +} + +/************************************ + * Compute reaching definitions. + * Note: RD vectors are destroyed by this. + */ + +static block *thisblock; + +STATIC void rd_compute() +{ register unsigned i; + + cmes("constprop()\n"); + assert(dfo); + flowrd(); /* compute reaching definitions (rd) */ + if (deftop == 0) /* if no reaching defs */ + return; + assert(rellist == NULL && inclist == NULL && eqeqlist == NULL && arraylist == NULL); + block_clearvisit(); + for (i = 0; i < dfotop; i++) /* for each block */ + { block *b = dfo[i]; + + switch (b->BC) + { +#if MARS + case BCjcatch: +#endif + case BC_finally: + case BCasm: + case BCcatch: + block_visit(b); + break; + } + } + + for (i = 0; i < dfotop; i++) /* for each block */ + { register block *b; + + b = dfo[i]; + thisblock = b; +#if 0 + dbg_printf("block %d Bin ",i); vec_println(b->Binrd); + dbg_printf(" Bout "); vec_println(b->Boutrd); +#endif + if (b->Bflags & BFLvisited) + continue; // not reliable for this block + if (b->Belem) + { + conpropwalk(b->Belem,b->Binrd); +#ifdef DEBUG + if (!(vec_equal(b->Binrd,b->Boutrd))) + { int j; + + dbg_printf("block %d Binrd ",i); vec_println(b->Binrd); + dbg_printf(" Boutrd "); vec_println(b->Boutrd); + WReqn(b->Belem); + dbg_printf("\n"); + vec_xorass(b->Binrd,b->Boutrd); + j = vec_index(0,b->Binrd); + WReqn(defnod[j].DNelem); + dbg_printf("\n"); + } +#endif + assert(vec_equal(b->Binrd,b->Boutrd)); + } + } +} + +/*************************** + * Support routine for constprop(). + * Visit each elem in order + * If elem is a reference to a variable, and + * all the reaching defs of that variable are + * defining it to be a specific constant, + * Replace reference with that constant. + * Generate warning if no reaching defs for a + * variable, and the variable is on the stack + * or in a register. + * If elem is an assignment or function call or OPasm + * Modify vector of reaching defs. + */ + +STATIC void conpropwalk(elem *n,vec_t IN) +{ register unsigned op; + Elemdata *pdata; + vec_t L,R; + elem *t; + + assert(n && IN); + /*chkvecdim(deftop,0);*/ + //printf("conpropwalk()\n"),elem_print(n); + op = n->Eoper; + if (op == OPcolon || op == OPcolon2) + { L = vec_clone(IN); + conpropwalk(n->E1,L); + conpropwalk(n->E2,IN); + vec_orass(IN,L); /* IN = L | R */ + vec_free(L); + } + else if (op == OPandand || op == OPoror) + { conpropwalk(n->E1,IN); + R = vec_clone(IN); + conpropwalk(n->E2,R); + vec_orass(IN,R); /* IN |= R */ + vec_free(R); + } + else if (OTunary(op)) + goto L3; + else if (ERTOL(n)) + { conpropwalk(n->E2,IN); + L3: + t = n->E1; + if (OTassign(op)) + { + if (t->Eoper == OPvar) + { + // Note that the following ignores OPnegass + if (rdtype == RDconstprop && + OTopeq(op) && sytab[t->EV.sp.Vsym->Sclass] & SCRD) + { elem *e; + list_t rdl; + + rdl = listrds(IN,t,NULL); + if (!(config.flags & CFGnowarning)) // if warnings are enabled + chkrd(t,rdl); + e = chkprop(t,rdl); + if (e) + { // Replace (t op= exp) with (t = e op exp) + + e = el_copytree(e); + e->Ety = t->Ety; + n->E2 = el_bin(opeqtoop(op),n->Ety,e,n->E2); + n->Eoper = OPeq; + } + list_free(&rdl); + } + } + else + conpropwalk(t,IN); + } + else + conpropwalk(t,IN); + } + else if (OTbinary(op)) + { + if (OTassign(op)) + { t = Elvalue(n); + if (t->Eoper != OPvar) + conpropwalk(t,IN); + } + else + conpropwalk(n->E1,IN); + conpropwalk(n->E2,IN); + } + + // Collect data for subsequent optimizations + if (OTbinary(op) && n->E1->Eoper == OPvar && n->E2->Eoper == OPconst) + { + switch (op) + { + case OPlt: + case OPgt: + case OPle: + case OPge: + // Collect compare elems and their rd's in the rellist list + if (rdtype == RDconstprop && + tyintegral(n->E1->Ety) && + tyintegral(n->E2->Ety) + ) + { + //dbg_printf("appending to rellist\n"); elem_print(n); + pdata = Elemdata::ctor(n,thisblock,listrds(IN,n->E1,NULL)); + pdata->next = rellist; + rellist = pdata; + } + break; + + case OPaddass: + case OPminass: + case OPpostinc: + case OPpostdec: + // Collect increment elems and their rd's in the inclist list + if (rdtype == RDconstprop && + tyintegral(n->E1->Ety)) + { + //dbg_printf("appending to inclist\n"); elem_print(n); + pdata = Elemdata::ctor(n,thisblock,listrds(IN,n->E1,NULL)); + pdata->next = inclist; + inclist = pdata; + } + break; + + case OPne: + case OPeqeq: + // Collect compare elems and their rd's in the rellist list + if (rdtype == RDconstprop && + tyintegral(n->E1->Ety)) + { //dbg_printf("appending to eqeqlist\n"); elem_print(n); + pdata = Elemdata::ctor(n,thisblock,listrds(IN,n->E1,NULL)); + pdata->next = eqeqlist; + eqeqlist = pdata; + } + break; + } + } + + + if (OTdef(op)) /* if definition elem */ + updaterd(n,IN,NULL); /* then update IN vector */ + + /* now we get to the part that checks to see if we can */ + /* propagate a constant. */ + if (op == OPvar && sytab[n->EV.sp.Vsym->Sclass] & SCRD) + { list_t rdl; + + //printf("const prop: %s\n", n->EV.sp.Vsym->Sident); + rdl = listrds(IN,n,NULL); + if (rdtype == RDconstprop) + { elem *e; + + if (!(config.flags & CFGnowarning)) // if warnings are enabled + chkrd(n,rdl); + e = chkprop(n,rdl); + if (e) + { tym_t nty; + + nty = n->Ety; + el_copy(n,e); + n->Ety = nty; // retain original type + } + list_free(&rdl); + } + } +} + +/****************************** + * Give error if there are no reaching defs for variable v. + */ + +STATIC void chkrd(elem *n,list_t rdlist) +{ unsigned i; + symbol *sv; + int unambig; + + sv = n->EV.sp.Vsym; + assert(sytab[sv->Sclass] & SCRD); + if (sv->Sflags & SFLnord) // if already printed a warning + return; + if (sv->ty() & mTYvolatile) + return; + unambig = sv->Sflags & SFLunambig; + for (list_t l = rdlist; l; l = list_next(l)) + { elem *d = (elem *) list_ptr(l); + + elem_debug(d); + if (d->Eoper == OPasm) /* OPasm elems ruin everything */ + return; + if (OTassign(d->Eoper)) + { + if (d->E1->Eoper == OPvar) + { if (d->E1->EV.sp.Vsym == sv) + return; + } + else if (!unambig) + return; + } + else + { if (!unambig) + return; + } + } + + // If there are any asm blocks, don't print the message + for (i = 0; i < dfotop; i++) + if (dfo[i]->BC == BCasm) + return; + + // If variable contains bit fields, don't print message (because if + // bit field is the first set, then we get a spurious warning). + // STL uses 0 sized structs to transmit type information, so + // don't complain about them being used before set. + if (type_struct(sv->Stype)) + { + if (sv->Stype->Ttag->Sstruct->Sflags & (STRbitfields | STR0size)) + return; + } +#if SCPP + { Outbuffer buf; + char *p2; + + type_tostring(&buf, sv->Stype); + buf.writeByte(' '); + buf.write(sv->Sident); + p2 = buf.toString(); + warerr(WM_used_b4_set, p2); // variable used before set + } +#endif +#if 1 && MARS + /* Watch out for: + void test() + { + void[0] x; + auto y = x; + } + */ + error(n->Esrcpos.Sfilename, n->Esrcpos.Slinnum, "variable %s used before set", sv->Sident); +#endif + + sv->Sflags |= SFLnord; // no redundant messages +#ifdef DEBUG + //elem_print(n); +#endif +} + +/********************************** + * Look through the vector of reaching defs (IN) to see + * if all defs of n are of the same constant. If so, replace + * n with that constant. + * Bit fields are gross, so don't propagate anything with assignments + * to a bit field. + * Note the flaw in the reaching def vector. There is currently no way + * to detect RDs from when the function is invoked, i.e. RDs for parameters, + * statics and globals. This could be fixed by adding dummy defs for + * them before startblock, but we just kludge it and don't propagate + * stuff for them. + * Returns: + * NULL do not propagate constant + * e constant elem that we should replace n with + */ + +STATIC elem * chkprop(elem *n,list_t rdlist) +{ + elem *foundelem = NULL; + int unambig; + symbol *sv; + tym_t nty; + unsigned nsize; + targ_size_t noff; + list_t l; + + //printf("checkprop: "); WReqn(n); printf("\n"); + assert(n && n->Eoper == OPvar); + elem_debug(n); + sv = n->EV.sp.Vsym; + assert(sytab[sv->Sclass] & SCRD); + nty = n->Ety; + if (!tyscalar(nty)) + goto noprop; + nsize = size(nty); + noff = n->EV.sp.Voffset; + unambig = sv->Sflags & SFLunambig; + for (l = rdlist; l; l = list_next(l)) + { elem *d = (elem *) list_ptr(l); + + elem_debug(d); + + //printf("\trd: "); WReqn(d); printf("\n"); + if (d->Eoper == OPasm) /* OPasm elems ruin everything */ + goto noprop; +#if 0 + // Runs afoul of Buzilla 4506 + if (OTassign(d->Eoper) && EBIN(d)) // if assignment elem +#else + if (OTassign(d->Eoper)) // if assignment elem +#endif + { elem *t = Elvalue(d); + + if (t->Eoper == OPvar) + { assert(t->EV.sp.Vsym == sv); + + if (d->Eoper == OPstreq || + !tyscalar(t->Ety)) + goto noprop; // not worth bothering with these cases + + if (d->Eoper == OPnegass) + goto noprop; // don't bother with this case, either + + /* Everything must match or we must skip this variable */ + /* (in case of assigning to overlapping unions, etc.) */ + if (t->EV.sp.Voffset != noff || + /* If sizes match, we are ok */ + size(t->Ety) != nsize && + !(d->E2->Eoper == OPconst && size(t->Ety) > nsize && !tyfloating(d->E2->Ety))) + goto noprop; + } + else + { if (unambig) /* unambiguous assignments only */ + continue; + goto noprop; + } + if (d->Eoper != OPeq) + goto noprop; + } + else /* must be a call elem */ + { + if (unambig) + continue; + else + goto noprop; /* could be affected */ + } + + if (d->E2->Eoper == OPconst || d->E2->Eoper == OPrelconst) + { + if (foundelem) /* already found one */ + { /* then they must be the same */ + if (!el_match(foundelem,d->E2)) + goto noprop; + } + else /* else this is it */ + foundelem = d->E2; + } + else + goto noprop; + } + + if (foundelem) /* if we got one */ + { /* replace n with foundelem */ +#ifdef DEBUG + if (debugc) + { dbg_printf("const prop ("); + WReqn(n); + dbg_printf(" replaced by "); + WReqn(foundelem); + dbg_printf("), %p to %p\n",foundelem,n); + } +#endif + changes++; + return foundelem; + } +noprop: + return NULL; +} + +/*********************************** + * Find all the reaching defs of OPvar e. + * Put into a linked list, or just set the RD bits in a vector. + * + */ + +list_t listrds(vec_t IN,elem *e,vec_t f) +{ list_t rdlist; + unsigned i; + unsigned unambig; + Symbol *s; + unsigned nsize; + targ_size_t noff; + tym_t ty; + + //printf("listrds: "); WReqn(e); printf("\n"); + assert(IN); + rdlist = NULL; + assert(e->Eoper == OPvar); + s = e->EV.sp.Vsym; + ty = e->Ety; + if (tyscalar(ty)) + nsize = size(ty); + noff = e->EV.sp.Voffset; + unambig = s->Sflags & SFLunambig; + if (f) + vec_clear(f); + foreach (i, deftop, IN) + { elem *d; + unsigned op; + + d = defnod[i].DNelem; + //dbg_printf("\tlooking at "); WReqn(d); dbg_printf("\n"); + op = d->Eoper; + if (op == OPasm) // assume ASM elems define everything + goto listit; + if (OTassign(op)) + { elem *t = Elvalue(d); + + if (t->Eoper == OPvar && t->EV.sp.Vsym == s) + { if (op == OPstreq) + goto listit; + if (!tyscalar(ty) || !tyscalar(t->Ety)) + goto listit; + // If t does not overlap e, then it doesn't affect things + if (noff + nsize > t->EV.sp.Voffset && + t->EV.sp.Voffset + size(t->Ety) > noff) + goto listit; // it's an assignment to s + } + else if (t->Eoper != OPvar && !unambig) + goto listit; /* assignment through pointer */ + } + else if (!unambig) + goto listit; /* probably a function call */ + continue; + + listit: + //dbg_printf("\tlisting "); WReqn(d); dbg_printf("\n"); + if (f) + vec_setbit(i,f); + else + list_append(&rdlist,d); // add the definition node + } + return rdlist; +} + +/******************************************** + * Look at reaching defs for expressions of the form (v == c) and (v != c). + * If all definitions of v are c or are not c, then we can replace the + * expression with 1 or 0. + */ + +STATIC void eqeqranges() +{ Elemdata *rel; + list_t rdl; + Symbol *v; + int sz; + elem *e; + targ_llong c; + int result; + + for (rel = eqeqlist; rel; rel = rel->next) + { + e = rel->pelem; + v = e->E1->EV.sp.Vsym; + if (!(sytab[v->Sclass] & SCRD)) + continue; + sz = tysize(e->E1->Ety); + c = el_tolong(e->E2); + + result = -1; // result not known yet + for (rdl = rel->rdlist; rdl; rdl = list_next(rdl)) + { elem *erd = (elem *) list_ptr(rdl); + elem *erd1; + int szrd; + int tmp; + + elem_debug(erd); + if (erd->Eoper != OPeq || + (erd1 = erd->E1)->Eoper != OPvar || + erd->E2->Eoper != OPconst + ) + goto L1; + szrd = tysize(erd1->Ety); + if (erd1->EV.sp.Voffset + szrd <= e->E1->EV.sp.Voffset || + e->E1->EV.sp.Voffset + sz <= erd1->EV.sp.Voffset) + continue; // doesn't affect us, skip it + if (szrd != sz || e->E1->EV.sp.Voffset != erd1->EV.sp.Voffset) + goto L1; // overlapping - forget it + + tmp = (c == el_tolong(erd->E2)); + if (result == -1) + result = tmp; + else if (result != tmp) + goto L1; + } + if (result >= 0) + { + //printf("replacing with %d\n",result); + el_free(e->E1); + el_free(e->E2); + e->EV.Vint = (e->Eoper == OPeqeq) ? result : result ^ 1; + e->Eoper = OPconst; + } + L1: ; + } +} + +/****************************** + * Examine rellist and inclist to determine if any of the signed compare + * elems in rellist can be replace by unsigned compares. + * rellist is list of relationals in function. + * inclist is list of increment elems in function. + */ + +STATIC void intranges() +{ Elemdata *rel,*iel; + block *rb,*ib; + symbol *v; + elem *rdeq,*rdinc; + unsigned incop,relatop; + targ_int initial,increment,final; + + cmes("intranges()\n"); + for (rel = rellist; rel; rel = rel->next) + { + rb = rel->pblock; + //dbg_printf("rel->pelem: "); WReqn(rel->pelem); dbg_printf("\n"); + assert(rel->pelem->E1->Eoper == OPvar); + v = rel->pelem->E1->EV.sp.Vsym; + + // RD info is only reliable for registers and autos + if (!(sytab[v->Sclass] & SCRD)) + continue; + + /* Look for two rd's: an = and an increment */ + if (list_nitems(rel->rdlist) != 2) + continue; + rdeq = list_elem(list_next(rel->rdlist)); + if (rdeq->Eoper != OPeq) + { rdinc = rdeq; + rdeq = list_elem(rel->rdlist); + if (rdeq->Eoper != OPeq) + continue; + } + else + rdinc = list_elem(rel->rdlist); +#if 0 + printf("\neq: "); WReqn(rdeq); printf("\n"); + printf("rel: "); WReqn(rel->pelem); printf("\n"); + printf("inc: "); WReqn(rdinc); printf("\n"); +#endif + incop = rdinc->Eoper; + if (!OTpost(incop) && incop != OPaddass && incop != OPminass) + continue; + + /* lvalues should be unambiguous defs */ + if (rdeq->E1->Eoper != OPvar || rdinc->E1->Eoper != OPvar) + continue; + /* rvalues should be constants */ + if (rdeq->E2->Eoper != OPconst || rdinc->E2->Eoper != OPconst) + continue; + + /* Ensure that the only defs reaching the increment elem (rdinc) */ + /* are rdeq and rdinc. */ + for (iel = inclist; TRUE; iel = iel->next) + { elem *rd1,*rd2; + + if (!iel) + goto nextrel; + ib = iel->pblock; + if (iel->pelem != rdinc) + continue; /* not our increment elem */ + if (list_nitems(iel->rdlist) != 2) + { //printf("!= 2\n"); + goto nextrel; + } + rd1 = list_elem(iel->rdlist); + rd2 = list_elem(list_next(iel->rdlist)); + /* The rd's for the relational elem (rdeq,rdinc) must be */ + /* the same as the rd's for tne increment elem (rd1,rd2). */ + if (rd1 == rdeq && rd2 == rdinc || rd1 == rdinc && rd2 == rdeq) + break; + } + + // Check that all paths from rdinc to rdinc must pass through rdrel + { int i; + + // ib: block of increment + // rb: block of relational + i = loopcheck(ib,ib,rb); + block_clearvisit(); + if (i) + continue; + } + + /* Gather initial, increment, and final values for loop */ + initial = el_tolong(rdeq->E2); + increment = el_tolong(rdinc->E2); + if (incop == OPpostdec || incop == OPminass) + increment = -increment; + relatop = rel->pelem->Eoper; + final = el_tolong(rel->pelem->E2); +// dbg_printf("initial = %d, increment = %d, final = %d\n", +// initial,increment,final); + + /* Determine if we can make the relational an unsigned */ + if (initial >= 0) + { if (final >= initial) + { if (increment > 0 && ((final - initial) % increment) == 0) + goto makeuns; + } + else if (final >= 0) + { /* 0 <= final < initial */ + if (increment < 0 && ((final - initial) % increment) == 0 && + !(final + increment < 0 && + (relatop == OPge || relatop == OPlt) + ) + ) + { + makeuns: + if (!tyuns(rel->pelem->E2->Ety)) + { + rel->pelem->E2->Ety = touns(rel->pelem->E2->Ety); + rel->pelem->Nflags |= NFLtouns; +#ifdef DEBUG + if (debugc) + { WReqn(rel->pelem); + dbg_printf(" made unsigned, initial = %ld, increment = %ld,\ + final = %ld\n",(long int)initial,(long int)increment,(long int)final); + } +#endif + changes++; + } +#if 0 + // Eliminate loop if it is empty + if (relatop == OPlt && + rb->BC == BCiftrue && + list_block(rb->Bsucc) == rb && + rb->Belem->Eoper == OPcomma && + rb->Belem->E1 == rdinc && + rb->Belem->E2 == rel->pelem + ) + { + rel->pelem->Eoper = OPeq; + rel->pelem->Ety = rel->pelem->E1->Ety; + rb->BC = BCgoto; + list_subtract(&rb->Bsucc,rb); + list_subtract(&rb->Bpred,rb); +#ifdef DEBUG + if (debugc) + { WReqn(rel->pelem); + dbg_printf(" eliminated loop\n"); + } +#endif + changes++; + } +#endif + } + } + } + + nextrel: + ; + } +} + +/*********************** + * Return TRUE if there is a path from start to inc without + * passing through rel. + */ + +STATIC int loopcheck(block *start,block *inc,block *rel) +{ list_t list; + block *b; + +#if __ZTC__ + _chkstack(); /* always check stack on recursive routines */ +#endif + if (!(start->Bflags & BFLvisited)) + { start->Bflags |= BFLvisited; /* guarantee eventual termination */ + for (list = start->Bsucc; list; list = list_next(list)) + { b = (block *) list_ptr(list); + if (b != rel && (b == inc || loopcheck(b,inc,rel))) + return TRUE; + } + } + return FALSE; +} + +/**************************** + * Do copy propagation. + * Copy propagation elems are of the form OPvar=OPvar, and they are + * in expnod[]. + */ + +static int recalc; + +void copyprop() +{ register unsigned i; + + out_regcand(&globsym); + cmes("copyprop()\n"); + assert(dfo); +Lagain: + flowcp(); /* compute available copy statements */ + if (exptop <= 1) + return; /* none available */ +#if 0 + for (i = 1; i < exptop; i++) + { dbg_printf("expnod[%d] = (",i); + WReqn(expnod[i]); + dbg_printf(");\n"); + } +#endif + recalc = 0; + for (i = 0; i < dfotop; i++) /* for each block */ + { register block *b; + + b = dfo[i]; + if (b->Belem) + { +#if 0 + dbg_printf("B%d, elem (",i); + WReqn(b->Belem); dbg_printf(")\nBin "); + vec_println(b->Bin); + cpwalk(b->Belem,b->Bin); + dbg_printf("Bino "); + vec_println(b->Bin); + dbg_printf("Bout "); + vec_println(b->Bout); +#else + cpwalk(b->Belem,b->Bin); +#endif + /*assert(vec_equal(b->Bin,b->Bout)); */ + /* The previous assert() is correct except */ + /* for the following case: */ + /* a=b; d=a; a=b; */ + /* The vectors don't match because the */ + /* equations changed to: */ + /* a=b; d=b; a=b; */ + /* and the d=b copy elem now reaches the end */ + /* of the block (the d=a elem didn't). */ + } + if (recalc) + goto Lagain; + } +} + +/***************************** + * Walk tree n, doing copy propagation as we go. + * Keep IN up to date. + */ + +STATIC void cpwalk(register elem *n,vec_t IN) +{ register unsigned op,i; + register elem *t; + vec_t L; + + static int nocp; + + assert(n && IN); + /*chkvecdim(exptop,0);*/ + if (recalc) + return; + op = n->Eoper; + if (op == OPcolon || op == OPcolon2) + { + L = vec_clone(IN); + cpwalk(n->E1,L); + cpwalk(n->E2,IN); + vec_andass(IN,L); // IN = L & R + vec_free(L); + } + else if (op == OPandand || op == OPoror) + { cpwalk(n->E1,IN); + L = vec_clone(IN); + cpwalk(n->E2,L); + vec_andass(IN,L); // IN = L & R + vec_free(L); + } + else if (OTunary(op)) + { + t = n->E1; + if (OTassign(op)) + { if (t->Eoper == OPind) + cpwalk(t->E1,IN); + } + else if (op == OPctor || op == OPdtor) + { + /* This kludge is necessary because in except_pop() + * an el_match is done on the lvalue. If copy propagation + * changes the OPctor but not the corresponding OPdtor, + * then the match won't happen and except_pop() + * will fail. + */ + nocp++; + cpwalk(t,IN); + nocp--; + } + else + cpwalk(t,IN); + } + else if (OTassign(op)) + { cpwalk(n->E2,IN); + t = Elvalue(n); + if (t->Eoper == OPind) + cpwalk(t,IN); + else + { +#ifdef DEBUG + if (t->Eoper != OPvar) elem_print(n); +#endif + assert(t->Eoper == OPvar); + } + } + else if (ERTOL(n)) + { cpwalk(n->E2,IN); + cpwalk(n->E1,IN); + } + else if (OTbinary(op)) + { + cpwalk(n->E1,IN); + cpwalk(n->E2,IN); + } + + if (OTdef(op)) // if definition elem + { int ambig; /* TRUE if ambiguous def */ + + ambig = !OTassign(op) || t->Eoper == OPind; + foreach (i,exptop,IN) /* for each active copy elem */ + { symbol *v; + + if (op == OPasm) + goto clr; + + /* If this elem could kill the lvalue or the rvalue, */ + /* Clear bit in IN. */ + v = expnod[i]->E1->EV.sp.Vsym; + if (ambig) + { if (!(v->Sflags & SFLunambig)) + goto clr; + } + else + { if (v == t->EV.sp.Vsym) + goto clr; + } + v = expnod[i]->E2->EV.sp.Vsym; + if (ambig) + { if (!(v->Sflags & SFLunambig)) + goto clr; + } + else + { if (v == t->EV.sp.Vsym) + goto clr; + } + continue; + + clr: /* this copy elem is not available */ + vec_clearbit(i,IN); /* so remove it from the vector */ + } /* foreach */ + + /* If this is a copy elem in expnod[] */ + /* Set bit in IN. */ + if (op == OPeq && n->E1->Eoper == OPvar && + n->E2->Eoper == OPvar && n->Eexp) + vec_setbit(n->Eexp,IN); + } + else if (op == OPvar && !nocp) // if reference to variable v + { symbol *v = n->EV.sp.Vsym; + symbol *f; + elem *foundelem = NULL; + unsigned sz; + tym_t ty; + + //dbg_printf("Checking copyprop for '%s', ty=x%x\n",v->Sident,n->Ety); + symbol_debug(v); + ty = n->Ety; + sz = tysize(n->Ety); + foreach(i,exptop,IN) /* for all active copy elems */ + { elem *c; + + c = expnod[i]; + assert(c); + + //dbg_printf("looking at: ("); WReqn(c); dbg_printf("), ty=x%x\n",c->E1->Ety); + /* Not only must symbol numbers match, but */ + /* offsets too (in case of arrays) and sizes */ + /* (in case of unions). */ + if (v == c->E1->EV.sp.Vsym && + n->EV.sp.Voffset == c->E1->EV.sp.Voffset && + sz <= tysize(c->E1->Ety)) + { if (foundelem) + { if (c->E2->EV.sp.Vsym != f) + goto noprop; + } + else + { foundelem = c; + f = foundelem->E2->EV.sp.Vsym; + } + } + } + if (foundelem) /* if we can do the copy prop */ + { + cmes3("Copyprop, from '%s' to '%s'\n", + (v->Sident) ? (char *)v->Sident : "temp", + (f->Sident) ? (char *)f->Sident : "temp"); + el_copy(n,foundelem->E2); + n->Ety = ty; // retain original type + changes++; + + // Mark ones we can no longer use + foreach(i,exptop,IN) + { + if (f == expnod[i]->E2->EV.sp.Vsym) + { recalc++; + break; + } + } + } + //else dbg_printf("not found\n"); + noprop: + ; + } +} + +/******************************** + * Remove dead assignments. Those are assignments to a variable v + * for which there are no subsequent uses of v. + */ + +static unsigned asstop, /* # of assignment elems in assnod[] */ + assmax = 0, /* size of assnod[] */ + assnum; /* current position in assnod[] */ +static elem **assnod = NULL; /* array of pointers to asg elems */ +static vec_t ambigref; /* vector of assignment elems that */ + /* are referenced when an ambiguous */ + /* reference is done (as in *p or call) */ + +void rmdeadass() +{ register unsigned i,j; + vec_t DEAD,POSS; + + cmes("rmdeadass()\n"); + flowlv(); /* compute live variables */ + for (i = 0; i < dfotop; i++) /* for each block b */ + { register block *b = dfo[i]; + + if (!b->Belem) /* if no elems at all */ + continue; + if (b->Btry) // if in try-block guarded body + continue; + asstop = numasg(b->Belem); /* # of assignment elems */ + if (asstop == 0) /* if no assignment elems */ + continue; + if (asstop > assmax) /* if we need to reallocate */ + { assnod = (elem **) + util_realloc(assnod,sizeof(elem *),asstop); + assmax = asstop; + } + /*setvecdim(asstop);*/ + DEAD = vec_calloc(asstop); + POSS = vec_calloc(asstop); + ambigref = vec_calloc(asstop); + assnum = 0; + accumda(b->Belem,DEAD,POSS); /* compute DEAD and POSS */ + assert(assnum == asstop); + vec_free(ambigref); + vec_orass(POSS,DEAD); /* POSS |= DEAD */ + foreach (j,asstop,POSS) /* for each possible dead asg. */ + { symbol *v; /* v = target of assignment */ + register elem *n,*nv; + + n = assnod[j]; + nv = Elvalue(n); + v = nv->EV.sp.Vsym; + if (!symbol_isintab(v)) // not considered + continue; +//printf("assnod[%d]: ",j); WReqn(n); printf("\n"); +//printf("\tPOSS\n"); + /* If not positively dead but v is live on a */ + /* successor to b, then v is live. */ +//printf("\tDEAD=%d, live=%d\n",vec_testbit(j,DEAD),vec_testbit(v->Ssymnum,b->Boutlv)); + if (!vec_testbit(j,DEAD) && vec_testbit(v->Ssymnum,b->Boutlv)) + continue; + /* volatile variables are not dead */ + if ((v->ty() | nv->Ety) & mTYvolatile) + continue; +#ifdef DEBUG + if (debugc) + { dbg_printf("dead assignment ("); + WReqn(n); + if (vec_testbit(j,DEAD)) + dbg_printf(") DEAD\n"); + else + dbg_printf(") Boutlv\n"); + } +#endif + elimass(n); + changes++; + } /* foreach */ + vec_free(DEAD); + vec_free(POSS); + } /* for */ + util_free(assnod); + assnod = NULL; + assmax = 0; +} + +/*************************** + * Remove side effect of assignment elem. + */ + +void elimass(elem *n) +{ elem *e1; + + switch (n->Eoper) + { case OPeq: + case OPstreq: + /* (V=e) => (random constant,e) */ + /* Watch out for (a=b=c) stuff! */ + /* Don't screw up assnod[]. */ + n->Eoper = OPcomma; + n->Ety |= n->E2->Ety & (mTYconst | mTYvolatile | mTYimmutable | mTYshared +#if TARGET_SEGMENTED + | mTYfar +#endif + ); + n->E1->Eoper = OPconst; + break; + /* Convert (V op= e) to (V op e) */ + case OPaddass: + case OPminass: + case OPmulass: + case OPdivass: + case OPorass: + case OPandass: + case OPxorass: + case OPmodass: + case OPshlass: + case OPshrass: + case OPashrass: + n->Eoper = opeqtoop(n->Eoper); + break; + case OPpostinc: /* (V i++ c) => V */ + case OPpostdec: /* (V i-- c) => V */ + e1 = n->E1; + el_free(n->E2); + el_copy(n,e1); + el_free(e1); + break; + case OPnegass: + n->Eoper = OPneg; + break; + case OPbtc: + case OPbtr: + case OPbts: + n->Eoper = OPbt; + break; + default: + assert(0); + } +} + +/************************ + * Compute number of =,op=,i++,i--,--i,++i elems. + * (Unambiguous assignments only. Ambiguous ones would always be live.) + * Some compilers generate better code for ?: than if-then-else. + */ + +STATIC unsigned numasg(elem *e) +{ + assert(e); + if (OTassign(e->Eoper) && e->E1->Eoper == OPvar) + { e->Nflags |= NFLassign; + return 1 + numasg(e->E1) + (OTbinary(e->Eoper) ? numasg(e->E2) : 0); + } + e->Nflags &= ~NFLassign; + return OTunary(e->Eoper) ? numasg(e->E1) : + OTbinary(e->Eoper) ? numasg(e->E1) + numasg(e->E2) : 0; +} + +/****************************** + * Tree walk routine for rmdeadass(). + * DEAD = assignments which are dead + * POSS = assignments which are possibly dead + * The algorithm is basically: + * if we have an assignment to v, + * for all defs of v in POSS + * set corresponding bits in DEAD + * set bit for this def in POSS + * if we have a reference to v, + * clear all bits in POSS that are refs of v + */ + +STATIC void accumda(elem *n,vec_t DEAD, vec_t POSS) +{ vec_t Pl,Pr,Dl,Dr; + register unsigned i,op,vecdim; + + /*chkvecdim(asstop,0);*/ + assert(n && DEAD && POSS); + op = n->Eoper; + switch (op) + { case OPcolon: + case OPcolon2: + Pl = vec_clone(POSS); + Pr = vec_clone(POSS); + Dl = vec_calloc(asstop); + Dr = vec_calloc(asstop); + accumda(n->E1,Dl,Pl); + accumda(n->E2,Dr,Pr); + + /* D |= P & (Dl & Dr) | ~P & (Dl | Dr) */ + /* P = P & (Pl & Pr) | ~P & (Pl | Pr) */ + /* = Pl & Pr | ~P & (Pl | Pr) */ + vecdim = vec_dim(DEAD); + for (i = 0; i < vecdim; i++) +#if MPW + { + unsigned tmp1,tmp2; + tmp1 = POSS[i] & Dl[i] & Dr[i] ; + tmp2 = ~POSS[i] & (Dl[i] | Dr[i]); + DEAD[i] |= tmp1 | tmp2; + tmp1 = Pl[i] & Pr[i]; + tmp2 = ~POSS[i] & (Pl[i] | Pr[i]); + POSS[i] = tmp1 | tmp2; + } +#else + { DEAD[i] |= POSS[i] & Dl[i] & Dr[i] | + ~POSS[i] & (Dl[i] | Dr[i]); + POSS[i] = Pl[i] & Pr[i] | ~POSS[i] & (Pl[i] | Pr[i]); + } +#endif + vec_free(Pl); vec_free(Pr); vec_free(Dl); vec_free(Dr); + break; + + case OPandand: + case OPoror: + accumda(n->E1,DEAD,POSS); + // Substituting into the above equations Pl=P and Dl=0: + // D |= Dr - P + // P = Pr + Pr = vec_clone(POSS); + Dr = vec_calloc(asstop); + accumda(n->E2,Dr,Pr); + vec_subass(Dr,POSS); + vec_orass(DEAD,Dr); + vec_copy(POSS,Pr); + vec_free(Pr); vec_free(Dr); + break; + + case OPvar: + { symbol *v = n->EV.sp.Vsym; + targ_size_t voff = n->EV.sp.Voffset; + unsigned vsize = tysize(n->Ety); + + // We have a reference. Clear all bits in POSS that + // could be referenced. + + for (i = 0; i < assnum; i++) + { register elem *ti; + + ti = Elvalue(assnod[i]); + if (v == ti->EV.sp.Vsym && + // If symbol references overlap + voff + vsize > ti->EV.sp.Voffset && + ti->EV.sp.Voffset + tysize(ti->Ety) > voff + ) + vec_clearbit(i,POSS); + } + break; + } + + case OPasm: // reference everything + for (i = 0; i < assnum; i++) + vec_clearbit(i,POSS); + break; + + case OPind: + case OPucall: + case OPucallns: +#if !TX86 + case OPvp_fp: +#endif + accumda(n->E1,DEAD,POSS); + vec_subass(POSS,ambigref); // remove possibly refed + // assignments from list + // of possibly dead ones + break; + + case OPconst: + break; + + case OPcall: + case OPcallns: + case OPmemcpy: + case OPstrcpy: + case OPmemset: + accumda(n->E2,DEAD,POSS); + case OPstrlen: + accumda(n->E1,DEAD,POSS); + vec_subass(POSS,ambigref); // remove possibly refed + // assignments from list + // of possibly dead ones + break; + + case OPnewarray: + case OPmultinewarray: + case OParray: + case OPfield: + case OPstrcat: + case OPstrcmp: + case OPmemcmp: + accumda(n->E1,DEAD,POSS); + accumda(n->E2,DEAD,POSS); + vec_subass(POSS,ambigref); // remove possibly refed + // assignments from list + // of possibly dead ones + break; + + default: + if (OTassign(op)) + { elem *t; + + if (ERTOL(n)) + accumda(n->E2,DEAD,POSS); + t = Elvalue(n); + // if not (v = expression) then gen refs of left tree + if (op != OPeq) + accumda(n->E1,DEAD,POSS); + else if (OTunary(t->Eoper)) // if (*e = expression) + accumda(t->E1,DEAD,POSS); + else if (OTbinary(t->Eoper)) + { accumda(t->E1,DEAD,POSS); + accumda(t->E2,DEAD,POSS); + } + if (!ERTOL(n) && op != OPnegass) + accumda(n->E2,DEAD,POSS); + + // if unambiguous assignment, post all possibilities + // to DEAD + if (op == OPeq && t->Eoper == OPvar) + { + for (i = 0; i < assnum; i++) + { register elem *ti; + + ti = Elvalue(assnod[i]); + // There may be some problem with this next + // statement with unions. + if (ti->EV.sp.Vsym == t->EV.sp.Vsym && + ti->EV.sp.Voffset == t->EV.sp.Voffset && + tysize(ti->Ety) == tysize(t->Ety) && + !(t->Ety & mTYvolatile) && + //t->EV.sp.Vsym->Sflags & SFLunambig && + vec_testbit(i,POSS)) + vec_setbit(i,DEAD); + } + } + + // if assignment operator, post this def to POSS + if (n->Nflags & NFLassign) + { assnod[assnum] = n; + vec_setbit(assnum,POSS); + + // if variable could be referenced by a pointer + // or a function call, mark the assignment in + // ambigref + if (!(t->EV.sp.Vsym->Sflags & SFLunambig)) + { vec_setbit(assnum,ambigref); +#if DEBUG + if (debugc) + { dbg_printf("ambiguous lvalue: "); + WReqn(n); + dbg_printf("\n"); + } +#endif + } + + assnum++; + } + } + else if (OTrtol(op)) + { accumda(n->E2,DEAD,POSS); + accumda(n->E1,DEAD,POSS); + } + else if (OTbinary(op)) + { accumda(n->E1,DEAD,POSS); + accumda(n->E2,DEAD,POSS); + } + else if (OTunary(op)) + accumda(n->E1,DEAD,POSS); + break; + } +} + +/*************************** + * Mark all dead variables. Only worry about register candidates. + * Compute live ranges for register candidates. + * Be careful not to compute live ranges for members of structures (CLMOS). + */ + +void deadvar() +{ SYMIDX i; + + assert(dfo); + + /* First, mark each candidate as dead. */ + /* Initialize vectors for live ranges. */ + /*setvecdim(dfotop);*/ + for (i = 0; i < globsym.top; i++) + { symbol *s = globsym.tab[i]; + + if (s->Sflags & SFLunambig) + { + s->Sflags |= SFLdead; + if (s->Sflags & GTregcand) + { if (s->Srange) + vec_clear(s->Srange); + else + s->Srange = vec_calloc(maxblks); + } + } + } + + /* Go through trees and "liven" each one we see. */ + for (i = 0; i < dfotop; i++) + if (dfo[i]->Belem) + dvwalk(dfo[i]->Belem,i); + + /* Compute live variables. Set bit for block in live range */ + /* if variable is in the IN set for that block. */ + flowlv(); /* compute live variables */ + for (i = 0; i < globsym.top; i++) + { register unsigned j; + + if (globsym.tab[i]->Srange /*&& globsym.tab[i]->Sclass != CLMOS*/) + for (j = 0; j < dfotop; j++) + if (vec_testbit(i,dfo[j]->Binlv)) + vec_setbit(j,globsym.tab[i]->Srange); + } + + /* Print results */ + for (i = 0; i < globsym.top; i++) + { register char *p; + symbol *s = globsym.tab[i]; + + if (s->Sflags & SFLdead && s->Sclass != SCparameter && s->Sclass != SCregpar) + s->Sflags &= ~GTregcand; // do not put dead variables in registers +#ifdef DEBUG + p = (char *) s->Sident ; + if (s->Sflags & SFLdead) + cmes3("Symbol %d '%s' is dead\n",i,p); + if (debugc && s->Srange /*&& s->Sclass != CLMOS*/) + { dbg_printf("Live range for %d '%s': ",i,p); + vec_println(s->Srange); + } +#endif + } +} + +/***************************** + * Tree walk support routine for deadvar(). + * Input: + * n = elem to look at + * i = block index + */ + +STATIC void dvwalk(register elem *n,register unsigned i) +{ + for (; TRUE; n = n->E1) + { assert(n); + if (n->Eoper == OPvar || n->Eoper == OPrelconst) + { register symbol *s = n->EV.sp.Vsym; + + s->Sflags &= ~SFLdead; + if (s->Srange) + vec_setbit(i,s->Srange); + } + else if (!OTleaf(n->Eoper)) + { if (OTbinary(n->Eoper)) + dvwalk(n->E2,i); + continue; + } + break; + } +} + +/********************************* + * Optimize very busy expressions (VBEs). + */ + +static vec_t blockseen; /* which blocks we have visited */ + +void verybusyexp() +{ elem **pn; + register int i; + register unsigned j,k,l; + + cmes("verybusyexp()\n"); + flowvbe(); /* compute VBEs */ + if (exptop <= 1) return; /* if no VBEs */ + assert(expblk); + if (blockinit()) + return; // can't handle ASM blocks + compdom(); /* compute dominators */ + /*setvecdim(exptop);*/ + genkillae(); /* compute Bgen and Bkill for */ + /* AEs */ + /*chkvecdim(exptop,0);*/ + blockseen = vec_calloc(dfotop); + + /* Go backwards through dfo so that VBEs are evaluated as */ + /* close as possible to where they are used. */ + for (i = dfotop; --i >= 0;) /* for each block */ + { register block *b = dfo[i]; + register int done; + + /* Do not hoist things to blocks that do not */ + /* divide the flow of control. */ + + switch (b->BC) + { case BCiftrue: + case BCswitch: + break; + default: + continue; + } + + /* Find pointer to last statement in current elem */ + pn = &(b->Belem); + if (*pn) + { while ((*pn)->Eoper == OPcomma) + pn = &((*pn)->E2); + /* If last statement has side effects, */ + /* don't do these VBEs. Potentially we */ + /* could by assigning the result to */ + /* a temporary, and rewriting the tree */ + /* from (n) to (T=n,T) and installing */ + /* the VBE as (T=n,VBE,T). This */ + /* may not buy us very much, so we will */ + /* just skip it for now. */ + /*if (sideeffect(*pn))*/ + if (!(*pn)->Eexp) + continue; + } + + /* Eliminate all elems that have already been */ + /* hoisted (indicated by expnod[] == 0). */ + /* Constants are not useful as VBEs. */ + /* Eliminate all elems from Bout that are not in blocks */ + /* that are dominated by b. */ +#if 0 + dbg_printf("block %d Bout = ",i); + vec_println(b->Bout); +#endif + done = TRUE; + foreach (j,exptop,b->Bout) + { if (expnod[j] == 0 || + !!OTleaf(expnod[j]->Eoper) || + !dom(b,expblk[j])) + vec_clearbit(j,b->Bout); + else + done = FALSE; + } + if (done) continue; + + /* Eliminate from Bout all elems that are killed by */ + /* a block between b and that elem. */ +#if 0 + dbg_printf("block %d Bout = ",i); + vec_println(b->Bout); +#endif + + foreach (j,exptop,b->Bout) + { register list_t bl; + + vec_clear(blockseen); + for (bl = expblk[j]->Bpred; bl; bl = list_next(bl)) + { if (killed(j,list_block(bl),b)) + { vec_clearbit(j,b->Bout); + break; + } + } + } + + /* For each elem still left, make sure that there */ + /* exists a path from b to j along which there is */ + /* no other use of j (else it would be a CSE, and */ + /* it would be a waste of time to hoist it). */ +#if 0 + dbg_printf("block %d Bout = ",i); + vec_println(b->Bout); +#endif + + foreach (j,exptop,b->Bout) + { register list_t bl; + + vec_clear(blockseen); + for (bl = expblk[j]->Bpred; bl; bl = list_next(bl)) + { if (ispath(j,list_block(bl),b)) + goto L2; + } + vec_clearbit(j,b->Bout); /* thar ain't no path */ + L2: ; + } + + + /* For each elem that appears more than once in Bout */ + /* We have a VBE. */ +#if 0 + dbg_printf("block %d Bout = ",i); + vec_println(b->Bout); +#endif + + foreach (j,exptop,b->Bout) + { + for (k = j + 1; k < exptop; k++) + { if (vec_testbit(k,b->Bout) && + el_match(expnod[j],expnod[k])) + goto foundvbe; + } + continue; /* no VBE here */ + + foundvbe: /* we got one */ +#ifdef DEBUG + if (debugc) + { dbg_printf("VBE %d,%d, block %d (",j,k,i); + WReqn(expnod[j]); + dbg_printf(");\n"); + } +#endif + *pn = el_bin(OPcomma,(*pn)->Ety, + el_copytree(expnod[j]),*pn); + + /* Mark all the vbe elems found but one (the */ + /* expnod[j] one) so that the expression will */ + /* only be hoisted again if other occurrances */ + /* of the expression are found later. This */ + /* will substitute for the fact that the */ + /* el_copytree() expression does not appear in expnod[]. */ + l = k; + do + { if ( k == l || (vec_testbit(k,b->Bout) && + el_match(expnod[j],expnod[k]))) + { + /* Fix so nobody else will */ + /* vbe this elem */ + expnod[k] = NULL; + vec_clearbit(k,b->Bout); + } + } while (++k < exptop); + changes++; + } /* foreach */ + } /* for */ + vec_free(blockseen); +} + +/**************************** + * Return TRUE if elem j is killed somewhere + * between b and bp. + */ + +STATIC int killed(register unsigned j,register block *bp,block *b) +{ register list_t bl; + + if (bp == b || vec_testbit(bp->Bdfoidx,blockseen)) + return FALSE; + if (vec_testbit(j,bp->Bkill)) + return TRUE; + vec_setbit(bp->Bdfoidx,blockseen); /* mark as visited */ + for (bl = bp->Bpred; bl; bl = list_next(bl)) + if (killed(j,list_block(bl),b)) + return TRUE; + return FALSE; +} + +/*************************** + * Return TRUE if there is a path from b to bp along which + * elem j is not used. + * Input: + * b -> block where we want to put the VBE + * bp -> block somewhere between b and block containing j + * j = VBE expression elem candidate (index into expnod[]) + */ + +STATIC int ispath(register unsigned j,register block *bp,block *b) +{ register list_t bl; + register unsigned i; + + /*chkvecdim(exptop,0);*/ + if (bp == b) return TRUE; /* the trivial case */ + if (vec_testbit(bp->Bdfoidx,blockseen)) + return FALSE; /* already seen this block */ + vec_setbit(bp->Bdfoidx,blockseen); /* we've visited this block */ + + /* FALSE if elem j is used in block bp (and reaches the end */ + /* of bp, indicated by it being an AE in Bgen) */ + foreach (i,exptop,bp->Bgen) /* look thru used expressions */ + { if (i != j && expnod[i] && el_match(expnod[i],expnod[j])) + return FALSE; + } + + /* Not used in bp, see if there is a path through a predecessor */ + /* of bp */ + for (bl = bp->Bpred; bl; bl = list_next(bl)) + if (ispath(j,list_block(bl),b)) + return TRUE; + + return FALSE; /* j is used along all paths */ +} + +#endif diff --git a/backend/html.c b/backend/html.c new file mode 100644 index 00000000..4e26303f --- /dev/null +++ b/backend/html.c @@ -0,0 +1,771 @@ + +// Copyright (c) 1999-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gpl.txt. +// See the included readme.txt for details. + + +/* HTML parser + */ + +#include +#include +#include +#include +#include +#include + +#include "html.h" + +#if MARS +#include +#include "root.h" +//#include "../mars/mars.h" +#else +#include "outbuf.h" +#include "msgs2.h" + +extern void html_err(const char *, unsigned, unsigned, ...); + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" +#endif + +#if __GNUC__ +int memicmp(const char *s1, const char *s2, int n); +#if 0 +{ + int result = 0; + + for (int i = 0; i < n; i++) + { char c1 = s1[i]; + char c2 = s2[i]; + + result = c1 - c2; + if (result) + { + if ('A' <= c1 && c1 <= 'Z') + c1 += 'a' - 'A'; + if ('A' <= c2 && c2 <= 'Z') + c2 += 'a' - 'A'; + result = c1 - c2; + if (result) + break; + } + } + return result; +} +#endif +#endif + +extern int HtmlNamedEntity(unsigned char *p, int length); + +static int isLineSeparator(const unsigned char* p); + +/********************************** + * Determine if beginning of tag identifier + * or a continuation of a tag identifier. + */ + +inline int istagstart(int c) +{ + return (isalpha(c) || c == '_'); +} + +inline int istag(int c) +{ + return (isalnum(c) || c == '_'); +} + +/********************************************** + */ + +Html::Html(const char *sourcename, unsigned char *base, unsigned length) +{ + //printf("Html::Html()\n"); + this->sourcename = sourcename; + this->base = base; + p = base; + end = base + length; + linnum = 1; + dbuf = NULL; + inCode = 0; +} + +/********************************************** + * Print error & quit. + */ + +void Html::error(const char *format, ...) +{ + printf("%s(%d) : HTML Error: ", sourcename, linnum); + + va_list ap; + va_start(ap, format); + vprintf(format, ap); + va_end(ap); + + printf("\n"); + fflush(stdout); + +//#if MARS +// global.errors++; +//#else + exit(EXIT_FAILURE); +//#endif +} + +/********************************************** + * Extract all the code from an HTML file, + * concatenate it all together, and store in buf. + */ + +#if MARS +void Html::extractCode(OutBuffer *buf) +#else +void Html::extractCode(Outbuffer *buf) +#endif +{ + //printf("Html::extractCode()\n"); + dbuf = buf; // save for other routines + buf->reserve(end - p); + inCode = 0; + while (1) + { + //printf("p = %p, *p = x%x\n", p, *p); + switch (*p) + { +#if 0 // strings are not recognized outside of tags + case '"': + case '\'': + skipString(); + continue; +#endif + case '<': + if (p[1] == '!' && isCommentStart()) + { // Comments start with + * Netscape: comments nest + * w3c: whitespace can appear between -- and > of comment close + */ + +void Html::scanComment() +{ + // Most of the complexity is dealing with the case that + // an arbitrary amount of whitespace can appear between + // the -- and the > of a comment close. + int scangt = 0; + + //printf("scanComment()\n"); + if (*p == '\n') + { linnum++; + // Always extract new lines, so that D lexer counts the + // lines right. + dbuf->writeByte(*p); + } + while (1) + { + //scangt = 1; // IE 5.0 compatibility + p++; + switch (*p) + { + case '-': + if (p[1] == '-') + { + if (p[2] == '>') // optimize for most common case + { + p += 3; + break; + } + p++; + scangt = 1; + } + else + scangt = 0; + continue; + + case '>': + if (scangt) + { // found --> + p++; + break; + } + continue; + + case ' ': + case '\t': + case '\f': + case '\v': + // skip white space + continue; + + case '\r': + if (p[1] == '\n') + goto Ldefault; + case '\n': + linnum++; // remember to count lines + // Always extract new lines, so that D lexer counts the + // lines right. + dbuf->writeByte(*p); + continue; + + case 0: + case 0x1a: + error("end of file before closing --> of comment"); + break; + + default: + Ldefault: + scangt = 0; // it's not --> + continue; + } + break; + } + //printf("*p = '%c'\n", *p); +} + +/******************************************** + * Determine if we are at the start of a comment. + * Input: + * p is on the opening '<' + * Returns: + * 0 if not start of a comment + * 1 if start of a comment, p is adjusted to point past -- + */ + +int Html::isCommentStart() +#ifdef __DMC__ + __out(result) + { + if (result == 0) + ; + else if (result == 1) + { + assert(p[-2] == '-' && p[-1] == '-'); + } + else + assert(0); + } + __body +#endif /* __DMC__ */ + { unsigned char *s; + + if (p[0] == '<' && p[1] == '!') + { + for (s = p + 2; 1; s++) + { + switch (*s) + { + case ' ': + case '\t': + case '\r': + case '\f': + case '\v': + // skip white space, even though spec says no + // white space is allowed + continue; + + case '-': + if (s[1] == '-') + { + p = s + 2; + return 1; + } + goto No; + + default: + goto No; + } + } + } + No: + return 0; + } + +int Html::isCDATAStart() +{ + const char * CDATA_START_MARKER = "0) + { + /* Always extract new lines, so that D lexer counts the lines + * right. + */ + linnum++; + dbuf->writeByte('\n'); + p += lineSepLength; + continue; + } + else if (p[0] == ']' && p[1] == ']' && p[2] == '>') + { + /* end of CDATA section */ + p += 3; + return; + } + else if (inCode) + { + /* this CDATA section contains D code */ + dbuf->writeByte(*p); + } + + p++; + } +} + + +/******************************************** + * Convert an HTML character entity into a character. + * Forms are: + * &name; named entity + * &#ddd; decimal + * &#xhhhh; hex + * Input: + * p is on the & + */ + +int Html::charEntity() +{ int c = 0; + int v; + int hex; + unsigned char *pstart = p; + + //printf("Html::charEntity('%c')\n", *p); + if (p[1] == '#') + { + p++; + if (p[1] == 'x' || p[1] == 'X') + { p++; + hex = 1; + } + else + hex = 0; + if (p[1] == ';') + goto Linvalid; + while (1) + { + p++; + switch (*p) + { + case 0: + case 0x1a: + error("end of file before end of character entity"); + goto Lignore; + + case '\n': + case '\r': + case '<': // tag start + // Termination is assumed + break; + + case ';': + // Termination is explicit + p++; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + v = *p - '0'; + goto Lvalue; + + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + if (!hex) + goto Linvalid; + v = (*p - 'a') + 10; + goto Lvalue; + + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + if (!hex) + goto Linvalid; + v = (*p - 'A') + 10; + goto Lvalue; + + Lvalue: + if (hex) + c = (c << 4) + v; + else + c = (c * 10) + v; + if (c > 0x10FFFF) + { + error("character entity out of range"); + goto Lignore; + } + continue; + + default: + Linvalid: + error("invalid numeric character reference"); + goto Lignore; + } + break; + } + } + else + { + // It's a named entity; gather all characters until ; + unsigned char *idstart = p + 1; + + while (1) + { + p++; + switch (*p) + { + case 0: + case 0x1a: + error("end of file before end of character entity"); + break; + + case '\n': + case '\r': + case '<': // tag start + // Termination is assumed + c = HtmlNamedEntity(idstart, p - idstart); + if (c == -1) + goto Lignore; + break; + + case ';': + // Termination is explicit + c = HtmlNamedEntity(idstart, p - idstart); + if (c == -1) + goto Lignore; + p++; + break; + + default: + continue; + } + break; + } + } + + // Kludge to convert non-breaking space to ascii space + if (c == 160) + c = ' '; + + return c; + +Lignore: + //printf("Lignore\n"); + p = pstart + 1; + return '&'; +} + +/** + * identify DOS, Linux, Mac, Next and Unicode line endings + * 0 if this is no line separator + * >0 the length of the separator + * Note: input has to be UTF-8 + */ +static int isLineSeparator(const unsigned char* p) +{ + // Linux + if( p[0]=='\n') + return 1; + + // Mac & Dos + if( p[0]=='\r') + return (p[1]=='\n') ? 2 : 1; + + // Unicode (line || paragraph sep.) + if( p[0]==0xE2 && p[1]==0x80 && (p[2]==0xA8 || p[2]==0xA9)) + return 3; + + // Next + if( p[0]==0xC2 && p[1]==0x85) + return 2; + + return 0; +} + + diff --git a/backend/html.h b/backend/html.h new file mode 100644 index 00000000..9b5a548f --- /dev/null +++ b/backend/html.h @@ -0,0 +1,50 @@ + +// Copyright (c) 1999-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gpl.txt. +// See the included readme.txt for details. + + +#if MARS +struct OutBuffer; +#else +struct Outbuffer; +#endif + +struct Html +{ + const char *sourcename; + + unsigned char *base; // pointer to start of buffer + unsigned char *end; // past end of buffer + unsigned char *p; // current character + unsigned linnum; // current line number +#if MARS + OutBuffer *dbuf; // code source buffer +#else + Outbuffer *dbuf; // code source buffer +#endif + int inCode; // !=0 if in code + + + Html(const char *sourcename, unsigned char *base, unsigned length); + + void error(const char *format, ...); +#if MARS + void extractCode(OutBuffer *buf); +#else + void extractCode(Outbuffer *buf); +#endif + void skipTag(); + void skipString(); + unsigned char *skipWhite(unsigned char *q); + void scanComment(); + int isCommentStart(); + void scanCDATA(); + int isCDATAStart(); + int charEntity(); + static int namedEntity(unsigned char *p, int length); +}; diff --git a/backend/iasm.h b/backend/iasm.h new file mode 100644 index 00000000..07268223 --- /dev/null +++ b/backend/iasm.h @@ -0,0 +1,430 @@ + +/* + * Copyright (c) 1992-1999 by Symantec + * Copyright (c) 1999-2011 by Digital Mars + * All Rights Reserved + * http://www.digitalmars.com + * Written by Mike Cote, John Micco and Walter Bright + * + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#include + +///////////////////////////////////////////////// +// Instruction flags (usFlags) +// +// + +// This is for when the reg field of modregrm specifies which instruction it is +#define NUM_MASK 0x7 +#define NUM_MASKR 0x8 // for REX extended registers +#define _0 (0x0 | _modrm) // insure that some _modrm bit is set +#define _1 0x1 // with _0 +#define _2 0x2 +#define _3 0x3 +#define _4 0x4 +#define _5 0x5 +#define _6 0x6 +#define _7 0x7 + +#define _modrm 0x10 + +#define _r _modrm +#define _cb _modrm +#define _cw _modrm +#define _cd _modrm +#define _cq _modrm +#define _cp _modrm +#define _ib 0 +#define _iw 0 +#define _id 0 +#define _rb 0 +#define _rw 0 +#define _rd 0 +#define _16_bit 0x20 +#define _32_bit 0x40 +#define _64_bit 0x10000 +#define _i64_bit 0x20000 // opcode is invalid in 64bit mode +#define _I386 0x80 // opcode is only for 386 and later +#define _16_bit_addr 0x100 +#define _32_bit_addr 0x200 +#define _fwait 0x400 // Add an FWAIT prior to the instruction opcode +#define _nfwait 0x800 // Do not add an FWAIT prior to the instruction + +#define MOD_MASK 0xF000 // Mod mask +#define _modsi 0x1000 // Instruction modifies SI +#define _moddx 0x2000 // Instruction modifies DX +#define _mod2 0x3000 // Instruction modifies second operand +#define _modax 0x4000 // Instruction modifies AX +#define _modnot1 0x5000 // Instruction does not modify first operand +#define _modaxdx 0x6000 // instruction modifies AX and DX +#define _moddi 0x7000 // Instruction modifies DI +#define _modsidi 0x8000 // Instruction modifies SI and DI +#define _modcx 0x9000 // Instruction modifies CX +#define _modes 0xa000 // Instruction modifies ES +#define _modall 0xb000 // Instruction modifies all register values +#define _modsiax 0xc000 // Instruction modifies AX and SI +#define _modsinot1 0xd000 // Instruction modifies SI and not first param +#define _modcxr11 0xe000 // Instruction modifies CX and R11 +#define _modxmm0 0xf000 // Instruction modifies XMM0 + +// translates opcode into equivalent vex encoding +#define VEX_128_W0(op) (_VEX(op)|_VEX_NOO) +#define VEX_128_W1(op) (_VEX(op)|_VEX_NOO|_VEX_W) +#define VEX_128_WIG(op) VEX_128_W0(op) +#define VEX_256_W0(op) (_VEX(op)|_VEX_NOO|_VEX_L) +#define VEX_256_W1(op) (_VEX(op)|_VEX_NOO|_VEX_W|_VEX_L) +#define VEX_256_WIG(op) VEX_256_W0(op) +#define VEX_NDS_128_W0(op) (_VEX(op)|_VEX_NDS) +#define VEX_NDS_128_W1(op) (_VEX(op)|_VEX_NDS|_VEX_W) +#define VEX_NDS_128_WIG(op) VEX_NDS_128_W0(op) +#define VEX_NDS_256_W0(op) (_VEX(op)|_VEX_NDS|_VEX_L) +#define VEX_NDS_256_W1(op) (_VEX(op)|_VEX_NDS|_VEX_W|_VEX_L) +#define VEX_NDS_256_WIG(op) VEX_NDS_256_W0(op) +#define VEX_NDD_128_W0(op) (_VEX(op)|_VEX_NDD) +#define VEX_NDD_128_W1(op) (_VEX(op)|_VEX_NDD|_VEX_W) +#define VEX_NDD_128_WIG(op) VEX_NDD_128_W0(op) +#define VEX_NDD_256_W0(op) (_VEX(op)|_VEX_NDD|_VEX_L) +#define VEX_NDD_256_W1(op) (_VEX(op)|_VEX_NDD|_VEX_W|_VEX_L) +#define VEX_NDD_256_WIG(op) VEX_NDD_256_W0(op) +#define VEX_DDS_128_W0(op) (_VEX(op)|_VEX_DDS) +#define VEX_DDS_128_W1(op) (_VEX(op)|_VEX_DDS|_VEX_W) +#define VEX_DDS_128_WIG(op) VEX_DDS_128_W0(op) +#define VEX_DDS_256_W0(op) (_VEX(op)|_VEX_DDS|_VEX_L) +#define VEX_DDS_256_W1(op) (_VEX(op)|_VEX_DDS|_VEX_W|_VEX_L) +#define VEX_DDS_256_WIG(op) VEX_DDS_256_W0(op) + +#define _VEX_W 0x8000 +/* Don't encode LIG/LZ use 128 for these. + */ +#define _VEX_L 0x0400 +/* Encode nds, ndd, dds in the vvvv field, it gets + * overwritten with the actual register later. + */ +#define VEX_NOO 0 // neither of nds, ndd, dds +#define VEX_NDS 1 +#define VEX_NDD 2 +#define VEX_DDS 3 +#define _VEX_NOO ( VEX_NOO << 11) +#define _VEX_NDS ( VEX_NDS << 11) +#define _VEX_NDD ( VEX_NDD << 11) +#define _VEX_DDS ( VEX_DDS << 11) + +#define _VEX(op) (0xC4 << 24 | _VEX_MM(op >> 8) | (op & 0xFF)) + +#define _VEX_MM(op) \ + ( \ + ((op) & 0x00FF) == 0x000F ? (0x1 << 16 | _VEX_PP((op) >> 8)) : \ + ((op) & 0xFFFF) == 0x0F38 ? (0x2 << 16 | _VEX_PP((op) >> 16)) : \ + ((op) & 0xFFFF) == 0x0F3A ? (0x3 << 16 | _VEX_PP((op) >> 16)) : \ + _VEX_ASSERT0 \ + ) + +#define _VEX_PP(op) \ + ( \ + (op) == 0x00 ? 0x00 << 8 : \ + (op) == 0x66 ? 0x01 << 8 : \ + (op) == 0xF3 ? 0x02 << 8 : \ + (op) == 0xF2 ? 0x03 << 8 : \ + _VEX_ASSERT0 \ + ) + +// avoid dynamic initialization of the asm tables +#if DEBUG + #define _VEX_ASSERT0 (assert(0)) +#else + #define _VEX_ASSERT0 (0) +#endif + + +///////////////////////////////////////////////// +// Operand flags - usOp1, usOp2, usOp3 +// + +typedef unsigned opflag_t; + +// Operand flags for normal opcodes + +#define _r8 CONSTRUCT_FLAGS( _8, _reg, _normal, 0 ) +#define _r16 CONSTRUCT_FLAGS(_16, _reg, _normal, 0 ) +#define _r32 CONSTRUCT_FLAGS(_32, _reg, _normal, 0 ) +#define _r64 CONSTRUCT_FLAGS(_64, _reg, _normal, 0 ) +#define _m8 CONSTRUCT_FLAGS(_8, _m, _normal, 0 ) +#define _m16 CONSTRUCT_FLAGS(_16, _m, _normal, 0 ) +#define _m32 CONSTRUCT_FLAGS(_32, _m, _normal, 0 ) +#define _m48 CONSTRUCT_FLAGS( _48, _m, _normal, 0 ) +#define _m64 CONSTRUCT_FLAGS( _64, _m, _normal, 0 ) +#define _m128 CONSTRUCT_FLAGS( _anysize, _m, _normal, 0 ) +#define _m256 CONSTRUCT_FLAGS( _anysize, _m, _normal, 0 ) +#define _rm8 CONSTRUCT_FLAGS(_8, _rm, _normal, 0 ) +#define _rm16 CONSTRUCT_FLAGS(_16, _rm, _normal, 0 ) +#define _rm32 CONSTRUCT_FLAGS(_32, _rm, _normal, 0) +#define _rm64 CONSTRUCT_FLAGS(_64, _rm, _normal, 0) +#define _r32m8 CONSTRUCT_FLAGS(_32|_8, _rm, _normal, 0) +#define _r32m16 CONSTRUCT_FLAGS(_32|_16, _rm, _normal, 0) +#define _regm8 CONSTRUCT_FLAGS(_64|_32|_8, _rm, _normal, 0) +#define _imm8 CONSTRUCT_FLAGS(_8, _imm, _normal, 0 ) +#define _imm16 CONSTRUCT_FLAGS(_16, _imm, _normal, 0) +#define _imm32 CONSTRUCT_FLAGS(_32, _imm, _normal, 0) +#define _imm64 CONSTRUCT_FLAGS(_64, _imm, _normal, 0) +#define _rel8 CONSTRUCT_FLAGS(_8, _rel, _normal, 0) +#define _rel16 CONSTRUCT_FLAGS(_16, _rel, _normal, 0) +#define _rel32 CONSTRUCT_FLAGS(_32, _rel, _normal, 0) +#define _p1616 CONSTRUCT_FLAGS(_32, _p, _normal, 0) +#define _m1616 CONSTRUCT_FLAGS(_32, _mnoi, _normal, 0) +#define _p1632 CONSTRUCT_FLAGS(_48, _p, _normal, 0 ) +#define _m1632 CONSTRUCT_FLAGS(_48, _mnoi, _normal, 0) +#define _special CONSTRUCT_FLAGS( 0, 0, _rspecial, 0 ) +#define _seg CONSTRUCT_FLAGS( 0, 0, _rseg, 0 ) +#define _a16 CONSTRUCT_FLAGS( 0, 0, _addr16, 0 ) +#define _a32 CONSTRUCT_FLAGS( 0, 0, _addr32, 0 ) +#define _f16 CONSTRUCT_FLAGS( 0, 0, _fn16, 0) + // Near function pointer +#define _f32 CONSTRUCT_FLAGS( 0, 0, _fn32, 0) + // Far function pointer +#define _lbl CONSTRUCT_FLAGS( 0, 0, _flbl, 0 ) + // Label (in current function) + +#define _mmm32 CONSTRUCT_FLAGS( 0, _m, 0, _32) +#define _mmm64 CONSTRUCT_FLAGS( _64, _m, 0, _f64) +#define _mmm128 CONSTRUCT_FLAGS( 0, _m, 0, _f128) + +#define _xmm_m16 CONSTRUCT_FLAGS( _16, _m, _rspecial, ASM_GET_uRegmask(_xmm)) +#define _xmm_m32 CONSTRUCT_FLAGS( _32, _m, _rspecial, ASM_GET_uRegmask(_xmm)) +#define _xmm_m64 CONSTRUCT_FLAGS( _anysize, _m, _rspecial, ASM_GET_uRegmask(_xmm)) +#define _xmm_m128 CONSTRUCT_FLAGS( _anysize, _m, _rspecial, ASM_GET_uRegmask(_xmm)) +#define _ymm_m256 CONSTRUCT_FLAGS( _anysize, _m, _rspecial, ASM_GET_uRegmask(_ymm)) + +#define _moffs8 (_rel8) +#define _moffs16 (_rel16 ) +#define _moffs32 (_rel32 ) + + +//////////////////////////////////////////////////////////////////// +// Operand flags for floating point opcodes are all just aliases for +// normal opcode variants and only asm_determine_operator_flags should +// need to care. +// +#define _fm80 CONSTRUCT_FLAGS( 0, _m, 0, _f80 ) +#define _fm64 CONSTRUCT_FLAGS( 0, _m, 0, _f64 ) +#define _fm128 CONSTRUCT_FLAGS( 0, _m, 0, _f128 ) +#define _fanysize (_f64 | _f80 | _f112 ) + +#define _float_m CONSTRUCT_FLAGS( _anysize, _float, 0, _fanysize) + +#define _st CONSTRUCT_FLAGS( 0, _float, 0, _rst ) // stack register 0 +#define _m112 CONSTRUCT_FLAGS( 0, _m, 0, _f112 ) +#define _m224 _m112 +#define _m512 _m224 +#define _sti CONSTRUCT_FLAGS( 0, _float, 0, _rsti ) + +////////////////// FLAGS ///////////////////////////////////// + +#if 1 +// bit size 5 3 3 7 +#define CONSTRUCT_FLAGS( uSizemask, aopty, amod, uRegmask ) \ + ( (uSizemask) | (aopty) << 5 | (amod) << 8 | (uRegmask) << 11) + +#define ASM_GET_uSizemask(us) ((us) & 0x1F) +#define ASM_GET_aopty(us) ((ASM_OPERAND_TYPE)(((us) >> 5) & 7)) +#define ASM_GET_amod(us) ((ASM_MODIFIERS)(((us) >> 8) & 7)) +#define ASM_GET_uRegmask(us) (((us) >> 11) & 0x7F) +#else +#define CONSTRUCT_FLAGS( uSizemask, aopty, amod, uRegmask ) \ + ( (uSizemask) | (aopty) << 4 | (amod) << 7 | (uRegmask) << 10) + +#define ASM_GET_uSizemask(us) ((us) & 0x0F) +#define ASM_GET_aopty(us) ((ASM_OPERAND_TYPE)(((us) & 0x70) >> 4)) +#define ASM_GET_amod(us) ((ASM_MODIFIERS)(((us) & 0x380) >> 7)) +#define ASM_GET_uRegmask(us) (((us) & 0xFC00) >> 10) +#endif + +// For uSizemask (5 bits) +#define _8 0x1 +#define _16 0x2 +#define _32 0x4 +#define _48 0x8 +#define _64 0x10 +#define _anysize (_8 | _16 | _32 | _48 | _64 ) + +// For aopty (3 bits) +enum ASM_OPERAND_TYPE { + _reg, // _r8, _r16, _r32 + _m, // _m8, _m16, _m32, _m48 + _imm, // _imm8, _imm16, _imm32, _imm64 + _rel, // _rel8, _rel16, _rel32 + _mnoi, // _m1616, _m1632 + _p, // _p1616, _p1632 + _rm, // _rm8, _rm16, _rm32 + _float // Floating point operand, look at cRegmask for the + // actual size +}; + +// For amod (3 bits) +enum ASM_MODIFIERS { + _normal, // Normal register value + _rseg, // Segment registers + _rspecial, // Special registers + _addr16, // 16 bit address + _addr32, // 32 bit address + _fn16, // 16 bit function call + _fn32, // 32 bit function call + _flbl // Label +}; + +// For uRegmask (7 bits) + +// uRegmask flags when aopty == _float +#define _rst 0x1 +#define _rsti 0x2 +#define _f64 0x4 +#define _f80 0x8 +#define _f112 0x10 +#define _f128 0x20 + +// _seg register values (amod == _rseg) +// +#define _ds CONSTRUCT_FLAGS( 0, 0, _rseg, 0x01 ) +#define _es CONSTRUCT_FLAGS( 0, 0, _rseg, 0x02 ) +#define _ss CONSTRUCT_FLAGS( 0, 0, _rseg, 0x04 ) +#define _fs CONSTRUCT_FLAGS( 0, 0, _rseg, 0x08 ) +#define _gs CONSTRUCT_FLAGS( 0, 0, _rseg, 0x10 ) +#define _cs CONSTRUCT_FLAGS( 0, 0, _rseg, 0x20 ) + +// +// _special register values +// +#define _crn CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x01 ) // CRn register (0,2,3) +#define _drn CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x02 ) // DRn register (0-3,6-7) +#define _trn CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x04 ) // TRn register (3-7) +#define _mm CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x08 ) // MMn register (0-7) +#define _xmm CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x10 ) // XMMn register (0-7) +#define _xmm0 CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x20 ) // XMM0 register +#define _ymm CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x40 ) // YMMn register (0-15) + +// +// Default register values +// + +#define _al CONSTRUCT_FLAGS( 0, 0, _normal, 0x01 ) // AL register +#define _ax CONSTRUCT_FLAGS( 0, 0, _normal, 0x02 ) // AX register +#define _eax CONSTRUCT_FLAGS( 0, 0, _normal, 0x04 ) // EAX register +#define _dx CONSTRUCT_FLAGS( 0, 0, _normal, 0x08 ) // DX register +#define _cl CONSTRUCT_FLAGS( 0, 0, _normal, 0x10 ) // CL register +#define _rax CONSTRUCT_FLAGS( 0, 0, _normal, 0x40 ) // RAX register + + +#define _rplus_r 0x20 +#define _plus_r CONSTRUCT_FLAGS( 0, 0, 0, _rplus_r ) + // Add the register to the opcode (no mod r/m) + + + +////////////////////////////////////////////////////////////////// + +#define ITprefix 0x10 // special prefix +#define ITjump 0x20 // jump instructions CALL, Jxx and LOOPxx +#define ITimmed 0x30 // value of an immediate operand controls + // code generation +#define ITopt 0x40 // not all operands are required +#define ITshift 0x50 // rotate and shift instructions +#define ITfloat 0x60 // floating point coprocessor instructions +#define ITdata 0x70 // DB, DW, DD, DQ, DT pseudo-ops +#define ITaddr 0x80 // DA (define addresss) pseudo-op +#define ITMASK 0xF0 +#define ITSIZE 0x0F // mask for size + +enum OP_DB +{ +#if SCPP + // These are the number of bytes + OPdb = 1, + OPdw = 2, + OPdd = 4, + OPdq = 8, + OPdt = 10, + OPdf = 4, + OPde = 10, + OPds = 2, + OPdi = 4, + OPdl = 8, +#endif +#if MARS + // Integral types + OPdb, + OPds, + OPdi, + OPdl, + + // Float types + OPdf, + OPdd, + OPde, + + // Deprecated + OPdw = OPds, + OPdq = OPdl, + OPdt = OPde, +#endif +}; + + +/* from iasm.c */ +int asm_state(int iFlags); + +void asm_process_fixup( block **ppblockLabels ); + +typedef struct _PTRNTAB4 { + unsigned usOpcode; + unsigned usFlags; + opflag_t usOp1; + opflag_t usOp2; + opflag_t usOp3; + opflag_t usOp4; +} PTRNTAB4, * PPTRNTAB4, ** PPPTRNTAB4; + +typedef struct _PTRNTAB3 { + unsigned usOpcode; + unsigned usFlags; + opflag_t usOp1; + opflag_t usOp2; + opflag_t usOp3; +} PTRNTAB3, * PPTRNTAB3, ** PPPTRNTAB3; + +typedef struct _PTRNTAB2 { + unsigned usOpcode; + unsigned usFlags; + opflag_t usOp1; + opflag_t usOp2; +} PTRNTAB2, * PPTRNTAB2, ** PPPTRNTAB2; + +typedef struct _PTRNTAB1 { + unsigned usOpcode; + unsigned usFlags; + opflag_t usOp1; +} PTRNTAB1, * PPTRNTAB1, ** PPPTRNTAB1; + +typedef struct _PTRNTAB0 { + unsigned usOpcode; + #define ASM_END 0xffff // special opcode meaning end of table + unsigned usFlags; +} PTRNTAB0, * PPTRNTAB0, ** PPPTRNTAB0; + +typedef union _PTRNTAB { + PTRNTAB0 *pptb0; + PTRNTAB1 *pptb1; + PTRNTAB2 *pptb2; + PTRNTAB3 *pptb3; + PTRNTAB4 *pptb4; +} PTRNTAB, * PPTRNTAB, ** PPPTRNTAB; + +typedef struct +{ + unsigned char usNumops; + PTRNTAB ptb; +} OP; + diff --git a/backend/mach.h b/backend/mach.h new file mode 100644 index 00000000..f0e82cc3 --- /dev/null +++ b/backend/mach.h @@ -0,0 +1,323 @@ + +/* Mach-O object file format */ + +#if __APPLE__ + +#include +#include +#include +#include +//#include + +#ifndef S_DTRACE_DOF + #define S_DTRACE_DOF 15 +#endif + +#else + +#include + +typedef int cpu_type_t; +typedef int cpu_subtype_t; +typedef int vm_prot_t; + +struct mach_header +{ + uint32_t magic; + #define MH_MAGIC 0xfeedface + #define MH_CIGAM 0xcefaedfe + cpu_type_t cputype; + #define CPU_TYPE_I386 ((cpu_type_t)7) + #define CPU_TYPE_X86_64 ((cpu_type_t)7 | 0x1000000) + #define CPU_TYPE_POWERPC ((cpu_type_t)18) + #define CPU_TYPE_POWERPC64 (CPU_TYPE_POWERPC | 0x1000000) + cpu_subtype_t cpusubtype; + #define CPU_SUBTYPE_POWERPC_ALL ((cpu_subtype_t)0) + #define CPU_SUBTYPE_I386_ALL ((cpu_subtype_t)3) + uint32_t filetype; + #define MH_OBJECT 1 + #define MH_EXECUTE 2 + #define MH_BUNDLE 8 + #define MH_DYLIB 6 + #define MH_PRELOAD 5 + #define MH_CORE 4 + #define MH_DYLINKER 7 + #define MH_DSYM 10 + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + #define MH_NOUNDEFS 1 + #define MH_INCRLINK 2 + #define MH_DYLDLINK 4 + #define MH_TWOLEVEL 0x80 + #define MH_BINDATLOAD 8 + #define MH_PREBOUND 0x10 + #define MH_PREBINDABLE 0x800 + #define MH_NOFIXPREBINDING 0x400 + #define MH_ALLMODSBOUND 0x1000 + #define MH_CANONICAL 0x4000 + #define MH_SPLIT_SEGS 0x20 + #define MH_FORCE_FLAT 0x100 + #define MH_SUBSECTIONS_VIA_SYMBOLS 0x2000 + #define MH_NOMULTIDEFS 0x200 +}; + +struct mach_header_64 +{ + uint32_t magic; + #define MH_MAGIC_64 0xfeedfacf + #define MH_CIGAM_64 0xcffaedfe + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + uint32_t reserved; +}; + +struct load_command +{ + uint32_t cmd; + #define LC_SEGMENT 1 + #define LC_SYMTAB 2 + #define LC_DYSYMTAB 11 + #define LC_SEGMENT_64 0x19 + uint32_t cmdsize; +}; + +struct uuid_command +{ + uint32_t cmd; + uint32_t cmdsize; + uint8_t uuid[16]; +}; + +struct segment_command +{ + uint32_t cmd; + uint32_t cmdsize; + char segname[16]; + uint32_t vmaddr; + uint32_t vmsize; + uint32_t fileoff; + uint32_t filesize; + vm_prot_t maxprot; + vm_prot_t initprot; + uint32_t nsects; + uint32_t flags; + #define SG_HIGHVM 1 + #define SG_FVMLIB 2 + #define SG_NORELOC 4 + #define SG_PROTECTED_VERSION_1 8 +}; + +struct segment_command_64 +{ + uint32_t cmd; + uint32_t cmdsize; + char segname[16]; + uint64_t vmaddr; + uint64_t vmsize; + uint64_t fileoff; + uint64_t filesize; + vm_prot_t maxprot; + vm_prot_t initprot; + uint32_t nsects; + uint32_t flags; +}; + +struct section +{ + char sectname[16]; + char segname[16]; + uint32_t addr; + uint32_t size; + uint32_t offset; + uint32_t align; + uint32_t reloff; + uint32_t nreloc; + uint32_t flags; + #define SECTION_TYPE 0xFF + #define SECTION_ATTRIBUTES 0xFFFFFF00 + + #define S_REGULAR 0 + #define S_ZEROFILL 1 + #define S_CSTRING_LITERALS 2 + #define S_4BYTE_LITERALS 3 + #define S_8BYTE_LITERALS 4 + #define S_LITERAL_POINTERS 5 + + #define S_NON_LAZY_SYMBOL_POINTERS 6 + #define S_LAZY_SYMBOL_POINTERS 7 + #define S_SYMBOL_STUBS 8 + #define S_MOD_INIT_FUNC_POINTERS 9 + #define S_MOD_TERM_FUNC_POINTERS 10 + #define S_COALESCED 11 + #define S_GB_ZEROFILL 12 + #define S_INTERPOSING 13 + #define S_16BYTE_LITERALS 14 + #define S_DTRACE_DOF 15 + + #define SECTION_ATTRIBUTES_USR 0xFF000000 + #define S_ATTR_PURE_INSTRUCTIONS 0x80000000 + #define S_ATTR_NO_TOC 0x40000000 + #define S_ATTR_STRIP_STATIC_SYMS 0x20000000 + #define S_ATTR_NO_DEAD_STRIP 0x10000000 + #define S_ATTR_LIVE_SUPPORT 0x8000000 + #define S_ATTR_SELF_MODIFYING_CODE 0x4000000 + #define S_ATTR_DEBUG 0x2000000 + + #define SECTION_ATTRIBUTES_SYS 0xFFFF00 + #define S_ATTR_SOME_INSTRUCTIONS 0x000400 + #define S_ATTR_EXT_RELOC 0x000200 + #define S_ATTR_LOC_RELOC 0x000100 + + uint32_t reserved1; + uint32_t reserved2; +}; + +struct section_64 +{ + char sectname[16]; + char segname[16]; + uint64_t addr; + uint64_t size; + uint32_t offset; + uint32_t align; + uint32_t reloff; + uint32_t nreloc; + uint32_t flags; + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; +}; + +struct twolevel_hints_command +{ + uint32_t cmd; + uint32_t cmdsize; + uint32_t offset; + uint32_t nhints; +}; + +struct twolevel_hint +{ + uint32_t isub_image:8, itoc:24; +}; + +struct symtab_command +{ + uint32_t cmd; + uint32_t cmdsize; + uint32_t symoff; + uint32_t nsyms; + uint32_t stroff; + uint32_t strsize; +}; + +struct nlist +{ + union + { + int32_t n_strx; + } n_un; + uint8_t n_type; + #define N_EXT 1 + #define N_STAB 0xE0 + #define N_PEXT 0x10 + #define N_TYPE 0x0E + #define N_UNDF 0 + #define N_ABS 2 + #define N_INDR 10 + #define N_PBUD 12 + #define N_SECT 14 + uint8_t n_sect; + int16_t n_desc; + uint32_t n_value; +}; + +struct nlist_64 +{ + union + { + uint32_t n_strx; + } n_un; + uint8_t n_type; + uint8_t n_sect; + uint16_t n_desc; + uint64_t n_value; +}; + +struct dysymtab_command +{ + uint32_t cmd; + uint32_t cmdsize; + uint32_t ilocalsym; + uint32_t nlocalsym; + uint32_t iextdefsym; + uint32_t nextdefsym; + uint32_t iundefsym; + uint32_t nundefsym; + uint32_t tocoff; + uint32_t ntoc; + uint32_t modtaboff; + uint32_t nmodtab; + uint32_t extrefsymoff; + uint32_t nextrefsyms; + uint32_t indirectsymoff; + uint32_t nindirectsyms; + uint32_t extreloff; + uint32_t nextrel; + uint32_t locreloff; + uint32_t nlocrel; +}; + +struct relocation_info +{ + int32_t r_address; + #define R_SCATTERED 0x80000000 + uint32_t r_symbolnum:24, + r_pcrel:1, + r_length:2, + r_extern:1, + r_type:4; + // for i386 + #define GENERIC_RELOC_VANILLA 0 + #define GENERIC_RELOC_PAIR 1 + #define GENERIC_RELOC_SECTDIFF 2 + #define GENERIC_RELOC_PB_LA_PTR 3 + #define GENERIC_RELOC_LOCAL_SECTDIFF 4 + + // for x86_64 + #define X86_64_RELOC_UNSIGNED 0 + #define X86_64_RELOC_SIGNED 1 + #define X86_64_RELOC_BRANCH 2 + #define X86_64_RELOC_GOT_LOAD 3 + #define X86_64_RELOC_GOT 4 + #define X86_64_RELOC_SUBTRACTOR 5 + #define X86_64_RELOC_SIGNED_1 6 + #define X86_64_RELOC_SIGNED_2 7 + #define X86_64_RELOC_SIGNED_4 8 +}; + +struct scattered_relocation_info +{ + #if LITTLE_ENDIAN + uint32_t r_address:24, + r_type:4, + r_length:2, + r_pcrel:1, + r_scattered:1; + int32_t r_value; + #elif BIG_ENDIAN + uint32_t r_scattered:1, + r_pcrel:1, + r_length:2, + r_type:4, + r_address:24; + int32_t r_value; + #endif +}; + +#endif diff --git a/backend/machobj.c b/backend/machobj.c new file mode 100644 index 00000000..a710ea02 --- /dev/null +++ b/backend/machobj.c @@ -0,0 +1,2757 @@ + +// Copyright (c) 2009-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gpl.txt. +// See the included readme.txt for details. + + +#if SCPP || MARS +#include +#include +#include +#include +#include +#include +#include + +#if _WIN32 || linux +#include +#endif + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#include +#include +#endif + +#if __APPLE__ +#import + +const SInt32 MacOSX_10_5 = 0x1050; +const SInt32 MacOSX_10_6 = 0x1060; +#endif + +#include "cc.h" +#include "global.h" +#include "code.h" +#include "type.h" +#include "mach.h" +#include "outbuf.h" +#include "filespec.h" +#include "cv4.h" +#include "cgcv.h" +#include "dt.h" + +#include "aa.h" +#include "tinfo.h" + +#if MACHOBJ + +#if MARS +#include "mars.h" +#endif + +#include "mach.h" +#include "dwarf.h" + +// for x86_64 +#define X86_64_RELOC_UNSIGNED 0 +#define X86_64_RELOC_SIGNED 1 +#define X86_64_RELOC_BRANCH 2 +#define X86_64_RELOC_GOT_LOAD 3 +#define X86_64_RELOC_GOT 4 +#define X86_64_RELOC_SUBTRACTOR 5 +#define X86_64_RELOC_SIGNED_1 6 +#define X86_64_RELOC_SIGNED_2 7 +#define X86_64_RELOC_SIGNED_4 8 + +static Outbuffer *fobjbuf; + +regm_t BYTEREGS = BYTEREGS_INIT; +regm_t ALLREGS = ALLREGS_INIT; + +static char __file__[] = __FILE__; // for tassert.h +#include "tassert.h" + +#define DEST_LEN (IDMAX + IDOHD + 1) +char *obj_mangle2(Symbol *s,char *dest); + +#if MARS +// C++ name mangling is handled by front end +#define cpp_mangle(s) ((s)->Sident) +#endif + + +/****************************************** + */ + +symbol *GOTsym; // global offset table reference + +symbol *elfobj_getGOTsym() +{ + if (!GOTsym) + { + GOTsym = symbol_name("_GLOBAL_OFFSET_TABLE_",SCglobal,tspvoid); + } + return GOTsym; +} + +static void objfile_write(FILE *fd, void *buffer, unsigned len); + +STATIC char * objmodtoseg (const char *modname); +STATIC void obj_browse_flush(); +STATIC void objfixupp (struct FIXUP *); +STATIC void ledata_new (int seg,targ_size_t offset); + +static long elf_align(targ_size_t size, long offset); + +// The object file is built is several separate pieces + + +// String Table - String table for all other names +static Outbuffer *symtab_strings; + +// Section Headers +Outbuffer *SECbuf; // Buffer to build section table in +#define SecHdrTab ((struct section *)SECbuf->buf) +#define SecHdrTab64 ((struct section_64 *)SECbuf->buf) + +// The relocation for text and data seems to get lost. +// Try matching the order gcc output them +// This means defining the sections and then removing them if they are +// not used. +static int section_cnt; // Number of sections in table +#define SEC_TAB_INIT 16 // Initial number of sections in buffer +#define SEC_TAB_INC 4 // Number of sections to increment buffer by + +#define SYM_TAB_INIT 100 // Initial number of symbol entries in buffer +#define SYM_TAB_INC 50 // Number of symbols to increment buffer by + +/* Three symbol tables, because the different types of symbols + * are grouped into 3 different types (and a 4th for comdef's). + */ + +static Outbuffer *local_symbuf; +static Outbuffer *public_symbuf; +static Outbuffer *extern_symbuf; + +struct Comdef { symbol *sym; targ_size_t size; int count; }; +static Outbuffer *comdef_symbuf; // Comdef's are stored here + +static Outbuffer *indirectsymbuf1; // indirect symbol table of Symbol*'s +static int jumpTableSeg; // segment index for __jump_table + +static Outbuffer *indirectsymbuf2; // indirect symbol table of Symbol*'s +static int pointersSeg; // segment index for __pointers + +/* If an objextdef() happens, set this to the string index, + * to be added last to the symbol table. + * Obviously, there can be only one. + */ +static IDXSTR extdef; + +#if 0 +#define STI_FILE 1 // Where file symbol table entry is +#define STI_TEXT 2 +#define STI_DATA 3 +#define STI_BSS 4 +#define STI_GCC 5 // Where "gcc2_compiled" symbol is */ +#define STI_RODAT 6 // Symbol for readonly data +#define STI_COM 8 +#endif + +// Each compiler segment is a section +// Predefined compiler segments CODE,DATA,CDATA,UDATA map to indexes +// into SegData[] +// New compiler segments are added to end. + +/****************************** + * Returns !=0 if this segment is a code segment. + */ + +int seg_data::isCode() +{ + if (I64) + { + //printf("SDshtidx = %d, x%x\n", SDshtidx, SecHdrTab64[SDshtidx].flags); + return strcmp(SecHdrTab64[SDshtidx].segname, "__TEXT") == 0; + } + else + { + //printf("SDshtidx = %d, x%x\n", SDshtidx, SecHdrTab[SDshtidx].flags); + return strcmp(SecHdrTab[SDshtidx].segname, "__TEXT") == 0; + } +} + + +seg_data **SegData; +int seg_count; +int seg_max; +int seg_tlsseg = UNKNOWN; +int seg_tlsseg_bss = UNKNOWN; + +/******************************************************* + * Because the Mach-O relocations cannot be computed until after + * all the segments are written out, and we need more information + * than the Mach-O relocations provide, make our own relocation + * type. Later, translate to Mach-O relocation structure. + */ + +struct Relocation +{ // Relocations are attached to the struct seg_data they refer to + targ_size_t offset; // location in segment to be fixed up + symbol *funcsym; // function in which offset lies, if any + symbol *targsym; // if !=NULL, then location is to be fixed up + // to address of this symbol + unsigned targseg; // if !=0, then location is to be fixed up + // to address of start of this segment + unsigned char rtype; // RELxxxx +#define RELaddr 0 // straight address +#define RELrel 1 // relative to location to be fixed up + short val; // 0, -1, -2, -4 +}; + + +/******************************* + * Output a string into a string table + * Input: + * strtab = string table for entry + * str = string to add + * + * Returns index into the specified string table. + */ + +IDXSTR elf_addstr(Outbuffer *strtab, const char *str) +{ + //printf("elf_addstr(strtab = %p str = '%s')\n",strtab,str); + IDXSTR idx = strtab->size(); // remember starting offset + strtab->writeString(str); + //printf("\tidx %d, new size %d\n",idx,strtab->size()); + return idx; +} + +/******************************* + * Find a string in a string table + * Input: + * strtab = string table for entry + * str = string to find + * + * Returns index into the specified string table or 0. + */ + +static IDXSTR elf_findstr(Outbuffer *strtab, const char *str, const char *suffix) +{ + const char *ent = (char *)strtab->buf+1; + const char *pend = ent+strtab->size() - 1; + const char *s = str; + const char *sx = suffix; + int len = strlen(str); + + if (suffix) + len += strlen(suffix); + + while(ent < pend) + { + if(*ent == 0) // end of table entry + { + if(*s == 0 && !sx) // end of string - found a match + { + return ent - (const char *)strtab->buf - len; + } + else // table entry too short + { + s = str; // back to beginning of string + sx = suffix; + ent++; // start of next table entry + } + } + else if (*s == 0 && sx && *sx == *ent) + { // matched first string + s = sx+1; // switch to suffix + ent++; + sx = NULL; + } + else // continue comparing + { + if (*ent == *s) + { // Have a match going + ent++; + s++; + } + else // no match + { + while(*ent != 0) // skip to end of entry + ent++; + ent++; // start of next table entry + s = str; // back to beginning of string + sx = suffix; + } + } + } + return 0; // never found match +} + +/******************************* + * Output a mangled string into the symbol string table + * Input: + * str = string to add + * + * Returns index into the table. + */ + +static IDXSTR elf_addmangled(Symbol *s) +{ + //printf("elf_addmangled(%s)\n", s->Sident); + char dest[DEST_LEN]; + char *destr; + const char *name; + int len; + IDXSTR namidx; + + namidx = symtab_strings->size(); + destr = obj_mangle2(s, dest); + name = destr; + if (CPP && name[0] == '_' && name[1] == '_') + { + if (strncmp(name,"__ct__",6) == 0) + name += 4; +#if 0 + switch(name[2]) + { + case 'c': + if (strncmp(name,"__ct__",6) == 0) + name += 4; + break; + case 'd': + if (strcmp(name,"__dl__FvP") == 0) + name = "__builtin_delete"; + break; + case 'v': + //if (strcmp(name,"__vec_delete__FvPiUIPi") == 0) + //name = "__builtin_vec_del"; + //else + //if (strcmp(name,"__vn__FPUI") == 0) + //name = "__builtin_vec_new"; + break; + case 'n': + if (strcmp(name,"__nw__FPUI") == 0) + name = "__builtin_new"; + break; + } +#endif + } + else if (tyfunc(s->ty()) && s->Sfunc && s->Sfunc->Fredirect) + name = s->Sfunc->Fredirect; + len = strlen(name); + symtab_strings->reserve(len+1); + strcpy((char *)symtab_strings->p,name); + symtab_strings->setsize(namidx+len+1); + if (destr != dest) // if we resized result + mem_free(destr); + //dbg_printf("\telf_addmagled symtab_strings %s namidx %d len %d size %d\n",name, namidx,len,symtab_strings->size()); + return namidx; +} + +/************************** + * Ouput read only data and generate a symbol for it. + * + */ + +symbol * elf_sym_cdata(tym_t ty,char *p,int len) +{ + symbol *s; + +#if 0 + if (I64) + { + alignOffset(DATA, tysize(ty)); + s = symboldata(Doffset, ty); + SegData[DATA]->SDbuf->write(p,len); + s->Sseg = DATA; + s->Soffset = Doffset; // Remember its offset into DATA section + Doffset += len; + } + else +#endif + { + //printf("elf_sym_cdata(ty = %x, p = %x, len = %d, CDoffset = %x)\n", ty, p, len, CDoffset); + alignOffset(CDATA, tysize(ty)); + s = symboldata(CDoffset, ty); + s->Sseg = CDATA; + //objpubdef(CDATA, s, CDoffset); + obj_bytes(CDATA, CDoffset, len, p); + } + + s->Sfl = /*(config.flags3 & CFG3pic) ? FLgotoff :*/ FLextern; + return s; +} + +/************************** + * Ouput read only data for data + * + */ + +int elf_data_cdata(char *p, int len, int *pseg) +{ + int oldoff; + if (I64) + { + oldoff = Doffset; + SegData[DATA]->SDbuf->reserve(len); + SegData[DATA]->SDbuf->writen(p,len); + Doffset += len; + *pseg = DATA; + } + else + { + oldoff = CDoffset; + SegData[CDATA]->SDbuf->reserve(len); + SegData[CDATA]->SDbuf->writen(p,len); + CDoffset += len; + *pseg = CDATA; + } + return oldoff; +} + +int elf_data_cdata(char *p, int len) +{ + int pseg; + + return elf_data_cdata(p, len, &pseg); +} + +/****************************** + * Perform initialization that applies to all .o output files. + * Called before any other obj_xxx routines + */ + +void obj_init(Outbuffer *objbuf, const char *filename, const char *csegname) +{ + //printf("obj_init()\n"); + cseg = CODE; + fobjbuf = objbuf; + + seg_tlsseg = UNKNOWN; + seg_tlsseg_bss = UNKNOWN; + GOTsym = NULL; + + // Initialize buffers + + if (symtab_strings) + symtab_strings->setsize(1); + else + { symtab_strings = new Outbuffer(1024); + symtab_strings->reserve(2048); + symtab_strings->writeByte(0); + } + + if (!local_symbuf) + local_symbuf = new Outbuffer(sizeof(symbol *) * SYM_TAB_INIT); + local_symbuf->setsize(0); + + if (!public_symbuf) + public_symbuf = new Outbuffer(sizeof(symbol *) * SYM_TAB_INIT); + public_symbuf->setsize(0); + + if (!extern_symbuf) + extern_symbuf = new Outbuffer(sizeof(symbol *) * SYM_TAB_INIT); + extern_symbuf->setsize(0); + + if (!comdef_symbuf) + comdef_symbuf = new Outbuffer(sizeof(symbol *) * SYM_TAB_INIT); + comdef_symbuf->setsize(0); + + extdef = 0; + + if (indirectsymbuf1) + indirectsymbuf1->setsize(0); + jumpTableSeg = 0; + + if (indirectsymbuf2) + indirectsymbuf2->setsize(0); + pointersSeg = 0; + + // Initialize segments for CODE, DATA, UDATA and CDATA + size_t struct_section_size = I64 ? sizeof(struct section_64) : sizeof(struct section); + if (SECbuf) + { + SECbuf->setsize(struct_section_size); + } + else + { + SECbuf = new Outbuffer(SYM_TAB_INC * struct_section_size); + SECbuf->reserve(SEC_TAB_INIT * struct_section_size); + // Ignore the first section - section numbers start at 1 + SECbuf->writezeros(struct_section_size); + } + section_cnt = 1; + + seg_count = 0; + int align = I64 ? 4 : 2; // align to 16 bytes for floating point + mach_getsegment("__text", "__TEXT", 2, S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS); + mach_getsegment("__data", "__DATA", align, S_REGULAR); // DATA + mach_getsegment("__const", "__TEXT", 2, S_REGULAR); // CDATA + mach_getsegment("__bss", "__DATA", 4, S_ZEROFILL); // UDATA + + if (config.fulltypes) + dwarf_initfile(filename); +} + +/************************** + * Initialize the start of object output for this particular .o file. + * + * Input: + * filename: Name of source file + * csegname: User specified default code segment name + */ + +void obj_initfile(const char *filename, const char *csegname, const char *modname) +{ + //dbg_printf("obj_initfile(filename = %s, modname = %s)\n",filename,modname); +#if SCPP + if (csegname && *csegname && strcmp(csegname,".text")) + { // Define new section and make it the default for cseg segment + // NOTE: cseg is initialized to CODE + IDXSEC newsecidx; + Elf32_Shdr *newtextsec; + IDXSYM newsymidx; + assert(!I64); // fix later + SegData[cseg]->SDshtidx = newsecidx = + elf_newsection(csegname,0,SHT_PROGDEF,SHF_ALLOC|SHF_EXECINSTR); + newtextsec = &SecHdrTab[newsecidx]; + newtextsec->sh_addralign = 4; + SegData[cseg]->SDsymidx = + elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, newsecidx); + } +#endif + if (config.fulltypes) + dwarf_initmodule(filename, modname); +} + +/************************************ + * Patch pseg/offset by adding in the vmaddr difference from + * pseg/offset to start of seg. + */ + +int32_t *patchAddr(int seg, targ_size_t offset) +{ + return(int32_t *)(fobjbuf->buf + SecHdrTab[SegData[seg]->SDshtidx].offset + offset); +} + +int32_t *patchAddr64(int seg, targ_size_t offset) +{ + return(int32_t *)(fobjbuf->buf + SecHdrTab64[SegData[seg]->SDshtidx].offset + offset); +} + +void patch(seg_data *pseg, targ_size_t offset, int seg, targ_size_t value) +{ + //printf("patch(offset = x%04x, seg = %d, value = x%llx)\n", (unsigned)offset, seg, value); + if (I64) + { + int32_t *p = (int32_t *)(fobjbuf->buf + SecHdrTab64[pseg->SDshtidx].offset + offset); +#if 0 + printf("\taddr1 = x%llx\n\taddr2 = x%llx\n\t*p = x%llx\n\tdelta = x%llx\n", + SecHdrTab64[pseg->SDshtidx].addr, + SecHdrTab64[SegData[seg]->SDshtidx].addr, + *p, + SecHdrTab64[SegData[seg]->SDshtidx].addr - + (SecHdrTab64[pseg->SDshtidx].addr + offset)); +#endif + *p += SecHdrTab64[SegData[seg]->SDshtidx].addr - + (SecHdrTab64[pseg->SDshtidx].addr - value); + } + else + { + int32_t *p = (int32_t *)(fobjbuf->buf + SecHdrTab[pseg->SDshtidx].offset + offset); +#if 0 + printf("\taddr1 = x%x\n\taddr2 = x%x\n\t*p = x%x\n\tdelta = x%x\n", + SecHdrTab[pseg->SDshtidx].addr, + SecHdrTab[SegData[seg]->SDshtidx].addr, + *p, + SecHdrTab[SegData[seg]->SDshtidx].addr - + (SecHdrTab[pseg->SDshtidx].addr + offset)); +#endif + *p += SecHdrTab[SegData[seg]->SDshtidx].addr - + (SecHdrTab[pseg->SDshtidx].addr - value); + } +} + +/*************************** + * Number symbols so they are + * ordered as locals, public and then extern/comdef + */ + +void mach_numbersyms() +{ + //printf("mach_numbersyms()\n"); + int n = 0; + + int dim; + dim = local_symbuf->size() / sizeof(symbol *); + for (int i = 0; i < dim; i++) + { symbol *s = ((symbol **)local_symbuf->buf)[i]; + s->Sxtrnnum = n; + n++; + } + + dim = public_symbuf->size() / sizeof(symbol *); + for (int i = 0; i < dim; i++) + { symbol *s = ((symbol **)public_symbuf->buf)[i]; + s->Sxtrnnum = n; + n++; + } + + dim = extern_symbuf->size() / sizeof(symbol *); + for (int i = 0; i < dim; i++) + { symbol *s = ((symbol **)extern_symbuf->buf)[i]; + s->Sxtrnnum = n; + n++; + } + + dim = comdef_symbuf->size() / sizeof(Comdef); + for (int i = 0; i < dim; i++) + { Comdef *c = ((Comdef *)comdef_symbuf->buf) + i; + c->sym->Sxtrnnum = n; + n++; + } +} + + +/*************************** + * Fixup and terminate object file. + */ + +void obj_termfile() +{ + //dbg_printf("obj_termfile\n"); + if (configv.addlinenumbers) + { + dwarf_termmodule(); + } +} + +/********************************* + * Terminate package. + */ + +void obj_term() +{ + //printf("obj_term()\n"); +#if SCPP + if (!errcnt) +#endif + { + outfixlist(); // backpatches + } + + if (configv.addlinenumbers) + { + dwarf_termfile(); + } + +#if SCPP + if (errcnt) + return; +#endif + + /* Write out the object file in the following order: + * header + * commands + * segment_command + * { sections } + * symtab_command + * dysymtab_command + * { segment contents } + * { relocations } + * symbol table + * string table + * indirect symbol table + */ + + unsigned foffset; + unsigned headersize; + unsigned sizeofcmds; + + // Write out the bytes for the header + if (I64) + { + mach_header_64 header; + + header.magic = MH_MAGIC_64; + header.cputype = CPU_TYPE_X86_64; + header.cpusubtype = CPU_SUBTYPE_I386_ALL; + header.filetype = MH_OBJECT; + header.ncmds = 3; + header.sizeofcmds = sizeof(segment_command_64) + + (section_cnt - 1) * sizeof(struct section_64) + + sizeof(symtab_command) + + sizeof(dysymtab_command); + header.flags = MH_SUBSECTIONS_VIA_SYMBOLS; + header.reserved = 0; + fobjbuf->write(&header, sizeof(header)); + foffset = sizeof(header); // start after header + headersize = sizeof(header); + sizeofcmds = header.sizeofcmds; + + // Write the actual data later + fobjbuf->writezeros(header.sizeofcmds); + foffset += header.sizeofcmds; + } + else + { + mach_header header; + + header.magic = MH_MAGIC; + header.cputype = CPU_TYPE_I386; + header.cpusubtype = CPU_SUBTYPE_I386_ALL; + header.filetype = MH_OBJECT; + header.ncmds = 3; + header.sizeofcmds = sizeof(segment_command) + + (section_cnt - 1) * sizeof(struct section) + + sizeof(symtab_command) + + sizeof(dysymtab_command); + header.flags = MH_SUBSECTIONS_VIA_SYMBOLS; + fobjbuf->write(&header, sizeof(header)); + foffset = sizeof(header); // start after header + headersize = sizeof(header); + sizeofcmds = header.sizeofcmds; + + // Write the actual data later + fobjbuf->writezeros(header.sizeofcmds); + foffset += header.sizeofcmds; + } + + struct segment_command segment_cmd; + struct segment_command_64 segment_cmd64; + struct symtab_command symtab_cmd; + struct dysymtab_command dysymtab_cmd; + + memset(&segment_cmd, 0, sizeof(segment_cmd)); + memset(&segment_cmd64, 0, sizeof(segment_cmd64)); + memset(&symtab_cmd, 0, sizeof(symtab_cmd)); + memset(&dysymtab_cmd, 0, sizeof(dysymtab_cmd)); + + if (I64) + { + segment_cmd64.cmd = LC_SEGMENT_64; + segment_cmd64.cmdsize = sizeof(segment_cmd64) + + (section_cnt - 1) * sizeof(struct section_64); + segment_cmd64.nsects = section_cnt - 1; + segment_cmd64.maxprot = 7; + segment_cmd64.initprot = 7; + } + else + { + segment_cmd.cmd = LC_SEGMENT; + segment_cmd.cmdsize = sizeof(segment_cmd) + + (section_cnt - 1) * sizeof(struct section); + segment_cmd.nsects = section_cnt - 1; + segment_cmd.maxprot = 7; + segment_cmd.initprot = 7; + } + + symtab_cmd.cmd = LC_SYMTAB; + symtab_cmd.cmdsize = sizeof(symtab_cmd); + + dysymtab_cmd.cmd = LC_DYSYMTAB; + dysymtab_cmd.cmdsize = sizeof(dysymtab_cmd); + + /* If a __pointers section was emitted, need to set the .reserved1 + * field to the symbol index in the indirect symbol table of the + * start of the __pointers symbols. + */ + if (pointersSeg) + { + seg_data *pseg = SegData[pointersSeg]; + if (I64) + { + struct section_64 *psechdr = &SecHdrTab64[pseg->SDshtidx]; // corresponding section + psechdr->reserved1 = indirectsymbuf1 + ? indirectsymbuf1->size() / sizeof(Symbol *) + : 0; + } + else + { + struct section *psechdr = &SecHdrTab[pseg->SDshtidx]; // corresponding section + psechdr->reserved1 = indirectsymbuf1 + ? indirectsymbuf1->size() / sizeof(Symbol *) + : 0; + } + } + + // Walk through sections determining size and file offsets + + // + // First output individual section data associate with program + // code and data + // + foffset = elf_align(I64 ? 8 : 4, foffset); + if (I64) + segment_cmd64.fileoff = foffset; + else + segment_cmd.fileoff = foffset; + unsigned vmaddr = 0; + + //printf("Setup offsets and sizes foffset %d\n\tsection_cnt %d, seg_count %d\n",foffset,section_cnt,seg_count); + // Zero filled segments go at the end, so go through segments twice + for (int i = 0; i < 2; i++) + { + for (int seg = 1; seg <= seg_count; seg++) + { + seg_data *pseg = SegData[seg]; + if (I64) + { + struct section_64 *psechdr = &SecHdrTab64[pseg->SDshtidx]; // corresponding section + + // Do zero-fill the second time through this loop + if (i ^ (psechdr->flags == S_ZEROFILL)) + continue; + + int align = 1 << psechdr->align; + foffset = elf_align(align, foffset); + vmaddr = (vmaddr + align - 1) & ~(align - 1); + if (psechdr->flags == S_ZEROFILL) + { + psechdr->offset = 0; + psechdr->size = pseg->SDoffset; // accumulated size + } + else + { + psechdr->offset = foffset; + psechdr->size = 0; + //printf("\tsection name %s,", psechdr->sectname); + if (pseg->SDbuf && pseg->SDbuf->size()) + { + //printf("\tsize %d\n", pseg->SDbuf->size()); + psechdr->size = pseg->SDbuf->size(); + fobjbuf->write(pseg->SDbuf->buf, psechdr->size); + foffset += psechdr->size; + } + } + psechdr->addr = vmaddr; + vmaddr += psechdr->size; + //printf(" assigned offset %d, size %d\n", foffset, psechdr->sh_size); + } + else + { + struct section *psechdr = &SecHdrTab[pseg->SDshtidx]; // corresponding section + + // Do zero-fill the second time through this loop + if (i ^ (psechdr->flags == S_ZEROFILL)) + continue; + + int align = 1 << psechdr->align; + foffset = elf_align(align, foffset); + vmaddr = (vmaddr + align - 1) & ~(align - 1); + if (psechdr->flags == S_ZEROFILL) + { + psechdr->offset = 0; + psechdr->size = pseg->SDoffset; // accumulated size + } + else + { + psechdr->offset = foffset; + psechdr->size = 0; + //printf("\tsection name %s,", psechdr->sectname); + if (pseg->SDbuf && pseg->SDbuf->size()) + { + //printf("\tsize %d\n", pseg->SDbuf->size()); + psechdr->size = pseg->SDbuf->size(); + fobjbuf->write(pseg->SDbuf->buf, psechdr->size); + foffset += psechdr->size; + } + } + psechdr->addr = vmaddr; + vmaddr += psechdr->size; + //printf(" assigned offset %d, size %d\n", foffset, psechdr->sh_size); + } + } + } + + if (I64) + { + segment_cmd64.vmsize = vmaddr; + segment_cmd64.filesize = foffset - segment_cmd64.fileoff; + /* Bugzilla 5331: Apparently having the filesize field greater than the vmsize field is an + * error, and is happening sometimes. + */ + if (segment_cmd64.filesize > vmaddr) + segment_cmd64.vmsize = segment_cmd64.filesize; + } + else + { + segment_cmd.vmsize = vmaddr; + segment_cmd.filesize = foffset - segment_cmd.fileoff; + /* Bugzilla 5331: Apparently having the filesize field greater than the vmsize field is an + * error, and is happening sometimes. + */ + if (segment_cmd.filesize > vmaddr) + segment_cmd.vmsize = segment_cmd.filesize; + } + + // Put out relocation data + mach_numbersyms(); + for (int seg = 1; seg <= seg_count; seg++) + { + seg_data *pseg = SegData[seg]; + struct section *psechdr = NULL; + struct section_64 *psechdr64 = NULL; + if (I64) + { + psechdr64 = &SecHdrTab64[pseg->SDshtidx]; // corresponding section + //printf("psechdr->addr = x%llx\n", psechdr64->addr); + } + else + { + psechdr = &SecHdrTab[pseg->SDshtidx]; // corresponding section + //printf("psechdr->addr = x%x\n", psechdr->addr); + } + foffset = elf_align(I64 ? 8 : 4, foffset); + unsigned reloff = foffset; + unsigned nreloc = 0; + if (pseg->SDrel) + { Relocation *r = (Relocation *)pseg->SDrel->buf; + Relocation *rend = (Relocation *)(pseg->SDrel->buf + pseg->SDrel->size()); + for (; r != rend; r++) + { symbol *s = r->targsym; + const char *rs = r->rtype == RELaddr ? "addr" : "rel"; + //printf("%d:x%04llx : tseg %d tsym %s REL%s\n", seg, r->offset, r->targseg, s ? s->Sident : "0", rs); + relocation_info rel; + scattered_relocation_info srel; + if (s) + { + //printf("Relocation\n"); + //symbol_print(s); + if (pseg->isCode()) + { + if (I64) + { + rel.r_type = (r->rtype == RELrel) + ? X86_64_RELOC_BRANCH + : X86_64_RELOC_SIGNED; + if (r->val == -1) + rel.r_type = X86_64_RELOC_SIGNED_1; + else if (r->val == -2) + rel.r_type = X86_64_RELOC_SIGNED_2; + if (r->val == -4) + rel.r_type = X86_64_RELOC_SIGNED_4; + + if (s->Sclass == SCextern || + s->Sclass == SCcomdef || + s->Sclass == SCcomdat || + s->Sclass == SCglobal) + { + if ((s->Sfl == FLfunc || s->Sfl == FLextern || s->Sclass == SCglobal || s->Sclass == SCcomdat || s->Sclass == SCcomdef) && r->rtype == RELaddr) + rel.r_type = X86_64_RELOC_GOT_LOAD; + rel.r_address = r->offset; + rel.r_symbolnum = s->Sxtrnnum; + rel.r_pcrel = 1; + rel.r_length = 2; + rel.r_extern = 1; + fobjbuf->write(&rel, sizeof(rel)); + foffset += sizeof(rel); + nreloc++; + continue; + } + else + { + rel.r_address = r->offset; + rel.r_symbolnum = s->Sseg; + rel.r_pcrel = 1; + rel.r_length = 2; + rel.r_extern = 0; + fobjbuf->write(&rel, sizeof(rel)); + foffset += sizeof(rel); + nreloc++; + + int32_t *p = patchAddr64(seg, r->offset); + // Absolute address; add in addr of start of targ seg +//printf("*p = x%x, .addr = x%x, Soffset = x%x\n", *p, (int)SecHdrTab64[SegData[s->Sseg]->SDshtidx].addr, (int)s->Soffset); +//printf("pseg = x%x, r->offset = x%x\n", (int)SecHdrTab64[pseg->SDshtidx].addr, (int)r->offset); + *p += SecHdrTab64[SegData[s->Sseg]->SDshtidx].addr; + *p += s->Soffset; + *p -= SecHdrTab64[pseg->SDshtidx].addr + r->offset + 4; + //patch(pseg, r->offset, s->Sseg, s->Soffset); + continue; + } + } + } + else + { + if (s->Sclass == SCextern || + s->Sclass == SCcomdef || + s->Sclass == SCcomdat) + { + rel.r_address = r->offset; + rel.r_symbolnum = s->Sxtrnnum; + rel.r_pcrel = 0; + rel.r_length = 2; + rel.r_extern = 1; + rel.r_type = GENERIC_RELOC_VANILLA; + if (I64) + { + rel.r_type = X86_64_RELOC_UNSIGNED; + rel.r_length = 3; + } + fobjbuf->write(&rel, sizeof(rel)); + foffset += sizeof(rel); + nreloc++; + continue; + } + else + { + rel.r_address = r->offset; + rel.r_symbolnum = s->Sseg; + rel.r_pcrel = 0; + rel.r_length = 2; + rel.r_extern = 0; + rel.r_type = GENERIC_RELOC_VANILLA; + if (I64) + { + rel.r_type = X86_64_RELOC_UNSIGNED; + rel.r_length = 3; + if (0 && s->Sseg != seg) + rel.r_type = X86_64_RELOC_BRANCH; + } + fobjbuf->write(&rel, sizeof(rel)); + foffset += sizeof(rel); + nreloc++; + if (I64) + { + rel.r_length = 3; + int32_t *p = patchAddr64(seg, r->offset); + // Absolute address; add in addr of start of targ seg + *p += SecHdrTab64[SegData[s->Sseg]->SDshtidx].addr + s->Soffset; + //patch(pseg, r->offset, s->Sseg, s->Soffset); + } + else + { + int32_t *p = patchAddr(seg, r->offset); + // Absolute address; add in addr of start of targ seg + *p += SecHdrTab[SegData[s->Sseg]->SDshtidx].addr + s->Soffset; + //patch(pseg, r->offset, s->Sseg, s->Soffset); + } + continue; + } + } + } + else if (r->rtype == RELaddr && pseg->isCode()) + { + int32_t *p = NULL; + int32_t *p64 = NULL; + if (I64) + p64 = patchAddr64(seg, r->offset); + else + p = patchAddr(seg, r->offset); + srel.r_scattered = 1; + + srel.r_address = r->offset; + srel.r_length = 2; + if (I64) + { + srel.r_type = X86_64_RELOC_GOT; + srel.r_value = SecHdrTab64[SegData[r->targseg]->SDshtidx].addr + *p64; + //printf("SECTDIFF: x%llx + x%llx = x%x\n", SecHdrTab[SegData[r->targseg]->SDshtidx].addr, *p, srel.r_value); + } + else + { + srel.r_type = GENERIC_RELOC_LOCAL_SECTDIFF; + srel.r_value = SecHdrTab[SegData[r->targseg]->SDshtidx].addr + *p; + //printf("SECTDIFF: x%x + x%x = x%x\n", SecHdrTab[SegData[r->targseg]->SDshtidx].addr, *p, srel.r_value); + } + srel.r_pcrel = 0; + fobjbuf->write(&srel, sizeof(srel)); + foffset += sizeof(srel); + nreloc++; + + srel.r_address = 0; + srel.r_type = GENERIC_RELOC_PAIR; + srel.r_length = 2; + if (I64) + srel.r_value = SecHdrTab64[pseg->SDshtidx].addr + + r->funcsym->Slocalgotoffset + NPTRSIZE; + else + srel.r_value = SecHdrTab[pseg->SDshtidx].addr + + r->funcsym->Slocalgotoffset + NPTRSIZE; + srel.r_pcrel = 0; + fobjbuf->write(&srel, sizeof(srel)); + foffset += sizeof(srel); + nreloc++; + + // Recalc due to possible realloc of fobjbuf->buf + if (I64) + { + p64 = patchAddr64(seg, r->offset); + //printf("address = x%x, p64 = %p *p64 = x%llx\n", r->offset, p64, *p64); + *p64 += SecHdrTab64[SegData[r->targseg]->SDshtidx].addr - + (SecHdrTab64[pseg->SDshtidx].addr + r->funcsym->Slocalgotoffset + NPTRSIZE); + } + else + { + p = patchAddr(seg, r->offset); + //printf("address = x%x, p = %p *p = x%x\n", r->offset, p, *p); + *p += SecHdrTab[SegData[r->targseg]->SDshtidx].addr - + (SecHdrTab[pseg->SDshtidx].addr + r->funcsym->Slocalgotoffset + NPTRSIZE); + } + continue; + } + else + { + rel.r_address = r->offset; + rel.r_symbolnum = r->targseg; + rel.r_pcrel = (r->rtype == RELaddr) ? 0 : 1; + rel.r_length = 2; + rel.r_extern = 0; + rel.r_type = GENERIC_RELOC_VANILLA; + if (I64) + { + rel.r_type = X86_64_RELOC_UNSIGNED; + rel.r_length = 3; + if (0 && r->targseg != seg) + rel.r_type = X86_64_RELOC_BRANCH; + } + fobjbuf->write(&rel, sizeof(rel)); + foffset += sizeof(rel); + nreloc++; + if (I64) + { + int32_t *p64 = patchAddr64(seg, r->offset); + //int64_t before = *p64; + if (rel.r_pcrel) + // Relative address + patch(pseg, r->offset, r->targseg, 0); + else + { // Absolute address; add in addr of start of targ seg +//printf("*p = x%x, targ.addr = x%x\n", *p64, (int)SecHdrTab64[SegData[r->targseg]->SDshtidx].addr); +//printf("pseg = x%x, r->offset = x%x\n", (int)SecHdrTab64[pseg->SDshtidx].addr, (int)r->offset); + *p64 += SecHdrTab64[SegData[r->targseg]->SDshtidx].addr; + //*p64 -= SecHdrTab64[pseg->SDshtidx].addr; + } + //printf("%d:x%04x before = x%04llx, after = x%04llx pcrel = %d\n", seg, r->offset, before, *p64, rel.r_pcrel); + } + else + { + int32_t *p = patchAddr(seg, r->offset); + //int32_t before = *p; + if (rel.r_pcrel) + // Relative address + patch(pseg, r->offset, r->targseg, 0); + else + // Absolute address; add in addr of start of targ seg + *p += SecHdrTab[SegData[r->targseg]->SDshtidx].addr; + //printf("%d:x%04x before = x%04x, after = x%04x pcrel = %d\n", seg, r->offset, before, *p, rel.r_pcrel); + } + continue; + } + } + } + if (nreloc) + { + if (I64) + { + psechdr64->reloff = reloff; + psechdr64->nreloc = nreloc; + } + else + { + psechdr->reloff = reloff; + psechdr->nreloc = nreloc; + } + } + } + + // Put out symbol table + foffset = elf_align(I64 ? 8 : 4, foffset); + symtab_cmd.symoff = foffset; + dysymtab_cmd.ilocalsym = 0; + dysymtab_cmd.nlocalsym = local_symbuf->size() / sizeof(symbol *); + dysymtab_cmd.iextdefsym = dysymtab_cmd.nlocalsym; + dysymtab_cmd.nextdefsym = public_symbuf->size() / sizeof(symbol *); + dysymtab_cmd.iundefsym = dysymtab_cmd.iextdefsym + dysymtab_cmd.nextdefsym; + int nexterns = extern_symbuf->size() / sizeof(symbol *); + int ncomdefs = comdef_symbuf->size() / sizeof(Comdef); + dysymtab_cmd.nundefsym = nexterns + ncomdefs; + symtab_cmd.nsyms = dysymtab_cmd.nlocalsym + + dysymtab_cmd.nextdefsym + + dysymtab_cmd.nundefsym; + fobjbuf->reserve(symtab_cmd.nsyms * (I64 ? sizeof(struct nlist_64) : sizeof(struct nlist))); + for (int i = 0; i < dysymtab_cmd.nlocalsym; i++) + { symbol *s = ((symbol **)local_symbuf->buf)[i]; + struct nlist_64 sym; + sym.n_un.n_strx = elf_addmangled(s); + sym.n_type = N_SECT; + sym.n_desc = 0; + if (s->Sclass == SCcomdat) + sym.n_desc = N_WEAK_DEF; + sym.n_sect = s->Sseg; + if (I64) + { + sym.n_value = s->Soffset + SecHdrTab64[SegData[s->Sseg]->SDshtidx].addr; + fobjbuf->write(&sym, sizeof(sym)); + } + else + { + struct nlist sym32; + sym32.n_un.n_strx = sym.n_un.n_strx; + sym32.n_value = s->Soffset + SecHdrTab[SegData[s->Sseg]->SDshtidx].addr; + sym32.n_type = sym.n_type; + sym32.n_desc = sym.n_desc; + sym32.n_sect = sym.n_sect; + fobjbuf->write(&sym32, sizeof(sym32)); + } + } + for (int i = 0; i < dysymtab_cmd.nextdefsym; i++) + { symbol *s = ((symbol **)public_symbuf->buf)[i]; + + //printf("Writing public symbol %d:x%x %s\n", s->Sseg, s->Soffset, s->Sident); + struct nlist_64 sym; + sym.n_un.n_strx = elf_addmangled(s); + sym.n_type = N_EXT | N_SECT; + sym.n_desc = 0; + if (s->Sclass == SCcomdat) + sym.n_desc = N_WEAK_DEF; + sym.n_sect = s->Sseg; + if (I64) + { + sym.n_value = s->Soffset + SecHdrTab64[SegData[s->Sseg]->SDshtidx].addr; + fobjbuf->write(&sym, sizeof(sym)); + } + else + { + struct nlist sym32; + sym32.n_un.n_strx = sym.n_un.n_strx; + sym32.n_value = s->Soffset + SecHdrTab[SegData[s->Sseg]->SDshtidx].addr; + sym32.n_type = sym.n_type; + sym32.n_desc = sym.n_desc; + sym32.n_sect = sym.n_sect; + fobjbuf->write(&sym32, sizeof(sym32)); + } + } + for (int i = 0; i < nexterns; i++) + { symbol *s = ((symbol **)extern_symbuf->buf)[i]; + struct nlist_64 sym; + sym.n_un.n_strx = elf_addmangled(s); + sym.n_value = s->Soffset; + sym.n_type = N_EXT | N_UNDF; + sym.n_desc = tyfunc(s->ty()) ? REFERENCE_FLAG_UNDEFINED_LAZY + : REFERENCE_FLAG_UNDEFINED_NON_LAZY; + sym.n_sect = 0; + if (I64) + fobjbuf->write(&sym, sizeof(sym)); + else + { + struct nlist sym32; + sym32.n_un.n_strx = sym.n_un.n_strx; + sym32.n_value = sym.n_value; + sym32.n_type = sym.n_type; + sym32.n_desc = sym.n_desc; + sym32.n_sect = sym.n_sect; + fobjbuf->write(&sym32, sizeof(sym32)); + } + } + for (int i = 0; i < ncomdefs; i++) + { Comdef *c = ((Comdef *)comdef_symbuf->buf) + i; + struct nlist_64 sym; + sym.n_un.n_strx = elf_addmangled(c->sym); + sym.n_value = c->size * c->count; + sym.n_type = N_EXT | N_UNDF; + int align; + if (c->size < 2) + align = 0; // align is expressed as power of 2 + else if (c->size < 4) + align = 1; + else if (c->size < 8) + align = 2; + else if (c->size < 16) + align = 3; + else + align = 4; + sym.n_desc = align << 8; + sym.n_sect = 0; + if (I64) + fobjbuf->write(&sym, sizeof(sym)); + else + { + struct nlist sym32; + sym32.n_un.n_strx = sym.n_un.n_strx; + sym32.n_value = sym.n_value; + sym32.n_type = sym.n_type; + sym32.n_desc = sym.n_desc; + sym32.n_sect = sym.n_sect; + fobjbuf->write(&sym32, sizeof(sym32)); + } + } + if (extdef) + { + struct nlist_64 sym; + sym.n_un.n_strx = extdef; + sym.n_value = 0; + sym.n_type = N_EXT | N_UNDF; + sym.n_desc = 0; + sym.n_sect = 0; + if (I64) + fobjbuf->write(&sym, sizeof(sym)); + else + { + struct nlist sym32; + sym32.n_un.n_strx = sym.n_un.n_strx; + sym32.n_value = sym.n_value; + sym32.n_type = sym.n_type; + sym32.n_desc = sym.n_desc; + sym32.n_sect = sym.n_sect; + fobjbuf->write(&sym32, sizeof(sym32)); + } + symtab_cmd.nsyms++; + } + foffset += symtab_cmd.nsyms * (I64 ? sizeof(struct nlist_64) : sizeof(struct nlist)); + + // Put out string table + foffset = elf_align(I64 ? 8 : 4, foffset); + symtab_cmd.stroff = foffset; + symtab_cmd.strsize = symtab_strings->size(); + fobjbuf->write(symtab_strings->buf, symtab_cmd.strsize); + foffset += symtab_cmd.strsize; + + // Put out indirectsym table, which is in two parts + foffset = elf_align(I64 ? 8 : 4, foffset); + dysymtab_cmd.indirectsymoff = foffset; + if (indirectsymbuf1) + { dysymtab_cmd.nindirectsyms += indirectsymbuf1->size() / sizeof(Symbol *); + for (int i = 0; i < dysymtab_cmd.nindirectsyms; i++) + { Symbol *s = ((Symbol **)indirectsymbuf1->buf)[i]; + fobjbuf->write32(s->Sxtrnnum); + } + } + if (indirectsymbuf2) + { int n = indirectsymbuf2->size() / sizeof(Symbol *); + dysymtab_cmd.nindirectsyms += n; + for (int i = 0; i < n; i++) + { Symbol *s = ((Symbol **)indirectsymbuf2->buf)[i]; + fobjbuf->write32(s->Sxtrnnum); + } + } + foffset += dysymtab_cmd.nindirectsyms * 4; + + /* The correct offsets are now determined, so + * rewind and fix the header. + */ + fobjbuf->position(headersize, sizeofcmds); + if (I64) + { + fobjbuf->write(&segment_cmd64, sizeof(segment_cmd64)); + fobjbuf->write(SECbuf->buf + sizeof(struct section_64), (section_cnt - 1) * sizeof(struct section_64)); + } + else + { + fobjbuf->write(&segment_cmd, sizeof(segment_cmd)); + fobjbuf->write(SECbuf->buf + sizeof(struct section), (section_cnt - 1) * sizeof(struct section)); + } + fobjbuf->write(&symtab_cmd, sizeof(symtab_cmd)); + fobjbuf->write(&dysymtab_cmd, sizeof(dysymtab_cmd)); + fobjbuf->position(foffset, 0); + fobjbuf->flush(); +} + +/***************************** + * Line number support. + */ + +/*************************** + * Record file and line number at segment and offset. + * The actual .debug_line segment is put out by dwarf_termfile(). + * Input: + * cseg current code segment + */ + +void objlinnum(Srcpos srcpos, targ_size_t offset) +{ + if (srcpos.Slinnum == 0) + return; + +#if 0 +#if MARS || SCPP + printf("objlinnum(cseg=%d, offset=x%lx) ", cseg, offset); +#endif + srcpos.print(""); +#endif + +#if MARS + if (!srcpos.Sfilename) + return; +#endif +#if SCPP + if (!srcpos.Sfilptr) + return; + sfile_debug(&srcpos_sfile(srcpos)); + Sfile *sf = *srcpos.Sfilptr; +#endif + + size_t i; + seg_data *seg = SegData[cseg]; + + // Find entry i in SDlinnum_data[] that corresponds to srcpos filename + for (i = 0; 1; i++) + { + if (i == seg->SDlinnum_count) + { // Create new entry + if (seg->SDlinnum_count == seg->SDlinnum_max) + { // Enlarge array + unsigned newmax = seg->SDlinnum_max * 2 + 1; + //printf("realloc %d\n", newmax * sizeof(linnum_data)); + seg->SDlinnum_data = (linnum_data *)mem_realloc( + seg->SDlinnum_data, newmax * sizeof(linnum_data)); + memset(seg->SDlinnum_data + seg->SDlinnum_max, 0, + (newmax - seg->SDlinnum_max) * sizeof(linnum_data)); + seg->SDlinnum_max = newmax; + } + seg->SDlinnum_count++; +#if MARS + seg->SDlinnum_data[i].filename = srcpos.Sfilename; +#endif +#if SCPP + seg->SDlinnum_data[i].filptr = sf; +#endif + break; + } +#if MARS + if (seg->SDlinnum_data[i].filename == srcpos.Sfilename) +#endif +#if SCPP + if (seg->SDlinnum_data[i].filptr == sf) +#endif + break; + } + + linnum_data *ld = &seg->SDlinnum_data[i]; +// printf("i = %d, ld = x%x\n", i, ld); + if (ld->linoff_count == ld->linoff_max) + { + if (!ld->linoff_max) + ld->linoff_max = 8; + ld->linoff_max *= 2; + ld->linoff = (unsigned (*)[2])mem_realloc(ld->linoff, ld->linoff_max * sizeof(unsigned) * 2); + } + ld->linoff[ld->linoff_count][0] = srcpos.Slinnum; + ld->linoff[ld->linoff_count][1] = offset; + ld->linoff_count++; +} + + +/******************************* + * Set start address + */ + +void obj_startaddress(Symbol *s) +{ + //dbg_printf("obj_startaddress(Symbol *%s)\n",s->Sident); + //obj.startaddress = s; +} + +/******************************* + * Output library name. + * Output: + */ + +void obj_includelib(const char *name) +{ + //dbg_printf("obj_includelib(name *%s)\n",name); +} + +/************************** + * Embed string in executable. + */ + +void obj_exestr(const char *p) +{ + //dbg_printf("obj_exestr(char *%s)\n",p); +} + +/************************** + * Embed string in obj. + */ + +void obj_user(const char *p) +{ + //dbg_printf("obj_user(char *%s)\n",p); +} + +/******************************* + * Output a weak extern record. + */ + +void obj_wkext(Symbol *s1,Symbol *s2) +{ + //dbg_printf("obj_wkext(Symbol *%s,Symbol *s2)\n",s1->Sident,s2->Sident); +} + +/******************************* + * Output file name record. + * + * Currently assumes that obj_filename will not be called + * twice for the same file. + */ + +void obj_filename(const char *modname) +{ + //dbg_printf("obj_filename(char *%s)\n",modname); + // Not supported by Mach-O +} + +/******************************* + * Embed compiler version in .obj file. + */ + +void obj_compiler() +{ + //dbg_printf("obj_compiler\n"); +} + +//#if NEWSTATICDTOR + +/************************************** + * Symbol is the function that calls the static constructors. + * Put a pointer to it into a special segment that the startup code + * looks at. + * Input: + * s static constructor function + * dtor !=0 if leave space for static destructor + * seg 1: user + * 2: lib + * 3: compiler + */ + +void obj_staticctor(Symbol *s,int dtor,int none) +{ +#if 0 + IDXSEC seg; + Outbuffer *buf; + + //dbg_printf("obj_staticctor(%s) offset %x\n",s->Sident,s->Soffset); + //symbol_print(s); + s->Sseg = seg = + elf_getsegment(".ctors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4); + buf = SegData[seg]->SDbuf; + if (I64) + buf->write64(s->Soffset); + else + buf->write32(s->Soffset); + mach_addrel(seg, SegData[seg]->SDoffset, s, RELaddr); + SegData[seg]->SDoffset = buf->size(); +#endif +} + +/************************************** + * Symbol is the function that calls the static destructors. + * Put a pointer to it into a special segment that the exit code + * looks at. + * Input: + * s static destructor function + */ + +void obj_staticdtor(Symbol *s) +{ +#if 0 + IDXSEC seg; + Outbuffer *buf; + + //dbg_printf("obj_staticdtor(%s) offset %x\n",s->Sident,s->Soffset); + //symbol_print(s); + seg = elf_getsegment(".dtors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4); + buf = SegData[seg]->SDbuf; + if (I64) + buf->write64(s->Soffset); + else + buf->write32(s->Soffset); + mach_addrel(seg, SegData[seg]->SDoffset, s, RELaddr); + SegData[seg]->SDoffset = buf->size(); +#endif +} + +//#else + +/*************************************** + * Stuff pointer to function in its own segment. + * Used for static ctor and dtor lists. + */ + +void obj_funcptr(Symbol *s) +{ + //dbg_printf("obj_funcptr(%s) \n",s->Sident); +} + +//#endif + +/*************************************** + * Stuff the following data (instance of struct FuncTable) in a separate segment: + * pointer to function + * pointer to ehsym + * length of function + */ + +void obj_ehtables(Symbol *sfunc,targ_size_t size,Symbol *ehsym) +{ + //dbg_printf("obj_ehtables(%s) \n",sfunc->Sident); + + /* BUG: this should go into a COMDAT if sfunc is in a COMDAT + * otherwise the duplicates aren't removed. + */ + + int align = I64 ? 3 : 2; // align to NPTRSIZE + // The size is sizeof(struct FuncTable) in deh2.d + mach_getsegment("__deh_beg", "__DATA", align, S_COALESCED, 3 * NPTRSIZE); + int seg = mach_getsegment("__deh_eh", "__DATA", align, S_REGULAR); + mach_getsegment("__deh_end", "__DATA", align, S_COALESCED, NPTRSIZE); + + Outbuffer *buf = SegData[seg]->SDbuf; + if (I64) + { reftoident(seg, buf->size(), sfunc, 0, CFoff | CFoffset64); + reftoident(seg, buf->size(), ehsym, 0, CFoff | CFoffset64); + buf->write64(sfunc->Ssize); + } + else + { reftoident(seg, buf->size(), sfunc, 0, CFoff); + reftoident(seg, buf->size(), ehsym, 0, CFoff); + buf->write32(sfunc->Ssize); + } +} + +/********************************************* + * Put out symbols that define the beginning/end of the .deh_eh section. + * This gets called if this is the module with "main()" in it. + */ + +void obj_ehsections() +{ + //printf("obj_ehsections()\n"); +#if 0 + /* Determine Mac OSX version, and put out the sections slightly differently for each. + * This is needed because the linker on OSX 10.5 behaves differently than + * the linker on 10.6. + * See Bugzilla 3502 for more information. + */ + static SInt32 MacVersion; + if (!MacVersion) + Gestalt(gestaltSystemVersion, &MacVersion); + + /* Exception handling sections + */ + // 12 is size of struct FuncTable in D runtime + mach_getsegment("__deh_beg", "__DATA", 2, S_COALESCED, 12); + int seg = mach_getsegment("__deh_eh", "__DATA", 2, S_REGULAR); + Outbuffer *buf = SegData[seg]->SDbuf; + buf->writezeros(12); // 12 is size of struct FuncTable in D runtime, + // this entry gets skipped over by __eh_finddata() + + mach_getsegment("__deh_end", "__DATA", 2, S_COALESCED, 4); + + /* Thread local storage sections + */ + mach_getsegment("__tls_beg", "__DATA", 2, S_COALESCED, 4); + mach_getsegment("__tls_data", "__DATA", 2, S_REGULAR, 4); + mach_getsegment("__tlscoal_nt", "__DATA", 4, S_COALESCED, 4); + mach_getsegment("__tls_end", "__DATA", 2, S_COALESCED, 4); + + /* Module info sections + */ + mach_getsegment("__minfo_beg", "__DATA", 2, S_COALESCED, 4); + mach_getsegment("__minfodata", "__DATA", 2, S_REGULAR, 4); + mach_getsegment("__minfo_end", "__DATA", 2, S_COALESCED, 4); +#endif +} + +/********************************* + * Setup for Symbol s to go into a COMDAT segment. + * Output (if s is a function): + * cseg segment index of new current code segment + * Coffset starting offset in cseg + * Returns: + * "segment index" of COMDAT + */ + +int obj_comdat(Symbol *s) +{ + const char *sectname; + const char *segname; + int align; + int flags; + + //printf("obj_comdat(Symbol* %s)\n",s->Sident); + //symbol_print(s); + symbol_debug(s); + + if (tyfunc(s->ty())) + { + sectname = "__textcoal_nt"; + segname = "__TEXT"; + align = 2; // 4 byte alignment + flags = S_COALESCED | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS; + s->Sseg = mach_getsegment(sectname, segname, align, flags); + } + else if ((s->ty() & mTYLINK) == mTYthread) + { + s->Sfl = FLtlsdata; + align = I64 ? 4 : 2; // align to 16 bytes for floating point + mach_getsegment("__tls_beg", "__DATA", align, S_COALESCED, 4); + mach_getsegment("__tls_data", "__DATA", align, S_REGULAR, 4); + s->Sseg = mach_getsegment("__tlscoal_nt", "__DATA", 4, S_COALESCED); + mach_getsegment("__tls_end", "__DATA", align, S_COALESCED, 4); + elf_data_start(s, 1 << align, s->Sseg); + } + else + { + s->Sfl = FLdata; + sectname = "__datacoal_nt"; + segname = "__DATA"; + align = 4; // 16 byte alignment + flags = S_COALESCED; + s->Sseg = mach_getsegment(sectname, segname, align, flags); + } + // find or create new segment + s->Soffset = SegData[s->Sseg]->SDoffset; + if (s->Sfl == FLdata || s->Sfl == FLtlsdata) + { // Code symbols are 'published' by elf_func_start() + + objpubdef(s->Sseg,s,s->Soffset); + searchfixlist(s); // backpatch any refs to this symbol + } + return s->Sseg; +} + +/********************************** + * Get segment. + * Input: + * flags2 put out some data for this, so the linker will keep things in order + * align segment alignment as power of 2 + * Returns: + * segment index of found or newly created segment + */ + +int mach_getsegment(const char *sectname, const char *segname, + int align, int flags, int flags2) +{ + assert(strlen(sectname) <= 16); + assert(strlen(segname) <= 16); + for (int seg = 1; seg <= seg_count; seg++) + { seg_data *pseg = SegData[seg]; + if (I64) + { + if (strncmp(SecHdrTab64[pseg->SDshtidx].sectname, sectname, 16) == 0 && + strncmp(SecHdrTab64[pseg->SDshtidx].segname, segname, 16) == 0) + return seg; // return existing segment + } + else + { + if (strncmp(SecHdrTab[pseg->SDshtidx].sectname, sectname, 16) == 0 && + strncmp(SecHdrTab[pseg->SDshtidx].segname, segname, 16) == 0) + return seg; // return existing segment + } + } + + int seg = ++seg_count; + if (seg_count >= seg_max) + { // need more room in segment table + seg_max += 10; + SegData = (seg_data **)mem_realloc(SegData,seg_max * sizeof(seg_data *)); + memset(&SegData[seg_count], 0, (seg_max - seg_count) * sizeof(seg_data *)); + } + assert(seg_count < seg_max); + if (SegData[seg]) + { seg_data *pseg = SegData[seg]; + Outbuffer *b1 = pseg->SDbuf; + Outbuffer *b2 = pseg->SDrel; + memset(pseg, 0, sizeof(seg_data)); + if (b1) + b1->setsize(0); + if (b2) + b2->setsize(0); + pseg->SDbuf = b1; + pseg->SDrel = b2; + } + else + { + seg_data *pseg = (seg_data *)mem_calloc(sizeof(seg_data)); + SegData[seg] = pseg; + if (flags != S_ZEROFILL) + { pseg->SDbuf = new Outbuffer(4096); + pseg->SDbuf->reserve(4096); + } + } + + //dbg_printf("\tNew segment - %d size %d\n", seg,SegData[seg]->SDbuf); + seg_data *pseg = SegData[seg]; + + pseg->SDseg = seg; + pseg->SDoffset = 0; + + if (I64) + { + struct section_64 *sec = (struct section_64 *) + SECbuf->writezeros(sizeof(struct section_64)); + strncpy(sec->sectname, sectname, 16); + strncpy(sec->segname, segname, 16); + sec->align = align; + sec->flags = flags; + } + else + { + struct section *sec = (struct section *) + SECbuf->writezeros(sizeof(struct section)); + strncpy(sec->sectname, sectname, 16); + strncpy(sec->segname, segname, 16); + sec->align = align; + sec->flags = flags; + } + + pseg->SDshtidx = section_cnt++; + pseg->SDaranges_offset = 0; + pseg->SDlinnum_count = 0; + + if (flags2) + { + /* If we don't write something to each seg, then the linker won't put + * them in this necessary order. In fact, it will ignore the segment entirely. + */ + static SInt32 MacVersion; + if (!MacVersion) + Gestalt(gestaltSystemVersion, &MacVersion); + + if (flags == S_COALESCED) + { type *t = type_fake(TYint); + t->Tmangle = mTYman_c; + symbol *s_deh_beg = symbol_name(sectname + 1, SCcomdat, t); + objpubdef(seg, s_deh_beg, 0); + } + if (MacVersion >= MacOSX_10_6) + obj_bytes(seg, 0, flags2, NULL); // 12 is size of struct FuncTable in D runtime + } + + //printf("seg_count = %d\n", seg_count); + return seg; +} + +/******************************** + * Define a new code segment. + * Input: + * name name of segment, if NULL then revert to default + * suffix 0 use name as is + * 1 append "_TEXT" to name + * Output: + * cseg segment index of new current code segment + * Coffset starting offset in cseg + * Returns: + * segment index of newly created code segment + */ + +int obj_codeseg(char *name,int suffix) +{ + //dbg_printf("obj_codeseg(%s,%x)\n",name,suffix); +#if 0 + const char *sfx = (suffix) ? "_TEXT" : NULL; + + if (!name) // returning to default code segment + { + if (cseg != CODE) // not the current default + { + SegData[cseg]->SDoffset = Coffset; + Coffset = SegData[CODE]->SDoffset; + cseg = CODE; + } + return cseg; + } + + int seg = elf_getsegment(name, sfx, SHT_PROGDEF, SHF_ALLOC|SHF_EXECINSTR, 4); + // find or create code segment + + cseg = seg; // new code segment index + Coffset = 0; + return seg; +#else + return 0; +#endif +} + +/********************************* + * Define segments for Thread Local Storage. + * Output: + * seg_tlsseg set to segment number for TLS segment. + * Returns: + * segment for TLS segment + */ + +seg_data *obj_tlsseg() +{ + //printf("obj_tlsseg(\n"); + + if (seg_tlsseg == UNKNOWN) + { + int align = I64 ? 4 : 2; // align to 16 bytes for floating point + mach_getsegment("__tls_beg", "__DATA", align, S_COALESCED, 4); + seg_tlsseg = mach_getsegment("__tls_data", "__DATA", align, S_REGULAR); + mach_getsegment("__tlscoal_nt", "__DATA", 4, S_COALESCED, 4); + mach_getsegment("__tls_end", "__DATA", align, S_COALESCED, 4); + } + return SegData[seg_tlsseg]; +} + + +/********************************* + * Define segments for Thread Local Storage. + * Output: + * seg_tlsseg_bss set to segment number for TLS segment. + * Returns: + * segment for TLS segment + */ + +seg_data *obj_tlsseg_bss() +{ + /* Because Mach-O does not support tls, it's easier to support + * if we have all the tls in one segment. + */ + return obj_tlsseg(); +} + + +/******************************* + * Output an alias definition record. + */ + +void obj_alias(const char *n1,const char *n2) +{ + //printf("obj_alias(%s,%s)\n",n1,n2); + assert(0); +#if NOT_DONE + unsigned len; + char *buffer; + + buffer = (char *) alloca(strlen(n1) + strlen(n2) + 2 * ONS_OHD); + len = obj_namestring(buffer,n1); + len += obj_namestring(buffer + len,n2); + objrecord(ALIAS,buffer,len); +#endif +} + +char *unsstr (unsigned value) +{ + static char buffer [64]; + + sprintf (buffer, "%d", value); + return buffer; +} + +/******************************* + * Mangle a name. + * Returns: + * mangled name + */ + +char *obj_mangle2(Symbol *s,char *dest) +{ + size_t len; + char *name; + + //printf("obj_mangle(s = %p, '%s'), mangle = x%x\n",s,s->Sident,type_mangle(s->Stype)); + symbol_debug(s); + assert(dest); +#if SCPP + name = CPP ? cpp_mangle(s) : s->Sident; +#elif MARS + name = cpp_mangle(s); +#else + name = s->Sident; +#endif + len = strlen(name); // # of bytes in name + //dbg_printf("len %d\n",len); + switch (type_mangle(s->Stype)) + { + case mTYman_pas: // if upper case + case mTYman_for: + if (len >= DEST_LEN) + dest = (char *)mem_malloc(len + 1); + memcpy(dest,name,len + 1); // copy in name and ending 0 + strupr(dest); // to upper case + break; + case mTYman_std: +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (tyfunc(s->ty()) && !variadic(s->Stype)) +#else + if (!(config.flags4 & CFG4oldstdmangle) && + config.exe == EX_NT && tyfunc(s->ty()) && + !variadic(s->Stype)) +#endif + { + char *pstr = unsstr(type_paramsize(s->Stype)); + size_t pstrlen = strlen(pstr); + size_t destlen = len + 1 + pstrlen + 1; + + if (destlen > DEST_LEN) + dest = (char *)mem_malloc(destlen); + memcpy(dest,name,len); + dest[len] = '@'; + memcpy(dest + 1 + len, pstr, pstrlen + 1); + break; + } + case mTYman_cpp: + case mTYman_d: + case mTYman_sys: + case 0: + if (len >= DEST_LEN) + dest = (char *)mem_malloc(len + 1); + memcpy(dest,name,len+1);// copy in name and trailing 0 + break; + + case mTYman_c: + if (len >= DEST_LEN - 1) + dest = (char *)mem_malloc(1 + len + 1); + dest[0] = '_'; + memcpy(dest + 1,name,len+1);// copy in name and trailing 0 + break; + + + default: +#ifdef DEBUG + printf("mangling %x\n",type_mangle(s->Stype)); + symbol_print(s); +#endif + printf("%d\n", type_mangle(s->Stype)); + assert(0); + } + //dbg_printf("\t %s\n",dest); + return dest; +} + +/******************************* + * Export a function name. + */ + +void obj_export(Symbol *s,unsigned argsize) +{ + //dbg_printf("obj_export(%s,%d)\n",s->Sident,argsize); +} + +/******************************* + * Update data information about symbol + * align for output and assign segment + * if not already specified. + * + * Input: + * sdata data symbol + * datasize output size + * seg default seg if not known + * Returns: + * actual seg + */ + +int elf_data_start(Symbol *sdata, targ_size_t datasize, int seg) +{ + targ_size_t alignbytes; + //dbg_printf("elf_data_start(%s,size %d,seg %d)\n",sdata->Sident,datasize,seg); + //symbol_print(sdata); + + if (sdata->Sseg == UNKNOWN) // if we don't know then there + sdata->Sseg = seg; // wasn't any segment override + else + seg = sdata->Sseg; + targ_size_t offset = Offset(seg); + alignbytes = align(datasize, offset) - offset; + if (alignbytes) + obj_lidata(seg, offset, alignbytes); + sdata->Soffset = offset + alignbytes; + return seg; +} + +/******************************* + * Update function info before codgen + * + * If code for this function is in a different segment + * than the current default in cseg, switch cseg to new segment. + */ + +void elf_func_start(Symbol *sfunc) +{ + //printf("elf_func_start(%s)\n",sfunc->Sident); + symbol_debug(sfunc); + + if (sfunc->Sseg == UNKNOWN) + sfunc->Sseg = CODE; + //printf("sfunc->Sseg %d CODE %d cseg %d Coffset x%x\n",sfunc->Sseg,CODE,cseg,Coffset); + cseg = sfunc->Sseg; + assert(cseg == CODE || cseg > UDATA); + objpubdef(cseg, sfunc, Coffset); + sfunc->Soffset = Coffset; + + if (config.fulltypes) + dwarf_func_start(sfunc); +} + +/******************************* + * Update function info after codgen + */ + +void elf_func_term(Symbol *sfunc) +{ + //dbg_printf("elf_func_term(%s) offset %x, Coffset %x symidx %d\n", +// sfunc->Sident, sfunc->Soffset,Coffset,sfunc->Sxtrnnum); + +#if 0 + // fill in the function size + if (I64) + SymbolTable64[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset; + else + SymbolTable[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset; +#endif + if (config.fulltypes) + dwarf_func_term(sfunc); +} + +/******************************** + * Output a public definition. + * Input: + * seg = segment index that symbol is defined in + * s -> symbol + * offset = offset of name within segment + */ + +void objpubdef(int seg, Symbol *s, targ_size_t offset) +{ +#if 0 + printf("objpubdef(%d:x%x s=%p, %s)\n", seg, offset, s, s->Sident); + //symbol_print(s); +#endif + symbol_debug(s); + + s->Soffset = offset; + s->Sseg = seg; + switch (s->Sclass) + { + case SCglobal: + case SCinline: + public_symbuf->write(&s, sizeof(s)); + break; + case SCcomdat: + case SCcomdef: + public_symbuf->write(&s, sizeof(s)); + break; + default: + local_symbuf->write(&s, sizeof(s)); + break; + } + //printf("%p\n", *(void**)public_symbuf->buf); + s->Sxtrnnum = 1; +} + +/******************************* + * Output an external symbol for name. + * Input: + * name Name to do EXTDEF on + * (Not to be mangled) + * Returns: + * Symbol table index of the definition + * NOTE: Numbers will not be linear. + */ + +int objextern(const char *name) +{ + //printf("objextdef('%s')\n",name); + assert(name); + assert(extdef == 0); + extdef = elf_addstr(symtab_strings, name); + return 0; +} + +int objextdef(const char *name) +{ + return objextern(name); +} + +/******************************* + * Output an external for existing symbol. + * Input: + * s Symbol to do EXTDEF on + * (Name is to be mangled) + * Returns: + * Symbol table index of the definition + * NOTE: Numbers will not be linear. + */ + +int objextern(Symbol *s) +{ + //printf("objextern('%s') %x\n",s->Sident,s->Svalue); + symbol_debug(s); + extern_symbuf->write(&s, sizeof(s)); + s->Sxtrnnum = 1; +} + +/******************************* + * Output a common block definition. + * Input: + * p -> external identifier + * size size in bytes of each elem + * count number of elems + * Returns: + * Symbol table index for symbol + */ + +int obj_comdef(Symbol *s,targ_size_t size,targ_size_t count) +{ + //printf("obj_comdef('%s', size=%d, count=%d)\n",s->Sident,size,count); + symbol_debug(s); + + // can't have code or thread local comdef's + assert(!(s->ty() & ( +#if TARGET_SEGMENTED + mTYcs | +#endif + mTYthread))); + + struct Comdef comdef; + comdef.sym = s; + comdef.size = size; + comdef.count = count; + comdef_symbuf->write(&comdef, sizeof(comdef)); + s->Sxtrnnum = 1; + return 0; // should return void +} + +int obj_comdef(Symbol *s, int flag, targ_size_t size, targ_size_t count) +{ + return obj_comdef(s, size, count); +} + +/*************************************** + * Append an iterated data block of 0s. + * (uninitialized data only) + */ + +void obj_write_zeros(seg_data *pseg, targ_size_t count) +{ + obj_lidata(pseg->SDseg, pseg->SDoffset, count); +} + +/*************************************** + * Output an iterated data block of 0s. + * + * For boundary alignment and initialization + */ + +void obj_lidata(int seg,targ_size_t offset,targ_size_t count) +{ + //printf("obj_lidata(%d,%x,%d)\n",seg,offset,count); + size_t idx = SegData[seg]->SDshtidx; + if ((I64 ? SecHdrTab64[idx].flags : SecHdrTab[idx].flags) == S_ZEROFILL) + { // Use SDoffset to record size of bss section + SegData[seg]->SDoffset += count; + } + else + { + obj_bytes(seg, offset, count, NULL); + } +} + +/*********************************** + * Append byte to segment. + */ + +void obj_write_byte(seg_data *pseg, unsigned byte) +{ + obj_byte(pseg->SDseg, pseg->SDoffset, byte); +} + +/************************************ + * Output byte to object file. + */ + +void obj_byte(int seg,targ_size_t offset,unsigned byte) +{ + Outbuffer *buf = SegData[seg]->SDbuf; + int save = buf->size(); + //dbg_printf("obj_byte(seg=%d, offset=x%lx, byte=x%x)\n",seg,offset,byte); + buf->setsize(offset); + buf->writeByte(byte); + if (save > offset+1) + buf->setsize(save); + else + SegData[seg]->SDoffset = offset+1; + //dbg_printf("\tsize now %d\n",buf->size()); +} + +/*********************************** + * Append bytes to segment. + */ + +void obj_write_bytes(seg_data *pseg, unsigned nbytes, void *p) +{ + obj_bytes(pseg->SDseg, pseg->SDoffset, nbytes, p); +} + +/************************************ + * Output bytes to object file. + * Returns: + * nbytes + */ + +unsigned obj_bytes(int seg, targ_size_t offset, unsigned nbytes, void *p) +{ +#if 0 + if (!(seg >= 0 && seg <= seg_count)) + { printf("obj_bytes: seg = %d, seg_count = %d\n", seg, seg_count); + *(char*)0=0; + } +#endif + assert(seg >= 0 && seg <= seg_count); + Outbuffer *buf = SegData[seg]->SDbuf; + if (buf == NULL) + { + //dbg_printf("obj_bytes(seg=%d, offset=x%lx, nbytes=%d, p=x%x)\n", seg, offset, nbytes, p); + //raise(SIGSEGV); +if (!buf) halt(); + assert(buf != NULL); + } + int save = buf->size(); + //dbg_printf("obj_bytes(seg=%d, offset=x%lx, nbytes=%d, p=x%x)\n", + //seg,offset,nbytes,p); + buf->setsize(offset); + buf->reserve(nbytes); + if (p) + { + buf->writen(p,nbytes); + } + else + { // Zero out the bytes + buf->clearn(nbytes); + } + if (save > offset+nbytes) + buf->setsize(save); + else + SegData[seg]->SDoffset = offset+nbytes; + return nbytes; +} + +/********************************************* + * Add a relocation entry for seg/offset. + */ + +void mach_addrel(int seg, targ_size_t offset, symbol *targsym, + unsigned targseg, int rtype, int val) +{ + Relocation rel; + rel.offset = offset; + rel.targsym = targsym; + rel.targseg = targseg; + rel.rtype = rtype; + rel.funcsym = funcsym_p; + rel.val = val; + seg_data *pseg = SegData[seg]; + if (!pseg->SDrel) + pseg->SDrel = new Outbuffer(); + pseg->SDrel->write(&rel, sizeof(rel)); +} + +/**************************************** + * Sort the relocation entry buffer. + */ + +#if __DMC__ +static int __cdecl rel_fp(const void *e1, const void *e2) +{ Relocation *r1 = (Relocation *)e1; + Relocation *r2 = (Relocation *)e2; + + return r1->offset - r2->offset; +} +#else +extern "C" { +static int rel_fp(const void *e1, const void *e2) +{ Relocation *r1 = (Relocation *)e1; + Relocation *r2 = (Relocation *)e2; + + return r1->offset - r2->offset; +} +} +#endif + +void mach_relsort(Outbuffer *buf) +{ + qsort(buf->buf, buf->size() / sizeof(Relocation), sizeof(Relocation), &rel_fp); +} + +/******************************* + * Output a relocation entry for a segment + * Input: + * seg = where the address is going + * offset = offset within seg + * type = ELF relocation type + * index = Related symbol table index + * val = addend or displacement from address + */ + +void elf_addrel(int seg, targ_size_t offset, unsigned type, + IDXSYM symidx, targ_size_t val) +{ +} + +/******************************* + * Refer to address that is in the data segment. + * Input: + * seg:offset = the address being fixed up + * val = displacement from start of target segment + * targetdatum = target segment number (DATA, CDATA or UDATA, etc.) + * flags = CFoff, CFseg + * Example: + * int *abc = &def[3]; + * to allocate storage: + * reftodatseg(DATA,offset,3 * sizeof(int *),UDATA); + */ + +void reftodatseg(int seg,targ_size_t offset,targ_size_t val, + unsigned targetdatum,int flags) +{ + Outbuffer *buf = SegData[seg]->SDbuf; + int save = buf->size(); + buf->setsize(offset); +#if 0 + printf("reftodatseg(seg:offset=%d:x%llx, val=x%llx, targetdatum %x, flags %x )\n", + seg,offset,val,targetdatum,flags); +#endif + if (SegData[seg]->isCode() && SegData[targetdatum]->isCode()) + { + assert(0); + } + mach_addrel(seg, offset, NULL, targetdatum, RELaddr); + if (I64) + { + if (flags & CFoffset64) + { + buf->write64(val); + if (save > offset + 8) + buf->setsize(save); + return; + } + } + buf->write32(val); + if (save > offset + 4) + buf->setsize(save); +} + +/******************************* + * Refer to address that is in the current function code (funcsym_p). + * Only offsets are output, regardless of the memory model. + * Used to put values in switch address tables. + * Input: + * seg = where the address is going (CODE or DATA) + * offset = offset within seg + * val = displacement from start of this module + */ + +void reftocodseg(int seg,targ_size_t offset,targ_size_t val) +{ + //printf("reftocodseg(seg=%d, offset=x%lx, val=x%lx )\n",seg,(unsigned long)offset,(unsigned long)val); + assert(seg > 0); + Outbuffer *buf = SegData[seg]->SDbuf; + int save = buf->size(); + buf->setsize(offset); + val -= funcsym_p->Soffset; + mach_addrel(seg, offset, funcsym_p, 0, RELaddr); +// if (I64) +// buf->write64(val); +// else + buf->write32(val); + if (save > offset + 4) + buf->setsize(save); +} + +/******************************* + * Refer to an identifier. + * Input: + * seg = where the address is going (CODE or DATA) + * offset = offset within seg + * s -> Symbol table entry for identifier + * val = displacement from identifier + * flags = CFselfrel: self-relative + * CFseg: get segment + * CFoff: get offset + * CFpc32: [RIP] addressing, val is 0, -1, -2 or -4 + * CFoffset64: 8 byte offset for 64 bit builds + * Returns: + * number of bytes in reference (4 or 8) + */ + +int reftoident(int seg, targ_size_t offset, Symbol *s, targ_size_t val, + int flags) +{ + int retsize = (flags & CFoffset64) ? 8 : 4; +#if 0 + dbg_printf("\nreftoident('%s' seg %d, offset x%llx, val x%llx, flags x%x)\n", + s->Sident,seg,(unsigned long long)offset,(unsigned long long)val,flags); + printf("retsize = %d\n", retsize); + //dbg_printf("Sseg = %d, Sxtrnnum = %d\n",s->Sseg,s->Sxtrnnum); + symbol_print(s); +#endif + assert(seg > 0); + if (s->Sclass != SClocstat && !s->Sxtrnnum) + { // It may get defined later as public or local, so defer + addtofixlist(s, offset, seg, val, flags); + } + else + { + if (I64) + { + //if (s->Sclass != SCcomdat) + //val += s->Soffset; + int v = 0; + if (flags & CFpc32) + v = (int)val; + if (flags & CFselfrel) + { + mach_addrel(seg, offset, s, 0, RELrel, v); + } + else + { + mach_addrel(seg, offset, s, 0, RELaddr, v); + } + } + else + { + if (SegData[seg]->isCode() && flags & CFselfrel) + { + if (!jumpTableSeg) + { + jumpTableSeg = + mach_getsegment("__jump_table", "__IMPORT", 0, S_SYMBOL_STUBS | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE); + } + seg_data *pseg = SegData[jumpTableSeg]; + if (I64) + SecHdrTab64[pseg->SDshtidx].reserved2 = 5; + else + SecHdrTab[pseg->SDshtidx].reserved2 = 5; + + if (!indirectsymbuf1) + indirectsymbuf1 = new Outbuffer(); + else + { // Look through indirectsym to see if it is already there + int n = indirectsymbuf1->size() / sizeof(Symbol *); + Symbol **psym = (Symbol **)indirectsymbuf1->buf; + for (int i = 0; i < n; i++) + { // Linear search, pretty pathetic + if (s == psym[i]) + { val = i * 5; + goto L1; + } + } + } + + val = pseg->SDbuf->size(); + static char halts[5] = { 0xF4,0xF4,0xF4,0xF4,0xF4 }; + pseg->SDbuf->write(halts, 5); + + // Add symbol s to indirectsymbuf1 + indirectsymbuf1->write(&s, sizeof(Symbol *)); + L1: + val -= offset + 4; + mach_addrel(seg, offset, NULL, jumpTableSeg, RELrel); + } + else if (SegData[seg]->isCode() && + ((s->Sclass != SCextern && SegData[s->Sseg]->isCode()) || s->Sclass == SClocstat || s->Sclass == SCstatic)) + { + val += s->Soffset; + mach_addrel(seg, offset, NULL, s->Sseg, RELaddr); + } + else if (SegData[seg]->isCode() && !tyfunc(s->ty())) + { + if (!pointersSeg) + { + pointersSeg = + mach_getsegment("__pointers", "__IMPORT", 0, S_NON_LAZY_SYMBOL_POINTERS); + } + seg_data *pseg = SegData[pointersSeg]; + + if (!indirectsymbuf2) + indirectsymbuf2 = new Outbuffer(); + else + { // Look through indirectsym to see if it is already there + int n = indirectsymbuf2->size() / sizeof(Symbol *); + Symbol **psym = (Symbol **)indirectsymbuf2->buf; + for (int i = 0; i < n; i++) + { // Linear search, pretty pathetic + if (s == psym[i]) + { val = i * 4; + goto L2; + } + } + } + + val = pseg->SDbuf->size(); + pseg->SDbuf->writezeros(NPTRSIZE); + + // Add symbol s to indirectsymbuf2 + indirectsymbuf2->write(&s, sizeof(Symbol *)); + + L2: + //printf("reftoident: seg = %d, offset = x%x, s = %s, val = x%x, pointersSeg = %d\n", seg, offset, s->Sident, val, pointersSeg); + mach_addrel(seg, offset, NULL, pointersSeg, RELaddr); + } + else + { //val -= s->Soffset; + mach_addrel(seg, offset, s, 0, RELaddr); + } + } + + Outbuffer *buf = SegData[seg]->SDbuf; + int save = buf->size(); + buf->setsize(offset); + //printf("offset = x%llx, val = x%llx\n", offset, val); + if (retsize == 8) + buf->write64(val); + else + buf->write32(val); + if (save > offset + retsize) + buf->setsize(save); + } + return retsize; +} + +/***************************************** + * Generate far16 thunk. + * Input: + * s Symbol to generate a thunk for + */ + +void obj_far16thunk(Symbol *s) +{ + //dbg_printf("obj_far16thunk('%s')\n", s->Sident); + assert(0); +} + +/************************************** + * Mark object file as using floating point. + */ + +void obj_fltused() +{ + //dbg_printf("obj_fltused()\n"); +} + +/************************************ + * Close and delete .OBJ file. + */ + +void objfile_delete() +{ + //remove(fobjname); // delete corrupt output file +} + +/********************************** + * Terminate. + */ + +void objfile_term() +{ +#if TERMCODE + mem_free(fobjname); + fobjname = NULL; +#endif +} + +/********************************** + * Write to the object file + */ +void objfile_write(FILE *fd, void *buffer, unsigned len) +{ + fobjbuf->write(buffer, len); +} + +long elf_align(targ_size_t size, long foffset) +{ + long offset; + switch (size) + { + case 0: + case 1: + return foffset; + case 2: + offset = (foffset + 1) & ~1; + break; + case 4: + offset = (foffset + 3) & ~3; + break; + case 8: + offset = (foffset + 7) & ~7; + break; + case 16: + offset = (foffset + 15) & ~15; + break; + case 32: + offset = (foffset + 31) & ~31; + break; + default: + dbg_printf("size was %lu\n",(unsigned long)size); + assert(0); + break; + } + if (offset > foffset) + fobjbuf->writezeros(offset - foffset); + return offset; +} + +/*************************************** + * Stuff pointer to ModuleInfo in its own segment. + */ + +#if MARS + +void obj_moduleinfo(Symbol *scc) +{ + int align = I64 ? 4 : 2; + + mach_getsegment("__minfo_beg", "__DATA", align, S_COALESCED, 4); + + int seg = mach_getsegment("__minfodata", "__DATA", align, S_REGULAR); + //printf("obj_moduleinfo(%s) seg = %d:x%x\n", scc->Sident, seg, Offset(seg)); + +#if 0 + type *t = type_fake(TYint); + t->Tmangle = mTYman_c; + char *p = (char *)malloc(5 + strlen(scc->Sident) + 1); + strcpy(p, "SUPER"); + strcpy(p + 5, scc->Sident); + symbol *s_minfo_beg = symbol_name(p, SCglobal, t); + objpubdef(seg, s_minfo_beg, 0); +#endif + + int flags = CFoff; + if (I64) + flags |= CFoffset64; + SegData[seg]->SDoffset += reftoident(seg, Offset(seg), scc, 0, flags); + + mach_getsegment("__minfo_end", "__DATA", align, S_COALESCED, 4); +} + +#endif + +/************************************* + */ + +void elfobj_gotref(symbol *s) +{ + //printf("elfobj_gotref(%x '%s', %d)\n",s,s->Sident, s->Sclass); + switch(s->Sclass) + { + case SCstatic: + case SClocstat: + s->Sfl = FLgotoff; + break; + + case SCextern: + case SCglobal: + case SCcomdat: + case SCcomdef: + s->Sfl = FLgot; + break; + + default: + break; + } +} + +#endif +#endif diff --git a/backend/md5.c b/backend/md5.c new file mode 100644 index 00000000..8675d4a2 --- /dev/null +++ b/backend/md5.c @@ -0,0 +1,277 @@ +/* + ********************************************************************** + ** md5.c ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* -- include the following line if the md5.h header file is separate -- */ +#include "md5.h" + +/* forward declaration */ +static void Transform (UINT4 *buf, UINT4 *in); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G and H are basic MD5 functions: selection, majority, parity */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +void MD5Init (mdContext) +MD5_CTX *mdContext; +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; +} + +void MD5Update (mdContext, inBuf, inLen) +MD5_CTX *mdContext; +unsigned char *inBuf; +unsigned int inLen; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +void MD5Final (mdContext) +MD5_CTX *mdContext; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } +} + +/* Basic MD5 step. Transform buf based on in. + */ +static void Transform (buf, in) +UINT4 *buf; +UINT4 *in; +{ + UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, 3614090360); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, 3905402710); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, 606105819); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, 3250441966); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, 4118548399); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, 1200080426); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, 2821735955); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, 4249261313); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, 1770035416); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, 2336552879); /* 10 */ + FF ( c, d, a, b, in[10], S13, 4294925233); /* 11 */ + FF ( b, c, d, a, in[11], S14, 2304563134); /* 12 */ + FF ( a, b, c, d, in[12], S11, 1804603682); /* 13 */ + FF ( d, a, b, c, in[13], S12, 4254626195); /* 14 */ + FF ( c, d, a, b, in[14], S13, 2792965006); /* 15 */ + FF ( b, c, d, a, in[15], S14, 1236535329); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, 4129170786); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, 3225465664); /* 18 */ + GG ( c, d, a, b, in[11], S23, 643717713); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, 3921069994); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, 3593408605); /* 21 */ + GG ( d, a, b, c, in[10], S22, 38016083); /* 22 */ + GG ( c, d, a, b, in[15], S23, 3634488961); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, 3889429448); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, 568446438); /* 25 */ + GG ( d, a, b, c, in[14], S22, 3275163606); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, 4107603335); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, 1163531501); /* 28 */ + GG ( a, b, c, d, in[13], S21, 2850285829); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, 4243563512); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, 1735328473); /* 31 */ + GG ( b, c, d, a, in[12], S24, 2368359562); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, 4294588738); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, 2272392833); /* 34 */ + HH ( c, d, a, b, in[11], S33, 1839030562); /* 35 */ + HH ( b, c, d, a, in[14], S34, 4259657740); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, 2763975236); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, 1272893353); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, 4139469664); /* 39 */ + HH ( b, c, d, a, in[10], S34, 3200236656); /* 40 */ + HH ( a, b, c, d, in[13], S31, 681279174); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, 3936430074); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, 3572445317); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, 76029189); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, 3654602809); /* 45 */ + HH ( d, a, b, c, in[12], S32, 3873151461); /* 46 */ + HH ( c, d, a, b, in[15], S33, 530742520); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, 3299628645); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, 4096336452); /* 49 */ + II ( d, a, b, c, in[ 7], S42, 1126891415); /* 50 */ + II ( c, d, a, b, in[14], S43, 2878612391); /* 51 */ + II ( b, c, d, a, in[ 5], S44, 4237533241); /* 52 */ + II ( a, b, c, d, in[12], S41, 1700485571); /* 53 */ + II ( d, a, b, c, in[ 3], S42, 2399980690); /* 54 */ + II ( c, d, a, b, in[10], S43, 4293915773); /* 55 */ + II ( b, c, d, a, in[ 1], S44, 2240044497); /* 56 */ + II ( a, b, c, d, in[ 8], S41, 1873313359); /* 57 */ + II ( d, a, b, c, in[15], S42, 4264355552); /* 58 */ + II ( c, d, a, b, in[ 6], S43, 2734768916); /* 59 */ + II ( b, c, d, a, in[13], S44, 1309151649); /* 60 */ + II ( a, b, c, d, in[ 4], S41, 4149444226); /* 61 */ + II ( d, a, b, c, in[11], S42, 3174756917); /* 62 */ + II ( c, d, a, b, in[ 2], S43, 718787259); /* 63 */ + II ( b, c, d, a, in[ 9], S44, 3951481745); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + ********************************************************************** + ** End of md5.c ** + ******************************* (cut) ******************************** + */ + diff --git a/backend/md5.h b/backend/md5.h new file mode 100644 index 00000000..b7a7418d --- /dev/null +++ b/backend/md5.h @@ -0,0 +1,61 @@ +/* + ********************************************************************** + ** md5.h -- Header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* typedef a 32 bit type */ +typedef unsigned long int UINT4; + +/* Data structure for MD5 (Message Digest) computation */ +typedef struct { + UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ + UINT4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init (MD5_CTX *mdContext); +void MD5Update (MD5_CTX *mdContext, unsigned char *inBuf, unsigned inLen); +void MD5Final (MD5_CTX *mdContext); + +/* + ********************************************************************** + ** End of md5.h ** + ******************************* (cut) ******************************** + */ + + diff --git a/backend/melf.h b/backend/melf.h new file mode 100644 index 00000000..3af989e3 --- /dev/null +++ b/backend/melf.h @@ -0,0 +1,381 @@ + +/* ELF file format */ + +typedef unsigned short elf_u16_f32; +typedef unsigned int elf_u32_f32; +typedef int elf_s32_f32; +typedef unsigned int elf_add_f32; +typedef unsigned int elf_off_f32; +typedef unsigned char elf_u8_f32; + +#define EI_NIDENT 16 +typedef struct + { +// unsigned char EHident[EI_NIDENT]; /* Header identification info */ + #define EI_MAG0 0 /* Identification byte offset 0*/ + #define EI_MAG1 1 /* Identification byte offset 1*/ + #define EI_MAG2 2 /* Identification byte offset 2*/ + #define EI_MAG3 3 /* Identification byte offset 3*/ + #define ELFMAG0 0x7f /* Magic number byte 0 */ + #define ELFMAG1 'E' /* Magic number byte 1 */ + #define ELFMAG2 'L' /* Magic number byte 2 */ + #define ELFMAG3 'F' /* Magic number byte 3 */ + + #define EI_CLASS 4 /* File class byte offset 4 */ + #define ELFCLASSNONE 0 // invalid + #define ELFCLASS32 1 /* 32-bit objects */ + #define ELFCLASS64 2 /* 64-bit objects */ + + #define EI_DATA 5 /* Data encoding byte offset 5 */ + #define ELFDATANONE 0 // invalid + #define ELFDATA2LSB 1 /* 2's comp,lsb low address */ + #define ELFDATA2MSB 2 /* 2's comp,msb low address */ + + #define EI_VERSION 6 /* Header version byte offset 6 */ + //#define EV_CURRENT 1 /* Current header format */ + + #define EI_OSABI 7 /* OS ABI byte offset 7 */ + #define ELFOSABI_SYSV 0 /* UNIX System V ABI */ + #define ELFOSABI_HPUX 1 /* HP-UX */ + #define ELFOSABI_NETBSD 2 + #define ELFOSABI_LINUX 3 + #define ELFOSABI_FREEBSD 9 + #define ELFOSABI_ARM 97 /* ARM */ + #define ELFOSABI_STANDALONE 255 /* Standalone/embedded */ + + #define EI_ABIVERSION 8 /* ABI version byte offset 8 */ + + #define EI_PAD 9 /* Byte to start of padding */ + + elf_u16_f32 e_type; /* Object file type */ + #define ET_NONE 0 /* No specified file type */ + #define ET_REL 1 /* Relocatable object file */ + #define ET_EXEC 2 /* Executable file */ + #define ET_DYN 3 /* Dynamic link object file */ + #define ET_CORE 4 /* Core file */ + #define ET_LOPROC 0xff00 /* Processor low index */ + #define ET_HIPROC 0xffff /* Processor hi index */ + + elf_u16_f32 e_machine; /* Machine architecture */ + #define EM_386 3 /* Intel 80386 */ + #define EM_486 6 /* Intel 80486 */ + #define EM_X86_64 62 // Advanced Micro Devices X86-64 processor + + elf_u32_f32 e_version; /* File format version */ + #define EV_NONE 0 // invalid version + #define EV_CURRENT 1 // Current file format + + elf_add_f32 e_entry; /* Entry point virtual address */ + elf_off_f32 e_phoff; /* Program header table(PHT)offset */ + elf_off_f32 e_shoff; /* Section header table(SHT)offset */ + elf_u32_f32 e_flags; /* Processor-specific flags */ + elf_u16_f32 e_ehsize; /* Size of ELF header (bytes) */ + #define EH_HEADER_SIZE 0x34 + elf_u16_f32 e_phentsize; /* Size of PHT (bytes) */ + #define EH_PHTENT_SIZE 0x20 + elf_u16_f32 e_phnum; /* Number of PHT entries */ + elf_u16_f32 e_shentsize; /* Size of SHT entry in bytes */ + #define EH_SHTENT_SIZE 0x28 + elf_u16_f32 e_shnum; /* Number of SHT entries */ + elf_u16_f32 e_shstrndx; /* SHT index for string table */ + } Elf32_Hdr; + +/* Section header. */ + +typedef struct +{ + elf_u32_f32 sh_name; /* String table offset for section name */ + elf_u32_f32 sh_type; /* Section type */ + #define SHT_NULL 0 /* SHT entry unused */ + #define SHT_PROGDEF 1 /* Program defined data */ + #define SHT_SYMTAB 2 /* Symbol table */ + #define SHT_STRTAB 3 /* String table */ + #define SHT_RELA 4 /* Relocations with addends */ + #define SHT_HASHTAB 5 /* Symbol hash table */ + #define SHT_DYNAMIC 6 /* String table for dynamic symbols */ + #define SHT_NOTE 7 /* Notes */ + #define SHT_RESDATA 8 /* Reserved data space */ + #define SHT_NOBITS SHT_RESDATA + #define SHT_REL 9 /* Relocations no addends */ + #define SHT_RESTYPE 10 /* Reserved section type*/ + #define SHT_DYNTAB 11 /* Dynamic linker symbol table */ + #define SHT_GROUP 17 /* Section group (COMDAT) */ + elf_u32_f32 sh_flags; /* Section attribute flags */ + #define SHF_WRITE (1 << 0) /* Writable during execution */ + #define SHF_ALLOC (1 << 1) /* In memory during execution */ + #define SHF_EXECINSTR (1 << 2) /* Executable machine instructions*/ + #define SHF_TLS (1 << 10) /* Thread local */ + #define SHF_MASKPROC 0xf0000000 /* Mask for processor-specific */ + elf_add_f32 sh_addr; /* Starting virtual memory address */ + elf_off_f32 sh_offset; /* Offset to section in file */ + elf_u32_f32 sh_size; /* Size of section */ + elf_u32_f32 sh_link; /* Index to optional related section */ + elf_u32_f32 sh_info; /* Optional extra section information */ + elf_u32_f32 sh_addralign; /* Required section alignment */ + elf_u32_f32 sh_entsize; /* Size of fixed size section entries */ +} Elf32_Shdr; + +// Special Section Header Table Indices +#define SHT_UNDEF 0 /* Undefined section */ +#define SHT_ABS 0xfff1 /* Absolute value for symbol references */ +#define SHT_COMMON 0xfff2 /* Symbol defined in common section */ +#define SHT_RESVSTART 0xff00 /* Start of reserved indices */ +#define SHT_PROCSTART 0xff00 /* Start of processor-specific */ +#define SHT_PROCEND 0xff1f /* End of processor-specific */ +#define SHT_RESVEND 0xffff /* End of reserved indices */ + +/* Symbol Table */ + +typedef struct +{ + elf_u32_f32 st_name; /* string table index for symbol name */ + elf_add_f32 st_value; /* Associated symbol value */ + elf_u32_f32 st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + #define ELF_ST_BIND(s) ((s)>>4) + #define ELF_ST_TYPE(s) ((s)&0xf) + #define ELF_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf)) + + #define STB_LOCAL 0 /* Local symbol */ + #define STB_GLOBAL 1 /* Global symbol */ + #define STB_WEAK 2 /* Weak symbol */ + #define ST_NUM_BINDINGS 3 /* Number of defined types. */ + #define STB_LOOS 10 /* Start of OS-specific */ + #define STB_HIOS 12 /* End of OS-specific */ + #define STB_LOPROC 13 /* Start of processor-specific */ + #define STB_HIPROC 15 /* End of processor-specific */ + + #define STT_NOTYPE 0 /* Symbol type is unspecified */ + #define STT_OBJECT 1 /* Symbol is a data object */ + #define STT_FUNC 2 /* Symbol is a code object */ + #define STT_SECTION 3 /* Symbol associated with a section */ + #define STT_FILE 4 /* Symbol's name is file name */ + #define STT_COMMON 5 + #define STT_TLS 6 + #define STT_NUM 5 /* Number of defined types. */ + #define STT_LOOS 11 /* Start of OS-specific */ + #define STT_HIOS 12 /* End of OS-specific */ + #define STT_LOPROC 13 /* Start of processor-specific */ + #define STT_HIPROC 15 /* End of processor-specific */ + + + unsigned char st_other; /* Currently not defined */ + elf_u16_f32 st_shndx; /* SHT index for symbol definition */ +} Elf32_Sym; + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + elf_add_f32 r_offset; /* Address */ + elf_u32_f32 r_info; /* Relocation type and symbol index */ + #define ELF32_R_IDX(i) ((i) >> 8) /* Symbol idx */ + #define ELF32_R_TYPE(i)((i) & 0xff) /* Type of relocation */ + #define ELF32_R_INFO(i, t) (((i) << 8) + ((t) & 0xff)) + + #define RI_TYPE_NONE 0 /* No reloc */ + #define RI_TYPE_SYM32 1 /* Symbol value 32 bit */ + #define RI_TYPE_PC32 2 /* PC relative 32 bit */ + #define RI_TYPE_GOT32 3 /* 32 bit GOT entry */ + #define RI_TYPE_PLT32 4 /* 32 bit PLT address */ + #define RI_TYPE_COPY 5 /* Copy symbol at runtime */ + #define RI_TYPE_GLOBDAT 6 /* Create GOT entry */ + #define RI_TYPE_JMPSLOT 7 /* Create PLT entry */ + #define RI_TYPE_REL 8 /* Adjust by program base */ + #define RI_TYPE_GOTOFF 9 /* 32 bit offset to GOT */ + #define RI_TYPE_GOTPC 10 /* 32 bit PC relative offset to GOT */ + #define RI_TYPE_TLS_TPOFF 14 + #define RI_TYPE_TLS_IE 15 + #define RI_TYPE_TLS_GOTIE 16 + #define RI_TYPE_TLS_LE 17 /* negative offset relative to static TLS */ + #define RI_TYPE_TLS_GD 18 + #define RI_TYPE_TLS_LDM 19 + #define RI_TYPE_TLS_GD_32 24 + #define RI_TYPE_TLS_GD_PUSH 25 + #define RI_TYPE_TLS_GD_CALL 26 + #define RI_TYPE_TLS_GD_POP 27 + #define RI_TYPE_TLS_LDM_32 28 + #define RI_TYPE_TLS_LDM_PUSH 29 + #define RI_TYPE_TLS_LDM_CALL 30 + #define RI_TYPE_TLS_LDM_POP 31 + #define RI_TYPE_TLS_LDO_32 32 + #define RI_TYPE_TLS_IE_32 33 + #define RI_TYPE_TLS_LE_32 34 + #define RI_TYPE_TLS_DTPMOD32 35 + #define RI_TYPE_TLS_DTPOFF32 36 + #define RI_TYPE_TLS_TPOFF32 37 +} Elf32_Rel; + +/* stabs debug records */ + +typedef struct +{ + elf_u32_f32 DBstring; /* string table index for the symbol */ + elf_u8_f32 DBtype; /* type of the symbol */ + #define DBT_UNDEF 0x00 /* undefined symbol */ + #define DBT_EXT 0x01 /* exernal modifier */ + #define DBT_ABS 0x02 /* absolute */ + #define DBT_TEXT 0x04 /* code text */ + #define DBT_DATA 0x06 /* data */ + #define DBT_BSS 0x08 /* BSS */ + #define DBT_INDR 0x0a /* indirect to another symbol */ + #define DBT_COMM 0x12 /* common -visible after shr'd lib link */ + #define DBT_SETA 0x14 /* Absolue set element */ + #define DBT_SETT 0x16 /* code text segment set element */ + #define DBT_SETD 0x18 /* data segment set element */ + #define DBT_SETB 0x1a /* BSS segment set element */ + #define DBT_SETV 0x1c /* Pointer to set vector */ + #define DBT_WARNING 0x1e /* print warning during link */ + #define DBT_FN 0x1f /* name of object file */ + + #define DBT_GSYM 0x20 /* global symbol */ + #define DBT_FUN 0x24 /* function name */ + #define DBT_STSYM 0x26 /* static data */ + #define DBT_LCSYM 0x28 /* static bss */ + #define DBT_MAIN 0x2a /* main routine */ + #define DBT_RO 0x2c /* read only */ + #define DBT_OPT 0x3c /* target option? */ + #define DBT_REG 0x40 /* register variable */ + #define DBT_TLINE 0x44 /* text line number */ + #define DBT_DLINE 0x46 /* dat line number */ + #define DBT_BLINE 0x48 /* bss line number */ + #define DBT_STUN 0x62 /* structure or union */ + #define DBT_SRCF 0x64 /* source file */ + #define DBT_AUTO 0x80 /* stack variable */ + #define DBT_TYPE 0x80 /* type definition */ + #define DBT_INCS 0x84 /* include file start */ + #define DBT_PARAM 0xa0 /* parameter */ + #define DBT_INCE 0xa2 /* include file end */ + elf_u8_f32 DBmisc; /* misc. info */ + elf_u16_f32 DBdesc; /* description field */ + elf_u32_f32 DBvalu; /* symbol value */ +} elf_stab; + + +/* Program header. */ + +typedef struct +{ + elf_u32_f32 PHtype; /* Program type */ + #define PHT_NULL 0 /* SHT entry unused */ + elf_off_f32 PHoff; /* Offset to segment in file */ + elf_add_f32 PHvaddr; /* Starting virtual memory address */ + elf_add_f32 PHpaddr; /* Starting absolute memory address */ + elf_u32_f32 PHfilesz; /* Size of file image */ + elf_u32_f32 PHmemsz; /* Size of memory image */ + elf_u32_f32 PHflags; /* Program attribute flags */ + elf_u32_f32 PHalign; /* Program loading alignment */ +} elf_pht; + + + +/* Legal values for sh_flags (section flags). */ + +/***************************** 64 bit Elf *****************************************/ + +typedef unsigned long long Elf64_Addr; +typedef unsigned long long Elf64_Off; +typedef unsigned long long Elf64_Xword; +typedef long long Elf64_Sxword; +typedef int Elf64_Sword; +typedef unsigned int Elf64_Word; +typedef unsigned short Elf64_Half; + +typedef struct +{ + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +typedef struct { + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + +typedef struct { + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + +typedef struct { + Elf64_Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf64_Half st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; + #define ELF64_R_SYM(i) ((Elf64_Word)((i)>>32)) + #define ELF64_R_TYPE(i) ((Elf64_Word)(i & 0xFFFFFFFF)) + #define ELF64_R_INFO(s,t) ((((Elf64_Xword)(s))<<32)|(Elf64_Word)(t)) + + // X86-64 Relocation types + + #define R_X86_64_NONE 0 // -- No relocation + #define R_X86_64_64 1 // 64 Direct 64 bit + #define R_X86_64_PC32 2 // 32 PC relative 32 bit signed + #define R_X86_64_GOT32 3 // 32 32 bit GOT entry + #define R_X86_64_PLT32 4 // 32 bit PLT address + #define R_X86_64_COPY 5 // -- Copy symbol at runtime + #define R_X86_64_GLOB_DAT 6 // 64 Create GOT entry + #define R_X86_64_JUMP_SLOT 7 // 64 Create PLT entry + #define R_X86_64_RELATIVE 8 // 64 Adjust by program base + #define R_X86_64_GOTPCREL 9 // 32 32 bit signed pc relative offset to GOT + #define R_X86_64_32 10 // 32 Direct 32 bit zero extended + #define R_X86_64_32S 11 // 32 Direct 32 bit sign extended + #define R_X86_64_16 12 // 16 Direct 16 bit zero extended + #define R_X86_64_PC16 13 // 16 16 bit sign extended pc relative + #define R_X86_64_8 14 // 8 Direct 8 bit sign extended + #define R_X86_64_PC8 15 // 8 8 bit sign extended pc relative + #define R_X86_64_DTPMOD64 16 // 64 ID of module containing symbol + #define R_X86_64_DTPOFF64 17 // 64 Offset in TLS block + #define R_X86_64_TPOFF64 18 // 64 Offset in initial TLS block + #define R_X86_64_TLSGD 19 // 32 PC relative offset to GD GOT block + #define R_X86_64_TLSLD 20 // 32 PC relative offset to LD GOT block + #define R_X86_64_DTPOFF32 21 // 32 Offset in TLS block + #define R_X86_64_GOTTPOFF 22 // 32 PC relative offset to IE GOT entry + #define R_X86_64_TPOFF32 23 // 32 Offset in initial TLS block + #define R_X86_64_PC64 24 // 64 + #define R_X86_64_GOTOFF64 25 // 64 + #define R_X86_64_GOTPC32 26 // 32 + #define R_X86_64_GNU_VTINHERIT 250 // GNU C++ hack + #define R_X86_64_GNU_VTENTRY 251 // GNU C++ hack +} Elf64_Rel; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_Rela; + + diff --git a/backend/newman.c b/backend/newman.c new file mode 100644 index 00000000..c0ac6c86 --- /dev/null +++ b/backend/newman.c @@ -0,0 +1,1707 @@ +// Copyright (C) 1992-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include +#include + +#include "cc.h" +#include "token.h" +#include "global.h" +#include "oper.h" +#include "el.h" +#include "type.h" +#include "filespec.h" + +#if NEWMANGLE + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +#define BUFIDMAX (2 * IDMAX) + +struct Mangle +{ + char buf[BUFIDMAX + 2]; + + char *np; // index into buf[] + + // Used for compression of redundant znames + const char *zname[10]; + int znamei; + + type *arg[10]; // argument_replicator + int argi; // number used in arg[] +}; + +static Mangle mangle; + +static int mangle_inuse; + +struct MangleInuse +{ + MangleInuse() + { +#if 0 + assert(mangle_inuse == 0); + mangle_inuse++; +#endif + } + + ~MangleInuse() + { +#if 0 + assert(mangle_inuse == 1); + mangle_inuse--; +#endif + } +}; + +/* Names for special variables */ +char cpp_name_new[] = "?2"; +char cpp_name_delete[] = "?3"; +char cpp_name_anew[] = "?_P"; +char cpp_name_adelete[] = "?_Q"; +char cpp_name_ct[] = "?0"; +char cpp_name_dt[] = "?1"; +char cpp_name_as[] = "?4"; +char cpp_name_vc[] = "?_H"; +char cpp_name_primdt[] = "?_D"; +char cpp_name_scaldeldt[] = "?_G"; +char cpp_name_priminv[] = "?_R"; + +STATIC int cpp_cvidx ( tym_t ty ); +STATIC int cpp_protection ( symbol *s ); +STATIC void cpp_decorated_name ( symbol *s ); +STATIC void cpp_symbol_name ( symbol *s ); +STATIC void cpp_zname ( const char *p ); +STATIC void cpp_scope ( symbol *s ); +STATIC void cpp_type_encoding ( symbol *s ); +STATIC void cpp_external_function_type(symbol *s); +STATIC void cpp_external_data_type ( symbol *s ); +STATIC void cpp_member_function_type ( symbol *s ); +STATIC void cpp_static_member_function_type ( symbol *s ); +STATIC void cpp_static_member_data_type ( symbol *s ); +STATIC void cpp_local_static_data_type ( symbol *s ); +STATIC void cpp_vftable_type(symbol *s); +STATIC void cpp_adjustor_thunk_type(symbol *s); +STATIC void cpp_function_type ( type *t ); +STATIC void cpp_throw_types ( type *t ); +STATIC void cpp_ecsu_name ( symbol *s ); +STATIC void cpp_return_type ( symbol *s ); +STATIC void cpp_data_type ( type *t ); +STATIC void cpp_storage_convention ( symbol *s ); +STATIC void cpp_this_type ( type *t,Classsym *s ); +STATIC void cpp_vcall_model_type ( void ); +STATIC void cpp_calling_convention ( type *t ); +STATIC void cpp_argument_types ( type *t ); +STATIC void cpp_argument_list ( type *t, int flag ); +STATIC void cpp_primary_data_type ( type *t ); +STATIC void cpp_reference_type ( type *t ); +STATIC void cpp_pointer_type ( type *t ); +STATIC void cpp_ecsu_data_indirect_type ( type *t ); +STATIC void cpp_data_indirect_type ( type *t ); +STATIC void cpp_function_indirect_type ( type *t ); +STATIC void cpp_basic_data_type ( type *t ); +STATIC void cpp_ecsu_data_type(type *t); +STATIC void cpp_pointer_data_type ( type *t ); +STATIC void cpp_reference_data_type ( type *t, int flag ); +STATIC void cpp_enum_name ( symbol *s ); +STATIC void cpp_dimension ( targ_ullong u ); +STATIC void cpp_dimension_ld ( targ_ldouble ld ); +STATIC void cpp_string ( char *s, size_t len ); + +/**************************** + */ + +struct OPTABLE +#if MARS +{ + unsigned char tokn; + unsigned char oper; + char __near *string; + char *pretty; +} +#endif + oparray[] = { + { TKnew, OPnew, cpp_name_new, "new" }, + { TKdelete, OPdelete, cpp_name_delete,"del" }, + { TKadd, OPadd, "?H", "+" }, + { TKadd, OPuadd, "?H", "+" }, + { TKmin, OPmin, "?G", "-" }, + { TKmin, OPneg, "?G", "-" }, + { TKstar, OPmul, "?D", "*" }, + { TKstar, OPind, "?D", "*" }, + { TKdiv, OPdiv, "?K", "/" }, + { TKmod, OPmod, "?L", "%" }, + { TKxor, OPxor, "?T", "^" }, + { TKand, OPand, "?I", "&" }, + { TKand, OPaddr, "?I", "&" }, + { TKor, OPor, "?U", "|" }, + { TKcom, OPcom, "?S", "~" }, + { TKnot, OPnot, "?7", "!" }, + { TKeq, OPeq, cpp_name_as, "=" }, + { TKeq, OPstreq, "?4", "=" }, + { TKlt, OPlt, "?M", "<" }, + { TKgt, OPgt, "?O", ">" }, + { TKnew, OPanew, cpp_name_anew, "n[]" }, + { TKdelete, OPadelete, cpp_name_adelete,"d[]" }, + { TKunord, OPunord, "?_S", "!<>=" }, + { TKlg, OPlg, "?_T", "<>" }, + { TKleg, OPleg, "?_U", "<>=" }, + { TKule, OPule, "?_V", "!>" }, + { TKul, OPul, "?_W", "!>=" }, + { TKuge, OPuge, "?_X", "!<" }, + { TKug, OPug, "?_Y", "!<=" }, + { TKue, OPue, "?_Z", "!<>" }, + { TKaddass, OPaddass, "?Y", "+=" }, + { TKminass, OPminass, "?Z", "-=" }, + { TKmulass, OPmulass, "?X", "*=" }, + { TKdivass, OPdivass, "?_0", "/=" }, + { TKmodass, OPmodass, "?_1", "%=" }, + { TKxorass, OPxorass, "?_6", "^=" }, + { TKandass, OPandass, "?_4", "&=" }, + { TKorass, OPorass, "?_5", "|=" }, + { TKshl, OPshl, "?6", "<<" }, + { TKshr, OPshr, "?5", ">>" }, + { TKshrass, OPshrass, "?_2", ">>=" }, + { TKshlass, OPshlass, "?_3", "<<=" }, + { TKeqeq, OPeqeq, "?8", "==" }, + { TKne, OPne, "?9", "!=" }, + { TKle, OPle, "?N", "<=" }, + { TKge, OPge, "?P", ">=" }, + { TKandand, OPandand, "?V", "&&" }, + { TKoror, OPoror, "?W", "||" }, + { TKplpl, OPpostinc, "?E", "++" }, + { TKplpl, OPpreinc, "?E", "++" }, + { TKmimi, OPpostdec, "?F", "--" }, + { TKmimi, OPpredec, "?F", "--" }, + { TKlpar, OPcall, "?R", "()" }, + { TKlbra, OPbrack, "?A", "[]" }, + { TKarrow, OParrow, "?C", "->" }, + { TKcomma, OPcomma, "?Q", "," }, + { TKarrowstar, OParrowstar, "?J", "->*" }, +}; + +/**************************************** + * Convert from identifier to operator + */ + +#if __GNUC__ // NOT DONE - FIX +char * unmangle_pt(const char **s) +{ + return (char *)*s; +} +#else +#if __cplusplus +extern "C" +#endif + char * __cdecl unmangle_pt(const char **); + +#endif + +char *cpp_unmangleident(const char *p) +{ int i; + MangleInuse m; + + //printf("cpp_unmangleident('%s')\n", p); + if (*p == '$') // if template name + { char *s; + const char *q; + + L1: + q = p; + s = unmangle_pt(&q); + if (s) + { if (strlen(s) <= BUFIDMAX) + p = strcpy(mangle.buf, s); + free(s); + } + } + else if (*p == '?') // if operator name + { int i; + + if (NEWTEMPMANGLE && p[1] == '$') // if template name + goto L1; + for (i = 0; i < arraysize(oparray); i++) + { if (strcmp(p,oparray[i].string) == 0) + { char *s; + + strcpy(mangle.buf, "operator "); + switch (oparray[i].oper) + { case OPanew: + s = "new[]"; + break; + case OPadelete: + s = "delete[]"; + break; + case OPdelete: + s = "delete"; + break; + default: + s = oparray[i].pretty; + break; + } + strcat(mangle.buf,s); + p = mangle.buf; + break; + } + } + } + //printf("-cpp_unmangleident() = '%s'\n", p); + return (char *)p; +} + +/**************************************** + * Find index in oparray[] for operator. + * Returns: + * index or -1 if not found + */ + +int cpp_opidx(int op) +{ int i; + + for (i = 0; i < arraysize(oparray); i++) + if (oparray[i].oper == op) + return i; + return -1; +} + +/*************************************** + * Find identifier string associated with operator. + * Returns: + * NULL if not found + */ + +#if SCPP + +char *cpp_opident(int op) +{ int i; + + i = cpp_opidx(op); + return (i == -1) ? NULL : oparray[i].string; +} + +#endif + +/********************************** + * Convert from operator token to name. + * Output: + * *poper OPxxxx + * *pt set to type for user defined conversion + * Returns: + * pointer to corresponding name + */ + +#if SCPP + +char *cpp_operator(int *poper,type **pt) +{ + int i; + type *typ_spec; + char *s; + + *pt = NULL; + stoken(); /* skip over operator keyword */ + for (i = 0; i < arraysize(oparray); i++) + { if (oparray[i].tokn == tok.TKval) + goto L1; + } + + /* Look for type conversion */ + if (type_specifier(&typ_spec)) + { type *t; + + t = ptr_operator(typ_spec); // parse ptr-operator + fixdeclar(t); + type_free(typ_spec); + *pt = t; + return cpp_typetostring(t,"?B"); + } + + cpperr(EM_not_overloadable); // that token cannot be overloaded + s = "_"; + goto L2; + +L1: + s = oparray[i].string; + *poper = oparray[i].oper; + switch (*poper) + { case OPcall: + if (stoken() != TKrpar) + synerr(EM_rpar); /* ')' expected */ + break; + + case OPbrack: + if (stoken() != TKrbra) + synerr(EM_rbra); /* ']' expected */ + break; + + case OPnew: + if (stoken() != TKlbra) + goto Lret; + *poper = OPanew; // operator new[] + s = cpp_name_anew; + goto L3; + + case OPdelete: + if (stoken() != TKlbra) + goto Lret; + *poper = OPadelete; // operator delete[] + s = cpp_name_adelete; + L3: + if (stoken() != TKrbra) + synerr(EM_rbra); // ']' expected + if (!(config.flags4 & CFG4anew)) + { cpperr(EM_enable_anew); // throw -Aa to support this + config.flags4 |= CFG4anew; + } + break; + } +L2: + stoken(); +Lret: + return s; +} + +/****************************************** + * Alternate version that works on a list of token's. + * Input: + * to list of tokens + * Output: + * *pcastoverload 1 if user defined type conversion + */ + +char *cpp_operator2(token_t *to, int *pcastoverload) +{ + int i; + char *s; + token_t *tn; + int oper; + + *pcastoverload = 0; + if (!to || !to->TKnext) + return NULL; + + for (i = 0; i < arraysize(oparray); i++) + { + //printf("[%d] %d, %d\n", i, oparray[i].tokn, tok.TKval); + if (oparray[i].tokn == to->TKval) + goto L1; + } + + //printf("cpp_operator2(): castoverload\n"); + *pcastoverload = 1; + return NULL; + +L1: + tn = to->TKnext; + s = oparray[i].string; + oper = oparray[i].oper; + switch (oper) + { case OPcall: + if (tn->TKval != TKrpar) + synerr(EM_rpar); // ')' expected + break; + + case OPbrack: + if (tn->TKval != TKrbra) + synerr(EM_rbra); // ']' expected + break; + + case OPnew: + if (tn->TKval != TKlbra) + break; + oper = OPanew; // operator new[] + s = cpp_name_anew; + goto L3; + + case OPdelete: + if (tn->TKval != TKlbra) + break; + oper = OPadelete; // operator delete[] + s = cpp_name_adelete; + L3: + if (tn->TKval != TKrbra) + synerr(EM_rbra); // ']' expected + if (!(config.flags4 & CFG4anew)) + { cpperr(EM_enable_anew); // throw -Aa to support this + config.flags4 |= CFG4anew; + } + break; + } +Lret: + return s; +} + +#endif + +/*********************************** + * Generate and return a pointer to a string constructed from + * the type, appended to the prefix. + * Since these generated strings determine the uniqueness of names, + * they are also used to determine if two types are the same. + * Returns: + * pointer to static name[] + */ + +char *cpp_typetostring(type *t,char *prefix) +{ int i; + + if (prefix) + { strcpy(mangle.buf,prefix); + i = strlen(prefix); + } + else + i = 0; + //dbg_printf("cpp_typetostring:\n"); + //type_print(t); + MangleInuse m; + mangle.znamei = 0; + mangle.argi = 0; + mangle.np = mangle.buf + i; + mangle.buf[BUFIDMAX + 1] = 0x55; + cpp_data_type(t); + *mangle.np = 0; // 0-terminate mangle.buf[] + //dbg_printf("cpp_typetostring: '%s'\n", mangle.buf); + assert(strlen(mangle.buf) <= BUFIDMAX); + assert(mangle.buf[BUFIDMAX + 1] == 0x55); + return mangle.buf; +} + +/******************************** + * 'Mangle' a name for output. + * Returns: + * pointer to mangled name (a static buffer) + */ + +char *cpp_mangle(symbol *s) +{ + symbol_debug(s); + //printf("cpp_mangle(s = %p, '%s')\n", s, s->Sident); + //type_print(s->Stype); + +#if SCPP + if (!CPP) + return symbol_ident(s); +#endif + + if (type_mangle(s->Stype) != mTYman_cpp) + return symbol_ident(s); + else + { + MangleInuse m; + + mangle.znamei = 0; + mangle.argi = 0; + mangle.np = mangle.buf; + mangle.buf[BUFIDMAX + 1] = 0x55; + cpp_decorated_name(s); + *mangle.np = 0; // 0-terminate cpp_name[] + //dbg_printf("cpp_mangle() = '%s'\n", mangle.buf); + assert(strlen(mangle.buf) <= BUFIDMAX); + assert(mangle.buf[BUFIDMAX + 1] == 0x55); + return mangle.buf; + } +} + +/////////////////////////////////////////////////////// + +/********************************* + * Add char into cpp_name[]. + */ + +STATIC void __inline CHAR(char c) +{ + if (mangle.np < &mangle.buf[BUFIDMAX]) + *mangle.np++ = c; +} + +/********************************* + * Add char into cpp_name[]. + */ + +STATIC void STR(const char *p) +{ + size_t len; + + len = strlen(p); + if (mangle.np + len <= &mangle.buf[BUFIDMAX]) + { memcpy(mangle.np,p,len); + mangle.np += len; + } + else + for (; *p; p++) + CHAR(*p); +} + +/*********************************** + * Convert const volatile combinations into 0..3 + */ + +STATIC int cpp_cvidx(tym_t ty) +{ int i; + + i = (ty & mTYconst) ? 1 : 0; + i |= (ty & mTYvolatile) ? 2 : 0; + return i; +} + +/****************************** + * Turn protection into 0..2 + */ + +STATIC int cpp_protection(symbol *s) +{ int i; + + switch (s->Sflags & SFLpmask) + { case SFLprivate: i = 0; break; + case SFLprotected: i = 1; break; + case SFLpublic: i = 2; break; + default: +#ifdef DEBUG + symbol_print(s); +#endif + assert(0); + } + return i; +} + +/*********************************** + * Create mangled name for template instantiation. + */ + +#if SCPP + +char *template_mangle(symbol *s,param_t *arglist) +{ + /* mangling ::= '$' template_name { type | expr } + type ::= "T" mangled type + expr ::= integer | string | address | float | double | long_double + integer ::= "I" dimension + string ::= "S" string + address ::= "R" zname + float ::= "F" hex_digits + double ::= "D" hex_digits + long_double ::= "L" hex_digits + */ + param_t *p; + + assert(s); + symbol_debug(s); + //assert(s->Sclass == SCtemplate); + + //printf("\ntemplate_mangle(s = '%s', arglist = %p)\n", s->Sident, arglist); + //arglist->print_list(); + + MangleInuse m; + mangle.znamei = 0; + mangle.argi = 0; + mangle.np = mangle.buf; + mangle.buf[BUFIDMAX + 1] = 0x55; + + if (NEWTEMPMANGLE) + STR("?$"); + else + CHAR('$'); + + // BUG: this is for templates nested inside class scopes. + // Need to check if it creates names that are properly unmanglable. + cpp_zname(s->Sident); + if (s->Sscope) + cpp_scope(s->Sscope); + + for (p = arglist; p; p = p->Pnext) + { + if (p->Ptype) + { /* Argument is a type */ + if (!NEWTEMPMANGLE) + CHAR('T'); + cpp_argument_list(p->Ptype, 1); + } + else if (p->Psym) + { + CHAR('V'); // this is a 'class' name, but it should be a 'template' name + cpp_ecsu_name(p->Psym); + } + else + { /* Argument is an expression */ + elem *e = p->Pelem; + tym_t ty = tybasic(e->ET->Tty); + char *p; + char a[2]; + int ni; + char c; + + L2: + switch (e->Eoper) + { case OPconst: + switch (ty) + { case TYfloat: ni = FLOATSIZE; c = 'F'; goto L1; + case TYdouble_alias: + case TYdouble: ni = DOUBLESIZE; c = 'D'; goto L1; + case TYldouble: ni = LNGDBLSIZE; c = 'L'; goto L1; + L1: + if (NEWTEMPMANGLE) + CHAR('$'); + CHAR(c); + p = (char *)&e->EV.Vdouble; + while (ni--) + { char c; +#if __GNUC__ + static char hex[16] = + {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; +#else + static char hex[16] = "0123456789ABCDEF"; +#endif + + c = *p++; + CHAR(hex[c & 15]); + CHAR(hex[(c >> 4) & 15]); + } + break; + default: +#ifdef DEBUG + if (!tyintegral(ty) && !tymptr(ty)) + elem_print(e); +#endif + assert(tyintegral(ty) || tymptr(ty)); + if (NEWTEMPMANGLE) + STR("$0"); + else + CHAR('I'); + cpp_dimension(el_tolongt(e)); + break; + } + break; + case OPstring: + if (NEWTEMPMANGLE) + STR("$S"); + else + CHAR('S'); + if (e->EV.ss.Voffset) + synerr(EM_const_init); // constant initializer expected + cpp_string(e->EV.ss.Vstring,e->EV.ss.Vstrlen); + break; + case OPrelconst: + if (e->EV.sp.Voffset) + synerr(EM_const_init); // constant initializer expected + s = e->EV.sp.Vsym; + if (NEWTEMPMANGLE) + { STR("$1"); + cpp_decorated_name(s); + } + else + { CHAR('R'); + cpp_zname(s->Sident); + } + break; + case OPvar: + if (e->EV.sp.Vsym->Sflags & SFLvalue && + tybasic(e->ET->Tty) != TYstruct) + { + e = e->EV.sp.Vsym->Svalue; + goto L2; + } + else if (e->EV.sp.Vsym->Sclass == SCconst /*&& + pstate.STintemplate*/) + { + CHAR('V'); // pretend to be a class name + cpp_zname(e->EV.sp.Vsym->Sident); + break; + } + default: +#if SCPP +#ifdef DEBUG + if (!errcnt) + elem_print(e); +#endif + synerr(EM_const_init); // constant initializer expected + assert(errcnt); +#endif + break; + } + } + } + *mangle.np = 0; + //printf("template_mangle() = '%s'\n", mangle.buf); + assert(strlen(mangle.buf) <= BUFIDMAX); + assert(mangle.buf[BUFIDMAX + 1] == 0x55); + return mangle.buf; +} + +#endif + +////////////////////////////////////////////////////// +// Functions corresponding to the name mangling grammar in the +// "Microsoft Object Mapping Specification" + +STATIC void cpp_string(char *s,size_t len) +{ char c; + + for (; --len; s++) + { static char special_char[] = ",/\\:. \n\t'-"; + char *p; + + c = *s; + if (c & 0x80 && isalpha(c & 0x7F)) + { CHAR('?'); + c &= 0x7F; + } + else if (isalnum(c)) + ; + else + { + CHAR('?'); + if ((p = (char *)strchr(special_char,c)) != NULL) + c = '0' + (p - special_char); + else + { + CHAR('$'); + CHAR('A' + ((c >> 4) & 0x0F)); + c = 'A' + (c & 0x0F); + } + } + CHAR(c); + } + CHAR('@'); +} + +STATIC void cpp_dimension(targ_ullong u) +{ + if (u && u <= 10) + CHAR('0' + (char)u - 1); + else + { char buffer[sizeof(u) * 2 + 1]; + char __ss *p; + + buffer[sizeof(buffer) - 1] = 0; + for (p = &buffer[sizeof(buffer) - 1]; u; u >>= 4) + { + *--p = 'A' + (u & 0x0F); + } + STR(p); + CHAR('@'); + } +} + +#if 0 +STATIC void cpp_dimension_ld(targ_ldouble ld) +{ unsigned char ldbuf[sizeof(targ_ldouble)]; + + memcpy(ldbuf,&ld,sizeof(ld)); + if (u && u <= 10) + CHAR('0' + (char)u - 1); + else + { char buffer[sizeof(u) * 2 + 1]; + char __ss *p; + + buffer[sizeof(buffer) - 1] = 0; + for (p = &buffer[sizeof(buffer) - 1]; u; u >>= 4) + { + *--p = 'A' + (u & 0x0F); + } + STR(p); + CHAR('@'); + } +} +#endif + +STATIC void cpp_enum_name(symbol *s) +{ type *t; + char c; + + t = tsint; + switch (tybasic(t->Tty)) + { + case TYschar: c = '0'; break; + case TYuchar: c = '1'; break; + case TYshort: c = '2'; break; + case TYushort: c = '3'; break; + case TYint: c = '4'; break; + case TYuint: c = '5'; break; + case TYlong: c = '6'; break; + case TYulong: c = '7'; break; + default: assert(0); + } + CHAR(c); + cpp_ecsu_name(s); +} + +STATIC void cpp_reference_data_type(type *t, int flag) +{ + if (tybasic(t->Tty) == TYarray) + { + int ndim; + type *tn; + int i; + + CHAR('Y'); + + // Compute number of dimensions (we have at least one) + ndim = 0; + tn = t; + do + { ndim++; + tn = tn->Tnext; + } while (tybasic(tn->Tty) == TYarray); + + cpp_dimension(ndim); + for (; tybasic(t->Tty) == TYarray; t = t->Tnext) + { + if (t->Tflags & TFvla) + CHAR('X'); // DMC++ extension + else + cpp_dimension(t->Tdim); + } + + // DMC++ extension + if (flag) // if template type argument + { + i = cpp_cvidx(t->Tty); + if (i) + { CHAR('_'); + //CHAR('X' + i - 1); // _X, _Y, _Z + CHAR('O' + i - 1); // _O, _P, _Q + } + } + + cpp_basic_data_type(t); + } + else + cpp_basic_data_type(t); +} + +STATIC void cpp_pointer_data_type(type *t) +{ + if (tybasic(t->Tty) == TYvoid) + CHAR('X'); + else + cpp_reference_data_type(t, 0); +} + +STATIC void cpp_ecsu_data_type(type *t) +{ char c; + symbol *stag; + int i; + + type_debug(t); + switch (tybasic(t->Tty)) + { + case TYstruct: + stag = t->Ttag; + switch (stag->Sstruct->Sflags & (STRclass | STRunion)) + { case 0: c = 'U'; break; + case STRunion: c = 'T'; break; + case STRclass: c = 'V'; break; + default: + assert(0); + } + CHAR(c); + cpp_ecsu_name(stag); + break; + case TYenum: + CHAR('W'); + cpp_enum_name(t->Ttag); + break; + default: +#ifdef DEBUG + type_print(t); +#endif + assert(0); + } +} + +STATIC void cpp_basic_data_type(type *t) +{ char c; + int i; + + //printf("cpp_basic_data_type(t)\n"); + //type_print(t); + switch (tybasic(t->Tty)) + { + case TYschar: c = 'C'; goto dochar; + case TYchar: c = 'D'; goto dochar; + case TYuchar: c = 'E'; goto dochar; + case TYshort: c = 'F'; goto dochar; + case TYushort: c = 'G'; goto dochar; + case TYint: c = 'H'; goto dochar; + case TYuint: c = 'I'; goto dochar; + case TYlong: c = 'J'; goto dochar; + case TYulong: c = 'K'; goto dochar; + case TYfloat: c = 'M'; goto dochar; + case TYdouble: c = 'N'; goto dochar; + + case TYdouble_alias: + if (intsize == 4) + { c = 'O'; + goto dochar; + } + c = 'Z'; + goto dochar2; + + case TYldouble: + if (intsize == 2) + { c = 'O'; + goto dochar; + } + c = 'Z'; + goto dochar2; + dochar: + CHAR(c); + break; + + case TYllong: c = 'J'; goto dochar2; + case TYullong: c = 'K'; goto dochar2; + case TYbool: c = 'N'; goto dochar2; // was 'X' prior to 8.1b8 + case TYwchar_t: + if (config.flags4 & CFG4nowchar_t) + { + c = 'G'; + goto dochar; // same as TYushort + } + else + { + pstate.STflags |= PFLmfc; + c = 'Y'; + goto dochar2; + } + + // Digital Mars extensions + case TYifloat: c = 'R'; goto dochar2; + case TYidouble: c = 'S'; goto dochar2; + case TYildouble: c = 'T'; goto dochar2; + case TYcfloat: c = 'U'; goto dochar2; + case TYcdouble: c = 'V'; goto dochar2; + case TYcldouble: c = 'W'; goto dochar2; + + case TYchar16: c = 'X'; goto dochar2; + case TYdchar: c = 'Y'; goto dochar2; + case TYnullptr: c = 'Z'; goto dochar2; + + dochar2: + CHAR('_'); + goto dochar; + +#if TARGET_SEGMENTED + case TYsptr: + case TYcptr: + case TYf16ptr: + case TYfptr: + case TYhptr: + case TYvptr: +#endif +#if !MARS + case TYmemptr: +#endif + case TYnptr: + c = 'P' + cpp_cvidx(t->Tty); + CHAR(c); + cpp_pointer_type(t); + break; + case TYstruct: + case TYenum: + cpp_ecsu_data_type(t); + break; + case TYarray: + i = cpp_cvidx(t->Tty); + i |= 1; // always const + CHAR('P' + i); + cpp_pointer_type(t); + break; + case TYvoid: + c = 'X'; + goto dochar; +#if !MARS + case TYident: + if (pstate.STintemplate) + { + CHAR('V'); // pretend to be a class name + cpp_zname(t->Tident); + } + else + { +#if SCPP + cpperr(EM_no_type,t->Tident); // no type for argument +#endif + c = 'X'; + goto dochar; + } + break; + case TYtemplate: + if (pstate.STintemplate) + { + CHAR('V'); // pretend to be a class name + cpp_zname(((typetemp_t *)t)->Tsym->Sident); + } + else + goto Ldefault; + break; +#endif + + default: + Ldefault: + if (tyfunc(t->Tty)) + cpp_function_type(t); + else + { +#if SCPP +#ifdef DEBUG + if (!errcnt) + type_print(t); +#endif + assert(errcnt); +#endif + } + } +} + +STATIC void cpp_function_indirect_type(type *t) +{ int farfunc; + + farfunc = tyfarfunc(t->Tnext->Tty) != 0; +#if !MARS + if (tybasic(t->Tty) == TYmemptr) + { + CHAR('8' + farfunc); + cpp_scope(t->Ttag); + CHAR('@'); + //cpp_this_type(t->Tnext,t->Ttag); // MSC doesn't do this + } + else +#endif + CHAR('6' + farfunc); +} + +STATIC void cpp_data_indirect_type(type *t) +{ int i; +#if !MARS + if (tybasic(t->Tty) == TYmemptr) // if pointer to member + { + i = cpp_cvidx(t->Tty); + if (t->Tty & mTYfar) + i += 4; + CHAR('Q' + i); + cpp_scope(t->Ttag); + CHAR('@'); + } + else +#endif + cpp_ecsu_data_indirect_type(t); +} + +STATIC void cpp_ecsu_data_indirect_type(type *t) +{ int i; + tym_t ty; + + i = 0; + if (t->Tnext) + { ty = t->Tnext->Tty & (mTYconst | mTYvolatile); + switch (tybasic(t->Tty)) + { +#if TARGET_SEGMENTED + case TYfptr: + case TYvptr: + case TYfref: + ty |= mTYfar; + break; + + case TYhptr: + i += 8; + break; + case TYref: + case TYarray: + if (LARGEDATA && !(ty & mTYLINK)) + ty |= mTYfar; + break; +#endif + } + } + else + ty = t->Tty & (mTYLINK | mTYconst | mTYvolatile); + i |= cpp_cvidx(ty); +#if TARGET_SEGMENTED + if (ty & (mTYcs | mTYfar)) + i += 4; +#endif + CHAR('A' + i); +} + +STATIC void cpp_pointer_type(type *t) +{ tym_t ty; + + if (tyfunc(t->Tnext->Tty)) + { + cpp_function_indirect_type(t); + cpp_function_type(t->Tnext); + } + else + { + cpp_data_indirect_type(t); + cpp_pointer_data_type(t->Tnext); + } +} + +STATIC void cpp_reference_type(type *t) +{ + cpp_data_indirect_type(t); + cpp_reference_data_type(t->Tnext, 0); +} + +STATIC void cpp_primary_data_type(type *t) +{ + if (tyref(t->Tty)) + { +#if 1 + // C++98 8.3.2 says cv-qualified references are ignored + CHAR('A'); +#else + switch (t->Tty & (mTYconst | mTYvolatile)) + { + case 0: CHAR('A'); break; + case mTYvolatile: CHAR('B'); break; + + // Digital Mars extensions + case mTYconst | mTYvolatile: CHAR('_'); CHAR('L'); break; + case mTYconst: CHAR('_'); CHAR('M'); break; + } +#endif + cpp_reference_type(t); + } + else + cpp_basic_data_type(t); +} + +/***** + * flag: 1 = template argument + */ + +STATIC void cpp_argument_list(type *t, int flag) +{ int i; + tym_t ty; + + //printf("cpp_argument_list(flag = %d)\n", flag); + // If a data type that encodes only into one character + ty = tybasic(t->Tty); + if (ty <= TYldouble && ty != TYenum + && ty != TYbool // added for versions >= 8.1b9 +#if OVERLOAD_CV_PARAM + && !(t->Tty & (mTYconst | mTYvolatile)) +#endif + ) + { + cpp_primary_data_type(t); + } + else + { + // See if a match with a previously used type + for (i = 0; 1; i++) + { + if (i == mangle.argi) // no match + { +#if OVERLOAD_CV_PARAM + if (ty <= TYcldouble || ty == TYstruct) + { + int cvidx = cpp_cvidx(t->Tty); + if (cvidx) + { + // Digital Mars extensions + CHAR('_'); + CHAR('N' + cvidx); // _O, _P, _Q prefix + } + } +#endif + if (flag && tybasic(t->Tty) == TYarray) + { + cpp_reference_data_type(t, flag); + } + else + cpp_primary_data_type(t); + if (mangle.argi < 10) + mangle.arg[mangle.argi++] = t; + break; + } + if (typematch(t,mangle.arg[i],0)) + { + CHAR('0' + i); // argument_replicator + break; + } + } + } +} + +STATIC void cpp_argument_types(type *t) +{ param_t *p; + char c; + + //printf("cpp_argument_types()\n"); + //type_debug(t); + for (p = t->Tparamtypes; p; p = p->Pnext) + cpp_argument_list(p->Ptype, 0); + if (t->Tflags & TFfixed) + c = t->Tparamtypes ? '@' : 'X'; + else + c = 'Z'; + CHAR(c); +} + +STATIC void cpp_calling_convention(type *t) +{ char c; + + switch (tybasic(t->Tty)) + { + case TYnfunc: + case TYhfunc: +#if TARGET_SEGMENTED + case TYffunc: +#endif + c = 'A'; break; +#if TARGET_SEGMENTED + case TYf16func: + case TYfpfunc: +#endif + case TYnpfunc: + c = 'C'; break; + case TYnsfunc: +#if TARGET_SEGMENTED + case TYfsfunc: +#endif + c = 'G'; break; + case TYjfunc: + case TYmfunc: +#if TARGET_SEGMENTED + case TYnsysfunc: + case TYfsysfunc: +#endif + c = 'E'; break; + case TYifunc: + c = 'K'; break; + default: + assert(0); + } + CHAR(c); +} + +STATIC void cpp_vcall_model_type() +{ +} + +#if SCPP || MARS + +STATIC void cpp_this_type(type *tfunc,Classsym *stag) +{ type *t; + + type_debug(tfunc); + symbol_debug(stag); +#if MARS + t = type_allocn(TYnptr, stag->Stype); + t->Tcount++; +#else + t = cpp_thistype(tfunc,stag); +#endif + //cpp_data_indirect_type(t); + cpp_ecsu_data_indirect_type(t); + type_free(t); +} + +#endif + +STATIC void cpp_storage_convention(symbol *s) +{ tym_t ty; + type *t = s->Stype; + + ty = t->Tty; +#if TARGET_SEGMENTED + if (LARGEDATA && !(ty & mTYLINK)) + t->Tty |= mTYfar; +#endif + cpp_data_indirect_type(t); + t->Tty = ty; +} + +STATIC void cpp_data_type(type *t) +{ + type_debug(t); + switch (tybasic(t->Tty)) + { case TYvoid: + CHAR('X'); + break; + case TYstruct: + case TYenum: + CHAR('?'); + cpp_ecsu_data_indirect_type(t); + cpp_ecsu_data_type(t); + break; + default: + cpp_primary_data_type(t); + break; + } +} + +STATIC void cpp_return_type(symbol *s) +{ + if (s->Sfunc->Fflags & (Fctor | Fdtor)) // if ctor or dtor + CHAR('@'); // no type + else + cpp_data_type(s->Stype->Tnext); +} + +STATIC void cpp_ecsu_name(symbol *s) +{ + //printf("cpp_ecsu_name(%s)\n", symbol_ident(s)); + cpp_zname(symbol_ident(s)); +#if SCPP || MARS + if (s->Sscope) + cpp_scope(s->Sscope); +#endif + CHAR('@'); +} + +STATIC void cpp_throw_types(type *t) +{ + //cpp_argument_types(?); + CHAR('Z'); +} + +STATIC void cpp_function_type(type *t) +{ tym_t ty; + type *tn; + + //printf("cpp_function_type()\n"); + //type_debug(t); + assert(tyfunc(t->Tty)); + cpp_calling_convention(t); + //cpp_return_type(s); + tn = t->Tnext; + ty = tn->Tty; +#if TARGET_SEGMENTED + if (LARGEDATA && (tybasic(ty) == TYstruct || tybasic(ty) == TYenum) && + !(ty & mTYLINK)) + tn->Tty |= mTYfar; +#endif + cpp_data_type(tn); + tn->Tty = ty; + cpp_argument_types(t); + cpp_throw_types(t); +} + +STATIC void cpp_adjustor_thunk_type(symbol *s) +{ +} + +STATIC void cpp_vftable_type(symbol *s) +{ + cpp_ecsu_data_indirect_type(s->Stype); +// vpath_name(); + CHAR('@'); +} + +STATIC void cpp_local_static_data_type(symbol *s) +{ + //cpp_lexical_frame(?); + cpp_external_data_type(s); +} + +STATIC void cpp_static_member_data_type(symbol *s) +{ + cpp_external_data_type(s); +} + +STATIC void cpp_static_member_function_type(symbol *s) +{ + cpp_function_type(s->Stype); +} + +#if SCPP || MARS +STATIC void cpp_member_function_type(symbol *s) +{ + assert(tyfunc(s->Stype->Tty)); + cpp_this_type(s->Stype,(Classsym *)s->Sscope); + if (s->Sfunc->Fflags & (Fctor | Fdtor)) + { type *t = s->Stype; + + cpp_calling_convention(t); + CHAR('@'); // return_type for ctors & dtors + cpp_argument_types(t); + cpp_throw_types(t); + } + else + cpp_static_member_function_type(s); +} +#endif + +STATIC void cpp_external_data_type(symbol *s) +{ + cpp_primary_data_type(s->Stype); + cpp_storage_convention(s); +} + +STATIC void cpp_external_function_type(symbol *s) +{ + cpp_function_type(s->Stype); +} + +STATIC void cpp_type_encoding(symbol *s) +{ char c; + + //printf("cpp_type_encoding()\n"); + if (tyfunc(s->Stype->Tty)) + { int farfunc; + + farfunc = tyfarfunc(s->Stype->Tty) != 0; +#if SCPP || MARS + if (isclassmember(s)) + { // Member function + int protection; + int ftype; + + protection = cpp_protection(s); + if (s->Sfunc->Fthunk && !(s->Sfunc->Fflags & Finstance)) + ftype = 3; + else + switch (s->Sfunc->Fflags & (Fvirtual | Fstatic)) + { case Fvirtual: ftype = 2; break; + case Fstatic: ftype = 1; break; + case 0: ftype = 0; break; + default: assert(0); + } + CHAR('A' + farfunc + protection * 8 + ftype * 2); + switch (ftype) + { case 0: cpp_member_function_type(s); break; + case 1: cpp_static_member_function_type(s); break; + case 2: cpp_member_function_type(s); break; + case 3: cpp_adjustor_thunk_type(s); break; + } + } + else +#endif + { // Non-member function + CHAR('Y' + farfunc); + cpp_external_function_type(s); + } + } + else + { +#if SCPP || MARS + if (isclassmember(s)) + { + { // Static data member + CHAR(cpp_protection(s) + '0'); + cpp_static_member_data_type(s); + } + } + else +#endif + { + if (s->Sclass == SCstatic +#if SCPP || MARS + || (s->Sscope && + s->Sscope->Sclass != SCstruct && + s->Sscope->Sclass != SCnamespace) +#endif + ) + { CHAR('4'); + cpp_local_static_data_type(s); + } + else + { CHAR('3'); + cpp_external_data_type(s); + } + } + } +} + +STATIC void cpp_scope(symbol *s) +{ + /* scope ::= + zname [ scope ] + '?' decorated_name [ scope ] + '?' lexical_frame [ scope ] + '?' '$' template_name [ scope ] + */ + while (s) + { char *p; + + symbol_debug(s); + switch (s->Sclass) + { + case SCnamespace: + cpp_zname(s->Sident); + break; + + case SCstruct: + cpp_zname(symbol_ident(s)); + break; + + default: + STR("?1?"); // Why? Who knows. + cpp_decorated_name(s); + break; + } +#if SCPP || MARS + s = s->Sscope; +#else + break; +#endif + } +} + +STATIC void cpp_zname(const char *p) +{ + //printf("cpp_zname(%s)\n", p); + if (*p != '?' || // if not operator_name + (NEWTEMPMANGLE && p[1] == '$')) // ?$ is a template name + { +#if MARS + /* Scan forward past any dots + */ + for (const char *q = p; *q; q++) + { + if (*q == '.') + p = q + 1; + } +#endif + + for (int i = 0; i < mangle.znamei; i++) + { + if (strcmp(p,mangle.zname[i]) == 0) + { CHAR('0' + i); + return; + } + } + if (mangle.znamei < 10) + mangle.zname[mangle.znamei++] = p; + STR(p); + CHAR('@'); + } + else if (p[1] == 'B') + STR("?B"); // skip return value encoding + else + { + STR(p); + } +} + +STATIC void cpp_symbol_name(symbol *s) +{ char *p; + + p = s->Sident; +#if SCPP + if (tyfunc(s->Stype->Tty) && s->Sfunc) + { + if (s->Sfunc->Fflags & Finstance) + { + Mangle save = mangle; + char *q; + int len; + + p = template_mangle(s, s->Sfunc->Fptal); + len = strlen(p); + q = (char *)alloca(len + 1); + memcpy(q, p, len + 1); + mangle = save; + p = q; + } + else if (s->Sfunc->Fflags & Foperator) + { // operator_name ::= '?' operator_code + //CHAR('?'); // already there + STR(p); + return; + } + } +#endif + cpp_zname(p); +} + +STATIC void cpp_decorated_name(symbol *s) +{ char *p; + + CHAR('?'); + cpp_symbol_name(s); +#if SCPP || MARS + if (s->Sscope) + cpp_scope(s->Sscope); +#endif + CHAR('@'); + cpp_type_encoding(s); +} + +/********************************* + * Mangle a vtbl or vbtbl name. + * Returns: + * pointer to generated symbol with mangled name + */ + +#if SCPP + +symbol *mangle_tbl( + int flag, // 0: vtbl, 1: vbtbl + type *t, // type for symbol + Classsym *stag, // class we're putting tbl in + baseclass_t *b) // base class (NULL if none) +{ const char *id; + symbol *s; + +#if 0 + dbg_printf("mangle_tbl(stag = '%s', sbase = '%s', parent = '%s')\n", + stag->Sident,b ? b->BCbase->Sident : "NULL", b ? b->parent->Sident : "NULL"); +#endif + if (flag == 0) + id = config.flags3 & CFG3rtti ? "?_Q" : "?_7"; + else + id = "?_8"; + MangleInuse m; + mangle.znamei = 0; + mangle.argi = 0; + mangle.np = mangle.buf; + CHAR('?'); + cpp_zname(id); + cpp_scope(stag); + CHAR('@'); + CHAR('6' + flag); + cpp_ecsu_data_indirect_type(t); +#if 1 + while (b) + { + cpp_scope(b->BCbase); + CHAR('@'); + b = b->BCpbase; + } +#else + if (b) + { cpp_scope(b->BCbase); + CHAR('@'); + // BUG: what if b is more than one level down? + if (b->parent != stag) + { cpp_scope(b->BCparent); + CHAR('@'); + } + } +#endif + CHAR('@'); + *mangle.np = 0; // 0-terminate mangle.buf[] + assert(strlen(mangle.buf) <= BUFIDMAX); + s = scope_define(mangle.buf,SCTglobal | SCTnspace | SCTlocal,SCunde); + s->Stype = t; + t->Tcount++; + return s; +} + +#endif + +#endif + +#endif diff --git a/backend/nteh.c b/backend/nteh.c new file mode 100644 index 00000000..f8c91249 --- /dev/null +++ b/backend/nteh.c @@ -0,0 +1,915 @@ +// Copyright (C) 1994-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +// Support for NT exception handling + +#include +#include +#include + +#include "cc.h" +#include "el.h" +#include "code.h" +#include "oper.h" +#include "global.h" +#include "type.h" +#include "dt.h" +#if SCPP +#include "scope.h" +#endif +#include "exh.h" + +#if !SPP && NTEXCEPTIONS + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +static symbol *s_table; +static symbol *s_context; +static char s_name_context_tag[] = "__nt_context"; +static char s_name_context[] = "__context"; +static char s_name_ecode[] = "__ecode"; + +static char text_nt[] = + "struct __nt_context {" + "int esp; int info; int prev; int handler; int stable; int sindex; int ebp;" + "};\n"; + +// member stable is not used for MARS or C++ + +int nteh_EBPoffset_sindex() { return -4; } +int nteh_EBPoffset_prev() { return -nteh_contextsym_size() + 8; } +int nteh_EBPoffset_info() { return -nteh_contextsym_size() + 4; } +int nteh_EBPoffset_esp() { return -nteh_contextsym_size() + 0; } + +int nteh_offset_sindex() { return MARS ? 16 : 20; } +int nteh_offset_sindex_seh() { return 20; } +int nteh_offset_info() { return 4; } + +/*********************************** + */ + +unsigned char *nteh_context_string() +{ + if (config.flags2 & CFG2seh) + return (unsigned char *)text_nt; + else + return NULL; +} + +/******************************* + * Get symbol for scope table for current function. + * Returns: + * symbol of table + */ + +STATIC symbol *nteh_scopetable() +{ symbol *s; + type *t; + + if (!s_table) + { + t = type_alloc(TYint); + s = symbol_generate(SCstatic,t); + s->Sseg = UNKNOWN; + symbol_keep(s); + s_table = s; + } + return s_table; +} + +/************************************* + */ + +void nteh_filltables() +{ +#if MARS + symbol *s = s_table; + symbol_debug(s); + except_fillInEHTable(s); +#endif +} + +/**************************** + * Generate and output scope table. + * Not called for NTEH C++ exceptions + */ + +void nteh_gentables() +{ + symbol *s = s_table; + symbol_debug(s); +#if MARS + //except_fillInEHTable(s); +#else + /* NTEH table for C. + * The table consists of triples: + * parent index + * filter address + * handler address + */ + unsigned fsize = 4; // target size of function pointer + dt_t **pdt = &s->Sdt; + int sz = 0; // size so far + + for (block *b = startblock; b; b = b->Bnext) + { + if (b->BC == BC_try) + { dt_t *dt; + block *bhandler; + + pdt = dtdword(pdt,b->Blast_index); // parent index + + // If try-finally + if (list_nitems(b->Bsucc) == 2) + { + pdt = dtdword(pdt,0); // filter address + bhandler = list_block(list_next(b->Bsucc)); + assert(bhandler->BC == BC_finally); + // To successor of BC_finally block + bhandler = list_block(bhandler->Bsucc); + } + else // try-except + { + bhandler = list_block(list_next(b->Bsucc)); + assert(bhandler->BC == BC_filter); + pdt = dtcoff(pdt,bhandler->Boffset); // filter address + bhandler = list_block(list_next(list_next(b->Bsucc))); + assert(bhandler->BC == BC_except); + } + pdt = dtcoff(pdt,bhandler->Boffset); // handler address + sz += 4 + fsize * 2; + } + } + assert(sz != 0); +#endif + + outdata(s); // output the scope table +#if MARS + nteh_framehandler(s); +#endif + s_table = NULL; +} + +/************************** + * Declare frame variables. + */ + +void nteh_declarvars(Blockx *bx) +{ symbol *s; + + //printf("nteh_declarvars()\n"); +#if MARS + if (!(bx->funcsym->Sfunc->Fflags3 & Fnteh)) // if haven't already done it + { bx->funcsym->Sfunc->Fflags3 |= Fnteh; + s = symbol_name(s_name_context,SCbprel,tsint); + s->Soffset = -5 * 4; // -6 * 4 for C __try, __except, __finally + s->Sflags |= SFLfree | SFLnodebug; + type_setty(&s->Stype,mTYvolatile | TYint); + symbol_add(s); + bx->context = s; + } +#else + if (!(funcsym_p->Sfunc->Fflags3 & Fnteh)) // if haven't already done it + { funcsym_p->Sfunc->Fflags3 |= Fnteh; + if (!s_context) + s_context = scope_search(s_name_context_tag,CPP ? SCTglobal : SCTglobaltag); + symbol_debug(s_context); + + s = symbol_name(s_name_context,SCbprel,s_context->Stype); + s->Soffset = -6 * 4; // -5 * 4 for C++ + s->Sflags |= SFLfree; + symbol_add(s); + type_setty(&s->Stype,mTYvolatile | TYstruct); + + s = symbol_name(s_name_ecode,SCauto,type_alloc(mTYvolatile | TYint)); + s->Sflags |= SFLfree; + symbol_add(s); + } +#endif +} + +/************************************** + * Generate elem that sets the context index into the scope table. + */ + +#if MARS +elem *nteh_setScopeTableIndex(Blockx *blx, int scope_index) +{ + elem *e; + Symbol *s; + + s = blx->context; + symbol_debug(s); + e = el_var(s); + e->EV.sp.Voffset = nteh_offset_sindex(); + return el_bin(OPeq, TYint, e, el_long(TYint, scope_index)); +} +#endif + + +/********************************** + * Return pointer to context symbol. + */ + +symbol *nteh_contextsym() +{ SYMIDX si; + symbol *sp; + + for (si = 0; 1; si++) + { assert(si < globsym.top); + sp = globsym.tab[si]; + symbol_debug(sp); + if (strcmp(sp->Sident,s_name_context) == 0) + return sp; + } +} + +/********************************** + * Return size of context symbol on stack. + */ + +unsigned nteh_contextsym_size() +{ int sz; + + if (usednteh & NTEH_try) + { +#if MARS + sz = 5 * 4; +#elif SCPP + sz = 6 * 4; +#else + assert(0); +#endif + assert(usedalloca != 1); + } + else if (usednteh & NTEHcpp) + { sz = 5 * 4; // C++ context record + assert(usedalloca != 1); + } + else if (usednteh & NTEHpassthru) + { sz = 1 * 4; + } + else + sz = 0; // no context record + return sz; +} + +/********************************** + * Return pointer to ecode symbol. + */ + +symbol *nteh_ecodesym() +{ SYMIDX si; + symbol *sp; + + for (si = 0; 1; si++) + { assert(si < globsym.top); + sp = globsym.tab[si]; + symbol_debug(sp); + if (strcmp(sp->Sident,s_name_ecode) == 0) + return sp; + } +} + +/********************************* + * Mark EH variables as used so that they don't get optimized away. + */ + +void nteh_usevars() +{ +#if SCPP + // Turn off SFLdead and SFLunambig in Sflags + nteh_contextsym()->Sflags &= ~(SFLdead | SFLunambig); + nteh_contextsym()->Sflags |= SFLread; + nteh_ecodesym()->Sflags &= ~(SFLdead | SFLunambig); + nteh_ecodesym()->Sflags |= SFLread; +#else + // Turn off SFLdead and SFLunambig in Sflags + nteh_contextsym()->Sflags &= ~SFLdead; + nteh_contextsym()->Sflags |= SFLread; +#endif +} + +/********************************* + * Generate NT exception handling function prolog. + */ + +code *nteh_prolog() +{ + code cs; + code *c1; + code *c; + + if (usednteh & NTEHpassthru) + { + /* An sindex value of -2 is a magic value that tells the + * stack unwinder to skip this frame. + */ + assert(config.exe & (EX_LINUX | EX_LINUX64 | EX_OSX | EX_OSX64 | EX_FREEBSD | EX_FREEBSD64 | EX_SOLARIS | EX_SOLARIS64)); + cs.Iop = 0x68; + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL2 = FLconst; + cs.IEV2.Vint = -2; + return gen(CNIL,&cs); // PUSH -2 + } + + /* Generate instance of struct __nt_context on stack frame: + [ ] // previous ebp already there + push -1 // sindex + mov EDX,FS:__except_list + push offset FLAT:scope_table // stable (not for MARS or C++) + push offset FLAT:__except_handler3 // handler + push EDX // prev + mov FS:__except_list,ESP + sub ESP,8 // info, esp for __except support + */ + +// useregs(mAX); // What is this for? + + cs.Iop = 0x68; + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL2 = FLconst; + cs.IEV2.Vint = -1; + c1 = gen(CNIL,&cs); // PUSH -1 + + if (usednteh & NTEHcpp || MARS) + { + // PUSH &framehandler + cs.IFL2 = FLframehandler; +#if MARS + nteh_scopetable(); +#endif + } + else + { + // Do stable + cs.Iflags |= CFoff; + cs.IFL2 = FLextern; + cs.IEVsym2 = nteh_scopetable(); + cs.IEVoffset2 = 0; + c1 = gen(c1,&cs); // PUSH &scope_table + + cs.IFL2 = FLextern; + cs.IEVsym2 = rtlsym[RTLSYM_EXCEPT_HANDLER3]; + makeitextern(rtlsym[RTLSYM_EXCEPT_HANDLER3]); + } + c = gen(NULL,&cs); // PUSH &__except_handler3 + + if (config.exe == EX_NT) + { + makeitextern(rtlsym[RTLSYM_EXCEPT_LIST]); + #if 0 + cs.Iop = 0xFF; + cs.Irm = modregrm(0,6,BPRM); + cs.Iflags = CFfs; + cs.Irex = 0; + cs.IFL1 = FLextern; + cs.IEVsym1 = rtlsym[RTLSYM_EXCEPT_LIST]; + cs.IEVoffset1 = 0; + gen(c,&cs); // PUSH FS:__except_list + #else + useregs(mDX); + cs.Iop = 0x8B; + cs.Irm = modregrm(0,DX,BPRM); + cs.Iflags = CFfs; + cs.Irex = 0; + cs.IFL1 = FLextern; + cs.IEVsym1 = rtlsym[RTLSYM_EXCEPT_LIST]; + cs.IEVoffset1 = 0; + gen(c1,&cs); // MOV EDX,FS:__except_list + + gen1(c,0x50 + DX); // PUSH EDX + #endif + cs.Iop = 0x89; + NEWREG(cs.Irm,SP); + gen(c,&cs); // MOV FS:__except_list,ESP + } + + c = genc2(c,0x81,modregrm(3,5,SP),8); // SUB ESP,8 + + return cat(c1,c); +} + +/********************************* + * Generate NT exception handling function epilog. + */ + +code *nteh_epilog() +{ + if (!(config.flags2 & CFG2seh)) + return NULL; + + /* Generate: + mov ECX,__context[EBP].prev + mov FS:__except_list,ECX + */ + code cs; + code *c; + unsigned reg; + +#if MARS + reg = CX; +#else + reg = (tybasic(funcsym_p->Stype->Tnext->Tty) == TYvoid) ? AX : CX; +#endif + useregs(mask[reg]); + + cs.Iop = 0x8B; + cs.Irm = modregrm(2,reg,BPRM); + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL1 = FLconst; + // EBP offset of __context.prev + cs.IEV1.Vint = nteh_EBPoffset_prev(); + c = gen(CNIL,&cs); + + cs.Iop = 0x89; + cs.Irm = modregrm(0,reg,BPRM); + cs.Iflags |= CFfs; + cs.IFL1 = FLextern; + cs.IEVsym1 = rtlsym[RTLSYM_EXCEPT_LIST]; + cs.IEVoffset1 = 0; + return gen(c,&cs); +} + +/************************** + * Set/Reset ESP from context. + */ + +code *nteh_setsp(int op) +{ code cs; + + cs.Iop = op; + cs.Irm = modregrm(2,SP,BPRM); + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL1 = FLconst; + // EBP offset of __context.esp + cs.IEV1.Vint = nteh_EBPoffset_esp(); + return gen(CNIL,&cs); // MOV ESP,__context[EBP].esp +} + +/**************************** + * Put out prolog for BC_filter block. + */ + +code *nteh_filter(block *b) +{ code *c; + code cs; + + assert(b->BC == BC_filter); + c = CNIL; + if (b->Bflags & BFLehcode) // if referenced __ecode + { + /* Generate: + mov EAX,__context[EBP].info + mov EAX,[EAX] + mov EAX,[EAX] + mov __ecode[EBP],EAX + */ + + c = getregs(mAX); + + cs.Iop = 0x8B; + cs.Irm = modregrm(2,AX,BPRM); + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL1 = FLconst; + // EBP offset of __context.info + cs.IEV1.Vint = nteh_EBPoffset_info(); + c = gen(c,&cs); // MOV EAX,__context[EBP].info + cs.Irm = modregrm(0,AX,0); + gen(c,&cs); // MOV EAX,[EAX] + gen(c,&cs); // MOV EAX,[EAX] + cs.Iop = 0x89; + cs.Irm = modregrm(2,AX,BPRM); + cs.IFL1 = FLauto; + cs.IEVsym1 = nteh_ecodesym(); + cs.IEVoffset1 = 0; + gen(c,&cs); // MOV __ecode[EBP],EAX + } + return c; +} + +/******************************* + * Generate C++ or D frame handler. + */ + +void nteh_framehandler(symbol *scopetable) +{ code *c; + + // Generate: + // MOV EAX,&scope_table + // JMP __cpp_framehandler + + if (scopetable) + { + symbol_debug(scopetable); + c = gencs(NULL,0xB8+AX,0,FLextern,scopetable); // MOV EAX,&scope_table + gencs(c,0xE9,0,FLfunc,rtlsym[RTLSYM_CPP_HANDLER]); // JMP __cpp_framehandler + + pinholeopt(c,NULL); + codout(c); + code_free(c); + } +} + +/********************************* + * Generate code to set scope index. + */ + +code *nteh_gensindex(int sindex) +{ code *c; + + if (!(config.flags2 & CFG2seh)) + return NULL; + + // Generate: + // MOV -4[EBP],sindex + + c = genc(NULL,0xC7,modregrm(1,0,BP),FLconst,(targ_uns)nteh_EBPoffset_sindex(),FLconst,sindex); // 7 bytes long + c->Iflags |= CFvolatile; +#ifdef DEBUG + //assert(GENSINDEXSIZE == calccodsize(c)); +#endif + return c; +} + +/********************************* + * Generate code for setjmp(). + */ + +code *cdsetjmp(elem *e,regm_t *pretregs) +{ code cs; + code *c; + regm_t retregs; + unsigned stackpushsave; + unsigned flag; + + c = NULL; + stackpushsave = stackpush; +#if SCPP + if (CPP && (funcsym_p->Sfunc->Fflags3 & Fcppeh || usednteh & NTEHcpp)) + { + /* If in C++ try block + If the frame that is calling setjmp has a try,catch block then + the call to setjmp3 is as follows: + __setjmp3(environment,3,__cpp_longjmp_unwind,trylevel,funcdata); + + __cpp_longjmp_unwind is a routine in the RTL. This is a + stdcall routine that will deal with unwinding for CPP Frames. + trylevel is the value that gets incremented at each catch, + constructor invocation. + funcdata is the same value that you put into EAX prior to + cppframehandler getting called. + */ + symbol *s; + + s = except_gensym(); + if (!s) + goto L1; + + c = gencs(c,0x68,0,FLextern,s); // PUSH &scope_table + stackpush += 4; + genadjesp(c,4); + + c = genc1(c,0xFF,modregrm(1,6,BP),FLconst,(targ_uns)-4); + // PUSH trylevel + stackpush += 4; + genadjesp(c,4); + + cs.Iop = 0x68; + cs.Iflags = CFoff; + cs.Irex = 0; + cs.IFL2 = FLextern; + cs.IEVsym2 = rtlsym[RTLSYM_CPP_LONGJMP]; + cs.IEVoffset2 = 0; + c = gen(c,&cs); // PUSH &_cpp_longjmp_unwind + stackpush += 4; + genadjesp(c,4); + + flag = 3; + } + else +#endif + if (funcsym_p->Sfunc->Fflags3 & Fnteh) + { + /* If in NT SEH try block + If the frame that is calling setjmp has a try, except block + then the call to setjmp3 is as follows: + __setjmp3(environment,2,__seh_longjmp_unwind,trylevel); + __seth_longjmp_unwind is supplied by the RTL and is a stdcall + function. It is the name that MSOFT uses, we should + probably use the same one. + trylevel is the value that you increment at each try and + decrement at the close of the try. This corresponds to the + index field of the ehrec. + */ + int sindex_off; + + sindex_off = 20; // offset of __context.sindex + cs.Iop = 0xFF; + cs.Irm = modregrm(2,6,BPRM); + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL1 = FLbprel; + cs.IEVsym1 = nteh_contextsym(); + cs.IEVoffset1 = sindex_off; + c = gen(c,&cs); // PUSH scope_index + stackpush += 4; + genadjesp(c,4); + + cs.Iop = 0x68; + cs.Iflags = CFoff; + cs.Irex = 0; + cs.IFL2 = FLextern; + cs.IEVsym2 = rtlsym[RTLSYM_LONGJMP]; + cs.IEVoffset2 = 0; + c = gen(c,&cs); // PUSH &_seh_longjmp_unwind + stackpush += 4; + genadjesp(c,4); + + flag = 2; + } + else + { + /* If the frame calling setjmp has neither a try..except, nor a + try..catch, then call setjmp3 as follows: + _setjmp3(environment,0) + */ + L1: + flag = 0; + } + + cs.Iop = 0x68; + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL2 = FLconst; + cs.IEV2.Vint = flag; + c = gen(c,&cs); // PUSH flag + stackpush += 4; + genadjesp(c,4); + + c = cat(c,params(e->E1,REGSIZE)); + + c = cat(c,getregs(~rtlsym[RTLSYM_SETJMP3]->Sregsaved & (ALLREGS | mES))); + gencs(c,0xE8,0,FLfunc,rtlsym[RTLSYM_SETJMP3]); // CALL __setjmp3 + + c = genc2(c,0x81,modregrm(3,0,SP),stackpush - stackpushsave); // ADD ESP,8 + genadjesp(c,-(stackpush - stackpushsave)); + + stackpush = stackpushsave; + retregs = regmask(e->Ety, TYnfunc); + return cat(c,fixresult(e,retregs,pretregs)); +} + +/**************************************** + * Call _local_unwind(), which means call the __finally blocks until + * index is reached. + */ + +code *nteh_unwind(regm_t retregs,unsigned index) +{ code *c; + code cs; + code *cs1; + code *cs2; + regm_t desregs; + int reg; + int local_unwind; + + // Shouldn't this always be CX? +#if SCPP + reg = AX; +#else + reg = CX; +#endif + +#if MARS + local_unwind = RTLSYM_D_LOCAL_UNWIND2; +#else + local_unwind = RTLSYM_LOCAL_UNWIND2; +#endif + desregs = (~rtlsym[local_unwind]->Sregsaved & (ALLREGS)) | mask[reg]; + gensaverestore(retregs & desregs,&cs1,&cs2); + + c = getregs(desregs); + + cs.Iop = 0x8D; + cs.Irm = modregrm(2,reg,BPRM); + cs.Iflags = 0; + cs.Irex = 0; + cs.IFL1 = FLconst; + // EBP offset of __context.prev + cs.IEV1.Vint = nteh_EBPoffset_prev(); + c = gen(c,&cs); // LEA ECX,contextsym + + genc2(c,0x68,0,index); // PUSH index + gen1(c,0x50 + reg); // PUSH ECX + +#if MARS + //gencs(c,0xB8+AX,0,FLextern,nteh_scopetable()); // MOV EAX,&scope_table + gencs(c,0x68,0,FLextern,nteh_scopetable()); // PUSH &scope_table + + gencs(c,0xE8,0,FLfunc,rtlsym[local_unwind]); // CALL __d_local_unwind2() + genc2(c,0x81,modregrm(3,0,SP),12); // ADD ESP,12 +#else + gencs(c,0xE8,0,FLfunc,rtlsym[local_unwind]); // CALL __local_unwind2() + genc2(c,0x81,modregrm(3,0,SP),8); // ADD ESP,8 +#endif + + c = cat4(cs1,c,cs2,NULL); + return c; +} + +/**************************************** + * Call _local_unwind(), which means call the __finally blocks until + * index is reached. + */ + +#if 0 // Replaced with inline calls to __finally blocks + +code *linux_unwind(regm_t retregs,unsigned index) +{ code *c; + code *cs1; + code *cs2; + int i; + regm_t desregs; + int reg; + int local_unwind; + + // Shouldn't this always be CX? +#if SCPP + reg = AX; +#else + reg = CX; +#endif + +#if MARS + local_unwind = RTLSYM_D_LOCAL_UNWIND2; +#else + local_unwind = RTLSYM_LOCAL_UNWIND2; +#endif + desregs = (~rtlsym[local_unwind]->Sregsaved & (ALLREGS)) | mask[reg]; + gensaverestore(retregs & desregs,&cs1,&cs2); + + c = getregs(desregs); + c = genc2(c,0x68,0,index); // PUSH index + +#if MARS +// gencs(c,0x68,0,FLextern,nteh_scopetable()); // PUSH &scope_table + + gencs(c,0xE8,0,FLfunc,rtlsym[local_unwind]); // CALL __d_local_unwind2() + genc2(c,0x81,modregrm(3,0,SP),4); // ADD ESP,12 +#else + gencs(c,0xE8,0,FLfunc,rtlsym[local_unwind]); // CALL __local_unwind2() + genc2(c,0x81,modregrm(3,0,SP),8); // ADD ESP,8 +#endif + + c = cat4(cs1,c,cs2,NULL); + return c; +} + +#endif + +/************************************************* + * Set monitor, hook monitor exception handler. + */ + +#if MARS + +code *nteh_monitor_prolog(Symbol *shandle) +{ + /* + * PUSH handle + * PUSH offset _d_monitor_handler + * PUSH FS:__except_list + * MOV FS:__except_list,ESP + * CALL _d_monitor_prolog + */ + code *c1 = NULL; + code *c; + code cs; + Symbol *s; + regm_t desregs; + + assert(config.flags2 & CFG2seh); // BUG: figure out how to implement for other EX's + + if (shandle->Sclass == SCfastpar) + { assert(shandle->Spreg != DX); + c = gen1(NULL,0x50 + shandle->Spreg); // PUSH shandle + } + else + { + // PUSH shandle +#if 0 + c = genc1(NULL,0xFF,modregrm(2,6,4),FLconst,4 * (1 + needframe) + shandle->Soffset + localsize); + c->Isib = modregrm(0,4,SP); +#else + useregs(mCX); + c = genc1(NULL,0x8B,modregrm(2,CX,4),FLconst,4 * (1 + needframe) + shandle->Soffset + localsize); + c->Isib = modregrm(0,4,SP); + gen1(c,0x50 + CX); // PUSH ECX +#endif + } + + s = rtlsym[RTLSYM_MONITOR_HANDLER]; + c = gencs(c,0x68,0,FLextern,s); // PUSH offset _d_monitor_handler + makeitextern(s); + +#if 0 + cs.Iop = 0xFF; + cs.Irm = modregrm(0,6,BPRM); + cs.Iflags = CFfs; + cs.Irex = 0; + cs.IFL1 = FLextern; + cs.IEVsym1 = rtlsym[RTLSYM_EXCEPT_LIST]; + cs.IEVoffset1 = 0; + gen(c,&cs); // PUSH FS:__except_list +#else + useregs(mDX); + cs.Iop = 0x8B; + cs.Irm = modregrm(0,DX,BPRM); + cs.Iflags = CFfs; + cs.Irex = 0; + cs.IFL1 = FLextern; + cs.IEVsym1 = rtlsym[RTLSYM_EXCEPT_LIST]; + cs.IEVoffset1 = 0; + c1 = gen(c1,&cs); // MOV EDX,FS:__except_list + + gen1(c,0x50 + DX); // PUSH EDX +#endif + + s = rtlsym[RTLSYM_MONITOR_PROLOG]; + desregs = ~s->Sregsaved & ALLREGS; + c = cat(c,getregs(desregs)); + c = gencs(c,0xE8,0,FLfunc,s); // CALL _d_monitor_prolog + + cs.Iop = 0x89; + NEWREG(cs.Irm,SP); + gen(c,&cs); // MOV FS:__except_list,ESP + + return cat(c1,c); +} + +#endif + +/************************************************* + * Release monitor, unhook monitor exception handler. + * Input: + * retregs registers to not destroy + */ + +#if MARS + +code *nteh_monitor_epilog(regm_t retregs) +{ + /* + * CALL _d_monitor_epilog + * POP FS:__except_list + */ + code cs; + code *c; + code *cs1; + code *cs2; + code *cpop; + regm_t desregs; + Symbol *s; + + assert(config.flags2 & CFG2seh); // BUG: figure out how to implement for other EX's + + s = rtlsym[RTLSYM_MONITOR_EPILOG]; + //desregs = ~s->Sregsaved & ALLREGS; + desregs = 0; + gensaverestore(retregs & desregs,&cs1,&cs2); + + c = getregs(desregs); + c = gencs(c,0xE8,0,FLfunc,s); // CALL __d_monitor_epilog + + cs.Iop = 0x8F; + cs.Irm = modregrm(0,0,BPRM); + cs.Iflags = CFfs; + cs.Irex = 0; + cs.IFL1 = FLextern; + cs.IEVsym1 = rtlsym[RTLSYM_EXCEPT_LIST]; + cs.IEVoffset1 = 0; + cpop = gen(NULL,&cs); // POP FS:__except_list + + c = cat4(cs1,c,cs2,cpop); + return c; +} + +#endif + +#endif diff --git a/backend/oper.h b/backend/oper.h new file mode 100644 index 00000000..6d1f2749 --- /dev/null +++ b/backend/oper.h @@ -0,0 +1,414 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +#if __SC__ +#pragma once +#endif + +#ifndef OPER_H +#define OPER_H 1 + +enum OPER +{ + OPunde, /* place holder for undefined operator */ + + OPadd, + OPmin, + OPmul, + OPdiv, + OPmod, + OPshr, // unsigned right shift + OPshl, + OPand, + OPxor, + OPor, + OPashr, // signed right shift + OPnot, + OPbool, /* "booleanize" */ + OPcom, + OPcond, + OPcomma, + OPoror, + OPandand, + OPbit, /* ref to bit field */ + OPind, /* *E */ + OPaddr, /* &E */ + OPneg, /* unary - */ + OPuadd, /* unary + */ +#if TX86 + OPvoid, // where casting to void is not a no-op + OPabs, /* absolute value */ + OPsqrt, /* square root */ + OPrndtol, // round to short, long, long long (inline 8087 only) + OPsin, // sine + OPcos, // cosine + OPrint, // round to int + OPscale, // ldexp + OPyl2x, // y * log2(x) + OPyl2xp1, // y * log2(x + 1) + OPstrlen, /* strlen() */ + OPstrcpy, /* strcpy() */ + OPstrcat, /* strcat() */ + OPstrcmp, /* strcmp() */ + OPmemcpy, + OPmemcmp, + OPmemset, + OPsetjmp, // setjmp() +#endif + + OPremquo, // / and % in one operation + +#if TX86 + OPbsf, // bit scan forward + OPbsr, // bit scan reverse + OPbt, // bit test + OPbtc, // bit test and complement + OPbtr, // bit test and reset + OPbts, // bit test and set + OPbswap, // swap bytes + OProl, // rotate left + OPror, // rotate right +#endif + + OPstreq, /* structure assignment */ + + OPnegass, // x = -x + OPpostinc, /* x++ */ + OPpostdec, /* x-- */ + + OPeq, + OPaddass, + OPminass, + OPmulass, + OPdivass, + OPmodass, + OPshrass, + OPshlass, + OPandass, + OPxorass, + OPorass, + +/* Convert from token to assignment operator */ +#define asgtoktoop(tok) ((int) (tok) + ((int)OPeq - (int) TKeq)) + + OPashrass, + + /* relational operators (in same order as corresponding tokens) */ +#define RELOPMIN ((int)OPle) + OPle, + OPgt, + OPlt, + OPge, + OPeqeq, + OPne, + + OPunord, /* !<>= */ + OPlg, /* <> */ + OPleg, /* <>= */ + OPule, /* !> */ + OPul, /* !>= */ + OPuge, /* !< */ + OPug, /* !<= */ + OPue, /* !<> */ + OPngt, + OPnge, + OPnlt, + OPnle, + OPord, + OPnlg, + OPnleg, + OPnule, + OPnul, + OPnuge, + OPnug, + OPnue, + +#define rel_toktoop(tk) ((enum OPER)((int)tk - (int)TKle + (int)OPle)) + +/***************** End of relational operators ******************/ + +/* Convert from conversion operator to conversion index */ +// parallel array invconvtab[] in cgelem.c) + +#define CNVOPMIN (OPb_8) +#define CNVOPMAX (OPld_u64) +#define convidx(op) ((int)(op) - CNVOPMIN) + +/* 8,16,32,64 integral type of unspecified sign + s,u signed/unsigned + f,d,ld float/double/long double + np,fp,vp,f16p near pointer/far pointer/handle pointer/far16 pointer + cvp const handle pointer +*/ + + OPb_8, // convert bit to byte + OPd_s32, + OPs32_d, + OPd_s16, + OPs16_d, + OPd_u16, + OPu16_d, + OPd_u32, + OPu32_d, + OPd_s64, + OPs64_d, + OPd_u64, + OPu64_d, + OPd_f, + OPf_d, + OPs16_32, // short to long + OPu16_32, // unsigned short to long + OP32_16, // long to short + OPu8_16, // unsigned char to short + OPs8_16, // signed char to short + OP16_8, // short to 8 bits + OPu32_64, // unsigned long to long long + OPs32_64, // long to long long + OP64_32, // long long to long + OPu64_128, + OPs64_128, + OP128_64, +#if TARGET_SEGMENTED + OPvp_fp, + OPcvp_fp, // const handle * => far * + OPoffset, // get offset of far pointer + OPnp_fp, // convert near pointer to far + OPnp_f16p, // from 0:32 to 16:16 + OPf16p_np, // from 16:16 to 0:32 +#endif + OPld_d, + OPd_ld, + OPld_u64, + +/***************** End of conversion operators ******************/ + + OPc_r, // complex to real + OPc_i, // complex to imaginary + OPmsw, // top 32 bits of 64 bit word (32 bit code gen) + // top 16 bits of 32 bit word (16 bit code gen) + + OPparam, /* function parameter separator */ + OPcall, /* binary function call */ + OPucall, /* unary function call */ + OPcallns, // binary function call, no side effects + OPucallns, // unary function call, no side effects + + OPsizeof, /* for forward-ref'd structs */ + OPstrctor, /* call ctor on struct param */ + OPstrthis, // 'this' pointer for OPstrctor + OPstrpar, /* structure func param */ + OPconst, /* constant */ + OPrelconst, /* constant that contains an address */ + OPvar, /* variable */ + OPreg, // register (used in inline asm operand expressions) + OPcolon, /* : as in ?: */ + OPcolon2, // alternate version with different EH semantics + OPstring, /* address of string */ + OPnullptr, // null pointer + OPasm, /* in-line assembly code */ + OPinfo, // attach info (used to attach ctor/dtor + OPhalt, // insert HLT instruction + // info for exception handling) + OPctor, + OPdtor, + OPmark, + OPdctor, // D constructor + OPddtor, // D destructor + + OPpair, // build register pair, E1 is lsb, E2 = msb + OPrpair, // build reversed register pair, E1 is msb, E2 = lsb + OPframeptr, // load pointer to base of frame + OPgot, // load pointer to global offset table + OPvector, // SIMD vector operations + + // Jupiter operators + OParray, // access Jupiter array, left is handle, right is index + OParraylength, // evaluates array handle into array length + OPfield, // access Jupiter object field, left is handle, right is offset + OPnewarray, // allocate Jupiter array, left is dimension, right is type + OPmultinewarray, // allocate multidimensional Jupiter array + // left is dimensions, right is (numdims,type signature) + OPinstanceof, // left is class id, right is handle + OPfinalinstanceof, // left is class id, right is handle + OPcheckcast, // left is class id, right is handle + OPhstring, // handle to static string + OPnullcheck, // check if pointer is null + +#if TX86 + OPinp, /* input from I/O port */ + OPoutp, /* output to I/O port */ +#endif + /* C++ operators */ + OPnew, // operator new + OPanew, // operator new[] + OPdelete, // operator delete + OPadelete, // operator delete[] + OPbrack, /* [] subscript */ + OParrow, /* for -> overloading */ + OParrowstar, /* for ->* overloading */ + OPpreinc, /* ++x overloading */ + OPpredec, /* --x overloading */ + +#ifdef TARGET_INLINEFUNC_OPS + TARGET_INLINEFUNC_OPS +#endif + + OPMAX /* 1 past last operator */ +}; +typedef enum OPER OPER; /* needed for optabgen */ + +/************************************ + * Determine things about relational operators. + */ + +extern unsigned char rel_not[]; +extern unsigned char rel_swap[]; +extern unsigned char rel_integral[]; +extern unsigned char rel_exception[]; +extern unsigned char rel_unord[]; + +#define rel_not(op) rel_not[(int)(op) - RELOPMIN] +#define rel_swap(op) rel_swap[(int)(op) - RELOPMIN] +#define rel_integral(op) rel_integral[(int)(op) - RELOPMIN] +#define rel_exception(op) rel_exception[(int)(op) - RELOPMIN] +#define rel_unord(op) rel_unord[(int)(op) - RELOPMIN] + + +/********************************** + * Various types of operators: + * OTbinary binary + * OTunary unary + * OTleaf leaf + * OTcommut commutative (e1 op e2) == (e2 op e1) + * (assoc == !=) + * OTassoc associative (e1 op (e2 op e3)) == ((e1 op e2) op e3) + * (also commutative) + * OTassign assignment = op= i++ i-- i=-i str= + * OTpost post inc or post dec operator + * OTeop0e if (e op 0) => e + * OTeop00 if (e op 0) => 0 + * OTeop1e if (e op 1) => e + * OTsideff there are side effects to the operator (assign call + * post ?: && ||) + * OTconv type conversion operator that could appear on lhs of + * assignment operator + * OTlogical logical operator (result is 0 or 1) + * OTwid high order bits of operation are irrelevant + * OTopeq an op= operator + * OTop an operator that has a corresponding op= + * OTcall function call + * OTrtol operators that evaluate right subtree first then left + * OTrel == != < <= > >= operators + * OTrel2 < <= > >= operators + * OTdef definition operator (assign call post asm) + * OTae potential common subexpression operator + * OTexp expression elem + * OTboolnop operation is a nop if boolean result is desired + */ + +#if TX86 +extern const unsigned char optab1[OPMAX],optab2[OPMAX],optab3[OPMAX]; +extern const unsigned char opcost[OPMAX]; +#else +extern unsigned char optab1[OPMAX],optab2[OPMAX]; +#endif +/* optab1[] */ /* Use byte arrays to avoid index scaling */ +#define _OTbinary 1 +#define _OTunary 2 +#define _OTcommut 4 +#define _OTassoc 8 +#define _OTsideff 0x10 +#define _OTeop0e 0x20 +#define _OTeop00 0x40 +#define _OTeop1e 0x80 + +/* optab2[] */ +#define _OTlogical 1 +#define _OTwid 2 +#define _OTcall 4 +#define _OTrtol 8 +#define _OTassign 0x10 +#define _OTdef 0x20 +#define _OTae 0x40 +#define _OTexp 0x80 + +#if TX86 +// optab3[] +#define _OTboolnop 1 +#endif +#define OTbinary(op) (optab1[op]&_OTbinary) +#define OTunary(op) (optab1[op]&_OTunary) +#define OTleaf(op) (!(optab1[op]&(_OTunary|_OTbinary))) +#define OTcommut(op) (optab1[op]&_OTcommut) +#define OTassoc(op) (optab1[op]&_OTassoc) +#define OTassign(op) (optab2[op]&_OTassign) +#define OTpost(op) ((op) == OPpostinc || (op) == OPpostdec) +#define OTeop0e(op) (optab1[op]&_OTeop0e) +#define OTeop00(op) (optab1[op]&_OTeop00) +#define OTeop1e(op) (optab1[op]&_OTeop1e) +#define OTsideff(op) (optab1[op]&_OTsideff) +#define OTconv(op) ((op) >= CNVOPMIN && (op) <= CNVOPMAX) +#define OTlogical(op) (optab2[op]&_OTlogical) +#define OTwid(op) (optab2[op]&_OTwid) +#define OTopeq(op) ((op) >= OPaddass && (op) <= OPashrass) +#define OTop(op) ((op) >= OPadd && (op) <= OPor) +#define OTcall(op) (optab2[op]&_OTcall) +#define OTrtol(op) (optab2[op]&_OTrtol) +#define OTrel(op) ((op) >= OPle && (op) <= OPnue) +#define OTrel2(op) ((op) >= OPle && (op) <= OPge) +#define OTdef(op) (optab2[op]&_OTdef) +#define OTae(op) (optab2[op]&_OTae) +#define OTexp(op) (optab2[op]&_OTexp) +#if TX86 +#define OTboolnop(op) (optab3[op]&_OTboolnop) +#define OTcalldef(op) (OTcall(op) || (op) == OPstrcpy || (op) == OPstrcat || (op) == OPmemcpy) +#else +#define OTcalldef(op) (OTcall(op)) +#endif + +/* Convert op= to op */ +#define opeqtoop(opx) ((opx) - OPaddass + OPadd) + +/* Convert op to op= */ +#define optoopeq(opx) ((opx) - OPadd + OPaddass) + +/*************************** + * Determine properties of an elem. + * EBIN binary node? + * EUNA unary node? + * EOP operator node (unary or binary)? + * ERTOL right to left evaluation (left to right is default) + * Eunambig unambiguous definition elem? + */ + +#define EBIN(e) (OTbinary((e)->Eoper)) +#define EUNA(e) (OTunary((e)->Eoper)) + +/* ERTOL(e) is moved to el.c */ + +#if KEEPBITFIELDS +#define Elvalue(e) (((e)->E1->Eoper == OPbit) ? (e)->E1->E1 : (e)->E1) +#define Eunambig(e) (OTassign((e)->Eoper) && \ + ((e)->E1->Eoper == OPvar || \ + ((e)->E1->Eoper == OPbit && \ + (e)->E1->E1->Eoper == OPvar))) +#else +#define Elvalue(e) ((e)->E1) +#define Eunambig(e) (OTassign((e)->Eoper) && \ + (e)->E1->Eoper == OPvar) +#endif + +#define EOP(e) (!OTleaf((e)->Eoper)) + +#endif /* OPER_H */ + diff --git a/backend/optabgen.c b/backend/optabgen.c new file mode 100644 index 00000000..69e2ac6d --- /dev/null +++ b/backend/optabgen.c @@ -0,0 +1,1131 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +/* Generate op-code tables + * Creates optab.c,debtab.c,cdxxx.c,elxxx.c + */ + +#include +#include +#include +#include +#include "cc.h" +#include "oper.h" + +void doreltables(FILE *f); +void dotab(); +void dotytab(); +void dooptab(); +void fltables(); + +unsigned char xptab1[OPMAX],xptab2[OPMAX],xptab3[OPMAX]; + +int _binary[] = + {OPadd,OPmul,OPand,OPmin,OPcond,OPcomma,OPdiv,OPmod,OPxor, + OPor,OPoror,OPandand,OPshl,OPshr,OPashr,OPstreq,OPstrcpy,OPstrcat,OPstrcmp, + OPpostinc,OPpostdec,OPeq,OPaddass,OPminass,OPmulass,OPdivass, + OPmodass,OPshrass,OPashrass,OPshlass,OPandass,OPxorass,OPorass, + OPle,OPgt,OPlt,OPge,OPeqeq,OPne,OPparam,OPcall,OPcallns,OPcolon,OPcolon2, + OPbit,OPoutp,OPbrack,OParrowstar,OPmemcpy,OPmemcmp,OPmemset, + OPunord,OPlg,OPleg,OPule,OPul,OPuge,OPug,OPue,OPngt,OPnge, + OPnlt,OPnle,OPord,OPnlg,OPnleg,OPnule,OPnul,OPnuge,OPnug,OPnue, + OPinfo,OParray,OPfield,OPnewarray,OPmultinewarray,OPinstanceof,OPfinalinstanceof, + OPcheckcast,OPpair,OPrpair, + OPbt,OPbtc,OPbtr,OPbts,OPror,OProl, + OPscale,OPremquo,OPyl2x,OPyl2xp1, + }; +int _unary[] = + {OPnot,OPcom,OPind,OPaddr,OPneg,OPuadd, + OPabs,OPsqrt,OPrndtol,OPsin,OPcos,OPrint, + OPpreinc,OPpredec, + OPbool,OPstrlen,OPnullcheck, + OPb_8,OPs16_32,OPu16_32,OPd_s32,OPd_u32, + OPs32_d,OPu32_d,OPd_s16,OPs16_d,OP32_16, + OPd_f,OPf_d,OPu8_16,OPs8_16,OP16_8, + OPd_ld, OPld_d,OPc_r,OPc_i, + OPu32_64,OPs32_64,OP64_32,OPmsw, + OPd_s64,OPs64_d,OPd_u64,OPu64_d,OPld_u64, + OP128_64,OPs64_128,OPu64_128, + OPucall,OPucallns,OPstrpar,OPstrctor,OPu16_d,OPd_u16, + OPinp,OParrow,OPnegass, + OPctor,OPdtor,OPsetjmp,OPvoid,OParraylength, + OPbsf,OPbsr,OPbswap, + OPddtor, + OPvector, +#if TARGET_SEGMENTED + OPvp_fp,OPcvp_fp,OPnp_fp,OPnp_f16p,OPf16p_np,OPoffset, +#endif + }; +int _commut[] = {OPadd,OPand,OPor,OPxor,OPmul,OPeqeq,OPne,OPle,OPlt,OPge,OPgt, + OPunord,OPlg,OPleg,OPule,OPul,OPuge,OPug,OPue,OPngt,OPnge, + OPnlt,OPnle,OPord,OPnlg,OPnleg,OPnule,OPnul,OPnuge,OPnug,OPnue, + }; +int _assoc[] = {OPadd,OPand,OPor,OPxor,OPmul}; +int _assign[] = + {OPstreq,OPeq,OPaddass,OPminass,OPmulass,OPdivass,OPmodass, + OPshrass,OPashrass,OPshlass,OPandass,OPxorass,OPorass,OPpostinc,OPpostdec, + OPnegass, + /* OPbtc,OPbtr,OPbts,*/ + }; +int _wid[] = + {OPadd,OPmin,OPand,OPor,OPxor,OPcom,OPneg,OPmul,OPaddass,OPnegass, + OPminass,OPandass,OPorass,OPxorass,OPmulass,OPshlass,OPshl,OPshrass, + OPashrass, + }; +int _eop0e[] = + {OPadd,OPmin,OPxor,OPor,OPshl,OPshr,OPashr,OPpostinc,OPpostdec,OPaddass, + OPminass,OPshrass,OPashrass,OPshlass,OPxorass,OPorass, + OPror,OProl, + }; +int _eop00[] = {OPmul,OPand,OPmulass,OPandass}; +int _eop1e[] = {OPmul,OPdiv,OPmulass,OPdivass}; +int _call[] = {OPcall,OPucall,OPcallns,OPucallns}; +int _rel[] = {OPeqeq,OPne,OPle,OPlt,OPgt,OPge, + OPunord,OPlg,OPleg,OPule,OPul,OPuge,OPug,OPue,OPngt,OPnge, + OPnlt,OPnle,OPord,OPnlg,OPnleg,OPnule,OPnul,OPnuge,OPnug,OPnue, + }; +int _logical[] = {OPeqeq,OPne,OPle,OPlt,OPgt,OPge,OPandand,OPoror,OPnot,OPbool, + OPunord,OPlg,OPleg,OPule,OPul,OPuge,OPug,OPue,OPngt,OPnge, + OPnlt,OPnle,OPord,OPnlg,OPnleg,OPnule,OPnul,OPnuge,OPnug,OPnue, + }; +int _def[] = {OPstreq,OPeq,OPaddass,OPminass,OPmulass,OPdivass,OPmodass, + OPshrass,OPashrass,OPshlass,OPandass,OPxorass,OPorass, + OPpostinc,OPpostdec, + OPcall,OPucall,OPasm,OPstrcpy,OPmemcpy,OPmemset,OPstrcat, + OPnegass,OPnewarray,OPmultinewarray, + OPbtc,OPbtr,OPbts, + }; +int _sideff[] = {OPasm,OPucall,OPstrcpy,OPmemcpy,OPmemset,OPstrcat, + OPcall,OPeq,OPstreq,OPpostinc,OPpostdec, + OPaddass,OPminass,OPmulass,OPdivass,OPmodass,OPandass, + OPorass,OPxorass,OPshlass,OPshrass,OPashrass, + OPinp,OPoutp,OPnegass,OPctor,OPdtor,OPmark,OPvoid,OPnewarray, + OPmultinewarray,OPcheckcast,OPnullcheck, + OPbtc,OPbtr,OPbts, + OPhalt,OPdctor,OPddtor, + }; +int _rtol[] = {OPeq,OPstreq,OPstrcpy,OPmemcpy,OPpostinc,OPpostdec,OPaddass, + OPminass,OPmulass,OPdivass,OPmodass,OPandass, + OPorass,OPxorass,OPshlass,OPshrass,OPashrass, + OPcall,OPcallns,OPinfo,OPmemset, + }; +int _ae[] = {OPvar,OPconst,OPrelconst,OPneg, + OPabs,OPsqrt,OPrndtol,OPsin,OPcos,OPrint,OPscale, + OPstrlen,OPstrcmp,OPind,OPaddr, + OPnot,OPbool,OPcom,OPadd,OPmin,OPmul,OPand,OPor,OPmemcmp, + OPxor,OPdiv,OPmod,OPshl,OPshr,OPashr,OPeqeq,OPne,OPle,OPlt,OPge,OPgt, + OPunord,OPlg,OPleg,OPule,OPul,OPuge,OPug,OPue,OPngt,OPnge, + OPnlt,OPnle,OPord,OPnlg,OPnleg,OPnule,OPnul,OPnuge,OPnug,OPnue, + OPs16_32,OPu16_32,OPd_s32,OPd_u32,OPu16_d,OPd_u16, + OPs32_d,OPu32_d,OPd_s16,OPs16_d,OP32_16, + OPd_f,OPf_d,OPu8_16,OPs8_16,OP16_8, + OPd_ld,OPld_d,OPc_r,OPc_i, + OPu32_64,OPs32_64,OP64_32,OPmsw, + OPd_s64,OPs64_d,OPd_u64,OPu64_d,OPld_u64, + OP128_64,OPs64_128,OPu64_128, + OPsizeof,OParray,OPfield,OPinstanceof,OPfinalinstanceof,OPcheckcast,OParraylength, + OPcallns,OPucallns,OPnullcheck,OPpair,OPrpair, + OPbsf,OPbsr,OPbt,OPbswap,OPb_8, + OPgot,OPremquo, + OPnullptr, + OProl,OPror, +#if TARGET_SEGMENTED + OPvp_fp,OPcvp_fp,OPnp_fp,OPnp_f16p,OPf16p_np,OPoffset, +#endif + /*OPcomma,OPbit,OPoror,OPandand,OPcond,OPcolon,OPcolon2*/}; +int _exp[] = {OPvar,OPconst,OPrelconst,OPneg,OPabs,OPsqrt,OPrndtol,OPrint, + OPsin,OPcos,OPscale,OPyl2x,OPyl2xp1, + OPstrlen,OPstrcmp,OPind,OPaddr, + OPnot,OPbool,OPcom,OPadd,OPmin,OPmul,OPand,OPor,OPstring, + OPxor,OPdiv,OPmod,OPshl,OPshr,OPashr,OPeqeq,OPne,OPle,OPlt,OPge,OPgt, + OPunord,OPlg,OPleg,OPule,OPul,OPuge,OPug,OPue,OPngt,OPnge, + OPnlt,OPnle,OPord,OPnlg,OPnleg,OPnule,OPnul,OPnuge,OPnug,OPnue, + OPcomma,OPasm,OPsizeof,OPmemcmp, + OPs16_32,OPu16_32,OPd_s32,OPd_u32,OPu16_d,OPd_u16, + OPs32_d,OPu32_d,OPd_s16,OPs16_d,OP32_16, + OPd_f,OPf_d,OPu8_16,OPs8_16,OP16_8, + OPd_ld, OPld_d,OPc_r,OPc_i, + OPu32_64,OPs32_64,OP64_32,OPmsw, + OPd_s64,OPs64_d,OPd_u64,OPu64_d,OPld_u64, + OP128_64,OPs64_128,OPu64_128, + OPbit,OPind,OPucall,OPucallns,OPnullcheck, + OParray,OPfield,OPinstanceof,OPfinalinstanceof,OPcheckcast,OParraylength,OPhstring, + OPcall,OPcallns,OPeq,OPstreq,OPpostinc,OPpostdec, + OPaddass,OPminass,OPmulass,OPdivass,OPmodass,OPandass, + OPorass,OPxorass,OPshlass,OPshrass,OPashrass,OPoror,OPandand,OPcond, + OPbsf,OPbsr,OPbt,OPbtc,OPbtr,OPbts,OPbswap, + OProl,OPror,OPvector, + OPpair,OPrpair,OPframeptr,OPgot,OPremquo, + OPcolon,OPcolon2,OPasm,OPstrcpy,OPmemcpy,OPmemset,OPstrcat,OPnegass, +#if TARGET_SEGMENTED + OPvp_fp,OPcvp_fp,OPoffset,OPnp_fp,OPnp_f16p,OPf16p_np, +#endif +}; +int _boolnop[] = {OPuadd,OPbool,OPs16_32,OPu16_32, + OPs16_d, + OPf_d,OPu8_16,OPs8_16, + OPd_ld, OPld_d, + OPu32_64,OPs32_64,/*OP64_32,OPmsw,*/ + OPs64_128,OPu64_128, + OPu16_d,OPb_8, + OPnullptr, +#if TARGET_SEGMENTED + OPnp_fp,OPvp_fp,OPcvp_fp, +#endif + }; +int _lvalue[] = {OPvar,OPind,OPcomma,OPbit, + OPfield,OParray}; + +FILE *fdeb; + +int main() +{ + printf("OPTABGEN... generating files\n"); + fdeb = fopen("debtab.c","w"); + dooptab(); + dotab(); + fltables(); + dotytab(); + fclose(fdeb); + return 0; +} + +int cost(unsigned op) +{ unsigned c; + + c = 0; /* default cost */ + if (xptab1[op] & _OTunary) + c += 2; + else if (xptab1[op] & _OTbinary) + c += 7; + if (xptab2[op] & _OTlogical) + c += 3; + switch (op) + { case OPvar: c += 1; break; + case OPmul: c += 3; break; + case OPdiv: + case OPmod: c += 4; break; + case OProl: + case OPror: + case OPshl: + case OPashr: + case OPshr: c += 2; break; + case OPnewarray: + case OPmultinewarray: + case OPcall: + case OPucall: + case OPcallns: + case OPucallns: + c += 10; break; // very high cost for function calls + case OParray: c = 5; break; + } + return c; +} + +void dooptab() +{ int i; + FILE *f; + + /* Load optab[] */ +#define X1(arr,mask) for(i=0;i= 0 && j < RELMAX); + rel_not [j] = reltables[i].inot; + rel_swap [j] = reltables[i].swap; + rel_integral [j] = reltables[i].integral; + rel_exception[j] = reltables[i].exception; + rel_unord [j] = reltables[i].unord; + } + + fprintf(f,"unsigned char rel_not[] =\n{ "); + for (i = 0; i < arraysize(rel_not); i++) + { fprintf(f,"0x%02x,",rel_not[i]); + if ((i & 7) == 7 && i < arraysize(rel_not) - 1) + fprintf(f,"\n "); + } + fprintf(f,"\n};\n"); + + fprintf(f,"unsigned char rel_swap[] =\n{ "); + for (i = 0; i < arraysize(rel_swap); i++) + { fprintf(f,"0x%02x,",rel_swap[i]); + if ((i & 7) == 7 && i < arraysize(rel_swap) - 1) + fprintf(f,"\n "); + } + fprintf(f,"\n};\n"); + + fprintf(f,"unsigned char rel_integral[] =\n{ "); + for (i = 0; i < arraysize(rel_integral); i++) + { fprintf(f,"0x%02x,",rel_integral[i]); + if ((i & 7) == 7 && i < arraysize(rel_integral) - 1) + fprintf(f,"\n "); + } + fprintf(f,"\n};\n"); + + fprintf(f,"unsigned char rel_exception[] =\n{ "); + for (i = 0; i < arraysize(rel_exception); i++) + { fprintf(f,"0x%02x,",rel_exception[i]); + if ((i & 7) == 7 && i < arraysize(rel_exception) - 1) + fprintf(f,"\n "); + } + fprintf(f,"\n};\n"); + + fprintf(f,"unsigned char rel_unord[] =\n{ "); + for (i = 0; i < arraysize(rel_unord); i++) + { fprintf(f,"0x%02x,",rel_unord[i]); + if ((i & 7) == 7 && i < arraysize(rel_unord) - 1) + fprintf(f,"\n "); + } + fprintf(f,"\n};\n"); +} + + +/******************************************************** + */ + +const char *debtab[OPMAX],*cdxxx[OPMAX],*elxxx[OPMAX]; + +void dotab() +{ int i; + FILE *f; + +#if BSDUNIX +#define X(d,e,c) debtab[i]=d;cdxxx[i]="c",elxxx[i]="e";break +#else +#define X(d,e,c) debtab[i]=d;cdxxx[i]=#c,elxxx[i]=#e;break +#endif + for (i = 0; i < OPMAX; i++) + { + switch (i) + { + case OPunde: X("unde", elerr, cderr); + case OPadd: X("+", eladd, cdorth); + case OPmul: X("*", elmul, cdmul); + case OPand: X("&", elbitwise,cdorth); + case OPmin: X("-", elmin, cdorth); + case OPnot: X("!", elnot, cdnot); + case OPcom: X("~", elcom, cdcom); + case OPcond: X("?", elcond, cdcond); + case OPcomma: X(",", elcomma,cdcomma); + case OPremquo: X("/%", elremquo, cdmul); + case OPdiv: X("/", eldiv, cdmul); + case OPmod: X("%", elmod, cdmul); + case OPxor: X("^", elxor, cdorth); + case OPstring: X("string", elstring,cderr); + case OPrelconst: X("relconst", elzot, cdrelconst); + case OPinp: X("inp", elzot, cdport); + case OPoutp: X("outp", elzot, cdport); + case OPasm: X("asm", elzot, cdasm); + case OPinfo: X("info", elinfo,cdinfo); + case OPdctor: X("dctor", elzot, cddctor); + case OPddtor: X("ddtor", elddtor, cdddtor); + case OPctor: X("ctor", elinfo,cdctor); + case OPdtor: X("dtor", elinfo,cddtor); + case OPmark: X("mark", elinfo,cdmark); + case OPvoid: X("void", elzot, cdvoid); + case OPhalt: X("halt", elzot, cdhalt); + case OPnullptr: X("nullptr", elerr, cderr); + case OPpair: X("pair", elpair, cdpair); + case OPrpair: X("rpair", elpair, cdpair); + + case OPnewarray: X("newarray", elnewarray,cderr); + case OPmultinewarray: X("mnewarray", elmultinewarray,cderr); + case OPinstanceof: X("instanceof", elinstanceof,cderr); + case OPfinalinstanceof: X("finalinstanceof", elfinalinstanceof,cderr); + case OPcheckcast: X("checkcast", elcheckcast,cderr); + case OParraylength: X("length", elarraylength,cderr); + case OParray: X("array", elarray,cderr); + case OPfield: X("field", elfield,cderr); + case OPhstring: X("hstring", elhstring,cderr); + case OPnullcheck: X("nullcheck", elnullcheck,cdnullcheck); + + case OPor: X("|", elor, cdorth); + case OPoror: X("||", eloror, cdloglog); + case OPandand: X("&&", elandand,cdloglog); + case OProl: X("<<|", elshl, cdshift); + case OPror: X(">>|", elshl, cdshift); + case OPshl: X("<<", elshl, cdshift); + case OPshr: X(">>>", elshr, cdshift); + case OPashr: X(">>", elshr, cdshift); + case OPbit: X("bit", elbit, cderr); + case OPind: X("*", elind, cdind); + case OPaddr: X("&", eladdr, cderr); + case OPneg: X("-", elneg, cdneg); + case OPuadd: X("+", elzot, cderr); + case OPabs: X("abs", evalu8, cdabs); + case OPsqrt: X("sqrt", evalu8, cdneg); + case OPsin: X("sin", evalu8, cdneg); + case OPcos: X("cos", evalu8, cdneg); + case OPrint: X("rint", evalu8, cdneg); + case OPrndtol: X("rndtol", evalu8, cdrndtol); + case OPscale: X("scale", elzot, cdscale); + case OPyl2x: X("yl2x", elzot, cdscale); + case OPyl2xp1: X("yl2xp1", elzot, cdscale); + case OPstrlen: X("strlen", elzot, cdstrlen); + case OPstrcpy: X("strcpy", elstrcpy,cdstrcpy); + case OPmemcpy: X("memcpy", elmemxxx,cdmemcpy); + case OPmemset: X("memset", elmemxxx,cdmemset); + case OPstrcat: X("strcat", elzot, cderr); + case OPstrcmp: X("strcmp", elstrcmp,cdstrcmp); + case OPmemcmp: X("memcmp", elmemxxx,cdmemcmp); + case OPsetjmp: X("setjmp", elzot, cdsetjmp); + case OPnegass: X("negass", elnegass, cdaddass); + case OPpreinc: X("U++", elzot, cderr); + case OPpredec: X("U--", elzot, cderr); + case OPstreq: X("streq", elstruct,cdstreq); + case OPpostinc: X("++", elpost, cdpost); + case OPpostdec: X("--", elpost, cdpost); + case OPeq: X("=", eleq, cdeq); + case OPaddass: X("+=", elopass,cdaddass); + case OPminass: X("-=", elopass,cdaddass); + case OPmulass: X("*=", elopass,cdmulass); + case OPdivass: X("/=", elopass,cdmulass); + case OPmodass: X("%=", elopass,cdmulass); + case OPshrass: X(">>>=", elopass,cdshass); + case OPashrass: X(">>=", elopass,cdshass); + case OPshlass: X("<<=", elopass,cdshass); + case OPandass: X("&=", elopass,cdaddass); + case OPxorass: X("^=", elopass,cdaddass); + case OPorass: X("|=", elopass,cdaddass); + + case OPle: X("<=", elcmp, cdcmp); + case OPgt: X(">", elcmp, cdcmp); + case OPlt: X("<", elcmp, cdcmp); + case OPge: X(">=", elcmp, cdcmp); + case OPeqeq: X("==", elcmp, cdcmp); + case OPne: X("!=", elcmp, cdcmp); + + case OPunord: X("!<>=", elcmp, cdcmp); + case OPlg: X("<>", elcmp, cdcmp); + case OPleg: X("<>=", elcmp, cdcmp); + case OPule: X("!>", elcmp, cdcmp); + case OPul: X("!>=", elcmp, cdcmp); + case OPuge: X("!<", elcmp, cdcmp); + case OPug: X("!<=", elcmp, cdcmp); + case OPue: X("!<>", elcmp, cdcmp); + case OPngt: X("~>", elcmp, cdcmp); + case OPnge: X("~>=", elcmp, cdcmp); + case OPnlt: X("~<", elcmp, cdcmp); + case OPnle: X("~<=", elcmp, cdcmp); + case OPord: X("~!<>=", elcmp, cdcmp); + case OPnlg: X("~<>", elcmp, cdcmp); + case OPnleg: X("~<>=", elcmp, cdcmp); + case OPnule: X("~!>", elcmp, cdcmp); + case OPnul: X("~!>=", elcmp, cdcmp); + case OPnuge: X("~!<", elcmp, cdcmp); + case OPnug: X("~!<=", elcmp, cdcmp); + case OPnue: X("~!<>", elcmp, cdcmp); + +#if TARGET_SEGMENTED + case OPvp_fp: X("vptrfptr", elvptrfptr,cdcnvt); + case OPcvp_fp: X("cvptrfptr", elvptrfptr,cdcnvt); + case OPoffset: X("offset", ellngsht,cdlngsht); + case OPnp_fp: X("ptrlptr", elptrlptr,cdshtlng); + case OPnp_f16p: X("tofar16", elzot, cdfar16); + case OPf16p_np: X("fromfar16", elzot, cdfar16); +#endif + case OPs16_32: X("s16_32", evalu8, cdshtlng); + case OPu16_32: X("u16_32", evalu8, cdshtlng); + case OPd_s32: X("d_s32", evalu8, cdcnvt); + case OPb_8: X("b_8", evalu8, cdcnvt); + case OPs32_d: X("s32_d", evalu8, cdcnvt); + case OPd_s16: X("d_s16", evalu8, cdcnvt); + case OPs16_d: X("s16_d", evalu8, cdcnvt); + case OPd_u16: X("d_u16", evalu8, cdcnvt); + case OPu16_d: X("u16_d", evalu8, cdcnvt); + case OPd_u32: X("d_u32", evalu8, cdcnvt); + case OPu32_d: X("u32_d", evalu8, cdcnvt); + case OP32_16: X("32_16", ellngsht,cdlngsht); + case OPd_f: X("d_f", evalu8, cdcnvt); + case OPf_d: X("f_d", evalu8, cdcnvt); + case OPd_ld: X("d_ld", evalu8, cdcnvt); + case OPld_d: X("ld_d", evalu8, cdcnvt); + case OPc_r: X("c_r", elc_r, cdconvt87); + case OPc_i: X("c_i", elc_i, cdconvt87); + case OPu8_16: X("u8_16", elbyteint, cdbyteint); + case OPs8_16: X("s8_16", elbyteint, cdbyteint); + case OP16_8: X("16_8", ellngsht,cdlngsht); + case OPu32_64: X("u32_64", evalu8, cdshtlng); + case OPs32_64: X("s32_64", evalu8, cdshtlng); + case OP64_32: X("64_32", el64_32, cdlngsht); + case OPu64_128: X("u64_128", evalu8, cdshtlng); + case OPs64_128: X("s64_128", evalu8, cdshtlng); + case OP128_64: X("128_64", el64_32, cdlngsht); + case OPmsw: X("msw", evalu8, cdmsw); + + case OPd_s64: X("d_s64", evalu8, cdcnvt); + case OPs64_d: X("s64_d", evalu8, cdcnvt); + case OPd_u64: X("d_u64", evalu8, cdcnvt); + case OPu64_d: X("u64_d", evalu8, cdcnvt); + case OPld_u64: X("ld_u64", evalu8, cdcnvt); + case OPparam: X("param", elparam, cderr); + case OPsizeof: X("sizeof", elzot, cderr); + case OParrow: X("->", elzot, cderr); + case OParrowstar: X("->*", elzot, cderr); + case OPcolon: X("colon", elzot, cderr); + case OPcolon2: X("colon2", elzot, cderr); + case OPbool: X("bool", elbool, cdnot); + case OPcall: X("call", elcall, cdfunc); + case OPucall: X("ucall", elcall, cdfunc); + case OPcallns: X("callns", elcall, cdfunc); + case OPucallns: X("ucallns", elcall, cdfunc); + case OPstrpar: X("strpar", elstruct, cderr); + case OPstrctor: X("strctor", elzot, cderr); + case OPstrthis: X("strthis", elzot, cdstrthis); + case OPconst: X("const", elerr, cderr); + case OPvar: X("var", elerr, loaddata); + case OPreg: X("reg", elerr, cderr); + case OPnew: X("new", elerr, cderr); + case OPanew: X("new[]", elerr, cderr); + case OPdelete: X("delete", elerr, cderr); + case OPadelete: X("delete[]", elerr, cderr); + case OPbrack: X("brack", elerr, cderr); + case OPframeptr: X("frameptr", elzot, cdframeptr); + case OPgot: X("got", elzot, cdgot); + + case OPbsf: X("bsf", elzot, cdbscan); + case OPbsr: X("bsr", elzot, cdbscan); + case OPbt: X("bt", elzot, cdbt); + case OPbtc: X("btc", elzot, cdbt); + case OPbtr: X("btr", elzot, cdbt); + case OPbts: X("bts", elzot, cdbt); + + case OPbswap: X("bswap", evalu8, cdbswap); + case OPvector: X("vector", elzot, cdvector); + + default: + printf("opcode hole x%x\n",i); + exit(EXIT_FAILURE); +#undef X + } + } + + fprintf(fdeb,"static const char *debtab[OPMAX] = \n\t{\n"); + for (i = 0; i < OPMAX - 1; i++) + fprintf(fdeb,"\t\"%s\",\n",debtab[i]); + fprintf(fdeb,"\t\"%s\"\n\t};\n",debtab[i]); + + f = fopen("cdxxx.c","w"); + fprintf(f,"code *(*cdxxx[OPMAX]) (elem *,regm_t *) = \n\t{\n"); + for (i = 0; i < OPMAX - 1; i++) + fprintf(f,"\t%s,\n",cdxxx[i]); + fprintf(f,"\t%s\n\t};\n",cdxxx[i]); + fclose(f); + + f = fopen("elxxx.c","w"); + fprintf(f,"static elem *(*elxxx[OPMAX]) (elem *) = \n\t{\n"); + for (i = 0; i < OPMAX - 1; i++) + fprintf(f,"\t%s,\n",elxxx[i]); + fprintf(f,"\t%s\n\t};\n",elxxx[i]); + fclose(f); +} + +void fltables() +{ FILE *f; + int i; + char segfl[FLMAX]; + char datafl[FLMAX]; + char stackfl[FLMAX]; + char flinsymtab[FLMAX]; + + static char indatafl[] = /* is FLxxxx a data type? */ + { FLdata,FLudata,FLreg,FLpseudo,FLauto,FLpara,FLextern,FLtmp, + FLcs,FLfltreg,FLallocatmp,FLdatseg,FLndp,FLtlsdata,FLbprel, + FLstack,FLregsave }; +#if TARGET_SEGMENTED + static char indatafl_s[] = { FLfardata, }; +#endif + + static char instackfl[] = /* is FLxxxx a stack data type? */ + { FLauto,FLpara,FLtmp,FLcs,FLfltreg,FLallocatmp,FLndp,FLbprel,FLstack,FLregsave }; + + static char inflinsymtab[] = /* is FLxxxx in the symbol table? */ + { FLdata,FLudata,FLreg,FLpseudo,FLauto,FLpara,FLextern,FLtmp,FLfunc, + FLtlsdata,FLbprel,FLstack }; +#if TARGET_SEGMENTED + static char inflinsymtab_s[] = { FLfardata,FLcsdata, }; +#endif + + for (i = 0; i < FLMAX; i++) + datafl[i] = stackfl[i] = flinsymtab[i] = 0; + + for (i = 0; i < sizeof(indatafl); i++) + datafl[indatafl[i]] = 1; + + for (i = 0; i < sizeof(instackfl); i++) + stackfl[instackfl[i]] = 1; + + for (i = 0; i < sizeof(inflinsymtab); i++) + flinsymtab[inflinsymtab[i]] = 1; + +#if TARGET_SEGMENTED + for (i = 0; i < sizeof(indatafl_s); i++) + datafl[indatafl_s[i]] = 1; + + for (i = 0; i < sizeof(inflinsymtab_s); i++) + flinsymtab[inflinsymtab_s[i]] = 1; +#endif + +/* Segment registers */ +#define ES 0 +#define CS 1 +#define SS 2 +#define DS 3 + + for (i = 0; i < FLMAX; i++) + { switch (i) + { + case 0: segfl[i] = -1; break; + case FLconst: segfl[i] = -1; break; + case FLoper: segfl[i] = -1; break; + case FLfunc: segfl[i] = CS; break; + case FLdata: segfl[i] = DS; break; + case FLudata: segfl[i] = DS; break; + case FLreg: segfl[i] = -1; break; + case FLpseudo: segfl[i] = -1; break; + case FLauto: segfl[i] = SS; break; + case FLstack: segfl[i] = SS; break; + case FLbprel: segfl[i] = SS; break; + case FLpara: segfl[i] = SS; break; + case FLextern: segfl[i] = DS; break; + case FLtmp: segfl[i] = SS; break; + case FLcode: segfl[i] = CS; break; + case FLblock: segfl[i] = CS; break; + case FLblockoff: segfl[i] = CS; break; + case FLcs: segfl[i] = SS; break; + case FLregsave: segfl[i] = SS; break; + case FLndp: segfl[i] = SS; break; + case FLswitch: segfl[i] = -1; break; + case FLfltreg: segfl[i] = SS; break; + case FLoffset: segfl[i] = -1; break; +#if TARGET_SEGMENTED + case FLfardata: segfl[i] = -1; break; + case FLcsdata: segfl[i] = CS; break; +#endif + case FLdatseg: segfl[i] = DS; break; + case FLctor: segfl[i] = -1; break; + case FLdtor: segfl[i] = -1; break; + case FLdsymbol: segfl[i] = -1; break; +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + case FLgot: segfl[i] = -1; break; + case FLgotoff: segfl[i] = -1; break; +#endif + case FLlocalsize: segfl[i] = -1; break; + case FLtlsdata: segfl[i] = -1; break; + case FLframehandler: segfl[i] = -1; break; + case FLasm: segfl[i] = -1; break; + case FLallocatmp: segfl[i] = SS; break; + default: + printf("error in segfl[%d]\n", i); + exit(1); + } + } + + f = fopen("fltables.c","w"); + + fprintf(f,"const char datafl[FLMAX] = \n\t{ "); + for (i = 0; i < FLMAX - 1; i++) + fprintf(f,"%d,",datafl[i]); + fprintf(f,"%d };\n",datafl[i]); + + fprintf(f,"const char stackfl[FLMAX] = \n\t{ "); + for (i = 0; i < FLMAX - 1; i++) + fprintf(f,"%d,",stackfl[i]); + fprintf(f,"%d };\n",stackfl[i]); + + fprintf(f,"const char segfl[FLMAX] = \n\t{ "); + for (i = 0; i < FLMAX - 1; i++) + fprintf(f,"%d,",segfl[i]); + fprintf(f,"%d };\n",segfl[i]); + + fprintf(f,"const char flinsymtab[FLMAX] = \n\t{ "); + for (i = 0; i < FLMAX - 1; i++) + fprintf(f,"%d,",flinsymtab[i]); + fprintf(f,"%d };\n",flinsymtab[i]); + + fclose(f); +} + +void dotytab() +{ + static tym_t _ptr[] = { TYjhandle,TYnptr }; +#if TARGET_SEGMENTED + static tym_t _ptr_nflat[]= { TYsptr,TYcptr,TYf16ptr,TYfptr,TYhptr,TYvptr }; +#endif + static tym_t _real[] = { TYfloat,TYdouble,TYdouble_alias,TYldouble, + TYfloat4,TYdouble2, + }; + static tym_t _imaginary[] = { + TYifloat,TYidouble,TYildouble, + }; + static tym_t _complex[] = { + TYcfloat,TYcdouble,TYcldouble, + }; + static tym_t _integral[] = { TYbool,TYchar,TYschar,TYuchar,TYshort, + TYwchar_t,TYushort,TYenum,TYint,TYuint, + TYlong,TYulong,TYllong,TYullong,TYdchar, + TYschar16,TYuchar16,TYshort8,TYushort8, + TYlong4,TYulong4,TYllong2,TYullong2, + TYchar16, TYcent, TYucent }; + static tym_t _ref[] = { TYnref,TYref }; + static tym_t _func[] = { TYnfunc,TYnpfunc,TYnsfunc,TYifunc,TYmfunc,TYjfunc,TYhfunc }; +#if TARGET_SEGMENTED + static tym_t _ref_nflat[] = { TYfref }; + static tym_t _func_nflat[]= { TYffunc,TYfpfunc,TYf16func,TYfsfunc,TYnsysfunc,TYfsysfunc, }; +#endif + static tym_t _uns[] = { TYuchar,TYushort,TYuint,TYulong, +#if MARS + TYwchar_t, +#endif + TYuchar16,TYushort8,TYulong4,TYullong2, + TYdchar,TYullong,TYucent,TYchar16 }; +#if !MARS + static tym_t _mptr[] = { TYmemptr }; +#endif + static tym_t _nullptr[] = { TYnullptr }; +#if TARGET_SEGMENTED +#if OMFOBJ + static tym_t _fv[] = { TYfptr, TYvptr }; +#endif +#if TARGET_WINDOS + static tym_t _farfunc[] = { TYffunc,TYfpfunc,TYfsfunc,TYfsysfunc }; +#endif +#endif + static tym_t _pasfunc[] = { TYnpfunc,TYnsfunc,TYmfunc,TYjfunc }; +#if TARGET_SEGMENTED + static tym_t _pasfunc_nf[] = { TYfpfunc,TYf16func,TYfsfunc, }; +#endif + static tym_t _revfunc[] = { TYnpfunc,TYjfunc }; +#if TARGET_SEGMENTED + static tym_t _revfunc_nf[] = { TYfpfunc,TYf16func, }; +#endif + static tym_t _short[] = { TYbool,TYchar,TYschar,TYuchar,TYshort, + TYwchar_t,TYushort,TYchar16 }; + static tym_t _aggregate[] = { TYstruct,TYarray }; +#if TX86 + static tym_t _xmmreg[] = { + TYfloat,TYdouble,TYifloat,TYidouble, + TYfloat4,TYdouble2, + TYschar16,TYuchar16,TYshort8,TYushort8, + TYlong4,TYulong4,TYllong2,TYullong2, + }; +#endif + + static struct + { + const char *string; /* name of type */ + tym_t ty; /* TYxxxx */ + tym_t unsty; /* conversion to unsigned type */ + tym_t relty; /* type for relaxed type checking */ + int size; + int debtyp; /* Codeview 1 type in debugger record */ + int debtyp4; /* Codeview 4 type in debugger record */ + } typetab[] = + { +/* Note that chars are signed, here */ +"bool", TYbool, TYbool, TYchar, 1, 0x80, 0x30, +"char", TYchar, TYuchar, TYchar, 1, 0x80, 0x70, +"signed char", TYschar, TYuchar, TYchar, 1, 0x80, 0x10, +"unsigned char",TYuchar, TYuchar, TYchar, 1, 0x84, 0x20, +"char16_t", TYchar16, TYchar16, TYint, 2, 0x85, 0x21, +"short", TYshort, TYushort, TYint, SHORTSIZE, 0x81,0x11, +"wchar_t", TYwchar_t, TYwchar_t, TYint, SHORTSIZE, 0x85,0x71, +"unsigned short",TYushort, TYushort, TYint, SHORTSIZE, 0x85,0x21, + +// These values are adjusted for 32 bit ints in cv_init() and util_set32() +"enum", TYenum, TYuint, TYint, -1, 0x81,0x72, +"int", TYint, TYuint, TYint, 2, 0x81,0x72, +"unsigned", TYuint, TYuint, TYint, 2, 0x85,0x73, + +"long", TYlong, TYulong, TYlong, LONGSIZE, 0x82,0x12, +"unsigned long",TYulong, TYulong, TYlong, LONGSIZE, 0x86,0x22, +"dchar", TYdchar, TYdchar, TYlong, 4, 0x86,0x78, +"long long", TYllong, TYullong, TYllong, LLONGSIZE, 0x82,0x13, +"uns long long",TYullong, TYullong, TYllong, LLONGSIZE, 0x86,0x23, +"cent", TYcent, TYucent, TYcent, 16, 0x82,0x13, +"ucent", TYucent, TYucent, TYcent, 16, 0x86,0x23, +"float", TYfloat, TYfloat, TYfloat, FLOATSIZE, 0x88,0x40, +"double", TYdouble, TYdouble, TYdouble, DOUBLESIZE,0x89,0x41, +"double alias", TYdouble_alias, TYdouble_alias, TYdouble_alias,8, 0x89,0x41, +"long double", TYldouble, TYldouble, TYldouble, LNGDBLSIZE, 0x89,0x42, + +"imaginary float", TYifloat, TYifloat, TYifloat, FLOATSIZE, 0x88,0x40, +"imaginary double", TYidouble, TYidouble, TYidouble, DOUBLESIZE,0x89,0x41, +"imaginary long double",TYildouble, TYildouble, TYildouble, LNGDBLSIZE,0x89,0x42, + +"complex float", TYcfloat, TYcfloat, TYcfloat, 2*FLOATSIZE, 0x88,0x50, +"complex double", TYcdouble, TYcdouble, TYcdouble, 2*DOUBLESIZE,0x89,0x51, +"complex long double", TYcldouble, TYcldouble, TYcldouble, 2*LNGDBLSIZE,0x89,0x52, + +"float[4]", TYfloat4, TYfloat4, TYfloat4, 16, 0, 0, +"double[2]", TYdouble2, TYdouble2, TYdouble2, 16, 0, 0, +"signed char[16]", TYschar16, TYuchar16, TYschar16, 16, 0, 0, +"unsigned char[16]", TYuchar16, TYuchar16, TYuchar16, 16, 0, 0, +"short[8]", TYshort8, TYushort8, TYshort8, 16, 0, 0, +"unsigned short[8]", TYushort8, TYushort8, TYushort8, 16, 0, 0, +"long[4]", TYlong4, TYulong4, TYlong4, 16, 0, 0, +"unsigned long[4]", TYulong4, TYulong4, TYulong4, 16, 0, 0, +"long long[2]", TYllong2, TYullong2, TYllong2, 16, 0, 0, +"unsigned long long[2]", TYullong2, TYullong2, TYullong2, 16, 0, 0, + +"__near *", TYjhandle, TYjhandle, TYjhandle, 2, 0x20, 0x100, +"nullptr_t", TYnullptr, TYnullptr, TYptr, 2, 0x20, 0x100, +"*", TYnptr, TYnptr, TYnptr, 2, 0x20, 0x100, +"&", TYref, TYref, TYref, -1, 0, 0, +"void", TYvoid, TYvoid, TYvoid, -1, 0x85, 3, +"struct", TYstruct, TYstruct, TYstruct, -1, 0, 0, +"array", TYarray, TYarray, TYarray, -1, 0x78, 0, +"C func", TYnfunc, TYnfunc, TYnfunc, -1, 0x63, 0, +"Pascal func", TYnpfunc, TYnpfunc, TYnpfunc, -1, 0x74, 0, +"std func", TYnsfunc, TYnsfunc, TYnsfunc, -1, 0x63, 0, +"*", TYptr, TYptr, TYptr, 2, 0x20, 0x100, +"member func", TYmfunc, TYmfunc, TYmfunc, -1, 0x64, 0, +"D func", TYjfunc, TYjfunc, TYjfunc, -1, 0x74, 0, +"C func", TYhfunc, TYhfunc, TYhfunc, -1, 0, 0, +"__near &", TYnref, TYnref, TYnref, 2, 0, 0, + +#if TARGET_SEGMENTED +"__ss *", TYsptr, TYsptr, TYsptr, 2, 0x20, 0x100, +"__cs *", TYcptr, TYcptr, TYcptr, 2, 0x20, 0x100, +"__far16 *", TYf16ptr, TYf16ptr, TYf16ptr, 4, 0x40, 0x200, +"__far *", TYfptr, TYfptr, TYfptr, 4, 0x40, 0x200, +"__huge *", TYhptr, TYhptr, TYhptr, 4, 0x40, 0x300, +"__handle *", TYvptr, TYvptr, TYvptr, 4, 0x40, 0x200, +"far C func", TYffunc, TYffunc, TYffunc, -1, 0x64, 0, +"far Pascal func", TYfpfunc, TYfpfunc, TYfpfunc, -1, 0x73, 0, +"far std func", TYfsfunc, TYfsfunc, TYfsfunc, -1, 0x64, 0, +"_far16 Pascal func", TYf16func, TYf16func, TYf16func, -1, 0x63, 0, +"sys func", TYnsysfunc, TYnsysfunc,TYnsysfunc, -1, 0x63, 0, +"far sys func", TYfsysfunc, TYfsysfunc,TYfsysfunc, -1, 0x64, 0, +"__far &", TYfref, TYfref, TYfref, 4, 0, 0, +#endif +#if !MARS +"interrupt func", TYifunc, TYifunc, TYifunc, -1, 0x64, 0, +"memptr", TYmemptr, TYmemptr, TYmemptr, -1, 0, 0, +"ident", TYident, TYident, TYident, -1, 0, 0, +"template", TYtemplate, TYtemplate, TYtemplate, -1, 0, 0, +"vtshape", TYvtshape, TYvtshape, TYvtshape, -1, 0, 0, +#endif + }; + + FILE *f; + static unsigned tytab[64 * 4]; + static tym_t tytouns[64 * 4]; + static tym_t _tyrelax[TYMAX]; + static tym_t _tyequiv[TYMAX]; + static signed char tysize[64 * 4]; + static const char *tystring[TYMAX]; + static unsigned char dttab[TYMAX]; + static unsigned short dttab4[TYMAX]; + int i; + +#define T1(arr,mask) for (i=0; i +#include +#include +#include + +#if DOS386 +#include +#include +#endif + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#include +#include +#include +#include +#include +#include +#define GetLastError() errno +#elif _WIN32 +#include +#include +#include +#endif + +#if __DMC__ || __GNUC__ +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" +#else +#include +#endif + +#if _WINDLL +extern void dll_printf(const char *format,...); +#define dbg_printf dll_printf +#else +#define dbg_printf printf +#endif + +int file_createdirs(char *name); + +/*********************************** + * Called when there is an error returned by the operating system. + * This function does not return. + */ + +void os_error(int line) +{ +#if _WIN32 + dbg_printf("System error: %ldL\n",GetLastError()); +#endif + local_assert(line); +} + +#if 1 +#undef dbg_printf +#define dbg_printf (void) +#endif + +#define os_error() os_error(__LINE__) +#pragma noreturn(os_error) + +#if _WIN32 +/********************************* + * Allocate a chunk of memory from the operating system. + * Bypass malloc and friends. + */ + +static HANDLE hHeap; + +void *globalrealloc(void *oldp,size_t newsize) +{ +#if 0 + void *p; + + // Initialize heap + if (!hHeap) + { hHeap = HeapCreate(0,0x10000,0); + if (!hHeap) + os_error(); + } + + newsize = (newsize + 3) & ~3L; // round up to dwords + if (newsize == 0) + { + if (oldp && HeapFree(hHeap,0,oldp) == FALSE) + os_error(); + p = NULL; + } + else if (!oldp) + { + p = newsize ? HeapAlloc(hHeap,0,newsize) : NULL; + } + else + p = HeapReAlloc(hHeap,0,oldp,newsize); +#elif 1 + MEMORY_BASIC_INFORMATION query; + void *p; + BOOL bSuccess; + + if (!oldp) + p = VirtualAlloc (NULL, newsize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + else + { + VirtualQuery (oldp, &query, sizeof(query)); + if (!newsize) + { + p = NULL; + goto L1; + } + else + { newsize = (newsize + 0xFFFF) & ~0xFFFFL; + + if (query.RegionSize >= newsize) + p = oldp; + else + { p = VirtualAlloc(NULL,newsize,MEM_COMMIT | MEM_RESERVE,PAGE_READWRITE); + if (p) + memcpy(p,oldp,query.RegionSize); + L1: + bSuccess = VirtualFree(oldp,query.RegionSize,MEM_DECOMMIT); + if (bSuccess) + bSuccess = VirtualFree(oldp,0,MEM_RELEASE); + if (!bSuccess) + os_error(); + } + } + } +#else + void *p; + + if (!oldp) + p = (void *)GlobalAlloc (0, newsize); + else if (!newsize) + { GlobalFree(oldp); + p = NULL; + } + else + p = (void *)GlobalReAlloc(oldp,newsize,0); +#endif + dbg_printf("globalrealloc(oldp = %p, size = x%x) = %p\n",oldp,newsize,p); + return p; +} + +/***************************************** + * Functions to manage allocating a single virtual address space. + */ + +void *vmem_reserve(void *ptr,unsigned long size) +{ void *p; + +#if 1 + p = VirtualAlloc(ptr,size,MEM_RESERVE,PAGE_READWRITE); + dbg_printf("vmem_reserve(ptr = %p, size = x%lx) = %p\n",ptr,size,p); +#else + dbg_printf("vmem_reserve(ptr = %p, size = x%lx) = %p\n",ptr,size,p); + p = VirtualAlloc(ptr,size,MEM_RESERVE,PAGE_READWRITE); + if (!p) + os_error(); +#endif + return p; +} + +/***************************************** + * Commit memory. + * Returns: + * 0 failure + * !=0 success + */ + +int vmem_commit(void *ptr, unsigned long size) +{ int i; + + dbg_printf("vmem_commit(ptr = %p,size = x%lx)\n",ptr,size); + i = (int) VirtualAlloc(ptr,size,MEM_COMMIT,PAGE_READWRITE); + if (i == 0) + dbg_printf("failed to commit\n"); + return i; +} + +void vmem_decommit(void *ptr,unsigned long size) +{ + dbg_printf("vmem_decommit(ptr = %p, size = x%lx)\n",ptr,size); + if (ptr) + { if (!VirtualFree(ptr, size, MEM_DECOMMIT)) + os_error(); + } +} + +void vmem_release(void *ptr,unsigned long size) +{ + dbg_printf("vmem_release(ptr = %p, size = x%lx)\n",ptr,size); + if (ptr) + { + if (!VirtualFree(ptr, 0, MEM_RELEASE)) + os_error(); + } +} + +/******************************************** + * Map file for read, copy on write, into virtual address space. + * Input: + * ptr address to map file to, if NULL then pick an address + * size length of the file + * flag 0 read / write + * 1 read / copy on write + * 2 read only + * Returns: + * NULL failure + * ptr pointer to start of mapped file + */ + +static HANDLE hFile = INVALID_HANDLE_VALUE; +static HANDLE hFileMap = NULL; +static void *pview; +static void *preserve; +static size_t preserve_size; + +void *vmem_mapfile(const char *filename,void *ptr,unsigned long size,int flag) +{ + OSVERSIONINFO OsVerInfo; + + OsVerInfo.dwOSVersionInfoSize = sizeof(OsVerInfo); + GetVersionEx(&OsVerInfo); + + dbg_printf("vmem_mapfile(filename = '%s', ptr = %p, size = x%lx, flag = %d)\n",filename,ptr,size,flag); + + hFile = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + goto L1; // failure + dbg_printf(" file created\n"); + + // Windows 95 does not implement PAGE_WRITECOPY (unfortunately treating + // it just like PAGE_READWRITE). + if (flag == 1 && OsVerInfo.dwPlatformId == 1) // Windows 95, 98, ME + hFileMap = NULL; + else + hFileMap = CreateFileMapping(hFile,NULL, + (flag == 1) ? PAGE_WRITECOPY : PAGE_READWRITE,0,size,NULL); + + if (hFileMap == NULL) // mapping failed + { +#if 1 + // Win32s seems to always fail here. + DWORD nbytes; + + dbg_printf(" mapping failed\n"); + // If it was NT failing, assert. + assert(OsVerInfo.dwPlatformId != VER_PLATFORM_WIN32_NT); + + // To work around, just read the file into memory. + assert(flag == 1); + preserve = vmem_reserve(ptr,size); + if (!preserve) + goto L2; + if (!vmem_commit(preserve,size)) + { + vmem_release(preserve,size); + preserve = NULL; + goto L2; + } + preserve_size = size; + if (!ReadFile(hFile,preserve,size,&nbytes,NULL)) + os_error(); + assert(nbytes == size); + if (CloseHandle(hFile) != TRUE) + os_error(); + hFile = INVALID_HANDLE_VALUE; + return preserve; +#else + // Instead of working around, we should find out why it failed. + os_error(); +#endif + } + else + { + dbg_printf(" mapping created\n"); + pview = MapViewOfFileEx(hFileMap,flag ? FILE_MAP_COPY : FILE_MAP_WRITE, + 0,0,size,ptr); + if (pview == NULL) // mapping view failed + { //os_error(); + goto L3; + } + } + dbg_printf(" pview = %p\n",pview); + + return pview; + +Terminate: + if (UnmapViewOfFile(pview) == FALSE) + os_error(); + pview = NULL; +L3: + if (CloseHandle(hFileMap) != TRUE) + os_error(); + hFileMap = NULL; +L2: + if (CloseHandle(hFile) != TRUE) + os_error(); + hFile = INVALID_HANDLE_VALUE; +L1: + return NULL; // failure +} + +/***************************** + * Set size of mapped file. + */ + +void vmem_setfilesize(unsigned long size) +{ + if (hFile != INVALID_HANDLE_VALUE) + { if (SetFilePointer(hFile,size,NULL,FILE_BEGIN) == 0xFFFFFFFF) + os_error(); + if (SetEndOfFile(hFile) == FALSE) + os_error(); + } +} + +/***************************** + * Unmap previous file mapping. + */ + +void vmem_unmapfile() +{ + dbg_printf("vmem_unmapfile()\n"); + + vmem_decommit(preserve,preserve_size); + vmem_release(preserve,preserve_size); + preserve = NULL; + preserve_size = 0; + +#if 0 + if (pview) + { int i; + + i = UnmapViewOfFile(pview); + dbg_printf("i = x%x\n",i); + if (i == FALSE) + os_error(); + } +#else + // Note that under Windows 95, UnmapViewOfFile() seems to return random + // values, not TRUE or FALSE. + if (pview && UnmapViewOfFile(pview) == FALSE) + os_error(); +#endif + pview = NULL; + + if (hFileMap != NULL && CloseHandle(hFileMap) != TRUE) + os_error(); + hFileMap = NULL; + + if (hFile != INVALID_HANDLE_VALUE && CloseHandle(hFile) != TRUE) + os_error(); + hFile = INVALID_HANDLE_VALUE; +} + +/**************************************** + * Determine a base address that we can use for mapping files to. + */ + +void *vmem_baseaddr() +{ + OSVERSIONINFO OsVerInfo; + void *p; + + OsVerInfo.dwOSVersionInfoSize = sizeof(OsVerInfo); + GetVersionEx(&OsVerInfo); + + // These values for the address were determined by trial and error. + switch (OsVerInfo.dwPlatformId) + { + case VER_PLATFORM_WIN32s: // Win32s + // The fact that this is a different address than other + // WIN32 implementations causes us a lot of grief. + p = (void *) 0xC0000000; + break; + + case 1: //VER_PLATFORM_WIN32_WINDOWS: // Windows 95 + // I've found 0x90000000..0xB work. All others fail. + default: // unknown + p = (void *) 0x90000000; + break; + + case VER_PLATFORM_WIN32_NT: // Windows NT + // Pick a value that is not coincident with the base address + // of any commonly used system DLLs. + p = (void *) 0x38000000; + break; + } + + return p; +} + +/******************************************** + * Calculate the amount of memory to reserve, adjusting + * *psize downwards. + */ + +void vmem_reservesize(unsigned long *psize) +{ + MEMORYSTATUS ms; + OSVERSIONINFO OsVerInfo; + + unsigned long size; + + ms.dwLength = sizeof(ms); + GlobalMemoryStatus(&ms); + dbg_printf("dwMemoryLoad x%lx\n",ms.dwMemoryLoad); + dbg_printf("dwTotalPhys x%lx\n",ms.dwTotalPhys); + dbg_printf("dwAvailPhys x%lx\n",ms.dwAvailPhys); + dbg_printf("dwTotalPageFile x%lx\n",ms.dwTotalPageFile); + dbg_printf("dwAvailPageFile x%lx\n",ms.dwAvailPageFile); + dbg_printf("dwTotalVirtual x%lx\n",ms.dwTotalVirtual); + dbg_printf("dwAvailVirtual x%lx\n",ms.dwAvailVirtual); + + + OsVerInfo.dwOSVersionInfoSize = sizeof(OsVerInfo); + GetVersionEx(&OsVerInfo); + + switch (OsVerInfo.dwPlatformId) + { + case VER_PLATFORM_WIN32s: // Win32s + case 1: //VER_PLATFORM_WIN32_WINDOWS: // Windows 95 + default: // unknown + size = (ms.dwAvailPageFile < ms.dwAvailVirtual) + ? ms.dwAvailPageFile + : ms.dwAvailVirtual; + size = (unsigned long long)size * 8 / 10; + size &= ~0xFFFFL; + if (size < *psize) + *psize = size; + break; + + case VER_PLATFORM_WIN32_NT: // Windows NT + // NT can expand the paging file + break; + } + +} + +/******************************************** + * Return amount of physical memory. + */ + +unsigned long vmem_physmem() +{ + MEMORYSTATUS ms; + + ms.dwLength = sizeof(ms); + GlobalMemoryStatus(&ms); + return ms.dwTotalPhys; +} + +////////////////////////////////////////////////////////////// + +/*************************************************** + * Load library. + */ + +static HINSTANCE hdll; + +void os_loadlibrary(const char *dllname) +{ + hdll = LoadLibrary((LPCTSTR) dllname); + if (!hdll) + os_error(); +} + +/************************************************* + */ + +void os_freelibrary() +{ + if (hdll) + { + if (FreeLibrary(hdll) != TRUE) + os_error(); + hdll = NULL; + } +} + +/************************************************* + */ + +void *os_getprocaddress(const char *funcname) +{ void *fp; + + //printf("getprocaddress('%s')\n",funcname); + assert(hdll); + fp = (void *)GetProcAddress(hdll,(LPCSTR)funcname); + if (!fp) + os_error(); + return fp; +} + +////////////////////////////////////////////////////////////// + + +/********************************* + */ + +void os_term() +{ + if (hHeap) + { if (HeapDestroy(hHeap) == FALSE) + { hHeap = NULL; + os_error(); + } + hHeap = NULL; + } + os_freelibrary(); +} + +/*************************************************** + * Do our own storage allocator (being suspicious of the library one). + */ + +#if 1 + +void os_heapinit() { } +void os_heapterm() { } + +#else + +static HANDLE hHeap; + +void os_heapinit() +{ + hHeap = HeapCreate(0,0x10000,0); + if (!hHeap) + os_error(); +} + +void os_heapterm() +{ + if (hHeap) + { if (HeapDestroy(hHeap) == FALSE) + os_error(); + } +} + +void * __cdecl calloc(size_t x,size_t y) +{ size_t size; + + size = x * y; + return size ? HeapAlloc(hHeap,HEAP_ZERO_MEMORY,size) : NULL; +} + +void __cdecl free(void *p) +{ + if (p && HeapFree(hHeap,0,p) == FALSE) + os_error(); +} + +void * __cdecl malloc(size_t size) +{ + return size ? HeapAlloc(hHeap,0,size) : NULL; +} + +void * __cdecl realloc(void *p,size_t newsize) +{ + if (newsize == 0) + free(p); + else if (!p) + p = malloc(newsize); + else + p = HeapReAlloc(hHeap,0,p,newsize); + return p; +} + +#endif + +////////////////////////////////////////// +// Return a value that will hopefully be unique every time +// we call it. + +unsigned long os_unique() +{ + unsigned long long x; + + QueryPerformanceCounter((LARGE_INTEGER *)&x); + return x; +} + +#elif DOS386 + +////////////////////////////////////////// +// Return a value that will hopefully be unique every time +// we call it. + +unsigned long os_unique() +{ + if (cputype() >= 5) // if cpuid instruction supported + { + __asm + { + mov EAX,1 + cpuid + test EDX,0x20 // is rdtsc supported? + jz L1 // no + rdtsc + } + } + else + { +L1: time(NULL); + } + return _EAX; +} + +#endif + +/******************************************* + * Return !=0 if file exists. + * 0: file doesn't exist + * 1: normal file + * 2: directory + */ + +int os_file_exists(const char *name) +{ +#if _WIN32 + DWORD dw; + int result; + + dw = GetFileAttributes(name); + if (dw == -1L) + result = 0; + else if (dw & FILE_ATTRIBUTE_DIRECTORY) + result = 2; + else + result = 1; + return result; +#elif DOS386 + struct FIND *find; + + find = findfirst(name,FA_DIREC | FA_SYSTEM | FA_HIDDEN); + if (!find) + return 0; + return (find->attribute & FA_DIREC) ? 2 : 1; +#elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + struct stat buf; + + return stat(name,&buf) == 0; /* file exists if stat succeeded */ + +#else + return filesize(name) != -1L; +#endif +} + +/************************************** + * Get file size of open file. Return -1L on error. + */ + +#if _WIN32 +extern "C" void * __cdecl _osfhnd[]; +#endif + +long os_file_size(int fd) +{ +#if _WIN32 + return GetFileSize(_osfhnd[fd],NULL); +#else + struct stat buf; + + return (fstat(fd,&buf)) ? -1L : buf.st_size; +#endif +} + +/************************************************** + * For 16 bit programs, we need the 16 bit filename. + * Returns: + * malloc'd string, NULL if none + */ + +#if _WIN32 + +char *file_8dot3name(const char *filename) +{ + HANDLE h; + WIN32_FIND_DATA fileinfo; + char *buf; + int i; + + h = FindFirstFile(filename,&fileinfo); + if (h == INVALID_HANDLE_VALUE) + return NULL; + if (fileinfo.cAlternateFileName[0]) + { + for (i = strlen(filename); i > 0; i--) + if (filename[i] == '\\' || filename[i] == ':') + { i++; + break; + } + buf = (char *) malloc(i + 14); + if (buf) + { + memcpy(buf,filename,i); + strcpy(buf + i,fileinfo.cAlternateFileName); + } + } + else + buf = strdup(filename); + FindClose(h); + return buf; +} + +#endif + +/********************************************** + * Write a file. + * Returns: + * 0 success + */ + +int file_write(char *name, void *buffer, unsigned len) +{ +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + int fd; + ssize_t numwritten; + + fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (fd == -1) + goto err; + + numwritten = ::write(fd, buffer, len); + if (len != numwritten) + goto err2; + + if (close(fd) == -1) + goto err; + + return 0; + +err2: + close(fd); +err: + return 1; +#endif +#if _WIN32 + HANDLE h; + DWORD numwritten; + + h = CreateFile((LPTSTR)name,GENERIC_WRITE,0,NULL,CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,NULL); + if (h == INVALID_HANDLE_VALUE) + { + if (GetLastError() == ERROR_PATH_NOT_FOUND) + { + if (!file_createdirs(name)) + { + h = CreateFile((LPTSTR)name,GENERIC_WRITE,0,NULL,CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,NULL); + if (h != INVALID_HANDLE_VALUE) + goto Lok; + } + } + goto err; + } + +Lok: + if (WriteFile(h,buffer,len,&numwritten,NULL) != TRUE) + goto err2; + + if (len != numwritten) + goto err2; + + if (!CloseHandle(h)) + goto err; + return 0; + +err2: + CloseHandle(h); +err: + return 1; +#endif +#if _MSDOS + return 1; +#endif +} + +/******************************** + * Create directories up to filename. + * Input: + * name path/filename + * Returns: + * 0 success + * !=0 failure + */ + +int file_createdirs(char *name) +{ +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + return 1; +#endif +#if _WIN32 + int len = strlen(name); + char *path = (char *)alloca(len + 1); + char *p; + + memcpy(path, name, len + 1); + + for (p = path + len; ; p--) + { + if (p == path) + goto Lfail; + switch (*p) + { + case ':': + case '/': + case '\\': + *p = 0; + if (!CreateDirectory((LPTSTR)path, NULL)) + { // Failed + if (file_createdirs(path)) + goto Lfail; + if (!CreateDirectory((LPTSTR)path, NULL)) + goto Lfail; + } + return 0; + } + } + +Lfail: + return 1; +#endif +#if _MSDOS + return 1; +#endif +} + +/*********************************** + * Return size of OS critical section. + * NOTE: can't use the sizeof() calls directly since cross compiling is + * supported and would end up using the host sizes rather than the target + * sizes. + */ + +#if _WIN32 +int os_critsecsize32() +{ + return sizeof(CRITICAL_SECTION); +} + +int os_critsecsize64() +{ + assert(0); + return 0; +} +#endif + +#if linux +int os_critsecsize32() +{ + return 24; // sizeof(pthread_mutex_t) on 32 bit +} + +int os_critsecsize64() +{ + return 40; // sizeof(pthread_mutex_t) on 64 bit +} +#endif + +#if __FreeBSD__ +int os_critsecsize32() +{ + return 4; // sizeof(pthread_mutex_t) on 32 bit +} + +int os_critsecsize64() +{ + return 8; // sizeof(pthread_mutex_t) on 64 bit +} +#endif + +#if __OpenBSD__ +int os_critsecsize32() +{ + return 4; // sizeof(pthread_mutex_t) on 32 bit +} + +int os_critsecsize64() +{ + assert(0); + return 8; // sizeof(pthread_mutex_t) on 64 bit +} +#endif + +#if __APPLE__ +int os_critsecsize32() +{ +#if __LP64__ // check for bit rot + assert(sizeof(pthread_mutex_t) == 64); +#else + assert(sizeof(pthread_mutex_t) == 44); +#endif + return 44; +} + +int os_critsecsize64() +{ + return 64; +} +#endif + + +#if __sun&&__SVR4 +int os_critsecsize32() +{ + return sizeof(pthread_mutex_t); +} + +int os_critsecsize64() +{ + assert(0); + return 0; +} +#endif + +/* This is the magic program to get the size on Posix systems: */ + +#if 0 +#include +#include + +int main() +{ + printf("%d\n", (int)sizeof(pthread_mutex_t)); + return 0; +} +#endif + diff --git a/backend/out.c b/backend/out.c new file mode 100644 index 00000000..64c8ab3c --- /dev/null +++ b/backend/out.c @@ -0,0 +1,1534 @@ +// Copyright (C) 1984-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +#if !SPP + +#include +#include +#include + +#include "cc.h" +#include "oper.h" +#include "global.h" +#include "type.h" +#include "filespec.h" +#include "code.h" +#include "cgcv.h" +#include "go.h" +#include "dt.h" +#if SCPP +#include "parser.h" +#include "cpp.h" +#include "el.h" +#endif + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +static int addrparam; /* see if any parameters get their address taken */ + +/********************************** + * We put out an external definition. + */ + +#if SCPP + +void out_extdef(symbol *s) +{ + pstate.STflags |= PFLextdef; + if (//config.flags2 & CFG2phgen || + (config.flags2 & (CFG2phauto | CFG2phautoy) && + !(pstate.STflags & (PFLhxwrote | PFLhxdone))) + ) + + synerr(EM_data_in_pch,prettyident(s)); // data or code in precompiled header +} + +#endif + +#if TX86 +#if SCPP +/******************************** + * Put out code segment name record. + */ + +void outcsegname(char *csegname) +{ + obj_codeseg(csegname,0); +} +#endif +#endif + +/*********************************** + * Output function thunk. + */ + +#if SCPP + +void outthunk(symbol *sthunk,symbol *sfunc,unsigned p,tym_t thisty, + targ_size_t d,int i,targ_size_t d2) +{ + cod3_thunk(sthunk,sfunc,p,thisty,d,i,d2); + sthunk->Sfunc->Fflags &= ~Fpending; + sthunk->Sfunc->Fflags |= Foutput; /* mark it as having been output */ +} + +#endif + +/*************************** + * Write out statically allocated data. + * Input: + * s symbol to be initialized + */ + +#if TX86 + +void outdata(symbol *s) +{ +#if HTOD + return; +#endif + dt_t *dtstart,*dt; + targ_size_t datasize,a; + int seg; + targ_size_t offset; + int flags; + tym_t ty; + + symbol_debug(s); +#ifdef DEBUG + debugy && dbg_printf("outdata('%s')\n",s->Sident); +#endif + //printf("outdata('%s', ty=x%x)\n",s->Sident,s->Stype->Tty); + //symbol_print(s); + + // Data segment variables are always live on exit from a function + s->Sflags |= SFLlivexit; + + dtstart = s->Sdt; + s->Sdt = NULL; // it will be free'd +#if OMFOBJ + int tls = 0; +#endif +#if SCPP && TARGET_WINDOS + if (eecontext.EEcompile) + { s->Sfl = (s->ty() & mTYfar) ? FLfardata : FLextern; + s->Sseg = UNKNOWN; + goto Lret; // don't output any data + } +#endif + datasize = 0; + ty = s->ty(); + if (ty & mTYexport && config.wflags & WFexpdef && s->Sclass != SCstatic) + obj_export(s,0); // export data definition + for (dt = dtstart; dt; dt = dt->DTnext) + { + //printf("dt = %p, dt = %d\n",dt,dt->dt); + switch (dt->dt) + { case DT_abytes: + { // Put out the data for the string, and + // reserve a spot for a pointer to that string + datasize += size(dt->Dty); // reserve spot for pointer to string +#if ELFOBJ || MACHOBJ + dt->DTabytes += elf_data_cdata(dt->DTpbytes,dt->DTnbytes,&dt->DTseg); +#else + int stringseg; + targ_size_t foffset; + targ_size_t *poffset; +#if TARGET_SEGMENTED + if (tybasic(dt->Dty) == TYcptr) + { stringseg = cseg; + poffset = &Coffset; + } + else if (tybasic(dt->Dty) == TYfptr && + dt->DTnbytes > config.threshold) + { + stringseg = obj_fardata(s->Sident,dt->DTnbytes,&foffset); + poffset = &foffset; + } + else +#endif + { stringseg = DATA; + poffset = &Doffset; + } + dt->DTseg = stringseg; + dt->DTabytes += *poffset; + obj_bytes(stringseg,*poffset,dt->DTnbytes,dt->DTpbytes); + *poffset += dt->DTnbytes; +#endif + break; + } + case DT_ibytes: + datasize += dt->DTn; + break; + case DT_nbytes: + //printf("DT_nbytes %d\n", dt->DTnbytes); + datasize += dt->DTnbytes; + break; + case DT_symsize: +#if MARS + assert(0); +#else + dt->DTazeros = type_size(s->Stype); +#endif + goto case_azeros; + case DT_azeros: + /* A block of zeros + */ + //printf("DT_azeros %d\n", dt->DTazeros); + case_azeros: + datasize += dt->DTazeros; + if (dt == dtstart && !dt->DTnext && s->Sclass != SCcomdat) + { /* first and only, so put in BSS segment + */ + switch (ty & mTYLINK) + { +#if TARGET_SEGMENTED + case mTYfar: // if far data + s->Sseg = obj_fardata(s->Sident,datasize,&s->Soffset); + s->Sfl = FLfardata; + break; + + case mTYcs: + s->Sseg = cseg; + Coffset = align(datasize,Coffset); + s->Soffset = Coffset; + Coffset += datasize; + s->Sfl = FLcsdata; + break; +#endif + case mTYthread: + { seg_data *pseg = obj_tlsseg_bss(); + s->Sseg = pseg->SDseg; +#if ELFOBJ || MACHOBJ + elf_data_start(s, datasize, pseg->SDseg); + obj_lidata(pseg->SDseg, pseg->SDoffset, datasize); +#else + targ_size_t TDoffset = pseg->SDoffset; + TDoffset = align(datasize,TDoffset); + s->Soffset = TDoffset; + TDoffset += datasize; + pseg->SDoffset = TDoffset; + tls = 1; +#endif + s->Sfl = FLtlsdata; + break; + } + default: +#if OMFOBJ + s->Sseg = UDATA; +#endif + elf_data_start(s,datasize,UDATA); + obj_lidata(s->Sseg,s->Soffset,datasize); + s->Sfl = FLudata; // uninitialized data + break; + } +#if ELFOBJ || MACHOBJ + assert(s->Sseg != UNKNOWN); + if (s->Sclass == SCglobal || s->Sclass == SCstatic) // if a pubdef to be done + objpubdefsize(s->Sseg,s,s->Soffset,datasize); // do the definition +#else + if (s->Sclass == SCglobal) /* if a pubdef to be done */ + objpubdef(s->Sseg,s,s->Soffset); /* do the definition */ +#endif + searchfixlist(s); + if (config.fulltypes && + !(s->Sclass == SCstatic && funcsym_p)) // not local static + cv_outsym(s); +#if SCPP + out_extdef(s); +#endif + goto Lret; + } + break; + case DT_common: + assert(!dt->DTnext); + outcommon(s,dt->DTazeros); + goto Lret; + + case DT_xoff: + { symbol *sb = dt->DTsym; + + if (tyfunc(sb->ty())) +#if SCPP + nwc_mustwrite(sb); +#else + ; +#endif + else if (sb->Sdt) // if initializer for symbol + outdata(sb); // write out data for symbol + } + case DT_coff: + datasize += size(dt->Dty); + break; + case DT_1byte: + datasize++; + break; + default: +#ifdef DEBUG + dbg_printf("dt = %p, dt = %d\n",dt,dt->dt); +#endif + assert(0); + } + } + + if (s->Sclass == SCcomdat) // if initialized common block + { +#if ELFOBJ + seg = obj_comdatsize(s, datasize); +#else + seg = obj_comdat(s); +#endif +#if ELFOBJ || OMFOBJ + s->Soffset = 0; +#endif + switch (ty & mTYLINK) + { +#if TARGET_SEGMENTED + case mTYfar: // if far data + s->Sfl = FLfardata; + break; + + case mTYcs: + s->Sfl = FLcsdata; + break; +#endif + case mTYnear: + case 0: + s->Sfl = FLdata; // initialized data + break; + case mTYthread: + s->Sfl = FLtlsdata; +#if OMFOBJ + tls = 1; +#endif + break; + + default: + assert(0); + } + } + else + { + switch (ty & mTYLINK) + { +#if TARGET_SEGMENTED + case mTYfar: // if far data + seg = obj_fardata(s->Sident,datasize,&s->Soffset); + s->Sfl = FLfardata; + break; + + case mTYcs: + assert(OMFOBJ); + seg = cseg; + Coffset = align(datasize,Coffset); + s->Soffset = Coffset; + s->Sfl = FLcsdata; + break; +#endif + case mTYthread: + { seg_data *pseg = obj_tlsseg(); +#if ELFOBJ || MACHOBJ + s->Sseg = pseg->SDseg; + elf_data_start(s, datasize, s->Sseg); +// s->Soffset = pseg->SDoffset; +#else + targ_size_t TDoffset = pseg->SDoffset; + TDoffset = align(datasize,TDoffset); + s->Soffset = TDoffset; + tls = 1; +#endif + seg = pseg->SDseg; + s->Sfl = FLtlsdata; + break; + } + case mTYnear: + case 0: +#if ELFOBJ || MACHOBJ + seg = elf_data_start(s,datasize,DATA); +#else + seg = DATA; + alignOffset(DATA, datasize); + s->Soffset = Doffset; +#endif + s->Sfl = FLdata; // initialized data + break; + default: + assert(0); + } + } +#if ELFOBJ || MACHOBJ + if (s->Sseg == UNKNOWN) + s->Sseg = seg; + else + seg = s->Sseg; + if (s->Sclass == SCglobal || s->Sclass == SCstatic) + { + objpubdefsize(s->Sseg,s,s->Soffset,datasize); // do the definition + } +#else + s->Sseg = seg; + if (s->Sclass == SCglobal) /* if a pubdef to be done */ + objpubdef(seg,s,s->Soffset); /* do the definition */ +#endif + assert(s->Sseg != UNKNOWN); + if (config.fulltypes && + !(s->Sclass == SCstatic && funcsym_p)) // not local static + cv_outsym(s); + searchfixlist(s); + + /* Go back through list, now that we know its size, and send out */ + /* the data. */ + + offset = s->Soffset; + + for (dt = dtstart; dt; dt = dt->DTnext) + { + switch (dt->dt) + { case DT_abytes: + if (tyreg(dt->Dty)) + flags = CFoff; + else + flags = CFoff | CFseg; + if (I64) + flags |= CFoffset64; +#if TARGET_SEGMENTED + if (tybasic(dt->Dty) == TYcptr) + reftocodseg(seg,offset,dt->DTabytes); + else +#endif +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + reftodatseg(seg,offset,dt->DTabytes,dt->DTseg,flags); +#else + /*else*/ if (dt->DTseg == DATA) + reftodatseg(seg,offset,dt->DTabytes,DATA,flags); + else + reftofarseg(seg,offset,dt->DTabytes,dt->DTseg,flags); +#endif + offset += size(dt->Dty); + break; + case DT_ibytes: + obj_bytes(seg,offset,dt->DTn,dt->DTdata); + offset += dt->DTn; + break; + case DT_nbytes: + obj_bytes(seg,offset,dt->DTnbytes,dt->DTpbytes); + offset += dt->DTnbytes; + break; + case DT_azeros: + //printf("obj_lidata(seg = %d, offset = %d, azeros = %d)\n", seg, offset, dt->DTazeros); + if (0 && seg == cseg) + { + obj_lidata(seg,offset,dt->DTazeros); + offset += dt->DTazeros; + } + else + { + SegData[seg]->SDoffset = offset; + obj_lidata(seg,offset,dt->DTazeros); + offset = SegData[seg]->SDoffset; + } + break; + case DT_xoff: + { + symbol *sb = dt->DTsym; // get external symbol pointer + a = dt->DToffset; // offset from it + if (tyreg(dt->Dty)) + flags = CFoff; + else + flags = CFoff | CFseg; + if (I64) + flags |= CFoffset64; + offset += reftoident(seg,offset,sb,a,flags); + break; + } + case DT_coff: + reftocodseg(seg,offset,dt->DToffset); + offset += intsize; + break; + case DT_1byte: + obj_byte(seg,offset++,dt->DTonebyte); + break; + default: +#ifdef DEBUG + dbg_printf("dt = %p, dt = %d\n",dt,dt->dt); +#endif + assert(0); + } + } + Offset(seg) = offset; +#if SCPP + out_extdef(s); +#endif +Lret: + dt_free(dtstart); +} + + + +/****************************** + * Output n bytes of a common block, n > 0. + */ + +void outcommon(symbol *s,targ_size_t n) +{ + //printf("outcommon('%s',%d)\n",s->Sident,n); + if (n != 0) + { + assert(s->Sclass == SCglobal); +#if TARGET_SEGMENTED + if (s->ty() & mTYcs) // if store in code segment + { + /* COMDEFs not supported in code segment + * so put them out as initialized 0s + */ + dtnzeros(&s->Sdt,n); + outdata(s); +#if SCPP + out_extdef(s); +#endif + } + else +#endif + if (s->ty() & mTYthread) // if store in thread local segment + { +#if ELFOBJ + s->Sclass = SCcomdef; + obj_comdef(s, 0, n, 1); +#else + /* COMDEFs not supported in tls segment + * so put them out as COMDATs with initialized 0s + */ + s->Sclass = SCcomdat; + dtnzeros(&s->Sdt,n); + outdata(s); +#if SCPP && OMFOBJ + out_extdef(s); +#endif +#endif + } + else + { +#if ELFOBJ || MACHOBJ + s->Sclass = SCcomdef; + obj_comdef(s, 0, n, 1); +#else + s->Sclass = SCcomdef; +#if TARGET_SEGMENTED + s->Sxtrnnum = obj_comdef(s,(s->ty() & mTYfar) == 0,n,1); +#else + s->Sxtrnnum = obj_comdef(s,true,n,1); +#endif + s->Sseg = UNKNOWN; +#if TARGET_SEGMENTED + if (s->ty() & mTYfar) + s->Sfl = FLfardata; + else +#endif + s->Sfl = FLextern; + pstate.STflags |= PFLcomdef; +#if SCPP + ph_comdef(s); // notify PH that a COMDEF went out +#endif +#endif + } + if (config.fulltypes) + cv_outsym(s); + } +} +#endif // TX86 + +/****************************** + * Walk expression tree, converting it from a PARSER tree to + * a code generator tree. + */ + +STATIC void outelem(elem *e) +{ + symbol *s; + tym_t tym; + elem *e1; +#if SCPP + type *t; +#endif + +again: + assert(e); + elem_debug(e); + +#ifdef DEBUG + if (EBIN(e)) + assert(e->E1 && e->E2); +// else if (EUNA(e)) +// assert(e->E1 && !e->E2); +#endif + +#if SCPP + t = e->ET; + assert(t); + type_debug(t); + tym = t->Tty; + switch (tybasic(tym)) + { case TYstruct: + t->Tcount++; + break; + + case TYarray: + t->Tcount++; + break; + + case TYbool: + case TYwchar_t: + case TYchar16: + case TYmemptr: + case TYvtshape: + case TYnullptr: + tym = tym_conv(t); + e->ET = NULL; + break; + + case TYenum: + tym = tym_conv(t->Tnext); + e->ET = NULL; + break; + + default: + e->ET = NULL; + break; + } + e->Nflags = 0; + e->Ety = tym; +#endif + + switch (e->Eoper) + { + default: + Lop: +#if DEBUG + //if (!EOP(e)) dbg_printf("e->Eoper = x%x\n",e->Eoper); +#endif + if (EBIN(e)) + { outelem(e->E1); + e = e->E2; + } + else if (EUNA(e)) + { + e = e->E1; + } + else + break; +#if SCPP + type_free(t); +#endif + goto again; /* iterate instead of recurse */ + case OPaddr: + e1 = e->E1; + if (e1->Eoper == OPvar) + { // Fold into an OPrelconst +#if SCPP + el_copy(e,e1); + e->ET = t; +#else + tym = e->Ety; + el_copy(e,e1); + e->Ety = tym; +#endif + e->Eoper = OPrelconst; + el_free(e1); + goto again; + } + goto Lop; + + case OPrelconst: + case OPvar: + L6: + s = e->EV.sp.Vsym; + assert(s); + symbol_debug(s); + switch (s->Sclass) + { + case SCregpar: + case SCparameter: + if (e->Eoper == OPrelconst) + addrparam = TRUE; // taking addr of param list + break; + + case SCstatic: + case SClocstat: + case SCextern: + case SCglobal: + case SCcomdat: + case SCcomdef: +#if PSEUDO_REGS + case SCpseudo: +#endif + case SCinline: + case SCsinline: + case SCeinline: + s->Sflags |= SFLlivexit; + /* FALL-THROUGH */ + case SCauto: + case SCregister: + case SCfastpar: + case SCbprel: + case SCtmp: + if (e->Eoper == OPrelconst) + { + s->Sflags &= ~(SFLunambig | GTregcand); + } +#if SCPP && TX86 && OMFOBJ + else if (s->ty() & mTYfar) + e->Ety |= mTYfar; +#endif + break; +#if SCPP + case SCmember: + err_noinstance(s->Sscope,s); + goto L5; + case SCstruct: + cpperr(EM_no_instance,s->Sident); // no instance of class + L5: + e->Eoper = OPconst; + e->Ety = TYint; + return; + + case SCfuncalias: + e->EV.sp.Vsym = s->Sfunc->Falias; + goto L6; + case SCstack: + break; + case SCfunctempl: + cpperr(EM_no_template_instance, s->Sident); + break; + default: +#ifdef DEBUG + symbol_print(s); + WRclass((enum SC) s->Sclass); +#endif + assert(0); +#endif + } +#if SCPP + if (tyfunc(s->ty())) + { +#if SCPP + nwc_mustwrite(s); /* must write out function */ +#else + ; +#endif + } + else if (s->Sdt) /* if initializer for symbol */ + outdata(s); // write out data for symbol +#if ELFOBJ || MACHOBJ + if (config.flags3 & CFG3pic) + { + elfobj_gotref(s); + } +#endif +#endif + break; + case OPstring: + case OPconst: + case OPstrthis: + break; + case OPsizeof: +#if SCPP + e->Eoper = OPconst; + e->EV.Vlong = type_size(e->EV.sp.Vsym->Stype); +#else + assert(0); +#endif + break; + +#if SCPP + case OPstreq: + case OPstrpar: + case OPstrctor: + type_size(e->E1->ET); + goto Lop; + + case OPasm: + break; + + case OPctor: + nwc_mustwrite(e->EV.eop.Edtor); + case OPdtor: + // Don't put 'this' pointers in registers if we need + // them for EH stack cleanup. + e1 = e->E1; + elem_debug(e1); + if (e1->Eoper == OPadd) + e1 = e1->E1; + if (e1->Eoper == OPvar) + e1->EV.sp.Vsym->Sflags &= ~GTregcand; + goto Lop; + case OPmark: + break; +#endif + } +#if SCPP + type_free(t); +#endif +} + +/************************************* + * Determine register candidates. + */ + +STATIC void out_regcand_walk(elem *e); + +void out_regcand(symtab_t *psymtab) +{ + block *b; + SYMIDX si; + int ifunc; + + //printf("out_regcand()\n"); + ifunc = (tybasic(funcsym_p->ty()) == TYifunc); + for (si = 0; si < psymtab->top; si++) + { symbol *s = psymtab->tab[si]; + + symbol_debug(s); + //assert(sytab[s->Sclass] & SCSS); // only stack variables + s->Ssymnum = si; // Ssymnum trashed by cpp_inlineexpand + if (!(s->ty() & mTYvolatile) && +#if TX86 + !(ifunc && (s->Sclass == SCparameter || s->Sclass == SCregpar)) && +#endif + s->Sclass != SCstatic) + s->Sflags |= (GTregcand | SFLunambig); // assume register candidate + else + s->Sflags &= ~(GTregcand | SFLunambig); + } + + addrparam = FALSE; // haven't taken addr of param yet + for (b = startblock; b; b = b->Bnext) + { + if (b->Belem) + out_regcand_walk(b->Belem); + + // Any assembler blocks make everything ambiguous + if (b->BC == BCasm) + for (si = 0; si < psymtab->top; si++) + psymtab->tab[si]->Sflags &= ~(SFLunambig | GTregcand); + } + + // If we took the address of one parameter, assume we took the + // address of all non-register parameters. + if (addrparam) // if took address of a parameter + { + for (si = 0; si < psymtab->top; si++) + if (psymtab->tab[si]->Sclass == SCparameter) + psymtab->tab[si]->Sflags &= ~(SFLunambig | GTregcand); + } + +} + +STATIC void out_regcand_walk(elem *e) +{ symbol *s; + + while (1) + { elem_debug(e); + + if (EBIN(e)) + { if (e->Eoper == OPstreq) + { if (e->E1->Eoper == OPvar) + { s = e->E1->EV.sp.Vsym; + s->Sflags &= ~(SFLunambig | GTregcand); + } + if (e->E2->Eoper == OPvar) + { s = e->E2->EV.sp.Vsym; + s->Sflags &= ~(SFLunambig | GTregcand); + } + } + out_regcand_walk(e->E1); + e = e->E2; + } + else if (EUNA(e)) + { + // Don't put 'this' pointers in registers if we need + // them for EH stack cleanup. + if (e->Eoper == OPctor) + { elem *e1 = e->E1; + + if (e1->Eoper == OPadd) + e1 = e1->E1; + if (e1->Eoper == OPvar) + e1->EV.sp.Vsym->Sflags &= ~GTregcand; + } + e = e->E1; + } + else + { if (e->Eoper == OPrelconst) + { + s = e->EV.sp.Vsym; + assert(s); + symbol_debug(s); + switch (s->Sclass) + { + case SCregpar: + case SCparameter: + addrparam = TRUE; // taking addr of param list + break; + case SCauto: + case SCregister: + case SCtmp: + case SCfastpar: + case SCbprel: + s->Sflags &= ~(SFLunambig | GTregcand); + break; + } + } + else if (e->Eoper == OPvar) + { + if (e->EV.sp.Voffset) + { if (!(e->EV.sp.Voffset == 1 && tybyte(e->Ety))) + e->EV.sp.Vsym->Sflags &= ~GTregcand; + } + } + break; + } + } +} + +/************************** + * Optimize function, + * generate code for it, + * and write it out. + */ + +STATIC void writefunc2(symbol *sfunc); + +void writefunc(symbol *sfunc) +{ +#if HTOD + return; +#elif SCPP + writefunc2(sfunc); +#else + cstate.CSpsymtab = &globsym; + writefunc2(sfunc); + cstate.CSpsymtab = NULL; +#endif +} + +STATIC void writefunc2(symbol *sfunc) +{ + block *b; + unsigned nsymbols; + SYMIDX si; + int anyasm; +#if OMFOBJ + int csegsave; + targ_size_t coffsetsave; +#endif + func_t *f = sfunc->Sfunc; + tym_t tyf; + + //printf("writefunc(%s)\n",sfunc->Sident); + debug(debugy && dbg_printf("writefunc(%s)\n",sfunc->Sident)); +#if SCPP + if (CPP) + { + + // If constructor or destructor, make sure it has been fixed. + if (f->Fflags & (Fctor | Fdtor)) + assert(errcnt || f->Fflags & Ffixed); + + // If this function is the 'trigger' to output the vtbl[], do so + if (f->Fflags3 & Fvtblgen && !eecontext.EEcompile) + { Classsym *stag; + + stag = (Classsym *) sfunc->Sscope; + { + enum SC scvtbl; + + scvtbl = (enum SC) ((config.flags2 & CFG2comdat) ? SCcomdat : SCglobal); + n2_genvtbl(stag,scvtbl,1); +#if VBTABLES + n2_genvbtbl(stag,scvtbl,1); +#endif +#if TX86 && OMFOBJ + if (config.fulltypes == CV4) + cv4_struct(stag,2); +#endif + } + } + } +#endif + + /* Signify that function has been output */ + /* (before inline_do() to prevent infinite recursion!) */ + f->Fflags &= ~Fpending; + f->Fflags |= Foutput; + + if ( +#if SCPP + errcnt || +#endif + (eecontext.EEcompile && eecontext.EEfunc != sfunc)) + return; + + /* Copy local symbol table onto main one, making sure */ + /* that the symbol numbers are adjusted accordingly */ + //dbg_printf("f->Flocsym.top = %d\n",f->Flocsym.top); + nsymbols = f->Flocsym.top; + if (nsymbols > globsym.symmax) + { /* Reallocate globsym.tab[] */ + globsym.symmax = nsymbols; + globsym.tab = symtab_realloc(globsym.tab, globsym.symmax); + } + debug(debugy && dbg_printf("appending symbols to symtab...\n")); + assert(globsym.top == 0); + memcpy(&globsym.tab[0],&f->Flocsym.tab[0],nsymbols * sizeof(symbol *)); + globsym.top = nsymbols; + + assert(startblock == NULL); + if (f->Fflags & Finline) // if keep function around + { // Generate copy of function + block *bf; + block **pb; + + pb = &startblock; + for (bf = f->Fstartblock; bf; bf = bf->Bnext) + { + b = block_calloc(); + *pb = b; + pb = &b->Bnext; + + *b = *bf; + assert(!b->Bsucc); + assert(!b->Bpred); + b->Belem = el_copytree(b->Belem); + } + } + else + { startblock = sfunc->Sfunc->Fstartblock; + sfunc->Sfunc->Fstartblock = NULL; + } + assert(startblock); + + /* Do any in-line expansion of function calls inside sfunc */ +#if SCPP + inline_do(sfunc); +#endif + +#if SCPP + /* If function is _STIxxxx, add in the auto destructors */ +#if NEWSTATICDTOR + if (cpp_stidtors && memcmp("__SI",sfunc->Sident,4) == 0) +#else + if (cpp_stidtors && memcmp("_STI",sfunc->Sident,4) == 0) +#endif + { list_t el; + + assert(startblock->Bnext == NULL); + el = cpp_stidtors; + do + { + startblock->Belem = el_combine(startblock->Belem,list_elem(el)); + el = list_next(el); + } while (el); + list_free(&cpp_stidtors,FPNULL); + } +#endif + assert(funcsym_p == NULL); + funcsym_p = sfunc; + tyf = tybasic(sfunc->ty()); + +#if SCPP + out_extdef(sfunc); +#endif + + // TX86 computes parameter offsets in stackoffsets() + //printf("globsym.top = %d\n", globsym.top); + for (si = 0; si < globsym.top; si++) + { symbol *s = globsym.tab[si]; + + symbol_debug(s); + //printf("symbol %d '%s'\n",si,s->Sident); + + type_size(s->Stype); // do any forward template instantiations + + s->Ssymnum = si; // Ssymnum trashed by cpp_inlineexpand + s->Sflags &= ~(SFLunambig | GTregcand); + switch (s->Sclass) + { +#if SCPP + case SCfastpar: + Lfp: + s->Spreg = (tyf == TYmfunc) ? CX : AX; + case SCauto: + case SCregister: + s->Sfl = FLauto; + goto L3; + case SCtmp: + s->Sfl = FLtmp; + goto L3; + case SCbprel: + s->Sfl = FLbprel; + goto L3; + case SCregpar: + case SCparameter: + if (tyf == TYjfunc && si == 0 && + type_jparam(s->Stype)) + { s->Sclass = SCfastpar; // put last parameter into register + goto Lfp; + } +#else + case SCfastpar: + case SCauto: + case SCregister: + s->Sfl = FLauto; + goto L3; + case SCtmp: + s->Sfl = FLtmp; + goto L3; + case SCbprel: + s->Sfl = FLbprel; + goto L3; + case SCregpar: + case SCparameter: +#endif + s->Sfl = FLpara; + if (tyf == TYifunc) + { s->Sflags |= SFLlivexit; + break; + } + L3: + if (!(s->ty() & mTYvolatile)) + s->Sflags |= GTregcand | SFLunambig; // assume register candidate */ + break; +#if PSEUDO_REGS + case SCpseudo: + s->Sfl = FLpseudo; + break; +#endif + case SCstatic: + break; // already taken care of by datadef() + case SCstack: + s->Sfl = FLstack; + break; + default: +#ifdef DEBUG + symbol_print(s); +#endif + assert(0); + } + } + + addrparam = FALSE; // haven't taken addr of param yet + anyasm = 0; + numblks = 0; + for (b = startblock; b; b = b->Bnext) + { + numblks++; // redo count + memset(&b->_BLU,0,sizeof(b->_BLU)); + if (b->Belem) + { outelem(b->Belem); +#if SCPP + if (el_noreturn(b->Belem) && !(config.flags3 & CFG3eh)) + { b->BC = BCexit; + list_free(&b->Bsucc,FPNULL); + } +#endif +#if MARS + if (b->Belem->Eoper == OPhalt) + { b->BC = BCexit; + list_free(&b->Bsucc,FPNULL); + } +#endif + } + if (b->BC == BCasm) + anyasm = 1; + if (sfunc->Sflags & SFLexit && (b->BC == BCret || b->BC == BCretexp)) + { b->BC = BCexit; + list_free(&b->Bsucc,FPNULL); + } + assert(b != b->Bnext); + } + PARSER = 0; + if (eecontext.EEelem) + { unsigned marksi = globsym.top; + + eecontext.EEin++; + outelem(eecontext.EEelem); + eecontext.EEelem = doptelem(eecontext.EEelem,TRUE); + eecontext.EEin--; + eecontext_convs(marksi); + } + maxblks = 3 * numblks; // allow for increase in # of blocks + // If we took the address of one parameter, assume we took the + // address of all non-register parameters. + if (addrparam | anyasm) // if took address of a parameter + { + for (si = 0; si < globsym.top; si++) + if (anyasm || globsym.tab[si]->Sclass == SCparameter) + globsym.tab[si]->Sflags &= ~(SFLunambig | GTregcand); + } + + block_pred(); // compute predecessors to blocks + block_compbcount(); // eliminate unreachable blocks + if (mfoptim) + { OPTIMIZER = 1; + optfunc(); /* optimize function */ + assert(dfo); + OPTIMIZER = 0; + } + else + { + //dbg_printf("blockopt()\n"); + blockopt(0); /* optimize */ + } + +#if SCPP + if (CPP) + { + // Look for any blocks that return nothing. + // Do it after optimization to eliminate any spurious + // messages like the implicit return on { while(1) { ... } } + if (tybasic(funcsym_p->Stype->Tnext->Tty) != TYvoid && + !(funcsym_p->Sfunc->Fflags & (Fctor | Fdtor | Finvariant)) +#if DEBUG_XSYMGEN + /* the internal dataview function is allowed to lie about its return value */ + && compile_state != kDataView +#endif + ) + { char err; + + err = 0; + for (b = startblock; b; b = b->Bnext) + { if (b->BC == BCasm) // no errors if any asm blocks + err |= 2; + else if (b->BC == BCret) + err |= 1; + } + if (err == 1) + func_noreturnvalue(); + } + } +#endif + assert(funcsym_p == sfunc); + if (eecontext.EEcompile != 1) + { +#if TX86 + if (symbol_iscomdat(sfunc)) + { +#if OMFOBJ + csegsave = cseg; + coffsetsave = Coffset; +#endif + obj_comdat(sfunc); + } + else + if (config.flags & CFGsegs) // if user set switch for this + { +#if SCPP || TARGET_WINDOS + obj_codeseg(cpp_mangle(funcsym_p),1); +#else + obj_codeseg(funcsym_p->Sident, 1); +#endif + // generate new code segment + } + cod3_align(); // align start of function +#if ELFOBJ || MACHOBJ + elf_func_start(sfunc); +#else + sfunc->Sseg = cseg; // current code seg +#endif +#endif + sfunc->Soffset = Coffset; // offset of start of function + searchfixlist(sfunc); // backpatch any refs to this function + } + + //dbg_printf("codgen()\n"); +#if SCPP + if (!errcnt) +#endif + codgen(); // generate code + //dbg_printf("after codgen for %s Coffset %x\n",sfunc->Sident,Coffset); + blocklist_free(&startblock); +#if SCPP + PARSER = 1; +#endif +#if ELFOBJ || MACHOBJ + elf_func_term(sfunc); +#endif +#if MARS + /* This is to make uplevel references to SCfastpar variables + * from nested functions work. + */ + for (si = 0; si < globsym.top; si++) + { + Symbol *s = globsym.tab[si]; + + switch (s->Sclass) + { case SCfastpar: + s->Sclass = SCauto; + break; + } + } +#endif + if (eecontext.EEcompile == 1) + goto Ldone; + if (sfunc->Sclass == SCglobal) + { +#if OMFOBJ + if (!(config.flags4 & CFG4allcomdat)) + objpubdef(cseg,sfunc,sfunc->Soffset); // make a public definition +#endif + +#if SCPP && _WIN32 + char *id; + // Determine which startup code to reference + if (!CPP || !isclassmember(sfunc)) // if not member function + { static char *startup[] = + { "__acrtused","__acrtused_winc","__acrtused_dll", + "__acrtused_con","__wacrtused","__wacrtused_con", + }; + int i; + + id = sfunc->Sident; + switch (id[0]) + { + case 'D': if (strcmp(id,"DllMain")) + break; + if (config.exe == EX_NT) + { i = 2; + goto L2; + } + break; + + case 'm': if (strcmp(id,"main")) + break; + if (config.exe == EX_NT) + i = 3; + else if (config.wflags & WFwindows) + i = 1; + else + i = 0; + goto L2; + + case 'w': if (strcmp(id,"wmain") == 0) + { + if (config.exe == EX_NT) + { i = 5; + goto L2; + } + break; + } + case 'W': if (stricmp(id,"WinMain") == 0) + { + i = 0; + goto L2; + } + if (stricmp(id,"wWinMain") == 0) + { + if (config.exe == EX_NT) + { i = 4; + goto L2; + } + } + break; + + case 'L': + case 'l': if (stricmp(id,"LibMain")) + break; + if (config.exe != EX_NT && config.wflags & WFwindows) + { i = 2; + goto L2; + } + break; + + L2: objextdef(startup[i]); // pull in startup code + break; + } + } +#endif + } + if (config.wflags & WFexpdef && + sfunc->Sclass != SCstatic && + sfunc->Sclass != SCsinline && + !(sfunc->Sclass == SCinline && !(config.flags2 & CFG2comdat)) && + sfunc->ty() & mTYexport) + obj_export(sfunc,Poffset); // export function definition + + if (config.fulltypes) + cv_func(sfunc); // debug info for function + +#if MARS + /* After codgen() and writing debug info for the locals, + * readjust the offsets of all stack variables so they + * are relative to the frame pointer. + * Necessary for nested function access to lexically enclosing frames. + */ + cod3_adjSymOffsets(); +#endif + +#if OMFOBJ + if (symbol_iscomdat(sfunc)) // if generated a COMDAT + obj_setcodeseg(csegsave,coffsetsave); // reset to real code seg +#endif + + /* Check if function is a constructor or destructor, by */ + /* seeing if the function name starts with _STI or _STD */ + { +#if _M_I86 + short *p; + + p = (short *) sfunc->Sident; + if (p[0] == 'S_' && (p[1] == 'IT' || p[1] == 'DT')) +#else + char *p; + + p = sfunc->Sident; + if (p[0] == '_' && p[1] == 'S' && p[2] == 'T' && + (p[3] == 'I' || p[3] == 'D')) +#endif + obj_funcptr(sfunc); + } + +Ldone: + funcsym_p = NULL; + +#if SCPP + // Free any added symbols + freesymtab(globsym.tab,nsymbols,globsym.top); +#endif + globsym.top = 0; + + //dbg_printf("done with writefunc()\n"); +#if TX86 + util_free(dfo); +#else + MEM_PARF_FREE(dfo); +#endif + dfo = NULL; +} + +/************************* + * Align segment offset. + * Input: + * seg segment to be aligned + * datasize size in bytes of object to be aligned + */ + +void alignOffset(int seg,targ_size_t datasize) +{ + targ_size_t alignbytes; + + alignbytes = align(datasize,Offset(seg)) - Offset(seg); + //dbg_printf("seg %d datasize = x%x, Offset(seg) = x%x, alignbytes = x%x\n", + //seg,datasize,Offset(seg),alignbytes); + if (alignbytes) + obj_lidata(seg,Offset(seg),alignbytes); +#if OMFOBJ + //Offset(seg) += alignbytes; /* offset of start of data */ +#endif +} + + +/*************************************** + * Write data into read-only data segment. + * Return symbol for it. + */ + +#define ROMAX 32 +struct Readonly +{ + symbol *sym; + size_t length; + unsigned char p[ROMAX]; +}; + +#define RMAX 16 +static Readonly readonly[RMAX]; +static size_t readonly_length; +static size_t readonly_i; + +void out_reset() +{ + readonly_length = 0; + readonly_i = 0; +} + +symbol *out_readonly_sym(tym_t ty, void *p, int len) +{ +#if 0 + printf("out_readonly_sym(ty = x%x)\n", ty); + for (int i = 0; i < len; i++) + printf(" [%d] = %02x\n", i, ((unsigned char*)p)[i]); +#endif + // Look for previous symbol we can reuse + for (int i = 0; i < readonly_length; i++) + { + Readonly *r = &readonly[i]; + if (r->length == len && memcmp(p, r->p, len) == 0) + return r->sym; + } + + symbol *s; + +#if ELFOBJ + /* MACHOBJ can't go here, because the const data segment goes into + * the _TEXT segment, and one cannot have a fixup from _TEXT to _TEXT. + */ + s = elf_sym_cdata(ty, (char *)p, len); +#else + unsigned sz = tysize(ty); + + alignOffset(DATA, sz); + s = symboldata(Doffset,ty | mTYconst); + obj_write_bytes(SegData[DATA], len, p); + //printf("s->Sseg = %d:x%x\n", s->Sseg, s->Soffset); +#endif + if (len <= ROMAX) + { Readonly *r; + + if (readonly_length < RMAX) + { + r = &readonly[readonly_length]; + readonly_length++; + } + else + { r = &readonly[readonly_i]; + readonly_i++; + if (readonly_i >= RMAX) + readonly_i = 0; + } + r->length = len; + r->sym = s; + memcpy(r->p, p, len); + } + return s; +} + +void Srcpos::print(const char *func) +{ + printf("%s(", func); +#if MARS + printf("Sfilename = %s", Sfilename ? Sfilename : "null"); +#else + Sfile *sf = Sfilptr ? *Sfilptr : NULL; + printf("Sfilptr = %p (filename = %s)", sf, sf ? sf->SFname : "null"); +#endif + printf(", Slinnum = %u", Slinnum); +#if SOURCE_OFFSETS + printf(", Sfiloff = %d", Sfiloff); +#endif + printf(")\n"); +} + + +#endif /* !SPP */ + diff --git a/backend/outbuf.c b/backend/outbuf.c new file mode 100644 index 00000000..a11e259e --- /dev/null +++ b/backend/outbuf.c @@ -0,0 +1,298 @@ +// Copyright (C) 1994-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +// Output buffer + +#include +#include +#include +#include + +#include "cc.h" + +#include "outbuf.h" +#include "mem.h" + +#if DEBUG +static char __file__[] = __FILE__; // for tassert.h +#include "tassert.h" +#endif + +Outbuffer::Outbuffer() +{ + buf = NULL; + pend = NULL; + p = NULL; + len = 0; + inc = 0; +} + +Outbuffer::Outbuffer(unsigned bufinc) +{ + buf = NULL; + pend = NULL; + p = NULL; + len = 0; + inc = bufinc; +} + +Outbuffer::~Outbuffer() +{ +#if MEM_DEBUG + mem_free(buf); +#else + if (buf) + free(buf); +#endif +} + +void Outbuffer::reset() +{ + p = buf; +} + +// Reserve nbytes in buffer +void Outbuffer::reserve(unsigned nbytes) +{ + if (pend - p < nbytes) + { unsigned oldlen = len; + unsigned used = p - buf; + + if (inc > nbytes) + { + len = used + inc; + } + else + { + len = used + nbytes; + if (len < 2 * oldlen) + { len = 2 * oldlen; + if (len < 8) + len = 8; + } + } +#if MEM_DEBUG + buf = (unsigned char *)mem_realloc(buf, len); +#else + if (buf) + buf = (unsigned char *) realloc(buf,len); + else + buf = (unsigned char *) malloc(len); +#endif + if (!buf) + { + fprintf(stderr, "Fatal Error: Out of memory"); + exit(EXIT_FAILURE); + } + + pend = buf + len; + p = buf + used; + } +} + +// Position buffer for output at a specified location and size. +// If data will extend buffer size, reserve space +// If data will rewrite existing data +// position for write and return previous buffer size +// +// If data will append to buffer +// position for write and return new size +int Outbuffer::position(unsigned pos, unsigned nbytes) +{ + int current_sz = size(); + unsigned char *fend = buf+pos+nbytes; // future end of buffer + if (fend >= pend) + { + reserve (fend - pend); + } + setsize(pos); + return pos+nbytes > current_sz ? pos+nbytes : current_sz; +} + +// Write an array to the buffer. +void Outbuffer::write(const void *b, int len) +{ + if (pend - p < len) + reserve(len); + memcpy(p,b,len); + p += len; +} + +// Write n zeros to the buffer. +void *Outbuffer::writezeros(unsigned len) +{ + if (pend - p < len) + reserve(len); + void *pstart = memset(p,0,len); + p += len; + return pstart; +} + +/** + * Writes an 8 bit byte. + */ +void Outbuffer::writeByte(int v) +{ + if (pend == p) + reserve(1); + *p++ = v; +} + +/** + * Writes a 32 bit int. + */ +void Outbuffer::write32(int v) +{ + if (pend - p < 4) + reserve(4); + *(int *)p = v; + p += 4; +} + +/** + * Writes a 64 bit long. + */ + +#if __INTSIZE == 4 +void Outbuffer::write64(long long v) +{ + if (pend - p < 8) + reserve(8); + *(long long *)p = v; + p += 8; +} +#endif + +/** + * Writes a 32 bit float. + */ +void Outbuffer::writeFloat(float v) +{ + if (pend - p < sizeof(float)) + reserve(sizeof(float)); + *(float *)p = v; + p += sizeof(float); +} + +/** + * Writes a 64 bit double. + */ +void Outbuffer::writeDouble(double v) +{ + if (pend - p < sizeof(double)) + reserve(sizeof(double)); + *(double *)p = v; + p += sizeof(double); +} + +/** + * Writes a String as a sequence of bytes. + */ +void Outbuffer::write(const char *s) +{ + write(s,strlen(s)); +} + + +/** + * Writes a String as a sequence of bytes. + */ +void Outbuffer::write(const unsigned char *s) +{ + write(s,strlen((const char *)s)); +} + + +/** + * Writes a NULL terminated String + */ +void Outbuffer::writeString(const char *s) +{ + write(s,strlen(s)+1); +} + +/** + * Inserts string at beginning of buffer. + */ + +void Outbuffer::prependBytes(const char *s) +{ + size_t len = strlen(s); + + reserve(len); + memmove(buf + len,buf,p - buf); + memcpy(buf,s,len); + p += len; +} + +/** + */ + +void Outbuffer::bracket(char c1,char c2) +{ + reserve(2); + memmove(buf + 1,buf,p - buf); + buf[0] = c1; + p[1] = c2; + p += 2; +} + +/** + * Convert to a string. + */ + +char *Outbuffer::toString() +{ + if (pend == p) + reserve(1); + *p = 0; // terminate string + return (char *)buf; +} + +/** + * Set current size of buffer. + */ + +void Outbuffer::setsize(unsigned size) +{ + p = buf + size; +} + +void Outbuffer::writesLEB128(int value) +{ + while (1) + { + unsigned char b = value & 0x7F; + + value >>= 7; // arithmetic right shift + if (value == 0 && !(b & 0x40) || + value == -1 && (b & 0x40)) + { + writeByte(b); + break; + } + writeByte(b | 0x80); + } +} + +void Outbuffer::writeuLEB128(unsigned value) +{ + do + { unsigned char b = value & 0x7F; + + value >>= 7; + if (value) + b |= 0x80; + writeByte(b); + } while (value); +} + diff --git a/backend/outbuf.h b/backend/outbuf.h new file mode 100644 index 00000000..ebf2d16b --- /dev/null +++ b/backend/outbuf.h @@ -0,0 +1,179 @@ +// Copyright (C) 1994-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +//#pragma once + +#include + +// Output buffer + +// (This used to be called OutBuffer, we renamed it to avoid name conflicts with Mars.) + +struct Outbuffer +{ + unsigned char *buf; // the buffer itself + unsigned char *pend; // pointer past the end of the buffer + unsigned char *p; // current position in buffer + unsigned len; // size of buffer + unsigned inc; // default increment size + + Outbuffer(); + Outbuffer(unsigned inc); + ~Outbuffer(); + void reset(); + + // Reserve nbytes in buffer + void reserve(unsigned nbytes); + + // Write n zeros; return pointer to start of zeros + void *writezeros(unsigned n); + + // Position buffer to accept the specified number of bytes at offset + int position(unsigned offset, unsigned nbytes); + + // Write an array to the buffer, no reserve check + void writen(const void *b, int len) + { + memcpy(p,b,len); + p += len; + } + + // Clear bytes, no reserve check + void clearn(int len) + { + int i; + for (i=0; i< len; i++) + *p++ = 0; + } + + // Write an array to the buffer. + void write(const void *b, int len); + + void write(Outbuffer *b) { write(b->buf,b->p - b->buf); } + + /** + * Flushes the stream. This will write any buffered + * output bytes. + */ + void flush() { } + + /** + * Writes an 8 bit byte, no reserve check. + */ + void writeByten(char v) + { + *p++ = v; + } + + /** + * Writes an 8 bit byte. + */ + void writeByte(int v); + + /** + * Writes a 16 bit little-end short, no reserve check. + */ + void writeWordn(int v) + { +#if _WIN32 + *(unsigned short *)p = v; +#else + p[0] = v; + p[1] = v >> 8; +#endif + p += 2; + } + + + /** + * Writes a 16 bit little-end short. + */ + void writeWord(int v) + { + reserve(2); + writeWordn(v); + } + + + /** + * Writes a 16 bit big-end short. + */ + void writeShort(int v) + { + if (pend - p < 2) + reserve(2); +#if 0 + p[0] = ((unsigned char *)&v)[1]; + p[1] = v; +#else + unsigned char *q = p; + q[0] = v >> 8; + q[1] = v; +#endif + p += 2; + } + + /** + * Writes a 16 bit char. + */ + void writeChar(int v) + { + writeShort(v); + } + + /** + * Writes a 32 bit int. + */ + void write32(int v); + + /** + * Writes a 64 bit long. + */ +#if __INTSIZE == 4 + void write64(long long v); +#endif + + /** + * Writes a 32 bit float. + */ + void writeFloat(float v); + + /** + * Writes a 64 bit double. + */ + void writeDouble(double v); + + void write(const char *s); + + void write(const unsigned char *s); + + void writeString(const char *s); + + void prependBytes(const char *s); + + void bracket(char c1,char c2); + + /** + * Returns the number of bytes written. + */ + int size() + { + return p - buf; + } + + char *toString(); + void setsize(unsigned size); + + void writesLEB128(int value); + void writeuLEB128(unsigned value); + +}; diff --git a/backend/ptrntab.c b/backend/ptrntab.c new file mode 100644 index 00000000..99b6c9df --- /dev/null +++ b/backend/ptrntab.c @@ -0,0 +1,5736 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2012 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !DEMO && !SPP + +#include +#include +#include +#include + +#include "cc.h" +#include "code.h" +#include "iasm.h" +#include "global.h" +#include "xmm.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +// +// NOTE: For 0 operand instructions, the opcode is taken from +// the first entry and no subsequent entries are required. +// for instructions with operands, a NULL entry is required at the end +// as a terminator +// +// 0 Operand instructions +// + +#define OPTABLE0(str,op,mod) PTRNTAB0 aptb0##str[] = { { op, mod }, }; + +OPTABLE0(AAA, 0x37 ,_i64_bit | _modax); +OPTABLE0(AAD, 0xd50a,_i64_bit | _modax); +OPTABLE0(AAM, 0xd40a,_i64_bit | _modax); +OPTABLE0(AAS, 0x3f, _i64_bit | _modax); +OPTABLE0(CBW, 0x98,_16_bit | _modax); +OPTABLE0(CWDE, 0x98,_32_bit | _I386 | _modax); +OPTABLE0(CDQE, 0x98,_64_bit | _modax); +OPTABLE0(CLC, 0xf8,0); +OPTABLE0(CLD, 0xfc,0); +OPTABLE0(CLI, 0xfa,0); +OPTABLE0(CLTS, 0x0f06,0); +OPTABLE0(CMC, 0xf5,0); +OPTABLE0(CMPSB, 0xa6,_modsidi); +OPTABLE0(CMPSW, 0xa7,_16_bit | _modsidi); +//OPTABLE0(CMPSD, 0xa7,_32_bit | _I386 | _modsidi); +OPTABLE0(CMPSQ, 0xa7,_64_bit | _modsidi); +OPTABLE0(CWD, 0x99, _16_bit | _modaxdx); +OPTABLE0(CDQ, 0x99,_32_bit | _I386 | _modaxdx); +OPTABLE0(CQO, 0x99, _64_bit | _modaxdx); +OPTABLE0(DAA, 0x27,_i64_bit | _modax ); +OPTABLE0(DAS, 0x2f,_i64_bit | _modax ); +OPTABLE0(HLT, 0xf4,0); +OPTABLE0(INSB, 0x6c,_I386 | _modsi); +OPTABLE0(INSW, 0x6d,_16_bit | _I386 | _modsi); +OPTABLE0(INSD, 0x6d,_32_bit | _I386 | _modsi); +OPTABLE0(INTO, 0xce,_i64_bit); +OPTABLE0(INVD, 0x0f08,_I386); // Actually a 486 only instruction +OPTABLE0(IRET, 0xcf,_16_bit); +OPTABLE0(IRETD, 0xcf,_32_bit | _I386); +OPTABLE0(LAHF, 0x9f,_modax); +OPTABLE0(LEAVE, 0xc9,_I386); +OPTABLE0(LOCK, 0xf0,0); +OPTABLE0(LODSB, 0xac,_modsiax); +OPTABLE0(LODSW, 0xad,_16_bit | _modsiax); +OPTABLE0(LODSD, 0xad,_32_bit | _I386 | _modsiax); +OPTABLE0(LODSQ, 0xad,_64_bit | _modsiax); +OPTABLE0(MOVSB, 0xa4, _modsidi); +OPTABLE0(MOVSW, 0xa5, _16_bit | _modsidi); +OPTABLE0(MOVSQ, 0xa5, _64_bit | _modsidi); +OPTABLE0(NOP, 0x90, 0); +OPTABLE0(OUTSB, 0x6e, _I386 | _modsi); +OPTABLE0(OUTSW, 0x6f, _16_bit | _I386 | _modsi); +OPTABLE0(OUTSD, 0x6f, _32_bit | _I386 | _modsi); +OPTABLE0(POPA, 0x61,_i64_bit | _16_bit | _I386 | _modall); +OPTABLE0(POPAD, 0x61,_i64_bit | _32_bit | _I386 | _modall); +OPTABLE0(POPF, 0x9d, _16_bit); +OPTABLE0(POPFD, 0x9d,_i64_bit | _32_bit | _I386); +OPTABLE0(POPFQ, 0x9d, _64_bit); +OPTABLE0(PUSHA, 0x60,_i64_bit | _16_bit | _I386); +OPTABLE0(PUSHAD, 0x60,_i64_bit | _32_bit | _I386); +OPTABLE0(PUSHF, 0x9c, _16_bit); +OPTABLE0(PUSHFD, 0x9c,_i64_bit | _32_bit | _I386); +OPTABLE0(PUSHFQ, 0x9c, _64_bit); // TODO REX_W override is implicit +OPTABLE0(REP, 0xf3, _modcx); +OPTABLE0(REPNE, 0xf2, _modcx); +OPTABLE0(SAHF, 0x9e, 0); +OPTABLE0(SCASB, 0xAE, _moddi); +OPTABLE0(SCASW, 0xAF, _16_bit | _moddi); +OPTABLE0(SCASD, 0xAF, _32_bit | _I386 | _moddi); +OPTABLE0(SCASQ, 0xAF, _64_bit | _moddi); +OPTABLE0(STC, 0xf9, 0); +OPTABLE0(STD, 0xfd, 0); +OPTABLE0(STI, 0xfb, 0); +OPTABLE0(STOSB, 0xaa, _moddi); +OPTABLE0(STOSW, 0xAB, _16_bit | _moddi); +OPTABLE0(STOSD, 0xAB, _32_bit | _I386 | _moddi); +OPTABLE0(STOSQ, 0xAB, _64_bit | _moddi); +OPTABLE0(WAIT, 0x9B, 0); +OPTABLE0(WBINVD, 0x0f09, _I386); // Really a 486 opcode +OPTABLE0(XLATB, 0xd7, _modax); +OPTABLE0(CPUID, 0x0fa2, _I386 | _modall); +OPTABLE0(RDMSR, 0x0f32, _I386 | _modaxdx); +OPTABLE0(RDPMC, 0x0f33, _I386 | _modaxdx); +OPTABLE0(RDTSC, 0x0f31, _I386 | _modaxdx); +OPTABLE0(WRMSR, 0x0f30, _I386); +OPTABLE0(RSM, 0x0faa,_i64_bit | _I386); + +// +// Now come the one operand instructions +// These will prove to be a little more challenging than the 0 +// operand instructions +// +PTRNTAB1 aptb1BSWAP[] = /* BSWAP */ { + // Really is a 486 only instruction + { 0x0fc8, _I386, _plus_r | _r32 }, + { 0x0fc8, _64_bit, _plus_r | _r64 }, + { ASM_END } +}; + +PTRNTAB1 aptb1CALL[] = /* CALL */ { + { 0xe8, _cw| _i64_bit | _modall, _rel16 }, + { 0xff, _2 | _i64_bit | _16_bit | _modall, _r16 }, + { 0xff, _2 | _i64_bit | _modall, _m16 }, + { 0x9a, _cd| _i64_bit | _modall, _p1616 }, + { 0xff, _3 | _modall, _m1616 }, + { 0xe8, _cd| _modall, _rel32 }, + { 0xff, _2 | _i64_bit | _32_bit | _modall, _r32 }, + { 0xff, _2 | _32_bit | _modall, _r64 }, // REX_W override is implicit + { 0xff, _2 | _i64_bit | _modall, _m32 }, + { 0xff, _2 | _64_bit | _modall, _m64 }, // TODO REX_W override is implicit + { 0x9a, _cp| _i64_bit | _modall, _p1632 }, + { 0xff, _3 | _modall, _m1632 }, + { ASM_END } +}; + +PTRNTAB1 aptb1DEC[] = /* DEC */ { + { 0xfe, _1, _rm8 }, + { 0x48, _rw | _i64_bit | _16_bit, _r16 | _plus_r }, + { 0x48, _rd | _i64_bit | _32_bit, _r32 | _plus_r }, + { 0xff, _1 | _16_bit, _rm16 }, + { 0xff, _1 | _32_bit, _rm32 }, + { 0xff, _1 | _64_bit, _rm64 }, + { ASM_END } +}; + +PTRNTAB1 aptb1INC[] = /* INC */ { + { 0xfe, _0, _rm8 }, + { 0x40, _rw | _i64_bit | _16_bit, _r16 | _plus_r }, + { 0x40, _rd | _i64_bit | _32_bit, _r32 | _plus_r }, + { 0xff, _0 | _16_bit, _rm16 }, + { 0xff, _0 | _32_bit, _rm32 }, + { 0xff, _0 | _64_bit, _rm64 }, + { ASM_END } +}; +// INT and INT 3 +PTRNTAB1 aptb1INT[]= /* INT */ { + { 0xcc, 3, 0 }, // The ulFlags here are meant to + // be the value of the immediate + // operand + { 0xcd, 0, _imm8 }, + { ASM_END } +}; +PTRNTAB1 aptb1INVLPG[] = /* INVLPG */ { // 486 only instruction + { 0x0f01, _I386|_7, _m8 | _m16 | _m32 | _m48 }, + { ASM_END } +}; + +#define OPTABLE(str,op) \ +PTRNTAB1 aptb1##str[] = { \ + { 0x70|op, _cb, _rel8 }, \ + { 0x0f80|op, _cw|_i64_bit,_rel16 }, \ + { 0x0f80|op, _cd, _rel32 }, \ + { ASM_END } \ +} + +OPTABLE(JO,0); +OPTABLE(JNO,1); +OPTABLE(JB,2); +OPTABLE(JNB,3); +OPTABLE(JZ,4); +OPTABLE(JNZ,5); +OPTABLE(JBE,6); +OPTABLE(JNBE,7); +OPTABLE(JS,8); +OPTABLE(JNS,9); +OPTABLE(JP,0xA); +OPTABLE(JNP,0xB); +OPTABLE(JL,0xC); +OPTABLE(JNL,0xD); +OPTABLE(JLE,0xE); +OPTABLE(JNLE,0xF); + +#undef OPTABLE + +PTRNTAB1 aptb1JCXZ[] = /* JCXZ */ { + { 0xe3, _cb | _i64_bit | _16_bit_addr, _rel8 }, + { ASM_END } +}; +PTRNTAB1 aptb1JECXZ[] = /* JECXZ */ { + { 0xe3, _cb | _32_bit_addr | _I386,_rel8 }, + { ASM_END } +}; +PTRNTAB1 aptb1JMP[] = /* JMP */ { + { 0xe9, _cw| _i64_bit, _rel16 }, + { 0xe9, _cd, _rel32 }, + { 0xeb, _cb, _rel8 }, + { 0xff, _4 | _i64_bit | _16_bit, _rm16 }, + { 0xea, _cd| _i64_bit, _p1616 }, + { 0xff, _5, _m1616 }, + { 0xff, _4 | _i64_bit | _32_bit, _rm32 }, + { 0xff, _4 | _64_bit, _rm64 }, // TODO REX_W override is implicit + { 0xea, _cp| _i64_bit, _p1632 }, + { 0xff, _5, _m1632 }, + { ASM_END } +}; +PTRNTAB1 aptb1LGDT[] = /* LGDT */ { + { 0x0f01, _2, _m48 }, + { ASM_END } +}; +PTRNTAB1 aptb1LIDT[] = /* LIDT */ { + { 0x0f01, _3, _m48 }, + { ASM_END } +}; +PTRNTAB1 aptb1LLDT[] = /* LLDT */ { + { 0x0f00, _2|_modnot1, _rm16 }, + { ASM_END } +}; +PTRNTAB1 aptb1LMSW[] = /* LMSW */ { + { 0x0f01, _6|_modnot1, _rm16 }, + { ASM_END } +}; +PTRNTAB1 aptb1LODS[] = /* LODS */ { + { 0xac, _modax,_m8 }, + { 0xad, _16_bit | _modax,_m16 }, + { 0xad, _32_bit | _I386 | _modax,_m32 }, + { ASM_END } +}; +PTRNTAB1 aptb1LOOP[] = /* LOOP */ { + { 0xe2, _cb | _modcx,_rel8 }, + { ASM_END } +}; +PTRNTAB1 aptb1LOOPE[] = /* LOOPE/LOOPZ */ { + { 0xe1, _cb | _modcx,_rel8 }, + { ASM_END } +}; +PTRNTAB1 aptb1LOOPNE[] = /* LOOPNE/LOOPNZ */ { + { 0xe0, _cb | _modcx,_rel8 }, + { ASM_END } +}; +PTRNTAB1 aptb1LTR[] = /* LTR */ { + { 0x0f00, _3|_modnot1, _rm16 }, + { ASM_END } +}; +PTRNTAB1 aptb1NEG[] = /* NEG */ { + { 0xf6, _3, _rm8 }, + { 0xf7, _3 | _16_bit, _rm16 }, + { 0xf7, _3 | _32_bit, _rm32 }, + { 0xf7, _3 | _64_bit, _rm64 }, + { ASM_END } +}; +PTRNTAB1 aptb1NOT[] = /* NOT */ { + { 0xf6, _2, _rm8 }, + { 0xf7, _2 | _16_bit, _rm16 }, + { 0xf7, _2 | _32_bit, _rm32 }, + { 0xf7, _2 | _64_bit, _rm64 }, + { ASM_END } +}; +PTRNTAB1 aptb1POP[] = /* POP */ { + { 0x8f, _0 | _16_bit, _m16 }, + { 0x8f, _0 | _i64_bit | _32_bit, _m32 }, + { 0x8f, _0 | _64_bit, _m64 }, // TODO REX_W override is implicit + { 0x58, _rw | _16_bit, _r16 | _plus_r }, + { 0x58, _rd | _i64_bit | _32_bit, _r32 | _plus_r }, + { 0x58, _r | _32_bit, _r64 | _plus_r }, // REX_W override is implicit + { 0x1f, _i64_bit, _ds | _seg }, + { 0x07, _i64_bit | _modes, _es | _seg }, + { 0x17, _i64_bit, _ss | _seg }, + { 0x0fa1, 0, _fs | _seg }, + { 0x0fa9, 0, _gs | _seg }, + { ASM_END } +}; +PTRNTAB1 aptb1PUSH[] = /* PUSH */ { + { 0xff, _6 | _16_bit, _m16 }, + { 0xff, _6 | _i64_bit | _32_bit, _m32 }, + { 0xff, _6 | _64_bit, _m64 }, // TODO REX_W override is implicit + { 0x50, _r | _16_bit, _r16 | _plus_r }, + { 0x50, _r | _i64_bit | _32_bit, _r32 | _plus_r }, + { 0x50, _r | _32_bit, _r64 | _plus_r }, // REX_W override is implicit + { 0x6a, 0,_imm8 }, + { 0x68, _16_bit,_imm16 }, + { 0x68, _16_bit,_rel16 }, + { 0x68, _32_bit,_imm32 }, + { 0x68, _32_bit,_rel32 }, + { 0x0e, _i64_bit,_cs | _seg }, + { 0x16, _i64_bit,_ss | _seg }, + { 0x1e, _i64_bit,_ds | _seg }, + { 0x06, _i64_bit,_es | _seg }, + { 0x0fa0, 0,_fs | _seg}, + { 0x0fa8, 0,_gs | _seg}, + { ASM_END } +}; +PTRNTAB1 aptb1RET[] = /* RET */ { + { 0xc3, 0, 0 }, + { 0xc2, _iw, _imm16 }, + { ASM_END } +}; +PTRNTAB1 aptb1RETF[] = /* RETF */ { + { 0xcb, 0, 0 }, + { 0xca, _iw, _imm16 }, + { ASM_END } +}; +PTRNTAB1 aptb1SCAS[] = /* SCAS */ { + { 0xae, _moddi, _m8 }, + { 0xaf, _16_bit | _moddi, _m16 }, + { 0xaf, _32_bit | _moddi, _m32 }, + { ASM_END } +}; + +#define OPTABLE(str,op) \ +PTRNTAB1 aptb1##str[] = { \ + { 0xf90|op, _cb, _rm8 }, \ + { ASM_END } \ +} + +OPTABLE(SETO,0); +OPTABLE(SETNO,1); +OPTABLE(SETB,2); +OPTABLE(SETNB,3); +OPTABLE(SETZ,4); +OPTABLE(SETNZ,5); +OPTABLE(SETBE,6); +OPTABLE(SETNBE,7); +OPTABLE(SETS,8); +OPTABLE(SETNS,9); +OPTABLE(SETP,0xA); +OPTABLE(SETNP,0xB); +OPTABLE(SETL,0xC); +OPTABLE(SETNL,0xD); +OPTABLE(SETLE,0xE); +OPTABLE(SETNLE,0xF); + +#undef OPTABLE + +PTRNTAB1 aptb1SGDT[]= /* SGDT */ { + { 0xf01, _0, _m48 }, + { ASM_END } +}; +PTRNTAB1 aptb1SIDT[] = /* SIDT */ { + { 0xf01, _1, _m48 }, + { ASM_END } +}; +PTRNTAB1 aptb1SLDT[] = /* SLDT */ { + { 0xf00, _0, _rm16 }, + { ASM_END } +}; +PTRNTAB1 aptb1SMSW[] = /* SMSW */ { + { 0xf01, _4, _rm16 }, + { 0xf01, _4, _r32 }, + { ASM_END } +}; +PTRNTAB1 aptb1STOS[] = /* STOS */ { + { 0xaa, _moddi, _m8 }, + { 0xab, _16_bit | _moddi, _m16 }, + { 0xab, _32_bit | _moddi, _m32 }, + { ASM_END } +}; +PTRNTAB1 aptb1STR[] = /* STR */ { + { 0xf00, _1, _rm16 }, + { ASM_END } +}; +PTRNTAB1 aptb1VERR[] = /* VERR */ { + { 0xf00, _4|_modnot1, _rm16 }, + { ASM_END } +}; +PTRNTAB1 aptb1VERW[] = /* VERW */ { + { 0xf00, _5|_modnot1, _rm16 }, + { ASM_END } +}; +PTRNTAB1 aptb1XLAT[] = /* XLAT */ { + { 0xd7, _modax, 0 }, + { 0xd7, _modax, _m8 }, + { ASM_END } +}; +PTRNTAB1 aptb1CMPXCH8B[] = /* CMPXCH8B */ { + { 0x0fc7, _1 | _modaxdx | _I386, _m64 }, + { ASM_END } +}; + +PTRNTAB1 aptb1CMPXCH16B[] = /* CMPXCH16B */ { + { 0x0fc7, _1 | _modaxdx | _64_bit, _m64 }, + { ASM_END } +}; + +#define OPTABLE(str,op,rr,m) \ +PTRNTAB2 aptb2##str[] = { \ + { op+4, _ib|m, _al, _imm8 }, \ + { 0x83, rr|_ib|_16_bit|m, _rm16, _imm8 }, \ + { op+5, _iw|_16_bit|m, _ax, _imm16 }, \ + { 0x83, rr|_ib|_32_bit|m, _rm32, _imm8 }, \ + { 0x83, rr|_ib|_64_bit|m, _rm64, _imm8 }, \ + { op+5, _id|_32_bit|m, _eax, _imm32 }, \ + { op+5, _id|_64_bit|m, _rax, _imm32 }, \ + { 0x80, rr|_ib|m, _rm8, _imm8 }, \ + { 0x81, rr|_iw|_16_bit|m, _rm16, _imm16 }, \ + { 0x81, rr|_id|_32_bit|m, _rm32, _imm32 }, \ + { 0x81, rr|_id|_64_bit|m, _rm64, _imm32 }, \ + { op+0, _r|m, _rm8, _r8 }, \ + { op+1, _r|_16_bit|m, _rm16, _r16 }, \ + { op+1, _r|_32_bit|m, _rm32, _r32 }, \ + { op+1, _r|_64_bit|m, _rm64, _r64 }, \ + { op+2, _r|m, _r8, _rm8 }, \ + { op+3, _r|_16_bit|m, _r16, _rm16 }, \ + { op+3, _r|_32_bit|m, _r32, _rm32 }, \ + { op+3, _r|_64_bit|m, _r64, _rm64 }, \ + { ASM_END } \ +} + +OPTABLE(ADD,0x00,_0,0); +OPTABLE(OR, 0x08,_1,0); +OPTABLE(ADC,0x10,_2,0); +OPTABLE(SBB,0x18,_3,0); +OPTABLE(AND,0x20,_4,0); +OPTABLE(SUB,0x28,_5,0); +OPTABLE(XOR,0x30,_6,0); +OPTABLE(CMP,0x38,_7,_modnot1); + +#undef OPTABLE + +PTRNTAB2 aptb2ARPL[] = /* ARPL */ { + { 0x63, _r|_i64_bit, _rm16, _r16 }, + { ASM_END } +}; +PTRNTAB2 aptb2BOUND[] = /* BOUND */ { + { 0x62, _r|_i64_bit|_16_bit|_modnot1,_r16,_m16 },// Should really b3 _m16_16 + { 0x62, _r|_i64_bit|_32_bit|_modnot1,_r32,_m32 },// Should really be _m32_32 + { ASM_END } +}; +PTRNTAB2 aptb2BSF[] = /* BSF */ { + { 0x0fbc, _cw | _16_bit, _r16, _rm16 }, + { 0x0fbc, _cd|_32_bit, _r32, _rm32 }, + { 0x0fbc, _cq|_64_bit, _r64, _rm64 }, + { ASM_END } +}; +PTRNTAB2 aptb2BSR[] = /* BSR */ { + { 0x0fbd, _cw|_16_bit, _r16, _rm16 }, + { 0x0fbd, _cd|_32_bit, _r32, _rm32 }, + { 0x0fbd, _cq|_64_bit, _r64, _rm64 }, + { ASM_END } +}; +PTRNTAB2 aptb2BT[] = /* BT */ { + { 0x0fa3, _cw|_16_bit|_modnot1, _rm16, _r16 }, + { 0x0fa3, _cd|_32_bit|_modnot1, _rm32, _r32 }, + { 0x0fa3, _cq|_64_bit|_modnot1, _rm64, _r64 }, + { 0x0fba, _4|_ib|_16_bit|_modnot1, _rm16, _imm8 }, + { 0x0fba, _4|_ib|_32_bit|_modnot1, _rm32, _imm8 }, + { 0x0fba, _4|_ib|_64_bit|_modnot1, _rm64, _imm8 }, + { ASM_END } +}; +PTRNTAB2 aptb2BTC[] = /* BTC */ { + { 0x0fbb, _cw|_16_bit, _rm16, _r16 }, + { 0x0fbb, _cd|_32_bit, _rm32, _r32 }, + { 0x0fbb, _cq|_64_bit, _rm64, _r64 }, + { 0x0fba, _7|_ib|_16_bit, _rm16, _imm8 }, + { 0x0fba, _7|_ib|_32_bit, _rm32, _imm8 }, + { 0x0fba, _7|_ib|_64_bit, _rm64, _imm8 }, + { ASM_END } +}; +PTRNTAB2 aptb2BTR[] = /* BTR */ { + { 0x0fb3, _cw|_16_bit, _rm16, _r16 }, + { 0x0fb3, _cd|_32_bit, _rm32, _r32 }, + { 0x0fb3, _cq|_64_bit, _rm64, _r64 }, + { 0x0fba, _6|_ib|_16_bit, _rm16, _imm8 }, + { 0x0fba, _6|_ib|_32_bit, _rm32, _imm8 }, + { 0x0fba, _6|_ib|_64_bit, _rm64, _imm8 }, + { ASM_END } +}; +PTRNTAB2 aptb2BTS[] = /* BTS */ { + { 0x0fab, _cw|_16_bit, _rm16, _r16 }, + { 0x0fab, _cd|_32_bit, _rm32, _r32 }, + { 0x0fab, _cq|_64_bit, _rm64, _r64 }, + { 0x0fba, _5|_ib|_16_bit, _rm16, _imm8 }, + { 0x0fba, _5|_ib|_32_bit, _rm32, _imm8 }, + { 0x0fba, _5|_ib|_64_bit, _rm64, _imm8 }, + { ASM_END } +}; +PTRNTAB2 aptb2CMPS[] = /* CMPS */ { + { 0xa6, _modsidi, _m8, _m8 }, + { 0xa7, _modsidi, _m16, _m16 }, + { 0xa7, _modsidi, _m32, _m32 }, + { ASM_END } +}; +PTRNTAB2 aptb2CMPXCHG[] = /* CMPXCHG */ { + { 0xfb0, _I386 | _cb|_mod2, _rm8, _r8 }, + // This is really a 486 only + // instruction + { 0xfb1, _I386 | _cw | _16_bit|_mod2, _rm16, _r16 }, + { 0xfb1, _I386 | _cd | _32_bit|_mod2, _rm32, _r32 }, + { 0xfb1, _I386 | _cq | _64_bit|_mod2, _rm64, _r64 }, + { ASM_END } +}; +PTRNTAB2 aptb2DIV[] = /* DIV */ { + { 0xf6, _6, _al, _rm8 }, + { 0xf7, _6 | _16_bit | _moddx, _ax, _rm16 }, + { 0xf7, _6 | _32_bit | _moddx, _eax, _rm32 }, + { 0xf7, _6 | _64_bit | _moddx, _rax, _rm64 }, + { 0xf6, _6 | _modax, _rm8, 0 }, + { 0xf7, _6 | _16_bit | _modaxdx, _rm16, 0 }, + { 0xf7, _6 | _32_bit | _modaxdx, _rm32, 0 }, + { 0xf7, _6 | _64_bit | _modaxdx, _rm64, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2ENTER[] = /* ENTER */ { + { 0xc8, _iw|_ib, _imm16, _imm8 }, + { ASM_END } +}; +PTRNTAB2 aptb2IDIV[] = /* IDIV */ { + { 0xf6, _7, _al, _rm8 }, + { 0xf7, _7|_16_bit|_moddx, _ax, _rm16 }, + { 0xf7, _7|_32_bit|_moddx, _eax, _rm32 }, + { 0xf7, _7|_64_bit|_moddx, _rax, _rm64 }, + { 0xf6, _7 | _modax, _rm8, 0 }, + { 0xf7, _7|_16_bit|_modaxdx, _rm16, 0 }, + { 0xf7, _7|_32_bit|_modaxdx, _rm32, 0 }, + { 0xf7, _7|_64_bit|_modaxdx, _rm64, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2IN[] = /* IN */ { + { 0xe4, _ib, _al, _imm8 }, + { 0xe5, _ib|_16_bit,_ax, _imm8 }, + { 0xe5, _ib|_32_bit,_eax, _imm8 }, + { 0xec, 0, _al, _dx }, + { 0xed, _16_bit, _ax, _dx }, + { 0xed, _32_bit, _eax, _dx }, + { ASM_END } +}; +PTRNTAB2 aptb2INS[] = /* INS */ { + { 0x6c, _modsi, _rm8, _dx }, + { 0x6d, _modsi|_16_bit, _rm16, _dx }, + { 0x6d, _32_bit|_modsi, _rm32, _dx }, + { ASM_END } +}; + +PTRNTAB2 aptb2LAR[] = /* LAR */ { + { 0x0f02, _r|_16_bit, _r16, _rm16 }, + { 0x0f02, _r|_32_bit, _r32, _rm32 }, + { ASM_END } +}; +PTRNTAB2 aptb2LDS[] = /* LDS */ { + { 0xc5, _r|_i64_bit|_16_bit, _r16, _m32 }, + { 0xc5, _r|_i64_bit|_32_bit, _r32, _m48 }, + { ASM_END } +}; + +PTRNTAB2 aptb2LEA[] = /* LEA */ { + { 0x8d, _r|_16_bit, _r16, _m8 | _m16 | _m32 | _m48 }, + { 0x8d, _r|_32_bit, _r32, _m8 | _m16 | _m32 | _m48 }, + { 0x8d, _r|_64_bit, _r64, _m8 | _m16 | _m32 | _m48 | _m64 }, + { 0x8d, _r|_16_bit, _r16, _rel16 }, + { 0x8d, _r|_32_bit, _r32, _rel32 }, + { 0x8d, _r|_64_bit, _r64, _rel32 }, + { ASM_END } +}; +PTRNTAB2 aptb2LES[] = /* LES */ { + { 0xc4, _r|_i64_bit|_16_bit|_modes, _r16, _m32 }, + { 0xc4, _r|_i64_bit|_32_bit|_modes, _r32, _m48 }, + { ASM_END } +}; +PTRNTAB2 aptb2LFS[] = /* LFS */ { + { 0x0fb4, _r|_16_bit, _r16, _m32 }, + { 0x0fb4, _r|_32_bit, _r32, _m48 }, + { ASM_END } +}; +PTRNTAB2 aptb2LGS[] = /* LGS */ { + { 0x0fb5, _r|_16_bit, _r16, _m32 }, + { 0x0fb5, _r|_32_bit, _r32, _m48 }, + { ASM_END } +}; +PTRNTAB2 aptb2LSS[] = /* LSS */ { + { 0x0fb2, _r|_16_bit, _r16, _m32 }, + { 0x0fb2, _r|_32_bit, _r32, _m48 }, + { ASM_END } +}; +PTRNTAB2 aptb2LSL[] = /* LSL */ { + { 0x0f03, _r|_16_bit, _r16, _rm16 }, + { 0x0f03, _r|_32_bit, _r32, _rm32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOV[] = /* MOV */ { +#if 0 // Let pinholeopt() do this + { 0xa0, 0, _al, _moffs8 }, + { 0xa1, _16_bit, _ax, _moffs16 }, + { 0xa1, _32_bit, _eax, _moffs32 }, + { 0xa2, 0, _moffs8, _al }, + { 0xa3, _16_bit, _moffs16, _ax }, + { 0xa3, _32_bit, _moffs32, _eax }, +#endif + { 0x88, _r, _rm8, _r8 }, + { 0x89, _r|_16_bit, _rm16, _r16 }, + { 0x89, _r|_32_bit, _rm32, _r32 }, + { 0x89, _r|_64_bit, _rm64, _r64 }, + { 0x8a, _r, _r8, _rm8 }, + { 0x8b, _r|_16_bit, _r16, _rm16 }, + { 0x8b, _r|_32_bit, _r32, _rm32 }, + { 0x8b, _r|_64_bit, _r64, _rm64 }, + { 0x8c, _r, _rm16, _seg|_ds|_es| _ss | _fs | _gs | _cs }, + { 0x8e, _r, _seg|_ds|_es|_ss|_fs|_gs|_cs, _rm16 }, + { 0xb0, _rb, _r8 | _plus_r, _imm8 }, + { 0xb8, _rw | _16_bit, _r16 | _plus_r, _imm16 }, + { 0xb8, _rd|_32_bit, _r32 | _plus_r, _imm32 }, + { 0xb8, _rd|_64_bit, _r64 | _plus_r, _imm64 }, + { 0xc6, _cb, _rm8, _imm8 }, + { 0xc7, _cw|_16_bit, _rm16, _imm16 }, + { 0xc7, _cd|_32_bit, _rm32, _imm32 }, +#if 0 // Let pinholeopt() do this + { 0xc6, _cb, _moffs8, _imm8 }, + { 0xc7, _cw|_16_bit, _moffs16, _imm16 }, + { 0xc7, _cd|_32_bit, _moffs32, _imm32 }, +#endif + { 0x0f20, _r, _r32, _special | _crn }, + { 0x0f22, _r, _special|_crn, _r32 }, + { 0x0f21, _r, _r32, _special | _drn }, + { 0x0f23, _r, _special|_drn, _r32 }, + { 0x0f24, _r, _r32, _special | _trn }, + { 0x0f26, _r, _special|_trn, _r32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVS[] = /* MOVS */ { + { 0xa4, _modsidi , _m8, _m8 }, + { 0xa5, _modsidi | _16_bit, _m16, _m16 }, + { 0xa5, _modsidi | _32_bit, _m32, _m32 }, + { ASM_END } +}; +PTRNTAB2 aptb2MOVSX[] = /* MOVSX */ { + { 0x0fbe, _r|_16_bit, _r16, _rm8 }, + { 0x0fbe, _r|_32_bit, _r32, _rm8 }, +#if 1 + { 0x0fbf, _r|_16_bit, _r16, _rm16 }, + { 0x0fbf, _r|_32_bit, _r32, _rm16 }, +#else + { 0x0fbf, _r, _r32, _rm16 }, +#endif + { ASM_END } +}; +PTRNTAB2 aptb2MOVZX[] = /* MOVZX */ { + { 0x0fb6, _r|_16_bit, _r16, _rm8 }, + { 0x0fb6, _r|_32_bit, _r32, _rm8 }, +#if 1 + { 0x0fb7, _r|_16_bit, _r16, _rm16 }, + { 0x0fb7, _r|_32_bit, _r32, _rm16 }, +#else + { 0x0fb7, _r, _r32, _rm16 }, +#endif + { ASM_END } +}; +PTRNTAB2 aptb2MUL[] = /* MUL */ { + { 0xf6, _4, _al, _rm8 }, + { 0xf7, _4|_16_bit|_moddx, _ax, _rm16 }, + { 0xf7, _4|_32_bit|_moddx, _eax, _rm32 }, + { 0xf7, _4|_64_bit|_moddx, _rax, _rm64 }, + { 0xf6, _4|_modax, _rm8, 0 }, + { 0xf7, _4|_16_bit|_modaxdx, _rm16, 0 }, + { 0xf7, _4|_32_bit|_modaxdx, _rm32, 0 }, + { 0xf7, _4|_64_bit|_modaxdx, _rm64, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2OUT[] = /* OUT */ { + { 0xe6, _ib, _imm8, _al }, + { 0xe7, _ib|_16_bit, _imm8, _ax }, + { 0xe7, _ib|_32_bit, _imm8, _eax }, + { 0xee, _modnot1, _dx, _al }, + { 0xef, _16_bit|_modnot1, _dx, _ax }, + { 0xef, _32_bit|_modnot1, _dx, _eax }, + { ASM_END } +}; +PTRNTAB2 aptb2OUTS[] = /* OUTS */ { + { 0x6e, _modsinot1, _dx, _rm8 }, + { 0x6f, _16_bit | _I386 |_modsinot1, _dx, _rm16 }, + { 0x6f, _32_bit | _I386| _modsinot1, _dx, _rm32 }, + { ASM_END } +}; + +#define OPTABLE(str,op) \ +PTRNTAB2 aptb2##str[] = { \ + { 0xd2, op, _rm8, _cl }, \ + { 0xc0, op|_ib, _rm8, _imm8 }, \ + { 0xd3, op|_16_bit, _rm16, _cl }, \ + { 0xc1, op|_ib|_16_bit, _rm16, _imm8 }, \ + { 0xd3, op|_32_bit, _rm32, _cl }, \ + { 0xc1, op|_ib|_32_bit, _rm32, _imm8, }, \ + { 0xd3, op|_64_bit, _rm64, _cl }, \ + { 0xc1, op|_ib|_64_bit, _rm64, _imm8, }, \ + { ASM_END } \ +} + +OPTABLE(ROL,_0); +OPTABLE(ROR,_1); +OPTABLE(RCL,_2); +OPTABLE(RCR,_3); +OPTABLE(SHL,_4); +OPTABLE(SHR,_5); +OPTABLE(SAR,_7); + +#undef OPTABLE + +PTRNTAB2 aptb2TEST[] = /* TEST */ { + { 0xa8, _ib|_modnot1, _al, _imm8 }, + { 0xa9, _iw|_16_bit|_modnot1, _ax, _imm16 }, + { 0xa9, _id|_32_bit|_modnot1, _eax, _imm32 }, + { 0xa9, _id|_64_bit|_modnot1, _rax, _imm32 }, + { 0xf6, _0|_modnot1, _rm8, _imm8 }, + { 0xf7, _0|_16_bit|_modnot1, _rm16, _imm16 }, + { 0xf7, _0|_32_bit|_modnot1, _rm32, _imm32 }, + { 0xf7, _0|_64_bit|_modnot1, _rm64, _imm32 }, + { 0x84, _r|_modnot1, _rm8, _r8 }, + { 0x85, _r|_16_bit|_modnot1, _rm16, _r16 }, + { 0x85, _r|_32_bit|_modnot1, _rm32, _r32 }, + { 0x85, _r|_64_bit|_modnot1, _rm64, _r64 }, + { ASM_END } +}; +PTRNTAB2 aptb2XADD[] = /* XADD */ { // 486 only instruction +// { 0x0fc0, _ib | _I386|_mod2, _rm8, _r8 }, +// { 0x0fc1, _iw | _I386|_16_bit|_mod2, _rm16, _r16 }, +// { 0x0fc1, _id | _I386|_32_bit|_mod2, _rm32, _r32 }, + { 0x0fc0, _r | _I386|_mod2, _rm8, _r8 }, + { 0x0fc1, _r | _I386|_16_bit|_mod2, _rm16, _r16 }, + { 0x0fc1, _r | _I386|_32_bit|_mod2, _rm32, _r32 }, + { 0x0fc1, _r | _64_bit|_mod2, _rm64, _r64 }, + { ASM_END } +}; +PTRNTAB2 aptb2XCHG[] = /* XCHG */ { + { 0x90, _r|_16_bit|_mod2, _ax , _r16 | _plus_r }, + { 0x90, _r|_16_bit|_mod2, _r16 | _plus_r, _ax }, + { 0x90, _r|_32_bit|_mod2, _eax, _r32 | _plus_r }, + { 0x90, _r|_32_bit|_mod2, _r32 | _plus_r, _eax }, + { 0x86, _r|_mod2, _rm8, _r8 }, + { 0x86, _r|_mod2, _r8, _rm8 }, + { 0x87, _r|_16_bit|_mod2, _rm16, _r16 }, + { 0x87, _r|_16_bit|_mod2, _r16, _rm16 }, + { 0x87, _r|_32_bit|_mod2, _rm32, _r32 }, + { 0x87, _r|_32_bit|_mod2, _r32, _rm32 }, + { 0x87, _r|_64_bit|_mod2, _rm64, _r64 }, + { 0x87, _r|_64_bit|_mod2, _r64, _rm64 }, + { ASM_END } +}; + +#define OPTABLE(str,op) \ +PTRNTAB2 aptb2##str[] = { \ + { 0x0F40|op, _r|_16_bit, _r16, _rm16 }, \ + { 0x0F40|op, _r|_32_bit, _r32, _rm32 }, \ + { 0x0F40|op, _r|_64_bit, _r64, _rm64 }, \ + { ASM_END } \ +} + +OPTABLE(CMOVO,0); +OPTABLE(CMOVNO,1); +OPTABLE(CMOVB,2); +OPTABLE(CMOVNB,3); +OPTABLE(CMOVZ,4); +OPTABLE(CMOVNZ,5); +OPTABLE(CMOVBE,6); +OPTABLE(CMOVNBE,7); +OPTABLE(CMOVS,8); +OPTABLE(CMOVNS,9); +OPTABLE(CMOVP,0xA); +OPTABLE(CMOVNP,0xB); +OPTABLE(CMOVL,0xC); +OPTABLE(CMOVNL,0xD); +OPTABLE(CMOVLE,0xE); +OPTABLE(CMOVNLE,0xF); + +#undef OPTABLE + +PTRNTAB3 aptb3IMUL[] = /* IMUL */ { + { 0x0faf, _r|_16_bit, _r16, _rm16, 0 }, + { 0x0faf, _r|_32_bit, _r32, _rm32, 0 }, + { 0x0faf, _r|_64_bit, _r64, _rm64, 0 }, + { 0xf6, _5|_modax, _rm8, 0, 0 }, + { 0xf7, _5|_16_bit|_modaxdx, _rm16, 0, 0 }, + { 0xf7, _5|_32_bit|_modaxdx, _rm32, 0, 0 }, + { 0xf7, _5|_64_bit|_modaxdx, _rm64, 0, 0 }, + { 0x6b, _r|_ib|_16_bit, _r16, _imm8, 0 }, + { 0x6b, _r|_ib|_32_bit, _r32, _imm8, 0 }, + { 0x69, _r|_iw|_16_bit, _r16, _imm16, 0 }, + { 0x69, _r|_id|_32_bit, _r32, _imm32, 0 }, + { 0x69, _r|_id|_64_bit, _r64, _imm32, 0 }, + { 0x6b, _r|_ib|_16_bit, _r16, _rm16, _imm8 }, + { 0x6b, _r|_ib|_32_bit, _r32, _rm32, _imm8 }, + { 0x6b, _r|_ib|_64_bit, _r64, _rm64, _imm8 }, + { 0x69, _r|_iw|_16_bit, _r16, _rm16, _imm16 }, + { 0x69, _r|_id|_32_bit, _r32, _rm32, _imm32 }, + { 0x69, _r|_id|_64_bit, _r64, _rm64, _imm32 }, + { ASM_END } +}; +PTRNTAB3 aptb3SHLD[] = /* SHLD */ { + { 0x0fa4, _cw|_16_bit, _rm16, _r16, _imm8 }, + { 0x0fa4, _cd|_32_bit, _rm32, _r32, _imm8 }, + { 0x0fa4, _cq|_64_bit, _rm64, _r64, _imm8 }, + { 0x0fa5, _cw|_16_bit, _rm16, _r16, _cl }, + { 0x0fa5, _cd|_32_bit, _rm32, _r32, _cl }, + { 0x0fa5, _cq|_64_bit, _rm64, _r64, _cl }, + { ASM_END } +}; +PTRNTAB3 aptb3SHRD[] = /* SHRD */ { + { 0x0fac, _cw|_16_bit, _rm16, _r16, _imm8 }, + { 0x0fac, _cd|_32_bit, _rm32, _r32, _imm8 }, + { 0x0fac, _cq|_64_bit, _rm64, _r64, _imm8 }, + { 0x0fad, _cw|_16_bit, _rm16, _r16, _cl }, + { 0x0fad, _cd|_32_bit, _rm32, _r32, _cl }, + { 0x0fad, _cq|_64_bit, _rm64, _r64, _cl }, + { ASM_END } +}; +// +// Floating point instructions which have entirely different flag +// interpretations +// + +OPTABLE0(F2XM1, 0xd9f0,0); +OPTABLE0(FABS, 0xd9e1,0); +OPTABLE0(FCHS, 0xd9e0,0); +OPTABLE0(FCLEX, 0xdbe2,_fwait); +OPTABLE0(FNCLEX, 0xdbe2, _nfwait); +OPTABLE0(FCOMPP, 0xded9, 0); +OPTABLE0(FCOS, 0xd9ff, 0); +OPTABLE0(FUCOMPP, 0xdae9, 0); +OPTABLE0(FDECSTP, 0xd9f6, 0); +OPTABLE0(FINCSTP, 0xd9f7, 0); +OPTABLE0(FINIT, 0xdbe3, _fwait); +OPTABLE0(FNINIT, 0xdbe3, _nfwait); +OPTABLE0(FENI, 0xdbe0, _fwait); +OPTABLE0(FNENI, 0xdbe0, _nfwait); +OPTABLE0(FDISI, 0xdbe1, _fwait); +OPTABLE0(FNDISI, 0xdbe1, _nfwait); +OPTABLE0(FLD1, 0xd9e8, 0); +OPTABLE0(FLDL2T, 0xd9e9, 0); +OPTABLE0(FLDL2E, 0xd9ea, 0); +OPTABLE0(FLDPI, 0xd9eb, 0); +OPTABLE0(FLDLG2, 0xd9ec, 0); +OPTABLE0(FLDLN2, 0xd9ed, 0); +OPTABLE0(FLDZ, 0xd9ee, 0); +OPTABLE0(FNOP, 0xd9d0, 0); +OPTABLE0(FPATAN, 0xd9f3, 0); +OPTABLE0(FPREM, 0xd9f8, 0); +OPTABLE0(FPREM1, 0xd9f5, 0); +OPTABLE0(FPTAN, 0xd9f2, 0); +OPTABLE0(FRNDINT, 0xd9fc, 0); +OPTABLE0(FSCALE, 0xd9fd, 0); +OPTABLE0(FSETPM, 0xdbe4, 0); +OPTABLE0(FSIN, 0xd9fe, 0); +OPTABLE0(FSINCOS, 0xd9fb, 0); +OPTABLE0(FSQRT, 0xd9fa, 0); +OPTABLE0(FTST, 0xd9e4, 0); +OPTABLE0(FWAIT, 0x9b, 0); +OPTABLE0(FXAM, 0xd9e5, 0); +OPTABLE0(FXTRACT, 0xd9f4, 0); +OPTABLE0(FYL2X, 0xd9f1, 0); +OPTABLE0(FYL2XP1, 0xd9f9, 0); +// +// Floating point instructions which have entirely different flag +// interpretations but they overlap, only asm_determine_operator +// flags needs to know the difference +// 1 operand floating point instructions follow +// +PTRNTAB1 aptb1FBLD[] = /* FBLD */ { + { 0xdf, _4, _fm80 }, + { ASM_END } +}; + +PTRNTAB1 aptb1FBSTP[] = /* FBSTP */ { + { 0xdf, _6, _fm80 }, + { ASM_END } +}; +PTRNTAB2 aptb2FCMOVB[] = /* FCMOVB */ { + { 0xdac0, 0, _st, _sti | _plus_r }, + { 0xdac1, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FCMOVE[] = /* FCMOVE */ { + { 0xdac8, 0, _st, _sti | _plus_r }, + { 0xdac9, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FCMOVBE[] = /* FCMOVBE */ { + { 0xdad0, 0, _st, _sti | _plus_r }, + { 0xdad1, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FCMOVU[] = /* FCMOVU */ { + { 0xdad8, 0, _st, _sti | _plus_r }, + { 0xdad9, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FCMOVNB[] = /* FCMOVNB */ { + { 0xdbc0, 0, _st, _sti | _plus_r }, + { 0xdbc1, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FCMOVNE[] = /* FCMOVNE */ { + { 0xdbc8, 0, _st, _sti | _plus_r }, + { 0xdbc9, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FCMOVNBE[] = /* FCMOVNBE */ { + { 0xdbd0, 0, _st, _sti | _plus_r }, + { 0xdbd1, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FCMOVNU[] = /* FCMOVNU */ { + { 0xdbd8, 0, _st, _sti | _plus_r }, + { 0xdbd9, 0, 0 }, + { ASM_END } +}; +PTRNTAB1 aptb1FCOM[] = /* FCOM */ { + { 0xd8, _2, _m32 }, + { 0xdc, _2, _fm64 }, + { 0xd8d0, 0, _sti | _plus_r }, + { 0xd8d1, 0, 0 }, + { ASM_END } +}; + +PTRNTAB2 aptb2FCOMI[] = /* FCOMI */ { + { 0xdbf0, 0, _st, _sti | _plus_r }, + { 0xdbf0, 0, _sti | _plus_r, 0 }, + { 0xdbf1, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FCOMIP[] = /* FCOMIP */ { + { 0xdff0, 0, _st, _sti | _plus_r }, + { 0xdff0, 0, _sti | _plus_r, 0 }, + { 0xdff1, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FUCOMI[] = /* FUCOMI */ { + { 0xdbe8, 0, _st, _sti | _plus_r }, + { 0xdbe8, 0, _sti | _plus_r, 0 }, + { 0xdbe9, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FUCOMIP[] = /* FUCOMIP */ { + { 0xdfe8, 0, _st, _sti | _plus_r }, + { 0xdfe8, 0, _sti | _plus_r, 0 }, + { 0xdfe9, 0, 0, 0 }, + { ASM_END } +}; + +PTRNTAB1 aptb1FCOMP[] = /* FCOMP */ { + { 0xd8, _3, _m32 }, + { 0xdc, _3, _fm64 }, + { 0xd8d8, 0, _sti | _plus_r }, + { 0xd8d9, 0, 0 }, + { ASM_END } +}; +PTRNTAB1 aptb1FFREE[] = /* FFREE */ { + { 0xddc0, 0, _sti | _plus_r }, + { ASM_END } +}; +PTRNTAB1 aptb1FICOM[] = /* FICOM */ { + { 0xde, _2, _m16 }, + { 0xda, _2, _m32 }, + { ASM_END } +}; +PTRNTAB1 aptb1FICOMP[] = /* FICOMP */ { + { 0xde, _3, _m16 }, + { 0xda, _3, _m32 }, + { ASM_END } +}; +PTRNTAB1 aptb1FILD[] = /* FILD */ { + { 0xdf, _0, _m16 }, + { 0xdb, _0, _m32 }, + { 0xdf, _5, _fm64 }, + { ASM_END } +}; +PTRNTAB1 aptb1FIST[] = /* FIST */ { + { 0xdf, _2, _m16 }, + { 0xdb, _2, _m32 }, + { ASM_END } +}; +PTRNTAB1 aptb1FISTP[] = /* FISTP */ { + { 0xdf, _3, _m16 }, + { 0xdb, _3, _m32 }, + { 0xdf, _7, _fm64 }, + { ASM_END } +}; +PTRNTAB1 aptb1FLD[] = /* FLD */ { + { 0xd9, _0, _m32 }, + { 0xdd, _0, _fm64 }, + { 0xdb, _5, _fm80 }, + { 0xd9c0, 0, _sti | _plus_r }, + { ASM_END } +}; +PTRNTAB1 aptb1FLDCW[] = /* FLDCW */ { + { 0xd9, _5, _m16 }, + { ASM_END } +}; +PTRNTAB1 aptb1FLDENV[] = /* FLDENV */ { + { 0xd9, _4, _m112 | _m224 }, + { ASM_END } +}; +PTRNTAB1 aptb1FRSTOR[] = /* FRSTOR */ { + { 0xdd, _4, _m112 | _m224 }, + { ASM_END } +}; +PTRNTAB1 aptb1FSAVE[] = /* FSAVE */ { + { 0xdd, _6 | _fwait, _m112 | _m224 }, + { ASM_END } +}; +PTRNTAB1 aptb1FNSAVE[] = /* FNSAVE */ { + { 0xdd, _6 | _nfwait, _m112 | _m224 }, + { ASM_END } +}; +PTRNTAB1 aptb1FST[] = /* FST */ { + { 0xd9, _2, _m32 }, + { 0xdd, _2, _fm64 }, + { 0xddd0, 0, _sti | _plus_r }, + { ASM_END } +}; + +PTRNTAB1 aptb1FSTP[] = /* FSTP */ { + { 0xd9, _3, _m32 }, + { 0xdd, _3, _fm64 }, + { 0xdb, _7, _fm80 }, + { 0xddd8, 0, _sti | _plus_r }, + { ASM_END } +}; +PTRNTAB1 aptb1FSTCW[] = /* FSTCW */ { + { 0xd9, _7 | _fwait , _m16 }, + { ASM_END } +}; +PTRNTAB1 aptb1FNSTCW[] = /* FNSTCW */ { + { 0xd9, _7 | _nfwait , _m16 }, + { ASM_END } +}; +PTRNTAB1 aptb1FSTENV[] = /* FSTENV */ { + { 0xd9, _6 | _fwait, _m112 | _m224 }, + { ASM_END } +}; +PTRNTAB1 aptb1FNSTENV[] = /* FNSTENV */ { + { 0xd9, _6 | _nfwait, _m112 | _m224 }, + { ASM_END } +}; +PTRNTAB1 aptb1FSTSW[] = /* FSTSW */ { + { 0xdd, _7 | _fwait, _m16 }, + { 0xdfe0, _fwait | _modax, _ax }, + { ASM_END } +}; +PTRNTAB1 aptb1FNSTSW[] = /* FNSTSW */ { + { 0xdd, _7 | _nfwait, _m16 }, + { 0xdfe0, _nfwait | _modax, _ax }, + { ASM_END } +}; +PTRNTAB1 aptb1FUCOM[] = /* FUCOM */ { + { 0xdde0, 0, _sti | _plus_r }, + { 0xdde1, 0, 0 }, + { ASM_END } +}; +PTRNTAB1 aptb1FUCOMP[] = /* FUCOMP */ { + { 0xdde8, 0, _sti | _plus_r }, + { 0xdde9, 0, 0 }, + { ASM_END } +}; +PTRNTAB1 aptb1FXCH[] = /* FXCH */ { + { 0xd9c8, 0, _sti | _plus_r }, + { 0xd9c9, 0, 0 }, + { ASM_END } +}; +// +// Floating point instructions which have entirely different flag +// interpretations but they overlap, only asm_determine_operator +// flags needs to know the difference +// 2 operand floating point instructions follow +// +PTRNTAB2 aptb2FADD[] = /* FADD */ { + { 0xd8, _0, _m32, 0 }, + { 0xdc, _0, _fm64, 0 }, + { 0xd8c0, 0, _st, _sti | _plus_r }, + { 0xdcc0, 0, _sti | _plus_r, _st }, + { 0xdec1, 0, 0, 0 }, + { ASM_END } +}; + +PTRNTAB2 aptb2FADDP[] = /* FADDP */ { + { 0xdec0, 0, _sti | _plus_r, _st }, + { 0xdec1, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FIADD[] = /* FIADD */ { + { 0xda, _0, _m32, 0 }, + { 0xde, _0, _m16, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FDIV[] = /* FDIV */ { + { 0xd8, _6, _m32, 0 }, + { 0xdc, _6, _fm64, 0 }, + { 0xd8f0, 0, _st, _sti | _plus_r }, + { 0xdcf8, 0, _sti | _plus_r, _st }, + { 0xdef9, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FDIVP[] = /* FDIVP */ { + { 0xdef9, 0, 0, 0 }, + { 0xdef8, 0, _sti | _plus_r, _st }, + { ASM_END } +}; +PTRNTAB2 aptb2FIDIV[] = /* FIDIV */ { + { 0xda, _6, _m32, 0 }, + { 0xde, _6, _m16, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FDIVR[] = /* FDIVR */ { + { 0xd8, _7, _m32, 0 }, + { 0xdc, _7, _fm64, 0 }, + { 0xd8f8, 0, _st, _sti | _plus_r }, + { 0xdcf0, 0, _sti | _plus_r, _st }, + { 0xdef1, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FDIVRP[] = /* FDIVRP */ { + { 0xdef1, 0, 0, 0 }, + { 0xdef0, 0, _sti | _plus_r, _st }, + { ASM_END } +}; +PTRNTAB2 aptb2FIDIVR[] = /* FIDIVR */ { + { 0xda, _7, _m32, 0 }, + { 0xde, _7, _m16, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FMUL[] = /* FMUL */ { + { 0xd8, _1, _m32, 0 }, + { 0xdc, _1, _fm64, 0 }, + { 0xd8c8, 0, _st, _sti | _plus_r }, + { 0xdcc8, 0, _sti | _plus_r, _st }, + { 0xdec9, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FMULP[] = /* FMULP */ { + { 0xdec8, 0, _sti | _plus_r, _st }, + { 0xdec9, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FIMUL[] = /* FIMUL */ { + { 0xda, _1, _m32, 0 }, + { 0xde, _1, _m16, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FSUB[] = /* FSUB */ { + { 0xd8, _4, _m32, 0 }, + { 0xdc, _4, _fm64, 0 }, + { 0xd8e0, 0, _st, _sti | _plus_r }, + { 0xdce8, 0, _sti | _plus_r, _st }, + { 0xdee9, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FSUBP[] = /* FSUBP */ { + { 0xdee8, 0, _sti | _plus_r, _st }, + { 0xdee9, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FISUB[] = /* FISUB */ { + { 0xda, _4, _m32, 0 }, + { 0xde, _4, _m16, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FSUBR[] = /* FSUBR */ { + { 0xd8, _5, _m32, 0 }, + { 0xdc, _5, _fm64, 0 }, + { 0xd8e8, 0, _st, _sti | _plus_r }, + { 0xdce0, 0, _sti | _plus_r, _st }, + { 0xdee1, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FSUBRP[] = /* FSUBRP */ { + { 0xdee0, 0, _sti | _plus_r, _st }, + { 0xdee1, 0, 0, 0 }, + { ASM_END } +}; +PTRNTAB2 aptb2FISUBR[] = /* FISUBR */ { + { 0xda, _5, _m32, 0 }, + { 0xde, _5, _m16, 0 }, + { ASM_END } +}; + +///////////////////////////// MMX Extensions ///////////////////////// + +PTRNTAB0 aptb0EMMS[] = /* EMMS */ { + { 0x0F77, 0 } +}; + +PTRNTAB2 aptb2MOVD[] = /* MOVD */ { + { 0x0F6E,_r,_mm,_rm32 }, + { 0x0F7E,_r,_rm32,_mm }, + { LODD,_r,_xmm,_rm32 }, + { STOD,_r,_rm32,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVD[] = /* VMOVD */ { + { VEX_128_WIG(LODD), _r, _xmm, _rm32 }, + { VEX_128_WIG(STOD), _r, _rm32, _xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVQ[] = /* MOVQ */ { + { 0x0F6F,_r,_mm,_mmm64 }, + { 0x0F7F,_r,_mmm64,_mm }, + { LODQ,_r,_xmm,_xmm_m64 }, + { STOQ,_r,_xmm_m64,_xmm }, + { 0x0F6E, _r|_64_bit,_mm, _rm64 }, + { 0x0F7E, _r|_64_bit,_rm64,_mm }, + { LODD,_r|_64_bit,_xmm, _rm64 }, + { STOD,_r|_64_bit,_rm64,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVQ[] = /* VMOVQ */ { + { VEX_128_W1(LODD), _r, _xmm, _rm64 }, + { VEX_128_W1(STOD), _r, _rm64, _xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2PACKSSDW[] = /* PACKSSDW */ { + { 0x0F6B, _r,_mm,_mmm64 }, + { PACKSSDW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPACKSSDW[] = /* VPACKSSDW */ { + { VEX_NDS_128_WIG(PACKSSDW), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PACKSSWB[] = /* PACKSSWB */ { + { 0x0F63, _r,_mm,_mmm64 }, + { PACKSSWB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPACKSSWB[] = /* VPACKSSWB */ { + { VEX_NDS_128_WIG(PACKSSWB), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PACKUSWB[] = /* PACKUSWB */ { + { 0x0F67, _r,_mm,_mmm64 }, + { PACKUSWB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPACKUSWB[] = /* VPACKUSWB */ { + { VEX_NDS_128_WIG(PACKUSWB), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PADDB[] = /* PADDB */ { + { 0x0FFC, _r,_mm,_mmm64 }, + { PADDB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPADDB[] = /* VPADDB */ { + { VEX_NDS_128_WIG(PADDB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PADDD[] = /* PADDD */ { + { 0x0FFE, _r,_mm,_mmm64 }, + { PADDD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPADDD[] = /* VPADDD */ { + { VEX_NDS_128_WIG(PADDD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PADDSB[] = /* PADDSB */ { + { 0x0FEC, _r,_mm,_mmm64 }, + { PADDSB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPADDSB[] = /* VPADDSB */ { + { VEX_NDS_128_WIG(PADDSB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PADDSW[] = /* PADDSW */ { + { 0x0FED, _r,_mm,_mmm64 }, + { PADDSW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPADDSW[] = /* VPADDSW */ { + { VEX_NDS_128_WIG(PADDSW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PADDUSB[] = /* PADDUSB */ { + { 0x0FDC, _r,_mm,_mmm64 }, + { PADDUSB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPADDUSB[] = /* VPADDUSB */ { + { VEX_NDS_128_WIG(PADDUSB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PADDUSW[] = /* PADDUSW */ { + { 0x0FDD, _r,_mm,_mmm64 }, + { PADDUSW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPADDUSW[] = /* VPADDUSW */ { + { VEX_NDS_128_WIG(PADDUSW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PADDW[] = /* PADDW */ { + { 0x0FFD, _r,_mm,_mmm64 }, + { PADDW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPADDW[] = /* VPADDW */ { + { VEX_NDS_128_WIG(PADDW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PAND[] = /* PAND */ { + { 0x0FDB, _r,_mm,_mmm64 }, + { PAND, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPAND[] = /* VPAND */ { + { VEX_NDS_128_WIG(PAND), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PANDN[] = /* PANDN */ { + { 0x0FDF, _r,_mm,_mmm64 }, + { PANDN, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPANDN[] = /* VPANDN */ { + { VEX_NDS_128_WIG(PANDN), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PCMPEQB[] = /* PCMPEQB */ { + { 0x0F74, _r,_mm,_mmm64 }, + { PCMPEQB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPEQB[] = /* VPCMPEQB */ { + { VEX_NDS_128_WIG(PCMPEQB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PCMPEQD[] = /* PCMPEQD */ { + { 0x0F76, _r,_mm,_mmm64 }, + { PCMPEQD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPEQD[] = /* VPCMPEQD */ { + { VEX_NDS_128_WIG(PCMPEQD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PCMPEQW[] = /* PCMPEQW */ { + { 0x0F75, _r,_mm,_mmm64 }, + { PCMPEQW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPEQW[] = /* VPCMPEQW */ { + { VEX_NDS_128_WIG(PCMPEQW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PCMPGTB[] = /* PCMPGTB */ { + { 0x0F64, _r,_mm,_mmm64 }, + { PCMPGTB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPGTB[] = /* VPCMPGTB */ { + { VEX_NDS_128_WIG(PCMPGTB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PCMPGTD[] = /* PCMPGTD */ { + { 0x0F66, _r,_mm,_mmm64 }, + { PCMPGTD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPGTD[] = /* VPCMPGTD */ { + { VEX_NDS_128_WIG(PCMPGTD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PCMPGTW[] = /* PCMPGTW */ { + { 0x0F65, _r,_mm,_mmm64 }, + { PCMPGTW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPGTW[] = /* VPCMPGTW */ { + { VEX_NDS_128_WIG(PCMPGTW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMADDWD[] = /* PMADDWD */ { + { 0x0FF5, _r,_mm,_mmm64 }, + { PMADDWD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMADDWD[] = /* VPMADDWD */ { + { VEX_NDS_128_WIG(PMADDWD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSLLW[] = /* PSLLW */ { + { 0x0FF1, _r,_mm,_mmm64 }, + { 0x0F71, _6,_mm,_imm8 }, + { PSLLW, _r,_xmm,_xmm_m128 }, + { 0x660F71, _6,_xmm,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSLLW[] = /* VPSLLW */ { + { VEX_NDS_128_WIG(PSLLW), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDD_128_WIG(0x660F71), _6, _xmm, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSLLD[] = /* PSLLD */ { + { 0x0FF2, _r,_mm,_mmm64 }, + { 0x0F72, _6,_mm,_imm8 }, + { PSLLD, _r,_xmm,_xmm_m128 }, + { 0x660F72, _6,_xmm,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSLLD[] = /* VPSLLD */ { + { VEX_NDS_128_WIG(PSLLD), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDD_128_WIG(0x660F72), _6, _xmm, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSLLQ[] = /* PSLLQ */ { + { 0x0FF3, _r,_mm,_mmm64 }, + { 0x0F73, _6,_mm,_imm8 }, + { PSLLQ, _r,_xmm,_xmm_m128 }, + { PSLLDQ, _6,_xmm,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSLLQ[] = /* VPSLLQ */ { + { VEX_NDS_128_WIG(PSLLQ), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDD_128_WIG(PSLLDQ), _6, _xmm, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSRAW[] = /* PSRAW */ { + { 0x0FE1, _r,_mm,_mmm64 }, + { 0x0F71, _4,_mm,_imm8 }, + { PSRAW, _r,_xmm,_xmm_m128 }, + { 0x660F71, _4,_xmm,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSRAW[] = /* VPSRAW */ { + { VEX_NDS_128_WIG(PSRAW), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDD_128_WIG(0x660F71), _4, _xmm, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSRAD[] = /* PSRAD */ { + { 0x0FE2, _r,_mm,_mmm64 }, + { 0x0F72, _4,_mm,_imm8 }, + { PSRAD, _r,_xmm,_xmm_m128 }, + { 0x660F72, _4,_xmm,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSRAD[] = /* VPSRAD */ { + { VEX_NDS_128_WIG(PSRAD), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDD_128_WIG(0x660F72), _4, _xmm, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSRLW[] = /* PSRLW */ { + { 0x0FD1, _r,_mm,_mmm64 }, + { 0x0F71, _2,_mm,_imm8 }, + { PSRLW, _r,_xmm,_xmm_m128 }, + { 0x660F71, _2,_xmm,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSRLW[] = /* VPSRLW */ { + { VEX_NDS_128_WIG(PSRLW), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDD_128_WIG(0x660F71), _2, _xmm, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSRLD[] = /* PSRLD */ { + { 0x0FD2, _r,_mm,_mmm64 }, + { 0x0F72, _2,_mm,_imm8 }, + { PSRLD, _r,_xmm,_xmm_m128 }, + { 0x660F72, _2,_xmm,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSRLD[] = /* VPSRLD */ { + { VEX_NDS_128_WIG(PSRLD), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDD_128_WIG(0x660F72), _2, _xmm, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSRLQ[] = /* PSRLQ */ { + { 0x0FD3, _r,_mm,_mmm64 }, + { 0x0F73, _2,_mm,_imm8 }, + { PSRLQ, _r,_xmm,_xmm_m128 }, + { PSLLDQ, _2,_xmm,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSRLQ[] = /* VPSRLQ */ { + { VEX_NDS_128_WIG(PSRLQ), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDD_128_WIG(PSLLDQ), _2, _xmm, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSUBB[] = /* PSUBB */ { + { 0x0FF8, _r,_mm,_mmm64 }, + { PSUBB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSUBB[] = /* VPSUBB */ { + { VEX_NDS_128_WIG(PSUBB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSUBD[] = /* PSUBD */ { + { 0x0FFA, _r,_mm,_mmm64 }, + { PSUBD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSUBD[] = /* VPSUBD */ { + { VEX_NDS_128_WIG(PSUBD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSUBSB[] = /* PSUBSB */ { + { 0x0FE8, _r,_mm,_mmm64 }, + { PSUBSB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSUBSB [] = /* VPSUBSB */ { + { VEX_NDS_128_WIG(PSUBSB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSUBSW[] = /* PSUBSW */ { + { 0x0FE9, _r,_mm,_mmm64 }, + { PSUBSW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSUBSW[] = /* VPSUBSW */ { + { VEX_NDS_128_WIG(PSUBSW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSUBUSB[] = /* PSUBUSB */ { + { 0x0FD8, _r,_mm,_mmm64 }, + { PSUBUSB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSUBUSB[] = /* VPSUBUSB */ { + { VEX_NDS_128_WIG(PSUBUSB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSUBUSW[] = /* PSUBUSW */ { + { 0x0FD9, _r,_mm,_mmm64 }, + { PSUBUSW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSUBUSW[] = /* VPSUBUSW */ { + { VEX_NDS_128_WIG(PSUBUSW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + + +PTRNTAB2 aptb2PSUBW[] = /* PSUBW */ { + { 0x0FF9, _r,_mm,_mmm64 }, + { PSUBW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSUBW[] = /* VPSUBW */ { + { VEX_NDS_128_WIG(PSUBW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PUNPCKHBW[] = /* PUNPCKHBW */ { + { 0x0F68, _r,_mm,_mmm64 }, + { PUNPCKHBW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPUNPCKHBW[] = /* VPUNPCKHBW */ { + { VEX_NDS_128_WIG(PUNPCKHBW), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PUNPCKHDQ[] = /* PUNPCKHDQ */ { + { 0x0F6A, _r,_mm,_mmm64 }, + { PUNPCKHDQ, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPUNPCKHDQ[] = /* VPUNPCKHDQ */ { + { VEX_NDS_128_WIG(PUNPCKHDQ), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PUNPCKHWD[] = /* PUNPCKHWD */ { + { 0x0F69, _r,_mm,_mmm64 }, + { PUNPCKHWD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPUNPCKHWD[] = /* VPUNPCKHWD */ { + { VEX_NDS_128_WIG(PUNPCKHWD), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PUNPCKLBW[] = /* PUNPCKLBW */ { + { 0x0F60, _r,_mm,_mmm64 }, + { PUNPCKLBW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPUNPCKLBW[] = /* VPUNPCKLBW */ { + { VEX_NDS_128_WIG(PUNPCKLBW), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PUNPCKLDQ[] = /* PUNPCKLDQ */ { + { 0x0F62, _r,_mm,_mmm64 }, + { PUNPCKLDQ, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPUNPCKLDQ[] = /* VPUNPCKLDQ */ { + { VEX_NDS_128_WIG(PUNPCKLDQ), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PUNPCKLWD[] = /* PUNPCKLWD */ { + { 0x0F61, _r,_mm,_mmm64 }, + { PUNPCKLWD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPUNPCKLWD[] = /* VPUNPCKLWD */ { + { VEX_NDS_128_WIG(PUNPCKLWD), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PXOR[] = /* PXOR */ { + { 0x0FEF, _r,_mm,_mmm64 }, + { PXOR, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPXOR[] = /* VPXOR */ { + { VEX_NDS_128_WIG(PXOR), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +////////////////////// New Opcodes ///////////////////////////// + +#if 0 // Use REP NOP instead +PTRNTAB0 aptb0PAUSE[] = /* PAUSE */ { + { 0xf390, 0 } +}; +#endif + +PTRNTAB0 aptb0SYSCALL[] = /* SYSCALL */ { + { 0x0f05, _modcxr11 } +}; + +PTRNTAB0 aptb0SYSRET[] = /* SYSRET */ { + { 0x0f07, 0 } +}; + +PTRNTAB0 aptb0SYSENTER[] = /* SYSENTER */ { + { 0x0f34, 0 } +}; + +PTRNTAB0 aptb0SYSEXIT[] = /* SYSEXIT */ { + { 0x0f35, 0 } +}; + +PTRNTAB0 aptb0UD2[] = /* UD2 */ { + { 0x0f0b, 0 } +}; + +PTRNTAB0 aptb0LFENCE[] = /* LFENCE */ { + { 0x0FAEE8, 0 } +}; + +PTRNTAB0 aptb0MFENCE[] = /* MFENCE */ { + { 0x0FAEF0, 0 } +}; + +PTRNTAB0 aptb0SFENCE[] = /* SFENCE */ { + { 0x0FAEF8, 0 } +}; + +PTRNTAB1 aptb1FXSAVE[] = /* FXSAVE */ { + { 0x0FAE, _0, _m512 }, + { ASM_END } +}; + +PTRNTAB1 aptb1FXRSTOR[] = /* FXRSTOR */ { + { 0x0FAE, _1, _m512 }, + { ASM_END } +}; + +PTRNTAB1 aptb1LDMXCSR[] = /* LDMXCSR */ { + { 0x0FAE, _2, _m32 }, + { ASM_END } +}; + +PTRNTAB1 aptb1VLDMXCSR[] = /* VLDMXCSR */ { + { VEX_128_WIG(0x0FAE), _2, _m32 }, + { ASM_END } +}; + +PTRNTAB1 aptb1STMXCSR[] = /* STMXCSR */ { + { 0x0FAE, _3, _m32 }, + { ASM_END } +}; + +PTRNTAB1 aptb1VSTMXCSR[] = /* VSTMXCSR */ { + { VEX_128_WIG(0x0FAE), _3, _m32 }, + { ASM_END } +}; + +PTRNTAB1 aptb1CLFLUSH[] = /* CLFLUSH */ { + { 0x0FAE, _7, _m8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2ADDPS[] = /* ADDPS */ { + { ADDPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VADDPS[] = /* VADDPS */ { + { VEX_NDS_128_WIG(ADDPS), _r, _xmm, _xmm, _xmm_m128, }, + { VEX_NDS_256_WIG(ADDPS), _r, _ymm, _ymm, _ymm_m256, }, + { ASM_END } +}; + +PTRNTAB2 aptb2ADDPD[] = /* ADDPD */ { + { ADDPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VADDPD[] = /* VADDPD */ { + { VEX_NDS_128_WIG(ADDPD), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDS_256_WIG(ADDPD), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2ADDSD[] = /* ADDSD */ { + { ADDSD, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VADDSD[] = /* VADDSD */ { + { VEX_NDS_128_WIG(ADDSD), _r, _xmm, _xmm, _xmm_m64, }, + { ASM_END } +}; + +PTRNTAB2 aptb2ADDSS[] = /* ADDSS */ { + { ADDSS, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VADDSS[] = /* VADDSS */ { + { VEX_NDS_128_WIG(ADDSS), _r, _xmm, _xmm, _xmm_m32, }, + { ASM_END } +}; + +PTRNTAB2 aptb2ANDPD[] = /* ANDPD */ { + { ANDPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VANDPD[] = /* VANDPD */ { + { VEX_NDS_128_WIG(ANDPD), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(ANDPD), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2ANDPS[] = /* ANDPS */ { + { ANDPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VANDPS[] = /* VANDPS */ { + { VEX_NDS_128_WIG(ANDPS), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(ANDPS), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2ANDNPD[] = /* ANDNPD */ { + { ANDNPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VANDNPD[] = /* VANDNPD */ { + { VEX_NDS_128_WIG(ANDNPD), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(ANDNPD), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2ANDNPS[] = /* ANDNPS */ { + { ANDNPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VANDNPS[] = /* VANDNPS */ { + { VEX_NDS_128_WIG(ANDNPS), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(ANDNPS), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB3 aptb3CMPPS[] = /* CMPPS */ { + { CMPPS, _r,_xmm,_xmm_m128,_imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VCMPPS[] = /* VCMPPS */ { + { VEX_NDS_128_WIG(CMPPS), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { VEX_NDS_256_WIG(CMPPS), _r, _ymm, _ymm, _ymm_m256, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3CMPPD[] = /* CMPPD */ { + { CMPPD, _r,_xmm,_xmm_m128,_imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VCMPPD[] = /* VCMPPD */ { + { VEX_NDS_128_WIG(CMPPD), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { VEX_NDS_256_WIG(CMPPD), _r, _ymm, _ymm, _ymm_m256, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3CMPSD[] = /* CMPSD */ { + { 0xa7, _32_bit | _I386 | _modsidi }, + { CMPSD, _r,_xmm,_xmm_m64,_imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VCMPSD[] = /* VCMPSD */ { + { VEX_NDS_128_WIG(CMPSD), _r, _xmm, _xmm, _xmm_m64, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3CMPSS[] = /* CMPSS */ { + { CMPSS, _r,_xmm,_xmm_m32,_imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VCMPSS[] = /* VCMPSS */ { + { VEX_NDS_128_WIG(CMPSS), _r, _xmm, _xmm, _xmm_m32, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2COMISD[] = /* COMISD */ { + { COMISD, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCOMISD[] = /* VCOMISD */ { + { VEX_128_WIG(COMISD), _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2COMISS[] = /* COMISS */ { + { COMISS, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCOMISS[] = /* VCOMISS */ { + { VEX_128_WIG(COMISS), _r, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTDQ2PD[] = /* CVTDQ2PD */ { + { CVTDQ2PD, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTDQ2PD[] = /* VCVTDQ2PD */ { + { VEX_128_WIG(CVTDQ2PD), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(CVTDQ2PD), _r, _ymm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTDQ2PS[] = /* CVTDQ2PS */ { + { CVTDQ2PS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTDQ2PS[] = /* VCVTDQ2PS */ { + { VEX_128_WIG(CVTDQ2PS), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(CVTDQ2PS), _r, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTPD2DQ[] = /* CVTPD2DQ */ { + { CVTPD2DQ, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTPD2DQ[] = /* VCVTPD2DQ */ { + { VEX_128_WIG(CVTPD2DQ), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(CVTPD2DQ), _r, _xmm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTPD2PI[] = /* CVTPD2PI */ { + { CVTPD2PI, _r,_mm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTPD2PS[] = /* CVTPD2PS */ { + { CVTPD2PS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTPD2PS[] = /* VCVTPD2PS */ { + { VEX_128_WIG(CVTPD2PS), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(CVTPD2PS), _r, _xmm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTPI2PD[] = /* CVTPI2PD */ { + { CVTPI2PD, _r,_xmm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTPI2PS[] = /* CVTPI2PS */ { + { CVTPI2PS, _r,_xmm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTPS2DQ[] = /* CVTPS2DQ */ { + { CVTPS2DQ, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTPS2DQ[] = /* VCVTPS2DQ */ { + { VEX_128_WIG(CVTPS2DQ), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(CVTPS2DQ), _r, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTPS2PD[] = /* CVTPS2PD */ { + { CVTPS2PD, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTPS2PD[] = /* VCVTPS2PD */ { + { VEX_128_WIG(CVTPS2PD), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(CVTPS2PD), _r, _ymm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTPS2PI[] = /* CVTPS2PI */ { + { CVTPS2PI, _r,_mm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTSD2SI[] = /* CVTSD2SI */ { + { CVTSD2SI, _r,_r32,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTSD2SI[] = /* VCVTSD2SI */ { + { VEX_128_WIG(CVTSD2SI), _r, _r32, _xmm_m64 }, + { VEX_128_W1(CVTSD2SI), _r, _r64, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTSD2SS[] = /* CVTSD2SS */ { + { CVTSD2SS, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VCVTSD2SS[] = /* VCVTSD2SS */ { + { VEX_NDS_128_WIG(CVTSD2SS), _r, _xmm, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTSI2SD[] = /* CVTSI2SD */ { + { CVTSI2SD, _r,_xmm,_rm32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VCVTSI2SD[] = /* VCVTSI2SD */ { + { VEX_NDS_128_WIG(CVTSI2SD), _r, _xmm, _xmm, _rm32 }, + { VEX_NDS_128_W1(CVTSI2SD), _r, _xmm, _xmm, _rm64 }, // implicit REX_W + { ASM_END } +}; + +PTRNTAB2 aptb2CVTSI2SS[] = /* CVTSI2SS */ { + { CVTSI2SS, _r,_xmm,_rm32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VCVTSI2SS[] = /* VCVTSI2SS */ { + { VEX_NDS_128_WIG(CVTSI2SS), _r, _xmm, _xmm, _rm32 }, + { VEX_NDS_128_W1(CVTSI2SS), _r, _xmm, _xmm, _rm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTSS2SD[] = /* CVTSS2SD */ { + { CVTSS2SD, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VCVTSS2SD[] = /* VCVTSS2SD */ { + { VEX_NDS_128_WIG(CVTSS2SD), _r, _xmm, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTSS2SI[] = /* CVTSS2SI */ { + { CVTSS2SI, _r,_r32,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTSS2SI[] = /* VCVTSS2SI */ { + { VEX_128_WIG(CVTSS2SI), _r, _r32, _xmm_m32 }, + { VEX_128_W1(CVTSS2SI), _r, _r64, _xmm_m32 }, // implicit REX_W + { ASM_END } +}; + +PTRNTAB2 aptb2CVTTPD2PI[] = /* CVTTPD2PI */ { + { CVTTPD2PI, _r,_mm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTTPD2DQ[] = /* CVTTPD2DQ */ { + { CVTTPD2DQ, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTTPD2DQ[] = /* VCVTTPD2DQ */ { + { VEX_128_WIG(CVTTPD2DQ), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(CVTTPD2DQ), _r, _xmm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTTPS2DQ[] = /* CVTTPS2DQ */ { + { CVTTPS2DQ, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTTPS2DQ[] = /* VCVTTPS2DQ */ { + { VEX_128_WIG(CVTTPS2DQ), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(CVTTPS2DQ), _r, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTTPS2PI[] = /* CVTTPS2PI */ { + { CVTTPS2PI, _r,_mm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2CVTTSD2SI[] = /* CVTTSD2SI */ { + { CVTTSD2SI, _r,_r32,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTTSD2SI[] = /* VCVTTSD2SI */ { + { VEX_128_WIG(CVTTSD2SI), _r, _r32, _xmm_m64 }, + { VEX_128_W1(CVTTSD2SI), _r, _r64, _xmm_m64 }, // implicit REX_W + { ASM_END } +}; + +PTRNTAB2 aptb2CVTTSS2SI[] = /* CVTTSS2SI */ { + { CVTTSS2SI, _r,_r32,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VCVTTSS2SI[] = /* VCVTTSS2SI */ { + { VEX_128_WIG(CVTTSS2SI), _r, _r32, _xmm_m64 }, + { VEX_128_W1(CVTTSS2SI), _r, _r64, _xmm_m64 }, // implicit REX_W + { ASM_END } +}; + +PTRNTAB2 aptb2DIVPD[] = /* DIVPD */ { + { DIVPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VDIVPD [] = /* VDIVPD */ { + { VEX_NDS_128_WIG(DIVPD), _r, _xmm, _xmm, _xmm_m128, }, + { VEX_NDS_256_WIG(DIVPD), _r, _ymm, _ymm, _ymm_m256, }, + { ASM_END } +}; + +PTRNTAB2 aptb2DIVPS[] = /* DIVPS */ { + { DIVPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VDIVPS [] = /* VDIVPS */ { + { VEX_NDS_128_WIG(DIVPS), _r, _xmm, _xmm, _xmm_m128, }, + { VEX_NDS_256_WIG(DIVPS), _r, _ymm, _ymm, _ymm_m256, }, + { ASM_END } +}; + +PTRNTAB2 aptb2DIVSD[] = /* DIVSD */ { + { DIVSD, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VDIVSD [] = /* VDIVSD */ { + { VEX_NDS_128_WIG(DIVSD), _r, _xmm, _xmm, _xmm_m64, }, + { ASM_END } +}; + +PTRNTAB2 aptb2DIVSS[] = /* DIVSS */ { + { DIVSS, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VDIVSS [] = /* VDIVSS */ { + { VEX_NDS_128_WIG(DIVSS), _r, _xmm, _xmm, _xmm_m32, }, + { ASM_END } +}; + +PTRNTAB2 aptb2MASKMOVDQU[] = /* MASKMOVDQU */ { + { MASKMOVDQU, _r,_xmm,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMASKMOVDQU[] = /* VMASKMOVDQU */ { + { VEX_128_WIG(MASKMOVDQU), _r, _xmm, _xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MASKMOVQ[] = /* MASKMOVQ */ { + { MASKMOVQ, _r,_mm,_mm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MAXPD[] = /* MAXPD */ { + { MAXPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMAXPD[] = /* VMAXPD */ { + { VEX_NDS_128_WIG(MAXPD), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDS_256_WIG(MAXPD), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MAXPS[] = /* MAXPS */ { + { MAXPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMAXPS[] = /* VMAXPS */ { + { VEX_NDS_128_WIG(MAXPS), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDS_256_WIG(MAXPS), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MAXSD[] = /* MAXSD */ { + { MAXSD, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMAXSD[] = /* VMAXSD */ { + { VEX_NDS_128_WIG(MAXSD), _r, _xmm, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MAXSS[] = /* MAXSS */ { + { MAXSS, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMAXSS[] = /* VMAXSS */ { + { VEX_NDS_128_WIG(MAXSS), _r, _xmm, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MINPD[] = /* MINPD */ { + { MINPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMINPD[] = /* VMINPD */ { + { VEX_NDS_128_WIG(MINPD), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDS_256_WIG(MINPD), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MINPS[] = /* MINPS */ { + { MINPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMINPS[] = /* VMINPS */ { + { VEX_NDS_128_WIG(MINPS), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDS_256_WIG(MINPS), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MINSD[] = /* MINSD */ { + { MINSD, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMINSD[] = /* VMINSD */ { + { VEX_NDS_128_WIG(MINSD), _r, _xmm, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MINSS[] = /* MINSS */ { + { MINSS, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMINSS[] = /* VMINSS */ { + { VEX_NDS_128_WIG(MINSS), _r, _xmm, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVAPD[] = /* MOVAPD */ { + { LODAPD, _r,_xmm,_xmm_m128 }, + { STOAPD, _r,_xmm_m128,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVAPD[] = /* VMOVAPD */ { + { VEX_128_WIG(LODAPD), _r, _xmm, _xmm_m128 }, + { VEX_128_WIG(STOAPD), _r, _xmm_m128, _xmm }, + { VEX_256_WIG(LODAPD), _r, _ymm, _ymm_m256 }, + { VEX_256_WIG(STOAPD), _r, _ymm_m256, _ymm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVAPS[] = /* MOVAPS */ { + { LODAPS, _r,_xmm,_xmm_m128 }, + { STOAPS, _r,_xmm_m128,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVAPS [] = /* VMOVAPS */ { + { VEX_128_WIG(LODAPS), _r, _xmm, _xmm_m128, }, + { VEX_128_WIG(STOAPS), _r, _xmm_m128, _xmm, }, + { VEX_256_WIG(LODAPS), _r, _ymm, _ymm_m256, }, + { VEX_256_WIG(STOAPS), _r, _ymm_m256, _ymm, }, + { ASM_END }, +}; + +PTRNTAB2 aptb2MOVDQA[] = /* MOVDQA */ { + { LODDQA, _r,_xmm,_xmm_m128 }, + { STODQA, _r,_xmm_m128,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVDQA[] = /* VMOVDQA */ { + { VEX_128_WIG(LODDQA), _r, _xmm, _xmm_m128 }, + { VEX_128_WIG(STODQA), _r, _xmm_m128, _xmm }, + { VEX_256_WIG(LODDQA), _r, _ymm, _ymm_m256 }, + { VEX_256_WIG(STODQA), _r, _ymm_m256, _ymm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVDQU[] = /* MOVDQU */ { + { LODDQU, _r,_xmm,_xmm_m128 }, + { STODQU, _r,_xmm_m128,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVDQU[] = /* VMOVDQU */ { + { VEX_128_WIG(LODDQU), _r, _xmm, _xmm_m128 }, + { VEX_128_WIG(STODQU), _r, _xmm_m128, _xmm }, + { VEX_256_WIG(LODDQU), _r, _ymm, _ymm_m256 }, + { VEX_256_WIG(STODQU), _r, _ymm_m256, _ymm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVDQ2Q[] = /* MOVDQ2Q */ { + { MOVDQ2Q, _r,_mm,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVHLPS[] = /* MOVHLPS */ { + { MOVHLPS, _r,_xmm,_xmm }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMOVHLPS[] = /* VMOVHLPS */ { + { VEX_NDS_128_WIG(MOVHLPS), _r, _xmm, _xmm, _xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVHPD[] = /* MOVHPD */ { + { LODHPD, _r,_xmm,_xmm_m64 }, + { STOHPD, _r,_xmm_m64,_xmm }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMOVHPD[] = /* VMOVHPD */ { + { VEX_NDS_128_WIG(LODHPD), _r, _xmm, _xmm, _m64 }, + { VEX_128_WIG(STOHPD), _r, _m64, _xmm, 0 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVHPS[] = /* MOVHPS */ { + { LODHPS, _r,_xmm,_xmm_m64 }, + { STOHPS, _r,_xmm_m64,_xmm }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMOVHPS[] = /* VMOVHPS */ { + { VEX_NDS_128_WIG(LODHPS), _r, _xmm, _xmm, _m64 }, + { VEX_128_WIG(STOHPS), _r, _m64, _xmm, 0 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVLHPS[] = /* MOVLHPS */ { + { MOVLHPS, _r,_xmm,_xmm }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMOVLHPS[] = /* VMOVLHPS */ { + { VEX_NDS_128_WIG(MOVLHPS), _r, _xmm, _xmm, _xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVLPD[] = /* MOVLPD */ { + { LODLPD, _r,_xmm,_xmm_m64 }, + { STOLPD, _r,_xmm_m64,_xmm }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMOVLPD[] = /* VMOVLPD */ { + { VEX_NDS_128_WIG(LODLPD), _r, _xmm, _xmm, _m64 }, + { VEX_128_WIG(STOLPD), _r, _m64, _xmm, 0 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVLPS[] = /* MOVLPS */ { + { LODLPS, _r,_xmm,_xmm_m64 }, + { STOLPS, _r,_xmm_m64,_xmm }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMOVLPS[] = /* VMOVLPS */ { + { VEX_NDS_128_WIG(LODLPS), _r, _xmm, _xmm, _m64 }, + { VEX_128_WIG(STOLPS), _r, _m64, _xmm, 0 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVMSKPD[] = /* MOVMSKPD */ { + { MOVMSKPD, _r,_r32,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVMSKPD [] = /* VMOVMSKPD */ { + { VEX_128_WIG(MOVMSKPD), _r, _r32, _xmm }, + { VEX_256_WIG(MOVMSKPD), _r, _r32, _ymm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVMSKPS[] = /* MOVMSKPS */ { + { MOVMSKPS, _r,_r32,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVMSKPS [] = /* VMOVMSKPS */ { + { VEX_128_WIG(MOVMSKPS), _r, _r32, _xmm }, + { VEX_256_WIG(MOVMSKPS), _r, _r32, _ymm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVNTDQ[] = /* MOVNTDQ */ { + { MOVNTDQ, _r,_m128,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVNTDQ[] = /* VMOVNTDQ */ { + { VEX_128_WIG(MOVNTDQ), _r, _m128, _xmm }, + { VEX_256_WIG(MOVNTDQ), _r, _m256, _ymm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVNTI[] = /* MOVNTI */ { + { MOVNTI, _r,_m32,_r32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVNTPD[] = /* MOVNTPD */ { + { MOVNTPD, _r,_m128,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVNTPD[] = /* VMOVNTPD */ { + { VEX_128_WIG(MOVNTPD), _r, _m128, _xmm }, + { VEX_256_WIG(MOVNTPD), _r, _m256, _ymm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVNTPS[] = /* MOVNTPS */ { + { MOVNTPS, _r,_m128,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVNTPS[] = /* VMOVNTPS */ { + { VEX_128_WIG(MOVNTPS), _r, _m128, _xmm }, + { VEX_256_WIG(MOVNTPS), _r, _m256, _ymm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVNTQ[] = /* MOVNTQ */ { + { MOVNTQ, _r,_m64,_mm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVQ2DQ[] = /* MOVQ2DQ */ { + { MOVQ2DQ, _r,_xmm,_mm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVSD[] = /* MOVSD */ { + { 0xa5, _32_bit | _I386 | _modsidi }, + { LODSD, _r, _xmm, _xmm_m64 }, + { STOSD, _r, _xmm_m64, _xmm }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMOVSD[] = /* VMOVSD */ { + { VEX_NDS_128_WIG(LODSD), _r, _xmm, _xmm, _xmm }, + { VEX_128_WIG(STOSD), _r, _m64, _xmm, 0 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVSS[] = /* MOVSS */ { + { LODSS, _r,_xmm,_xmm_m32 }, + { STOSS, _r,_xmm_m32,_xmm }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMOVSS[] = /* VMOVSS */ { + { VEX_NDS_128_WIG(LODSS), _r, _xmm, _xmm, _xmm }, + { VEX_128_WIG(STOSS), _r, _m32, _xmm, 0 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVUPD[] = /* MOVUPD */ { + { LODUPD, _r,_xmm,_xmm_m128 }, + { STOUPD, _r,_xmm_m128,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVUPD[] = /* VMOVUPD */ { + { VEX_128_WIG(LODUPD), _r, _xmm, _xmm_m128 }, + { VEX_128_WIG(STOUPD), _r, _xmm_m128, _xmm }, + { VEX_256_WIG(LODUPD), _r, _ymm, _ymm_m256 }, + { VEX_256_WIG(STOUPD), _r, _ymm_m256, _ymm }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVUPS[] = /* MOVUPS */ { + { LODUPS, _r,_xmm,_xmm_m128 }, + { STOUPS, _r,_xmm_m128,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVUPS [] = /* VMOVUPS */ { + { VEX_128_WIG(LODUPS), _r, _xmm, _xmm_m128, }, + { VEX_128_WIG(STOUPS), _r, _xmm_m128, _xmm, }, + { VEX_256_WIG(LODUPS), _r, _ymm, _ymm_m256, }, + { VEX_256_WIG(STOUPS), _r, _ymm_m256, _ymm, }, + { ASM_END } +}; + +PTRNTAB2 aptb2MULPD[] = /* MULPD */ { + { MULPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMULPD [] = /* VMULPD */ { + { VEX_NDS_128_WIG(MULPD), _r, _xmm, _xmm, _xmm_m128, }, + { VEX_NDS_256_WIG(MULPD), _r, _ymm, _ymm, _ymm_m256, }, + { ASM_END } +}; + +PTRNTAB2 aptb2MULPS[] = /* MULPS */ { + { MULPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMULPS [] = /* VMULPS */ { + { VEX_NDS_128_WIG(MULPS), _r, _xmm, _xmm, _xmm_m128, }, + { VEX_NDS_256_WIG(MULPS), _r, _ymm, _ymm, _ymm_m256, }, + { ASM_END } +}; + +PTRNTAB2 aptb2MULSD[] = /* MULSD */ { + { MULSD, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMULSD [] = /* VMULSD */ { + { VEX_NDS_128_WIG(MULSD), _r, _xmm, _xmm, _xmm_m64, }, + { ASM_END } +}; + +PTRNTAB2 aptb2MULSS[] = /* MULSS */ { + { MULSS, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMULSS [] = /* VMULSS */ { + { VEX_NDS_128_WIG(MULSS), _r, _xmm, _xmm, _xmm_m32, }, + { ASM_END } +}; + +PTRNTAB2 aptb2ORPD[] = /* ORPD */ { + { ORPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VORPD[] = /* VORPD */ { + { VEX_NDS_128_WIG(ORPD), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(ORPD), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2ORPS[] = /* ORPS */ { + { ORPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VORPS[] = /* VORPS */ { + { VEX_NDS_128_WIG(ORPS), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(ORPS), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PADDQ[] = /* PADDQ */ { + { 0x0FD4, _r,_mm,_mmm64 }, + { PADDQ, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPADDQ[] = /* VPADDQ */ { + { VEX_NDS_128_WIG(PADDQ), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PAVGB[] = /* PAVGB */ { + { 0x0FE0, _r,_mm,_mmm64 }, + { PAVGB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPAVGB[] = /* VPAVGB */ { + { VEX_NDS_128_WIG(PAVGB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PAVGW[] = /* PAVGW */ { + { 0x0FE3, _r,_mm,_mmm64 }, + { PAVGW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPAVGW[] = /* VPAVGW */ { + { VEX_NDS_128_WIG(PAVGW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PEXTRW[] = /* PEXTRW */ { + { 0x0FC5, _r,_r32,_mm,_imm8 }, + { 0x0FC5, _r,_r64,_mm,_imm8 }, + { 0x660FC5, _r,_r32,_xmm,_imm8 }, + { 0x660FC5, _r,_r64,_xmm,_imm8 }, + { 0x660F3A15, _r,_m16,_xmm,_imm8 }, // synonym for r32/r64 + { ASM_END } +}; + +PTRNTAB3 aptb3VPEXTRW[] = /* VPEXTRW */ { + { VEX_128_WIG(0x660FC5), _r,_r32,_xmm,_imm8 }, + { VEX_128_WIG(0x660FC5), _r,_r64,_xmm,_imm8 }, + { VEX_128_WIG(0x660F3A15), _r,_m16,_xmm,_imm8 }, // synonym for r32/r64 + { ASM_END } +}; + +PTRNTAB3 aptb3PINSRW[] = /* PINSRW */ { + { 0x0FC4, _r,_mm,_r32m16,_imm8 }, + { PINSRW, _r,_xmm,_r32m16,_imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VPINSRW[] = /* VPINSRW */ { + { VEX_NDS_128_WIG(PINSRW), _r, _xmm, _xmm, _r32m16, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMAXSW[] = /* PMAXSW */ { + { 0x0FEE, _r,_mm,_mmm64 }, + { PMAXSW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMAXSW[] = /* VPMAXSW */ { + { VEX_NDS_128_WIG(PMAXSW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMAXUB[] = /* PMAXUB */ { + { 0x0FDE, _r,_mm,_mmm64 }, + { PMAXUB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMAXUB[] = /* VPMAXUB */ { + { VEX_NDS_128_WIG(PMAXUB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMINSW[] = /* PMINSW */ { + { 0x0FEA, _r,_mm,_mmm64 }, + { PMINSW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMINSW[] = /* VPMINSW */ { + { VEX_NDS_128_WIG(PMINSW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMINUB[] = /* PMINUB */ { + { 0x0FDA, _r,_mm,_mmm64 }, + { PMINUB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMINUB[] = /* VPMINUB */ { + { VEX_NDS_128_WIG(PMINUB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVMSKB[] = /* PMOVMSKB */ { + { 0x0FD7, _r,_r32,_mm }, + { PMOVMSKB, _r,_r32,_xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVMSKB[] = /* VPMOVMSKB */ { + { VEX_128_WIG(PMOVMSKB), _r, _r32, _xmm }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMULHUW[] = /* PMULHUW */ { + { 0x0FE4, _r,_mm,_mmm64 }, + { PMULHUW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMULHUW[] = /* VPMULHUW */ { + { VEX_NDS_128_WIG(PMULHUW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMULHW[] = /* PMULHW */ { + { 0x0FE5, _r,_mm,_mmm64 }, + { PMULHW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMULHW[] = /* VPMULHW */ { + { VEX_NDS_128_WIG(PMULHW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMULLW[] = /* PMULLW */ { + { 0x0FD5, _r,_mm,_mmm64 }, + { PMULLW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMULLW[] = /* VPMULLW */ { + { VEX_NDS_128_WIG(PMULLW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMULUDQ[] = /* PMULUDQ */ { + { 0x0FF4, _r,_mm,_mmm64 }, + { PMULUDQ, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMULUDQ[] = /* VPMULUDQ */ { + { VEX_NDS_128_WIG(PMULUDQ), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2POR[] = /* POR */ { + { 0x0FEB, _r,_mm,_mmm64 }, + { POR, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPOR[] = /* VPOR */ { + { VEX_NDS_128_WIG(POR), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB1 aptb1PREFETCHNTA[] = /* PREFETCHNTA */ { + { PREFETCH, _0,_m8 }, + { ASM_END } +}; + +PTRNTAB1 aptb1PREFETCHT0[] = /* PREFETCHT0 */ { + { PREFETCH, _1,_m8 }, + { ASM_END } +}; + +PTRNTAB1 aptb1PREFETCHT1[] = /* PREFETCHT1 */ { + { PREFETCH, _2,_m8 }, + { ASM_END } +}; + +PTRNTAB1 aptb1PREFETCHT2[] = /* PREFETCHT2 */ { + { PREFETCH, _3,_m8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSADBW[] = /* PSADBW */ { + { 0x0FF6, _r,_mm,_mmm64 }, + { PSADBW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSADBW[] = /* VPSADBW */ { + { VEX_NDS_128_WIG(PSADBW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + + +PTRNTAB3 aptb3PSHUFD[] = /* PSHUFD */ { + { PSHUFD, _r,_xmm,_xmm_m128,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSHUFD[] = /* VPSHUFD */ { + { VEX_128_WIG(PSHUFD), _r,_xmm,_xmm_m128,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PSHUFHW[] = /* PSHUFHW */ { + { PSHUFHW, _r,_xmm,_xmm_m128,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSHUFHW[] = /* VPSHUFHW */ { + { VEX_128_WIG(PSHUFHW), _r,_xmm,_xmm_m128,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PSHUFLW[] = /* PSHUFLW */ { + { PSHUFLW, _r,_xmm,_xmm_m128,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSHUFLW[] = /* VPSHUFLW */ { + { VEX_128_WIG(PSHUFLW), _r,_xmm,_xmm_m128,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PSHUFW[] = /* PSHUFW */ { + { PSHUFW, _r,_mm,_mmm64,_imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSLLDQ[] = /* PSLLDQ */ { + { PSLLDQ, _7,_xmm,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSLLDQ[] = /* VPSLLDQ */ { + { VEX_NDD_128_WIG(PSLLDQ), _7, _xmm, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSRLDQ[] = /* PSRLDQ */ { + { PSRLDQ, _3,_xmm,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSRLDQ[] = /* VPSRLDQ */ { + { VEX_NDD_128_WIG(PSRLDQ), _3, _xmm, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSUBQ[] = /* PSUBQ */ { + { 0x0FFB, _r,_mm,_mmm64 }, + { PSUBQ, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSUBQ[] = /* VPSUBQ */ { + { VEX_NDS_128_WIG(PSUBQ), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PUNPCKHQDQ[] = /* PUNPCKHQDQ */ { + { PUNPCKHQDQ, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPUNPCKHQDQ[] = /* VPUNPCKHQDQ */ { + { VEX_NDS_128_WIG(PUNPCKHQDQ), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PUNPCKLQDQ[] = /* PUNPCKLQDQ */ { + { PUNPCKLQDQ, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPUNPCKLQDQ[] = /* VPUNPCKLQDQ */ { + { VEX_NDS_128_WIG(PUNPCKLQDQ), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2RCPPS[] = /* RCPPS */ { + { RCPPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VRCPPS[] = /* VRCPPS */ { + { VEX_128_WIG(RCPPS), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(RCPPS), _r, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2RCPSS[] = /* RCPSS */ { + { RCPSS, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VRCPSS[] = /* VRCPSS */ { + { VEX_NDS_128_WIG(RCPSS), _r, _xmm, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2RSQRTPS[] = /* RSQRTPS */ { + { RSQRTPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2RSQRTSS[] = /* RSQRTSS */ { + { RSQRTSS, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3SHUFPD[] = /* SHUFPD */ { + { SHUFPD, _r,_xmm,_xmm_m128,_imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VSHUFPD[] = /* VSHUFPD */ { + { VEX_NDS_128_WIG(SHUFPD), _r,_xmm,_xmm,_xmm_m128,_imm8 }, + { VEX_NDS_256_WIG(SHUFPD), _r,_ymm,_ymm,_ymm_m256,_imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3SHUFPS[] = /* SHUFPS */ { + { SHUFPS, _r,_xmm,_xmm_m128,_imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VSHUFPS[] = /* VSHUFPS */ { + { VEX_NDS_128_WIG(SHUFPS), _r,_xmm,_xmm,_xmm_m128,_imm8 }, + { VEX_NDS_256_WIG(SHUFPS), _r,_ymm,_ymm,_ymm_m256,_imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2SQRTPD[] = /* SQRTPD */ { + { SQRTPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VSQRTPD[] = /* VSQRTPD */ { + { VEX_128_WIG(SQRTPD), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(SQRTPD), _r, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2SQRTPS[] = /* SQRTPS */ { + { SQRTPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VSQRTPS[] = /* VSQRTPS */ { + { VEX_128_WIG(SQRTPS), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(SQRTPS), _r, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2SQRTSD[] = /* SQRTSD */ { + { SQRTSD, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VSQRTSD[] = /* VSQRTSD */ { + { VEX_NDS_128_WIG(SQRTSD), _r, _xmm, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2SQRTSS[] = /* SQRTSS */ { + { SQRTSS, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VSQRTSS[] = /* VSQRTSS */ { + { VEX_NDS_128_WIG(SQRTSS), _r, _xmm, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2SUBPD[] = /* SUBPD */ { + { SUBPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VSUBPD [] = /* VSUBPD */ { + { VEX_NDS_128_WIG(SUBPD), _r, _xmm, _xmm, _xmm_m128, }, + { VEX_NDS_256_WIG(SUBPD), _r, _ymm, _ymm, _ymm_m256, }, + { ASM_END } +}; + +PTRNTAB2 aptb2SUBPS[] = /* SUBPS */ { + { SUBPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VSUBPS [] = /* VSUBPS */ { + { VEX_NDS_128_WIG(SUBPS), _r, _xmm, _xmm, _xmm_m128, }, + { VEX_NDS_256_WIG(SUBPS), _r, _ymm, _ymm, _ymm_m256, }, + { ASM_END } +}; + +PTRNTAB2 aptb2SUBSD[] = /* SUBSD */ { + { SUBSD, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VSUBSD[] = /* VSUBSD */ { + { VEX_NDS_128_WIG(SUBSD), _r, _xmm, _xmm, _xmm_m64, }, + { ASM_END } +}; + +PTRNTAB2 aptb2SUBSS[] = /* SUBSS */ { + { SUBSS, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VSUBSS[] = /* VSUBSS */ { + { VEX_NDS_128_WIG(SUBSS), _r, _xmm, _xmm, _xmm_m32, }, + { ASM_END } +}; + +PTRNTAB2 aptb2UCOMISD[] = /* UCOMISD */ { + { UCOMISD, _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VUCOMISD[] = /* VUCOMISD */ { + { VEX_128_WIG(UCOMISD), _r,_xmm,_xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2UCOMISS[] = /* UCOMISS */ { + { UCOMISS, _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VUCOMISS[] = /* VUCOMISS */ { + { VEX_128_WIG(UCOMISS), _r,_xmm,_xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2UNPCKHPD[] = /* UNPCKHPD */ { + { UNPCKHPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VUNPCKHPD[] = /* VUNPCKHPD */ { + { VEX_NDS_128_WIG(UNPCKHPD), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(UNPCKHPD), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2UNPCKHPS[] = /* UNPCKHPS */ { + { UNPCKHPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VUNPCKHPS[] = /* VUNPCKHPS */ { + { VEX_NDS_128_WIG(UNPCKHPS), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(UNPCKHPS), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2UNPCKLPD[] = /* UNPCKLPD */ { + { UNPCKLPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VUNPCKLPD[] = /* VUNPCKLPD */ { + { VEX_NDS_128_WIG(UNPCKLPD), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(UNPCKLPD), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2UNPCKLPS[] = /* UNPCKLPS */ { + { UNPCKLPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VUNPCKLPS[] = /* VUNPCKLPS */ { + { VEX_NDS_128_WIG(UNPCKLPS), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(UNPCKLPS), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2XORPD[] = /* XORPD */ { + { XORPD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VXORPD[] = /* VXORPD */ { + { VEX_NDS_128_WIG(XORPD), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(XORPD), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2XORPS[] = /* XORPS */ { + { XORPS, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VXORPS[] = /* VXORPS */ { + { VEX_NDS_128_WIG(XORPS), _r,_xmm,_xmm,_xmm_m128 }, + { VEX_NDS_256_WIG(XORPS), _r,_ymm,_ymm,_ymm_m256 }, + { ASM_END } +}; + +/**** AMD only instructions ****/ + +/* + pavgusb + pf2id + pfacc + pfadd + pfcmpeq + pfcmpge + pfcmpgt + pfmax + pfmin + pfmul + pfnacc + pfpnacc + pfrcp + pfrcpit1 + pfrcpit2 + pfrsqit1 + pfrsqrt + pfsub + pfsubr + pi2fd + pmulhrw + pswapd +*/ + +PTRNTAB2 aptb2PAVGUSB[] = /* PAVGUSB */ { + { 0x0F0FBF, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PF2ID[] = /* PF2ID */ { + { 0x0F0F1D, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFACC[] = /* PFACC */ { + { 0x0F0FAE, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFADD[] = /* PFADD */ { + { 0x0F0F9E, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFCMPEQ[] = /* PFCMPEQ */ { + { 0x0F0FB0, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFCMPGE[] = /* PFCMPGE */ { + { 0x0F0F90, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFCMPGT[] = /* PFCMPGT */ { + { 0x0F0FA0, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFMAX[] = /* PFMAX */ { + { 0x0F0FA4, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFMIN[] = /* PFMIN */ { + { 0x0F0F94, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFMUL[] = /* PFMUL */ { + { 0x0F0FB4, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFNACC[] = /* PFNACC */ { + { 0x0F0F8A, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFPNACC[] = /* PFPNACC */ { + { 0x0F0F8E, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFRCP[] = /* PFRCP */ { + { 0x0F0F96, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFRCPIT1[] = /* PFRCPIT1 */ { + { 0x0F0FA6, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFRCPIT2[] = /* PFRCPIT2 */ { + { 0x0F0FB6, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFRSQIT1[] = /* PFRSQIT1 */ { + { 0x0F0FA7, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFRSQRT[] = /* PFRSQRT */ { + { 0x0F0F97, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFSUB[] = /* PFSUB */ { + { 0x0F0F9A, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PFSUBR[] = /* PFSUBR */ { + { 0x0F0FAA, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PI2FD[] = /* PI2FD */ { + { 0x0F0F0D, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMULHRW[] = /* PMULHRW */ { + { 0x0F0FB7, _r,_mm,_mmm64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSWAPD[] = /* PSWAPD */ { + { 0x0F0FBB, _r,_mm,_mmm64 }, + { ASM_END } +}; + +/* ======================= Pentium 4 (Prescott) ======================= */ + +/* + ADDSUBPD + ADDSUBPS + FISTTP + HADDPD + HADDPS + HSUBPD + HSUBPS + LDDQU + MONITOR + MOVDDUP + MOVSHDUP + MOVSLDUP + MWAIT + */ + +PTRNTAB1 aptb1FISTTP[] = /* FISTTP */ { + { 0xdf, _1, _m16 }, + { 0xdb, _1, _m32 }, + { 0xdd, _1, _fm64 }, + { ASM_END } +}; + +PTRNTAB0 aptb0MONITOR[] = /* MONITOR */ { + { MONITOR, 0 } +}; + +PTRNTAB0 aptb0MWAIT[] = /* MWAIT */ { + { MWAIT, 0 } +}; + +PTRNTAB2 aptb2ADDSUBPD[] = /* ADDSUBPD */ { + { ADDSUBPD, _r,_xmm,_xmm_m128 }, // xmm1,xmm2/m128 + { ASM_END } +}; + +PTRNTAB3 aptb3VADDSUBPD[] = /* VADDSUBPD */ { + { VEX_NDS_128_WIG(ADDSUBPD), _r, _xmm, _xmm, _xmm_m128, }, + { VEX_NDS_256_WIG(ADDSUBPD), _r, _ymm, _ymm, _ymm_m256, }, + { ASM_END } +}; + +PTRNTAB2 aptb2ADDSUBPS[] = /* ADDSUBPS */ { + { ADDSUBPS, _r,_xmm,_xmm_m128 }, // xmm1,xmm2/m128 + { ASM_END } +}; + +PTRNTAB3 aptb3VADDSUBPS[] = /* VADDSUBPS */ { + { VEX_NDS_128_WIG(ADDSUBPS), _r, _xmm, _xmm, _xmm_m128, }, + { VEX_NDS_256_WIG(ADDSUBPS), _r, _ymm, _ymm, _ymm_m256, }, + { ASM_END } +}; + +PTRNTAB2 aptb2HADDPD[] = /* HADDPD */ { + { HADDPD, _r,_xmm,_xmm_m128 }, // xmm1,xmm2/m128 + { ASM_END } +}; + +PTRNTAB3 aptb3VHADDPD[] = /* VHADDPD */ { + { VEX_NDS_128_WIG(HADDPD), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDS_256_WIG(HADDPD), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2HADDPS[] = /* HADDPS */ { + { HADDPS, _r,_xmm,_xmm_m128 }, // xmm1,xmm2/m128 + { ASM_END } +}; + +PTRNTAB3 aptb3VHADDPS[] = /* VHADDPS */ { + { VEX_NDS_128_WIG(HADDPS), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDS_256_WIG(HADDPS), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2HSUBPD[] = /* HSUBPD */ { + { HSUBPD, _r,_xmm,_xmm_m128 }, // xmm1,xmm2/m128 + { ASM_END } +}; + +PTRNTAB2 aptb2HSUBPS[] = /* HSUBPS */ { + { HSUBPS, _r,_xmm,_xmm_m128 }, // xmm1,xmm2/m128 + { ASM_END } +}; + +PTRNTAB2 aptb2LDDQU[] = /* LDDQU */ { + { LDDQU, _r,_xmm,_m128 }, // xmm1,mem + { ASM_END } +}; + +PTRNTAB2 aptb2VLDDQU[] = /* VLDDQU */ { + { VEX_128_WIG(LDDQU), _r, _xmm, _m128 }, + { VEX_256_WIG(LDDQU), _r, _ymm, _m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVDDUP[] = /* MOVDDUP */ { + { MOVDDUP, _r,_xmm,_xmm_m64 }, // xmm1,xmm2/m64 + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVDDUP[] = /* VMOVDDUP */ { + { VEX_128_WIG(MOVDDUP), _r,_xmm,_xmm_m64 }, + { VEX_256_WIG(MOVDDUP), _r,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVSHDUP[] = /* MOVSHDUP */ { + { MOVSHDUP, _r,_xmm,_xmm_m128 }, // xmm1,xmm2/m128 + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVSHDUP[] = /* VMOVSHDUP */ { + { VEX_128_WIG(MOVSHDUP), _r,_xmm,_xmm_m128 }, + { VEX_256_WIG(MOVSHDUP), _r,_ymm,_ymm_m256 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVSLDUP[] = /* MOVSLDUP */ { + { MOVSLDUP, _r,_xmm,_xmm_m128 }, // xmm1,xmm2/m128 + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVSLDUP[] = /* VMOVSLDUP */ { + { VEX_128_WIG(MOVSLDUP), _r,_xmm,_xmm_m128 }, + { VEX_256_WIG(MOVSLDUP), _r,_ymm,_ymm_m256 }, + { ASM_END } +}; + +/* ======================= SSSE3 ======================= */ + +/* +palignr +phaddd +phaddw +phaddsw +phsubd +phsubw +phsubsw +pmaddubsw +pmulhrsw +pshufb +pabsb +pabsd +pabsw +psignb +psignd +psignw +*/ + +PTRNTAB3 aptb3PALIGNR[] = /* PALIGNR */ { + { 0x0F3A0F, _r,_mm,_mmm64, _imm8 }, + { PALIGNR, _r,_xmm,_xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VPALIGNR[] = /* VPALIGNR */ { + { VEX_NDS_128_WIG(PALIGNR), _r,_xmm,_xmm,_xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PHADDD[] = /* PHADDD */ { + { 0x0F3802, _r,_mm,_mmm64 }, + { PHADDD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPHADDD[] = /* VPHADDD */ { + { VEX_NDS_128_WIG(PHADDD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PHADDW[] = /* PHADDW */ { + { 0x0F3801, _r,_mm,_mmm64 }, + { PHADDW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPHADDW[] = /* VPHADDW */ { + { VEX_NDS_128_WIG(PHADDW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PHADDSW[] = /* PHADDSW */ { + { 0x0F3803, _r,_mm,_mmm64 }, + { PHADDSW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPHADDSW[] = /* VPHADDSW */ { + { VEX_NDS_128_WIG(PHADDSW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PHSUBD[] = /* PHSUBD */ { + { 0x0F3806, _r,_mm,_mmm64 }, + { PHSUBD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPHSUBD[] = /* VPHSUBD */ { + { VEX_NDS_128_WIG(PHSUBD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PHSUBW[] = /* PHSUBW */ { + { 0x0F3805, _r,_mm,_mmm64 }, + { PHSUBW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPHSUBW[] = /* VPHSUBW */ { + { VEX_NDS_128_WIG(PHSUBW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PHSUBSW[] = /* PHSUBSW */ { + { 0x0F3807, _r,_mm,_mmm64 }, + { PHSUBSW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPHSUBSW[] = /* VPHSUBSW */ { + { VEX_NDS_128_WIG(PHSUBSW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMADDUBSW[] = /* PMADDUBSW */ { + { 0x0F3804, _r,_mm,_mmm64 }, + { PMADDUBSW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMADDUBSW[] = /* VPMADDUBSW */ { + { VEX_NDS_128_WIG(PMADDUBSW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMULHRSW[] = /* PMULHRSW */ { + { 0x0F380B, _r,_mm,_mmm64 }, + { PMULHRSW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMULHRSW[] = /* VPMULHRSW */ { + { VEX_NDS_128_WIG(PMULHRSW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSHUFB[] = /* PSHUFB */ { + { 0x0F3800, _r,_mm,_mmm64 }, + { PSHUFB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSHUFB[] = /* VPSHUFB */ { + { VEX_NDS_128_WIG(PSHUFB), _r,_xmm,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PABSB[] = /* PABSB */ { + { 0x0F381C, _r,_mm,_mmm64 }, + { PABSB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPABSB [] = /* VPABSB */ { + { VEX_128_WIG(PABSB), _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + + +PTRNTAB2 aptb2PABSD[] = /* PABSD */ { + { 0x0F381E, _r,_mm,_mmm64 }, + { PABSD, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPABSD [] = /* VPABSD */ { + { VEX_128_WIG(PABSD), _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PABSW[] = /* PABSW */ { + { 0x0F381D, _r,_mm,_mmm64 }, + { PABSW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPABSW [] = /* VPABSW */ { + { VEX_128_WIG(PABSW), _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSIGNB[] = /* PSIGNB */ { + { 0x0F3808, _r,_mm,_mmm64 }, + { PSIGNB, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSIGNB[] = /* VPSIGNB */ { + { VEX_NDS_128_WIG(PSIGNB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSIGND[] = /* PSIGND */ { + { 0x0F380A, _r,_mm,_mmm64 }, + { PSIGND, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSIGND[] = /* VPSIGND */ { + { VEX_NDS_128_WIG(PSIGND), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PSIGNW[] = /* PSIGNW */ { + { 0x0F3809, _r,_mm,_mmm64 }, + { PSIGNW, _r,_xmm,_xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPSIGNW[] = /* VPSIGNW */ { + { VEX_NDS_128_WIG(PSIGNW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +/* ======================= SSE4.1 ======================= */ + +/* +blendpd +blendps +blendvpd +blendvps +dppd +dpps +extractps +insertps +movntdqa +mpsadbw +packusdw +pblendvb +pblendw +pcmpeqq +pextrb +pextrd +pextrq +pextrw +phminposuw +pinsrb +pinsrd +pinsrq +pmaxsb +pmaxsd +pmaxud +pmaxuw +pminsb +pminsd +pminud +pminuw +pmovsxbd +pmovsxbq +pmovsxbw +pmovsxwd +pmovsxwq +pmovsxdq +pmovzxbd +pmovzxbq +pmovzxbw +pmovzxwd +pmovzxwq +pmovzxdq +pmuldq +pmulld +ptest +roundpd +roundps +roundsd +roundss + */ + +PTRNTAB3 aptb3BLENDPD[] = /* BLENDPD */ { + { BLENDPD, _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VBLENDPD[] = /* VBLENDPD */ { + { VEX_NDS_128_WIG(BLENDPD), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { VEX_NDS_256_WIG(BLENDPD), _r, _ymm, _ymm, _ymm_m256, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3BLENDPS[] = /* BLENDPS */ { + { BLENDPS, _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VBLENDPS[] = /* VBLENDPS */ { + { VEX_NDS_128_WIG(BLENDPS), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { VEX_NDS_256_WIG(BLENDPS), _r, _ymm, _ymm, _ymm_m256, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3BLENDVPD[] = /* BLENDVPD */ { + { BLENDVPD, _r, _xmm, _xmm_m128, _xmm0 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VBLENDVPD[] = /* VBLENDVPD */ { + { VEX_NDS_128_WIG(0x660F3A4B), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { VEX_NDS_256_WIG(0x660F3A4B), _r, _ymm, _ymm, _ymm_m256, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3BLENDVPS[] = /* BLENDVPS */ { + { BLENDVPS, _r, _xmm, _xmm_m128, _xmm0 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VBLENDVPS[] = /* VBLENDVPS */ { + { VEX_NDS_128_WIG(0x660F3A4A), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { VEX_NDS_256_WIG(0x660F3A4A), _r, _ymm, _ymm, _ymm_m256, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3DPPD[] = /* DPPD */ { + { DPPD, _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VDPPD[] = /* VDPPD */ { + { VEX_NDS_128_WIG(DPPD), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3DPPS[] = /* DPPS */ { + { DPPS, _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VDPPS[] = /* VDPPS */ { + { VEX_NDS_128_WIG(DPPS), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { VEX_NDS_256_WIG(DPPS), _r, _ymm, _ymm, _ymm_m256, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3EXTRACTPS[] = /* EXTRACTPS */ { + { EXTRACTPS, _r, _rm32, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VEXTRACTPS[] = /* VEXTRACTPS */ { + { VEX_128_WIG(EXTRACTPS), _r, _rm32, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3INSERTPS[] = /* INSERTPS */ { + { INSERTPS, _r, _xmm, _xmm_m32, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VINSERTPS[] = /* VINSERTPS */ { + { VEX_NDS_128_WIG(INSERTPS), _r, _xmm, _xmm, _xmm_m32, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2MOVNTDQA[] = /* MOVNTDQA */ { + { MOVNTDQA, _r, _xmm, _m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VMOVNTDQA[] = /* VMOVNTDQA */ { + { VEX_128_WIG(MOVNTDQA), _r, _xmm, _m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3MPSADBW[] = /* MPSADBW */ { + { MPSADBW, _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VMPSADBW [] = /* VMPSADBW */ { + { VEX_NDS_128_WIG(MPSADBW), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PACKUSDW[] = /* PACKUSDW */ { + { PACKUSDW, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPACKUSDW[] = /* VPACKUSDW */ { + { VEX_NDS_128_WIG(PACKUSDW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PBLENDVB[] = /* PBLENDVB */ { + { PBLENDVB, _r, _xmm, _xmm_m128, _xmm0 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VPBLENDVB[] = /* VPBLENDVB */ { + { VEX_NDS_128_WIG(0x660F3A4C), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PBLENDW[] = /* PBLENDW */ { + { PBLENDW, _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VPBLENDW[] = /* VPBLENDW */ { + { VEX_NDS_128_WIG(PBLENDW), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PCMPEQQ[] = /* PCMPEQQ */ { + { PCMPEQQ, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPEQQ[] = /* VPCMPEQQ */ { + { VEX_NDS_128_WIG(PCMPEQQ), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PEXTRB[] = /* PEXTRB */ { + { PEXTRB, _r, _regm8, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPEXTRB[] = /* VPEXTRB */ { + { VEX_128_WIG(PEXTRB), _r, _regm8, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PEXTRD[] = /* PEXTRD */ { + { PEXTRD, _r, _rm32, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPEXTRD[] = /* VPEXTRD */ { + { VEX_128_WIG(PEXTRD), _r, _rm32, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PEXTRQ[] = /* PEXTRQ */ { + { PEXTRQ, _r|_64_bit, _rm64, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPEXTRQ[] = /* VPEXTRQ */ { + { VEX_128_W1(PEXTRD), _r, _rm64, _xmm, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PHMINPOSUW[] = /* PHMINPOSUW */ { + { PHMINPOSUW, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPHMINPOSUW[] = /* VPHMINPOSUW */ { + { VEX_128_WIG(PHMINPOSUW), _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PINSRB[] = /* PINSRB */ { + { PINSRB, _r, _xmm, _r32, _imm8 }, + { PINSRB, _r, _xmm, _rm8, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VPINSRB[] = /* VPINSRB */ { + { VEX_NDS_128_WIG(PINSRB), _r, _xmm, _xmm, _r32m8, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PINSRD[] = /* PINSRD */ { + { PINSRD, _r, _xmm, _rm32, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VPINSRD[] = /* VPINSRD */ { + { VEX_NDS_128_WIG(PINSRD), _r, _xmm, _xmm, _rm32, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PINSRQ[] = /* PINSRQ */ { + { PINSRQ, _r|_64_bit, _xmm, _rm64, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VPINSRQ[] = /* VPINSRQ */ { + { VEX_NDS_128_W1(PINSRD), _r, _xmm, _xmm, _rm64, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMAXSB[] = /* PMAXSB */ { + { PMAXSB, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMAXSB[] = /* VPMAXSB */ { + { VEX_NDS_128_WIG(PMAXSB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMAXSD[] = /* PMAXSD */ { + { PMAXSD, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMAXSD[] = /* VPMAXSD */ { + { VEX_NDS_128_WIG(PMAXSD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMAXUD[] = /* PMAXUD */ { + { PMAXUD, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMAXUD[] = /* VPMAXUD */ { + { VEX_NDS_128_WIG(PMAXUD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMAXUW[] = /* PMAXUW */ { + { PMAXUW, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMAXUW[] = /* VPMAXUW */ { + { VEX_NDS_128_WIG(PMAXUW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMINSB[] = /* PMINSB */ { + { PMINSB, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMINSB[] = /* VPMINSB */ { + { VEX_NDS_128_WIG(PMINSB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMINSD[] = /* PMINSD */ { + { PMINSD, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMINSD[] = /* VPMINSD */ { + { VEX_NDS_128_WIG(PMINSD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMINUD[] = /* PMINUD */ { + { PMINUD, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMINUD[] = /* VPMINUD */ { + { VEX_NDS_128_WIG(PMINUD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMINUW[] = /* PMINUW */ { + { PMINUW, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMINUW[] = /* VPMINUW */ { + { VEX_NDS_128_WIG(PMINUW), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVSXBW[] = /* PMOVSXBW */ { + { PMOVSXBW, _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVSXBW[] = /* VPMOVSXBW */ { + { VEX_128_WIG(PMOVSXBW), _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVSXBD[] = /* PMOVSXBD */ { + { PMOVSXBD, _r, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVSXBD[] = /* VPMOVSXBD */ { + { VEX_128_WIG(PMOVSXBD), _r, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVSXBQ[] = /* PMOVSXBQ */ { + { PMOVSXBQ, _r, _xmm, _xmm_m16 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVSXBQ[] = /* VPMOVSXBQ */ { + { VEX_128_WIG(PMOVSXBQ), _r, _xmm, _xmm_m16 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVSXWD[] = /* PMOVSXWD */ { + { PMOVSXWD, _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVSXWD[] = /* VPMOVSXWD */ { + { VEX_128_WIG(PMOVSXWD), _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVSXWQ[] = /* PMOVSXWQ */ { + { PMOVSXWQ, _r, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVSXWQ[] = /* VPMOVSXWQ */ { + { VEX_128_WIG(PMOVSXWQ), _r, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVSXDQ[] = /* PMOVSXDQ */ { + { PMOVSXDQ, _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVSXDQ[] = /* VPMOVSXDQ */ { + { VEX_128_WIG(PMOVSXDQ), _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVZXBW[] = /* PMOVZXBW */ { + { PMOVZXBW, _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVZXBW[] = /* VPMOVZXBW */ { + { VEX_128_WIG(PMOVZXBW), _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVZXBD[] = /* PMOVZXBD */ { + { PMOVZXBD, _r, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVZXBD[] = /* VPMOVZXBD */ { + { VEX_128_WIG(PMOVZXBD), _r, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVZXBQ[] = /* PMOVZXBQ */ { + { PMOVZXBQ, _r, _xmm, _xmm_m16 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVZXBQ[] = /* VPMOVZXBQ */ { + { VEX_128_WIG(PMOVZXBQ), _r, _xmm, _xmm_m16 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVZXWD[] = /* PMOVZXWD */ { + { PMOVZXWD, _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVZXWD[] = /* VPMOVZXWD */ { + { VEX_128_WIG(PMOVZXWD), _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVZXWQ[] = /* PMOVZXWQ */ { + { PMOVZXWQ, _r, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVZXWQ[] = /* VPMOVZXWQ */ { + { VEX_128_WIG(PMOVZXWQ), _r, _xmm, _xmm_m32 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMOVZXDQ[] = /* PMOVZXDQ */ { + { PMOVZXDQ, _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPMOVZXDQ[] = /* VPMOVZXDQ */ { + { VEX_128_WIG(PMOVZXDQ), _r, _xmm, _xmm_m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMULDQ[] = /* PMULDQ */ { + { PMULDQ, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PMULLD[] = /* PMULLD */ { + { PMULLD, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPMULLD[] = /* VPMULLD */ { + { VEX_NDS_128_WIG(PMULLD), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PTEST[] = /* PTEST */ { + { PTEST, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VPTEST[] = /* VPTEST */ { + { VEX_128_WIG(PTEST), _r, _xmm, _xmm_m128 }, + { VEX_256_WIG(PTEST), _r, _ymm, _ymm_m256 }, + { ASM_END } +}; + +PTRNTAB3 aptb3ROUNDPD[] = /* ROUNDPD */ { + { ROUNDPD, _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VROUNDPD[] = /* VROUNDPD */ { + { VEX_128_WIG(ROUNDPD), _r, _xmm, _xmm_m128, _imm8 }, + { VEX_256_WIG(ROUNDPD), _r, _ymm, _ymm_m256, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3ROUNDPS[] = /* ROUNDPS */ { + { ROUNDPS, _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VROUNDPS[] = /* VROUNDPS */ { + { VEX_128_WIG(ROUNDPS), _r, _xmm, _xmm_m128, _imm8 }, + { VEX_256_WIG(ROUNDPS), _r, _ymm, _ymm_m256, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3ROUNDSD[] = /* ROUNDSD */ { + { ROUNDSD, _r, _xmm, _xmm_m64, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VROUNDSD[] = /* VROUNDSD */ { + { VEX_NDS_128_WIG(ROUNDSD), _r, _xmm, _xmm, _xmm_m64, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3ROUNDSS[] = /* ROUNDSS */ { + { ROUNDSS, _r, _xmm, _xmm_m32, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VROUNDSS[] = /* VROUNDSS */ { + { VEX_NDS_128_WIG(ROUNDSS), _r, _xmm, _xmm, _xmm_m32, _imm8 }, + { ASM_END } +}; + +/* ======================= SSE4.2 ======================= */ + +/* +crc32 +pcmpestri +pcmpestrm +pcmpistri +pcmpistrm +pcmpgtq +popcnt + */ + +PTRNTAB2 aptb2CRC32[] = /* CRC32 */ { + { 0xF20F38F0, _r , _r32, _rm8 }, + { 0xF20F38F0, _r|_64_bit, _r64, _rm8 }, + { 0xF20F38F1, _r|_16_bit, _r32, _rm16 }, + { 0xF20F38F1, _r|_32_bit, _r32, _rm32 }, + { 0xF20F38F1, _r|_64_bit, _r64, _rm64 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PCMPESTRI [] = /* PCMPESTRI */ { + { PCMPESTRI, _r|_modcx , _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPESTRI[] = /* VPCMPESTRI */ { + { VEX_128_WIG(PCMPESTRI), _r|_modcx, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PCMPESTRM[] = /* PCMPESTRM */ { + { PCMPESTRM, _r|_modxmm0, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPESTRM[] = /* VPCMPESTRM */ { + { VEX_128_WIG(PCMPESTRM), _r|_modxmm0, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PCMPISTRI [] = /* PCMPISTRI */ { + { PCMPISTRI, _r|_modcx , _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPISTRI[] = /* VPCMPISTRI */ { + { VEX_128_WIG(PCMPISTRI), _r|_modcx, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3PCMPISTRM [] = /* PCMPISTRM */ { + { PCMPISTRM, _r|_modxmm0, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPISTRM[] = /* VPCMPISTRM */ { + { VEX_128_WIG(PCMPISTRM), _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB2 aptb2PCMPGTQ [] = /* PCMPGTQ */ { + { PCMPGTQ, _r, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VPCMPGTQ[] = /* VPCMPGTQ */ { + { VEX_NDS_128_WIG(PCMPGTQ), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2POPCNT [] = /* POPCNT */ { + { POPCNT, _r|_16_bit, _r16, _rm16 }, + { POPCNT, _r|_32_bit, _r32, _rm32 }, + { POPCNT, _r|_64_bit, _r64, _rm64 }, + { ASM_END } +}; + +/* ======================= VMS ======================= */ + +/* +invept +invvpid +vmcall +vmclear +vmlaunch +vmresume +vmptrld +vmptrst +vmread +vmwrite +vmxoff +vmxon + */ + +/* ======================= SMX ======================= */ + +/* +getsec + */ + +/* ======================= CLMUL ======================= */ + +PTRNTAB3 aptb3PCLMULQDQ[] = /* PCLMULQDQ */ { + { 0x660F3A44, _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VPCLMULQDQ[] = /* VPCLMULQDQ */ { + { VEX_NDS_128_WIG(0x660F3A44), _r, _xmm, _xmm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +/* ======================= AVX ======================= */ + +PTRNTAB2 aptb2VBROADCASTF128[] = /* VBROADCASTF128 */ { + { VEX_256_WIG(0x660F381A), _r, _ymm, _m128 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VBROADCASTSD[] = /* VBROADCASTSD */ { + { VEX_256_WIG(0x660F3819), _r, _ymm, _m64 }, + { ASM_END } +}; + +PTRNTAB2 aptb2VBROADCASTSS[] = /* VBROADCASTSS */ { + { VEX_128_WIG(0x660F3818), _r, _xmm, _m32 }, + { VEX_256_WIG(0x660F3818), _r, _ymm, _m32 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VEXTRACTF128[] = /* VEXTRACTF128 */ { + { VEX_256_WIG(0x660F3A19), _r, _xmm_m128, _ymm, _imm8 }, + { ASM_END } +}; + +PTRNTAB4 aptb4VINSERTF128[] = /* VINSERTF128 */ { + { VEX_NDS_256_WIG(0x660F3A18), _r, _ymm, _ymm, _xmm_m128, _imm8 }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMASKMOVPS[] = /* VMASKMOVPS */ { + { VEX_NDS_128_WIG(0x660F382C), _r, _xmm, _xmm, _m128 }, + { VEX_NDS_256_WIG(0x660F382C), _r, _ymm, _ymm, _m256 }, + { VEX_NDS_128_WIG(0x660F382E), _r, _m128, _xmm, _xmm }, + { VEX_NDS_256_WIG(0x660F382E), _r, _m256, _ymm, _ymm }, + { ASM_END } +}; + +PTRNTAB3 aptb3VMASKMOVPD[] = /* VMASKMOVPD */ { + { VEX_NDS_128_WIG(0x660F382D), _r, _xmm, _xmm, _m128 }, + { VEX_NDS_256_WIG(0x660F382D), _r, _ymm, _ymm, _m256 }, + { VEX_NDS_128_WIG(0x660F382F), _r, _m128, _xmm, _xmm }, + { VEX_NDS_256_WIG(0x660F382F), _r, _m256, _ymm, _ymm }, + { ASM_END } +}; + +PTRNTAB0 aptb0VZEROALL[] = /* VZEROALL */ { + { VEX_256_WIG(0x0F77), _modall }, // FIXME: need _modxmm + { ASM_END }, +}; + +PTRNTAB0 aptb0VZEROUPPER[] = /* VZEROUPPER */ { + { VEX_128_WIG(0x0F77), _modall }, // FIXME: need _modxmm + { ASM_END }, +}; + +PTRNTAB0 aptb0XGETBV[] = /* XGETBV */ { + { XGETBV, _modaxdx }, + { ASM_END }, +}; + +PTRNTAB1 aptb1XRSTOR[] = /* XRSTOR */ { + { 0x0FAE, _5, _m512 }, + { ASM_END } +}; + +PTRNTAB1 aptb1XRSTOR64[] = /* XRSTOR64 */ { + { 0x0FAE, _5|_64_bit, _m512 }, // TODO: REX_W override is implicit + { ASM_END } +}; + +PTRNTAB1 aptb1XSAVE[] = /* XSAVE */ { + { 0x0FAE, _4, _m512 }, + { ASM_END } +}; + +PTRNTAB1 aptb1XSAVE64[] = /* XSAVE64 */ { + { 0x0FAE, _4|_64_bit, _m512 }, // TODO: REX_W override is implicit + { ASM_END } +}; + +PTRNTAB1 aptb1XSAVEOPT[] = /* XSAVEOPT */ { + { 0x0FAE, _6, _m512 }, + { ASM_END } +}; + +PTRNTAB1 aptb1XSAVEOPT64[] = /* XSAVEOPT64 */ { + { 0x0FAE, _6|_64_bit, _m512 }, // TODO: REX_W override is implicit + { ASM_END } +}; + +PTRNTAB0 aptb0XSETBV[] = /* XSETBV */ { + { XSETBV, 0 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VPERMILPD[] = /* VPERMILPD */ { + { VEX_NDS_128_WIG(0x660F380D), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDS_256_WIG(0x660F380D), _r, _ymm, _ymm, _ymm_m256 }, + { VEX_128_WIG(0x660F3A05), _r, _xmm, _xmm_m128, _imm8 }, + { VEX_256_WIG(0x660F3A05), _r, _ymm, _ymm_m256, _imm8 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VPERMILPS[] = /* VPERMILPS */ { + { VEX_NDS_128_WIG(0x660F380C), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_NDS_256_WIG(0x660F380C), _r, _ymm, _ymm, _ymm_m256 }, + { VEX_128_WIG(0x660F3A04), _r, _xmm, _xmm_m128, _imm8 }, + { VEX_256_WIG(0x660F3A04), _r, _ymm, _ymm_m256, _imm8 }, + { ASM_END }, +}; + +PTRNTAB4 aptb3VPERM2F128[] = /* VPERM2F128 */ { + { VEX_NDS_256_WIG(0x660F3A06), _r, _ymm, _ymm, _ymm_m256, _imm8 }, + { ASM_END }, +}; + +/* ======================= AES ======================= */ + +PTRNTAB2 aptb2AESENC[] = /* AESENC */ { + { AESENC, _r, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VAESENC[] = /* VAESENC */ { + { VEX_NDS_128_WIG(AESENC), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB2 aptb2AESENCLAST[] = /* AESENCLAST */ { + { AESENCLAST, _r, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VAESENCLAST[] = /* VAESENCLAST */ { + { VEX_NDS_128_WIG(AESENCLAST), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB2 aptb2AESDEC[] = /* AESDEC */ { + { AESDEC, _r, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VAESDEC[] = /* VAESDEC */ { + { VEX_NDS_128_WIG(AESDEC), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB2 aptb2AESDECLAST[] = /* AESDECLAST */ { + { AESDECLAST, _r, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VAESDECLAST[] = /* VAESDECLAST */ { + { VEX_NDS_128_WIG(AESDECLAST), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB2 aptb2AESIMC[] = /* AESIMC */ { + { AESIMC, _r, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB2 aptb2VAESIMC[] = /* VAESIMC */ { + { VEX_128_WIG(AESIMC), _r, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3AESKEYGENASSIST[] = /* AESKEYGENASSIST */ { + { AESKEYGENASSIST, _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VAESKEYGENASSIST[] = /* VAESKEYGENASSIST */ { + { VEX_128_WIG(AESKEYGENASSIST), _r, _xmm, _xmm_m128, _imm8 }, + { ASM_END }, +}; + +/* ======================= FSGSBASE ======================= */ + +PTRNTAB1 aptb1RDFSBASE[] = /* RDFSBASE */ { + { 0xF30FAE, _0, _r32 }, + { 0xF30FAE, _0|_64_bit, _r64 }, + { ASM_END }, +}; + +PTRNTAB1 aptb1RDGSBASE[] = /* RDGSBASE */ { + { 0xF30FAE, _1, _r32 }, + { 0xF30FAE, _1|_64_bit, _r64 }, + { ASM_END }, +}; + +PTRNTAB1 aptb1WRFSBASE[] = /* WRFSBASE */ { + { 0xF30FAE, _2, _r32 }, + { 0xF30FAE, _2|_64_bit, _r64 }, + { ASM_END }, +}; + +PTRNTAB1 aptb1WRGSBASE[] = /* WRGSBASE */ { + { 0xF30FAE, _3, _r32 }, + { 0xF30FAE, _3|_64_bit, _r64 }, + { ASM_END }, +}; + +/* ======================= RDRAND ======================= */ + +PTRNTAB1 aptb1RDRAND[] = /* RDRAND */ { + { 0x0FC7, _6|_16_bit, _r16 }, + { 0x0FC7, _6|_32_bit, _r32 }, + { 0x0FC7, _6|_64_bit, _r64 }, + { ASM_END }, +}; + +/* ======================= FP16C ======================= */ + +PTRNTAB2 aptb2VCVTPH2PS[] = /* VCVTPH2PS */ { + { VEX_128_WIG(0x660F3813), _r, _xmm, _xmm_m64 }, + { VEX_256_WIG(0x660F3813), _r, _ymm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VCVTPS2PH[] = /* VCVTPS2PH */ { + { VEX_128_WIG(0x660F3A13), _r, _xmm_m64, _xmm, _imm8 }, + { VEX_256_WIG(0x660F3A13), _r, _xmm_m128, _ymm, _imm8 }, + { ASM_END }, +}; + +/* ======================= FMA ======================= */ + +PTRNTAB3 aptb3VFMADD132PD[] = /* VFMADD132PD */ { + { VEX_DDS_128_W1(0x660F3898), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F3898), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADD213PD[] = /* VFMADD213PD */ { + { VEX_DDS_128_W1(0x660F38A8), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F38A8), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADD231PD[] = /* VFMADD231PD */ { + { VEX_DDS_128_W1(0x660F38B8), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F38B8), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADD132PS[] = /* VFMADD132PS */ { + { VEX_DDS_128_WIG(0x660F3898), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F3898), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADD213PS[] = /* VFMADD213PS */ { + { VEX_DDS_128_WIG(0x660F38A8), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F38A8), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADD231PS[] = /* VFMADD231PS */ { + { VEX_DDS_128_WIG(0x660F38B8), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F38B8), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADD132SD[] = /* VFMADD132SD */ { + { VEX_DDS_128_W1(0x660F3899), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADD213SD[] = /* VFMADD213SD */ { + { VEX_DDS_128_W1(0x660F38A9), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADD231SD[] = /* VFMADD231SD */ { + { VEX_DDS_128_W1(0x660F38B9), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADD132SS[] = /* VFMADD132SS */ { + { VEX_DDS_128_WIG(0x660F3899), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADD213SS[] = /* VFMADD213SS */ { + { VEX_DDS_128_WIG(0x660F38A9), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADD231SS[] = /* VFMADD231SS */ { + { VEX_DDS_128_WIG(0x660F38B9), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADDSUB132PD[] = /* VFMADDSUB132PD */ { + { VEX_DDS_128_W1(0x660F3896), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F3896), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADDSUB213PD[] = /* VFMADDSUB213PD */ { + { VEX_DDS_128_W1(0x660F38A6), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F38A6), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADDSUB231PD[] = /* VFMADDSUB231PD */ { + { VEX_DDS_128_W1(0x660F38B6), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F38B6), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADDSUB132PS[] = /* VFMADDSUB132PS */ { + { VEX_DDS_128_WIG(0x660F3896), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F3896), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADDSUB213PS[] = /* VFMADDSUB213PS */ { + { VEX_DDS_128_WIG(0x660F38A6), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F38A6), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMADDSUB231PS[] = /* VFMADDSUB231PS */ { + { VEX_DDS_128_WIG(0x660F38B6), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F38B6), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUBADD132PD[] = /* VFMSUBADD132PD */ { + { VEX_DDS_128_W1(0x660F3897), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F3897), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUBADD213PD[] = /* VFMSUBADD213PD */ { + { VEX_DDS_128_W1(0x660F38A7), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F38A7), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUBADD231PD[] = /* VFMSUBADD231PD */ { + { VEX_DDS_128_W1(0x660F38B7), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F38B7), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUBADD132PS[] = /* VFMSUBADD132PS */ { + { VEX_DDS_128_WIG(0x660F3897), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F3897), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUBADD213PS[] = /* VFMSUBADD213PS */ { + { VEX_DDS_128_WIG(0x660F38A7), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F38A7), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUBADD231PS[] = /* VFMSUBADD231PS */ { + { VEX_DDS_128_WIG(0x660F38B7), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F38B7), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB132PD[] = /* VFMSUB132PD */ { + { VEX_DDS_128_W1(0x660F389A), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F389A), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB213PD[] = /* VFMSUB213PD */ { + { VEX_DDS_128_W1(0x660F38AA), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F38AA), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB231PD[] = /* VFMSUB231PD */ { + { VEX_DDS_128_W1(0x660F38BA), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_W1(0x660F38BA), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB132PS[] = /* VFMSUB132PS */ { + { VEX_DDS_128_WIG(0x660F389A), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F389A), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB213PS[] = /* VFMSUB213PS */ { + { VEX_DDS_128_WIG(0x660F38AA), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F38AA), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB231PS[] = /* VFMSUB231PS */ { + { VEX_DDS_128_WIG(0x660F38BA), _r, _xmm, _xmm, _xmm_m128 }, + { VEX_DDS_256_WIG(0x660F38BA), _r, _ymm, _ymm, _ymm_m256 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB132SD[] = /* VFMSUB132SD */ { + { VEX_DDS_128_W1(0x660F389B), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB213SD[] = /* VFMSUB213SD */ { + { VEX_DDS_128_W1(0x660F38AB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB231SD[] = /* VFMSUB231SD */ { + { VEX_DDS_128_W1(0x660F38BB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB132SS[] = /* VFMSUB132SS */ { + { VEX_DDS_128_WIG(0x660F389B), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB213SS[] = /* VFMSUB213SS */ { + { VEX_DDS_128_WIG(0x660F38AB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +PTRNTAB3 aptb3VFMSUB231SS[] = /* VFMSUB231SS */ { + { VEX_DDS_128_WIG(0x660F38BB), _r, _xmm, _xmm, _xmm_m128 }, + { ASM_END }, +}; + +////////////////////////////////////////////////////////////////////// + + +// +// usNumops should be 0, 1, 2, or 3 other things are added into it +// for flag indications +// 10, 11, 12, and 13 indicate that it is a special prefix + +// 20, 21, 22, and 23 indicate that this statement is a control transfer +// and that a new block should be created when this statement is +// finished. (All Jxx and LOOPxx instructions.) + +// 30, 31, 32, 33 are reserved for instructions where the value of an +// immediate operand controls the code generation. +// 40, 41, 42, 43 are reserved for instructions where all of the operands +// are not required +// 50, 51, 52, 53 are reserved for the rotate and shift instructions that +// have extremely strange encodings for the second operand which is sometimes +// used to select an opcode and then discarded. The second operand is 0 +// if it is immediate 1, _cl for the CL register and _imm8 for the immediate +// 8 operand. If the operand is an immediate 1 or the cl register, it should +// be discarded and the opcode should be encoded as a 1 operand instruction. +// +// 60, 61, 62, 63 are reserved for floating point coprocessor operations +// +// ITdata is for the DB (_EMIT), DD, DW, DQ, DT pseudo-ops + +// BT is a 486 instruction. +// The encoding is 0f C0+reg and it is always a 32 +// bit operation + +#define P PPTRNTAB0 + +#if 0 +#define OPCODETABLE \ + X("aaa", 0, aptb0AAA ) +#else +#define OPCODETABLE1 \ + X("__emit", ITdata | OPdb, NULL ) \ + X("_emit", ITdata | OPdb, NULL ) \ + X("aaa", 0, aptb0AAA ) \ + X("aad", 0, aptb0AAD ) \ + X("aam", 0, aptb0AAM ) \ + X("aas", 0, aptb0AAS ) \ + X("adc", 2, (P) aptb2ADC ) \ + X("add", 2, (P) aptb2ADD ) \ + X("addpd", 2, (P) aptb2ADDPD ) \ + X("addps", 2, (P) aptb2ADDPS ) \ + X("addsd", 2, (P) aptb2ADDSD ) \ + X("addss", 2, (P) aptb2ADDSS ) \ + X("addsubpd", 2, (P) aptb2ADDSUBPD ) \ + X("addsubps", 2, (P) aptb2ADDSUBPS ) \ + X("aesdec", 2, (P) aptb2AESDEC ) \ + X("aesdeclast", 2, (P) aptb2AESDECLAST ) \ + X("aesenc", 2, (P) aptb2AESENC ) \ + X("aesenclast", 2, (P) aptb2AESENCLAST ) \ + X("aesimc", 2, (P) aptb2AESIMC ) \ + X("aeskeygenassist", 3, (P) aptb3AESKEYGENASSIST ) \ + X("and", 2, (P) aptb2AND ) \ + X("andnpd", 2, (P) aptb2ANDNPD ) \ + X("andnps", 2, (P) aptb2ANDNPS ) \ + X("andpd", 2, (P) aptb2ANDPD ) \ + X("andps", 2, (P) aptb2ANDPS ) \ + X("arpl", 2, (P) aptb2ARPL ) \ + X("blendpd", 3, (P) aptb3BLENDPD ) \ + X("blendps", 3, (P) aptb3BLENDPS ) \ + X("blendvpd", 3, (P) aptb3BLENDVPD ) \ + X("blendvps", 3, (P) aptb3BLENDVPS ) \ + X("bound", 2, (P) aptb2BOUND ) \ + X("bsf", 2, (P) aptb2BSF ) \ + X("bsr", 2, (P) aptb2BSR ) \ + X("bswap", 1, (P) aptb1BSWAP ) \ + X("bt", 2, (P) aptb2BT ) \ + X("btc", 2, (P) aptb2BTC ) \ + X("btr", 2, (P) aptb2BTR ) \ + X("bts", 2, (P) aptb2BTS ) \ + X("call", ITjump | 1, (P) aptb1CALL ) \ + X("cbw", 0, aptb0CBW ) \ + X("cdq", 0, aptb0CDQ ) \ + X("cdqe", 0, aptb0CDQE ) \ + X("clc", 0, aptb0CLC ) \ + X("cld", 0, aptb0CLD ) \ + X("clflush", 1, (P) aptb1CLFLUSH ) \ + X("cli", 0, aptb0CLI ) \ + X("clts", 0, aptb0CLTS ) \ + X("cmc", 0, aptb0CMC ) \ + X("cmova", 2, (P) aptb2CMOVNBE ) \ + X("cmovae", 2, (P) aptb2CMOVNB ) \ + X("cmovb", 2, (P) aptb2CMOVB ) \ + X("cmovbe", 2, (P) aptb2CMOVBE ) \ + X("cmovc", 2, (P) aptb2CMOVB ) \ + X("cmove", 2, (P) aptb2CMOVZ ) \ + X("cmovg", 2, (P) aptb2CMOVNLE ) \ + X("cmovge", 2, (P) aptb2CMOVNL ) \ + X("cmovl", 2, (P) aptb2CMOVL ) \ + X("cmovle", 2, (P) aptb2CMOVLE ) \ + X("cmovna", 2, (P) aptb2CMOVBE ) \ + X("cmovnae", 2, (P) aptb2CMOVB ) \ + X("cmovnb", 2, (P) aptb2CMOVNB ) \ + X("cmovnbe", 2, (P) aptb2CMOVNBE ) \ + X("cmovnc", 2, (P) aptb2CMOVNB ) \ + X("cmovne", 2, (P) aptb2CMOVNZ ) \ + X("cmovng", 2, (P) aptb2CMOVLE ) \ + X("cmovnge", 2, (P) aptb2CMOVL ) \ + X("cmovnl", 2, (P) aptb2CMOVNL ) \ + X("cmovnle", 2, (P) aptb2CMOVNLE ) \ + X("cmovno", 2, (P) aptb2CMOVNO ) \ + X("cmovnp", 2, (P) aptb2CMOVNP ) \ + X("cmovns", 2, (P) aptb2CMOVNS ) \ + X("cmovnz", 2, (P) aptb2CMOVNZ ) \ + X("cmovo", 2, (P) aptb2CMOVO ) \ + X("cmovp", 2, (P) aptb2CMOVP ) \ + X("cmovpe", 2, (P) aptb2CMOVP ) \ + X("cmovpo", 2, (P) aptb2CMOVNP ) \ + X("cmovs", 2, (P) aptb2CMOVS ) \ + X("cmovz", 2, (P) aptb2CMOVZ ) \ + X("cmp", 2, (P) aptb2CMP ) \ + X("cmppd", 3, (P) aptb3CMPPD ) \ + X("cmpps", 3, (P) aptb3CMPPS ) \ + X("cmps", 2, (P) aptb2CMPS ) \ + X("cmpsb", 0, aptb0CMPSB ) \ + /*X("cmpsd", 0, aptb0CMPSD )*/ \ + X("cmpsd", ITopt|3, (P) aptb3CMPSD ) \ + X("cmpsq", 0, aptb0CMPSQ ) \ + X("cmpss", 3, (P) aptb3CMPSS ) \ + X("cmpsw", 0, aptb0CMPSW ) \ + X("cmpxchg", 2, (P) aptb2CMPXCHG ) \ + X("cmpxchg16b", 1, (P) aptb1CMPXCH16B ) \ + X("cmpxchg8b", 1, (P) aptb1CMPXCH8B ) \ + X("comisd", 2, (P) aptb2COMISD ) \ + X("comiss", 2, (P) aptb2COMISS ) \ + X("cpuid", 0, aptb0CPUID ) \ + X("cqo", 0, aptb0CQO ) \ + X("crc32", 2, (P) aptb2CRC32 ) \ + X("cvtdq2pd", 2, (P) aptb2CVTDQ2PD ) \ + X("cvtdq2ps", 2, (P) aptb2CVTDQ2PS ) \ + X("cvtpd2dq", 2, (P) aptb2CVTPD2DQ ) \ + X("cvtpd2pi", 2, (P) aptb2CVTPD2PI ) \ + X("cvtpd2ps", 2, (P) aptb2CVTPD2PS ) \ + X("cvtpi2pd", 2, (P) aptb2CVTPI2PD ) \ + X("cvtpi2ps", 2, (P) aptb2CVTPI2PS ) \ + X("cvtps2dq", 2, (P) aptb2CVTPS2DQ ) \ + X("cvtps2pd", 2, (P) aptb2CVTPS2PD ) \ + X("cvtps2pi", 2, (P) aptb2CVTPS2PI ) \ + X("cvtsd2si", 2, (P) aptb2CVTSD2SI ) \ + X("cvtsd2ss", 2, (P) aptb2CVTSD2SS ) \ + X("cvtsi2sd", 2, (P) aptb2CVTSI2SD ) \ + X("cvtsi2ss", 2, (P) aptb2CVTSI2SS ) \ + X("cvtss2sd", 2, (P) aptb2CVTSS2SD ) \ + X("cvtss2si", 2, (P) aptb2CVTSS2SI ) \ + X("cvttpd2dq", 2, (P) aptb2CVTTPD2DQ ) \ + X("cvttpd2pi", 2, (P) aptb2CVTTPD2PI ) \ + X("cvttps2dq", 2, (P) aptb2CVTTPS2DQ ) \ + X("cvttps2pi", 2, (P) aptb2CVTTPS2PI ) \ + X("cvttsd2si", 2, (P) aptb2CVTTSD2SI ) \ + X("cvttss2si", 2, (P) aptb2CVTTSS2SI ) \ + X("cwd", 0, aptb0CWD ) \ + X("cwde", 0, aptb0CWDE ) \ + X("da", ITaddr | 4, NULL ) \ + X("daa", 0, aptb0DAA ) \ + X("das", 0, aptb0DAS ) \ + X("db", ITdata | OPdb, NULL ) \ + X("dd", ITdata | OPdd, NULL ) \ + X("de", ITdata | OPde, NULL ) \ + X("dec", 1, (P) aptb1DEC ) \ + X("df", ITdata | OPdf, NULL ) \ + X("di", ITdata | OPdi, NULL ) \ + X("div", ITopt | 2, (P) aptb2DIV ) \ + X("divpd", 2, (P) aptb2DIVPD ) \ + X("divps", 2, (P) aptb2DIVPS ) \ + X("divsd", 2, (P) aptb2DIVSD ) \ + X("divss", 2, (P) aptb2DIVSS ) \ + X("dl", ITdata | OPdl, NULL ) \ + X("dppd", 3, (P) aptb3DPPD ) \ + X("dpps", 3, (P) aptb3DPPS ) \ + X("dq", ITdata | OPdq, NULL ) \ + X("ds", ITdata | OPds, NULL ) \ + X("dt", ITdata | OPdt, NULL ) \ + X("dw", ITdata | OPdw, NULL ) \ + X("emms", 0, aptb0EMMS ) \ + X("enter", 2, (P) aptb2ENTER ) \ + X("extractps", 3, (P) aptb3EXTRACTPS ) \ + X("f2xm1", ITfloat | 0, aptb0F2XM1 ) \ + X("fabs", ITfloat | 0, aptb0FABS ) \ + X("fadd", ITfloat | 2, (P) aptb2FADD ) \ + X("faddp", ITfloat | 2, (P) aptb2FADDP ) \ + X("fbld", ITfloat | 1, (P) aptb1FBLD ) \ + X("fbstp", ITfloat | 1, (P) aptb1FBSTP ) \ + X("fchs", ITfloat | 0, aptb0FCHS ) \ + X("fclex", ITfloat | 0, aptb0FCLEX ) \ + X("fcmovb", ITfloat | 2, (P) aptb2FCMOVB ) \ + X("fcmovbe", ITfloat | 2, (P) aptb2FCMOVBE ) \ + X("fcmove", ITfloat | 2, (P) aptb2FCMOVE ) \ + X("fcmovnb", ITfloat | 2, (P) aptb2FCMOVNB ) \ + X("fcmovnbe", ITfloat | 2, (P) aptb2FCMOVNBE ) \ + X("fcmovne", ITfloat | 2, (P) aptb2FCMOVNE ) \ + X("fcmovnu", ITfloat | 2, (P) aptb2FCMOVNU ) \ + X("fcmovu", ITfloat | 2, (P) aptb2FCMOVU ) \ + X("fcom", ITfloat | 1, (P) aptb1FCOM ) \ + X("fcomi", ITfloat | 2, (P) aptb2FCOMI ) \ + X("fcomip", ITfloat | 2, (P) aptb2FCOMIP ) \ + X("fcomp", ITfloat | 1, (P) aptb1FCOMP ) \ + X("fcompp", ITfloat | 0, aptb0FCOMPP ) \ + X("fcos", ITfloat | 0, aptb0FCOS ) \ + X("fdecstp", ITfloat | 0, aptb0FDECSTP ) \ + X("fdisi", ITfloat | 0, aptb0FDISI ) \ + X("fdiv", ITfloat | 2, (P) aptb2FDIV ) \ + X("fdivp", ITfloat | 2, (P) aptb2FDIVP ) \ + X("fdivr", ITfloat | 2, (P) aptb2FDIVR ) \ + X("fdivrp", ITfloat | 2, (P) aptb2FDIVRP ) \ + X("feni", ITfloat | 0, aptb0FENI ) \ + X("ffree", ITfloat | 1, (P) aptb1FFREE ) \ + X("fiadd", ITfloat | 2, (P) aptb2FIADD ) \ + X("ficom", ITfloat | 1, (P) aptb1FICOM ) \ + X("ficomp", ITfloat | 1, (P) aptb1FICOMP ) \ + X("fidiv", ITfloat | 2, (P) aptb2FIDIV ) \ + X("fidivr", ITfloat | 2, (P) aptb2FIDIVR ) \ + X("fild", ITfloat | 1, (P) aptb1FILD ) \ + X("fimul", ITfloat | 2, (P) aptb2FIMUL ) \ + X("fincstp", ITfloat | 0, aptb0FINCSTP ) \ + X("finit", ITfloat | 0, aptb0FINIT ) \ + X("fist", ITfloat | 1, (P) aptb1FIST ) \ + X("fistp", ITfloat | 1, (P) aptb1FISTP ) \ + X("fisttp", ITfloat | 1, (P) aptb1FISTTP ) \ + X("fisub", ITfloat | 2, (P) aptb2FISUB ) \ + X("fisubr", ITfloat | 2, (P) aptb2FISUBR ) \ + X("fld", ITfloat | 1, (P) aptb1FLD ) \ + X("fld1", ITfloat | 0, aptb0FLD1 ) \ + X("fldcw", ITfloat | 1, (P) aptb1FLDCW ) \ + X("fldenv", ITfloat | 1, (P) aptb1FLDENV ) \ + X("fldl2e", ITfloat | 0, aptb0FLDL2E ) \ + X("fldl2t", ITfloat | 0, aptb0FLDL2T ) \ + X("fldlg2", ITfloat | 0, aptb0FLDLG2 ) \ + X("fldln2", ITfloat | 0, aptb0FLDLN2 ) \ + X("fldpi", ITfloat | 0, aptb0FLDPI ) \ + X("fldz", ITfloat | 0, aptb0FLDZ ) \ + X("fmul", ITfloat | 2, (P) aptb2FMUL ) \ + X("fmulp", ITfloat | 2, (P) aptb2FMULP ) \ + X("fnclex", ITfloat | 0, aptb0FNCLEX ) \ + X("fndisi", ITfloat | 0, aptb0FNDISI ) \ + X("fneni", ITfloat | 0, aptb0FNENI ) \ + X("fninit", ITfloat | 0, aptb0FNINIT ) \ + X("fnop", ITfloat | 0, aptb0FNOP ) \ + X("fnsave", ITfloat | 1, (P) aptb1FNSAVE ) \ + X("fnstcw", ITfloat | 1, (P) aptb1FNSTCW ) \ + X("fnstenv", ITfloat | 1, (P) aptb1FNSTENV ) \ + X("fnstsw", 1, (P) aptb1FNSTSW ) \ + X("fpatan", ITfloat | 0, aptb0FPATAN ) \ + X("fprem", ITfloat | 0, aptb0FPREM ) \ + X("fprem1", ITfloat | 0, aptb0FPREM1 ) \ + X("fptan", ITfloat | 0, aptb0FPTAN ) \ + X("frndint", ITfloat | 0, aptb0FRNDINT ) \ + X("frstor", ITfloat | 1, (P) aptb1FRSTOR ) \ + X("fsave", ITfloat | 1, (P) aptb1FSAVE ) \ + X("fscale", ITfloat | 0, aptb0FSCALE ) \ + X("fsetpm", ITfloat | 0, aptb0FSETPM ) \ + X("fsin", ITfloat | 0, aptb0FSIN ) \ + X("fsincos", ITfloat | 0, aptb0FSINCOS ) \ + X("fsqrt", ITfloat | 0, aptb0FSQRT ) \ + X("fst", ITfloat | 1, (P) aptb1FST ) \ + X("fstcw", ITfloat | 1, (P) aptb1FSTCW ) \ + X("fstenv", ITfloat | 1, (P) aptb1FSTENV ) \ + X("fstp", ITfloat | 1, (P) aptb1FSTP ) \ + X("fstsw", 1, (P) aptb1FSTSW ) \ + X("fsub", ITfloat | 2, (P) aptb2FSUB ) \ + X("fsubp", ITfloat | 2, (P) aptb2FSUBP ) \ + X("fsubr", ITfloat | 2, (P) aptb2FSUBR ) \ + X("fsubrp", ITfloat | 2, (P) aptb2FSUBRP ) \ + X("ftst", ITfloat | 0, aptb0FTST ) \ + X("fucom", ITfloat | 1, (P) aptb1FUCOM ) \ + X("fucomi", ITfloat | 2, (P) aptb2FUCOMI ) \ + X("fucomip", ITfloat | 2, (P) aptb2FUCOMIP ) \ + X("fucomp", ITfloat | 1, (P) aptb1FUCOMP ) \ + X("fucompp", ITfloat | 0, aptb0FUCOMPP ) \ + X("fwait", ITfloat | 0, aptb0FWAIT ) \ + X("fxam", ITfloat | 0, aptb0FXAM ) \ + X("fxch", ITfloat | 1, (P) aptb1FXCH ) \ + X("fxrstor", ITfloat | 1, (P) aptb1FXRSTOR ) \ + X("fxsave", ITfloat | 1, (P) aptb1FXSAVE ) \ + X("fxtract", ITfloat | 0, aptb0FXTRACT ) \ + X("fyl2x", ITfloat | 0, aptb0FYL2X ) \ + X("fyl2xp1", ITfloat | 0, aptb0FYL2XP1 ) \ + X("haddpd", 2, (P) aptb2HADDPD ) \ + X("haddps", 2, (P) aptb2HADDPS ) \ + X("hlt", 0, aptb0HLT ) \ + X("hsubpd", 2, (P) aptb2HSUBPD ) \ + X("hsubps", 2, (P) aptb2HSUBPS ) \ + X("idiv", ITopt | 2, (P) aptb2IDIV ) \ + X("imul", ITopt | 3, (P) aptb3IMUL ) \ + X("in", 2, (P) aptb2IN ) \ + X("inc", 1, (P) aptb1INC ) \ + X("ins", 2, (P) aptb2INS ) \ + X("insb", 0, aptb0INSB ) \ + X("insd", 0, aptb0INSD ) \ + X("insertps", 3, (P) aptb3INSERTPS ) \ + X("insw", 0, aptb0INSW ) \ + X("int", ITimmed | 1, (P) aptb1INT ) \ + X("into", 0, aptb0INTO ) \ + X("invd", 0, aptb0INVD ) \ + X("invlpg", 1, (P) aptb1INVLPG ) \ + X("iret", 0, aptb0IRET ) \ + X("iretd", 0, aptb0IRETD ) \ + X("ja", ITjump | 1, (P) aptb1JNBE ) \ + X("jae", ITjump | 1, (P) aptb1JNB ) \ + X("jb", ITjump | 1, (P) aptb1JB ) \ + X("jbe", ITjump | 1, (P) aptb1JBE ) \ + X("jc", ITjump | 1, (P) aptb1JB ) \ + X("jcxz", ITjump | 1, (P) aptb1JCXZ ) \ + X("je", ITjump | 1, (P) aptb1JZ ) \ + X("jecxz", ITjump | 1, (P) aptb1JECXZ ) \ + X("jg", ITjump | 1, (P) aptb1JNLE ) \ + X("jge", ITjump | 1, (P) aptb1JNL ) \ + X("jl", ITjump | 1, (P) aptb1JL ) \ + X("jle", ITjump | 1, (P) aptb1JLE ) \ + X("jmp", ITjump | 1, (P) aptb1JMP ) \ + X("jna", ITjump | 1, (P) aptb1JBE ) \ + X("jnae", ITjump | 1, (P) aptb1JB ) \ + X("jnb", ITjump | 1, (P) aptb1JNB ) \ + X("jnbe", ITjump | 1, (P) aptb1JNBE ) \ + X("jnc", ITjump | 1, (P) aptb1JNB ) \ + X("jne", ITjump | 1, (P) aptb1JNZ ) \ + X("jng", ITjump | 1, (P) aptb1JLE ) \ + X("jnge", ITjump | 1, (P) aptb1JL ) \ + X("jnl", ITjump | 1, (P) aptb1JNL ) \ + X("jnle", ITjump | 1, (P) aptb1JNLE ) \ + X("jno", ITjump | 1, (P) aptb1JNO ) \ + X("jnp", ITjump | 1, (P) aptb1JNP ) \ + X("jns", ITjump | 1, (P) aptb1JNS ) \ + X("jnz", ITjump | 1, (P) aptb1JNZ ) \ + X("jo", ITjump | 1, (P) aptb1JO ) \ + X("jp", ITjump | 1, (P) aptb1JP ) \ + X("jpe", ITjump | 1, (P) aptb1JP ) \ + X("jpo", ITjump | 1, (P) aptb1JNP ) \ + X("js", ITjump | 1, (P) aptb1JS ) \ + X("jz", ITjump | 1, (P) aptb1JZ ) \ + + +#define OPCODETABLE2 \ + X("lahf", 0, aptb0LAHF ) \ + X("lar", 2, (P) aptb2LAR ) \ + X("lddqu", 2, (P) aptb2LDDQU ) \ + X("ldmxcsr", 1, (P) aptb1LDMXCSR ) \ + X("lds", 2, (P) aptb2LDS ) \ + X("lea", 2, (P) aptb2LEA ) \ + X("leave", 0, aptb0LEAVE ) \ + X("les", 2, (P) aptb2LES ) \ + X("lfence", 0, aptb0LFENCE) \ + X("lfs", 2, (P) aptb2LFS ) \ + X("lgdt", 1, (P) aptb1LGDT ) \ + X("lgs", 2, (P) aptb2LGS ) \ + X("lidt", 1, (P) aptb1LIDT ) \ + X("lldt", 1, (P) aptb1LLDT ) \ + X("lmsw", 1, (P) aptb1LMSW ) \ + X("lock", ITprefix | 0, aptb0LOCK ) \ + X("lods", 1, (P) aptb1LODS ) \ + X("lodsb", 0, aptb0LODSB ) \ + X("lodsd", 0, aptb0LODSD ) \ + X("lodsq", 0, aptb0LODSQ ) \ + X("lodsw", 0, aptb0LODSW ) \ + X("loop", ITjump | 1, (P) aptb1LOOP ) \ + X("loope", ITjump | 1, (P) aptb1LOOPE ) \ + X("loopne", ITjump | 1, (P) aptb1LOOPNE ) \ + X("loopnz", ITjump | 1, (P) aptb1LOOPNE ) \ + X("loopz", ITjump | 1, (P) aptb1LOOPE ) \ + X("lsl", 2, (P) aptb2LSL ) \ + X("lss", 2, (P) aptb2LSS ) \ + X("ltr", 1, (P) aptb1LTR ) \ + X("maskmovdqu", 2, (P) aptb2MASKMOVDQU ) \ + X("maskmovq", 2, (P) aptb2MASKMOVQ ) \ + X("maxpd", 2, (P) aptb2MAXPD ) \ + X("maxps", 2, (P) aptb2MAXPS ) \ + X("maxsd", 2, (P) aptb2MAXSD ) \ + X("maxss", 2, (P) aptb2MAXSS ) \ + X("mfence", 0, aptb0MFENCE) \ + X("minpd", 2, (P) aptb2MINPD ) \ + X("minps", 2, (P) aptb2MINPS ) \ + X("minsd", 2, (P) aptb2MINSD ) \ + X("minss", 2, (P) aptb2MINSS ) \ + X("monitor", 0, (P) aptb0MONITOR ) \ + X("mov", 2, (P) aptb2MOV ) \ + X("movapd", 2, (P) aptb2MOVAPD ) \ + X("movaps", 2, (P) aptb2MOVAPS ) \ + X("movd", 2, (P) aptb2MOVD ) \ + X("movddup", 2, (P) aptb2MOVDDUP ) \ + X("movdq2q", 2, (P) aptb2MOVDQ2Q ) \ + X("movdqa", 2, (P) aptb2MOVDQA ) \ + X("movdqu", 2, (P) aptb2MOVDQU ) \ + X("movhlps", 2, (P) aptb2MOVHLPS ) \ + X("movhpd", 2, (P) aptb2MOVHPD ) \ + X("movhps", 2, (P) aptb2MOVHPS ) \ + X("movlhps", 2, (P) aptb2MOVLHPS ) \ + X("movlpd", 2, (P) aptb2MOVLPD ) \ + X("movlps", 2, (P) aptb2MOVLPS ) \ + X("movmskpd", 2, (P) aptb2MOVMSKPD ) \ + X("movmskps", 2, (P) aptb2MOVMSKPS ) \ + X("movntdq", 2, (P) aptb2MOVNTDQ ) \ + X("movntdqa", 2, (P) aptb2MOVNTDQA ) \ + X("movnti", 2, (P) aptb2MOVNTI ) \ + X("movntpd", 2, (P) aptb2MOVNTPD ) \ + X("movntps", 2, (P) aptb2MOVNTPS ) \ + X("movntq", 2, (P) aptb2MOVNTQ ) \ + X("movq", 2, (P) aptb2MOVQ ) \ + X("movq2dq", 2, (P) aptb2MOVQ2DQ ) \ + X("movs", 2, (P) aptb2MOVS ) \ + X("movsb", 0, aptb0MOVSB ) \ + X("movsd", ITopt | 2, (P) aptb2MOVSD ) \ + X("movshdup", 2, (P) aptb2MOVSHDUP ) \ + X("movsldup", 2, (P) aptb2MOVSLDUP ) \ + X("movsq", 0, aptb0MOVSQ ) \ + X("movss", 2, (P) aptb2MOVSS ) \ + X("movsw", 0, aptb0MOVSW ) \ + X("movsx", 2, (P) aptb2MOVSX ) \ + X("movupd", 2, (P) aptb2MOVUPD ) \ + X("movups", 2, (P) aptb2MOVUPS ) \ + X("movzx", 2, (P) aptb2MOVZX ) \ + X("mpsadbw", 3, (P) aptb3MPSADBW ) \ + X("mul", ITopt | 2, (P) aptb2MUL ) \ + X("mulpd", 2, (P) aptb2MULPD ) \ + X("mulps", 2, (P) aptb2MULPS ) \ + X("mulsd", 2, (P) aptb2MULSD ) \ + X("mulss", 2, (P) aptb2MULSS ) \ + X("mwait", 0, (P) aptb0MWAIT ) \ + X("neg", 1, (P) aptb1NEG ) \ + X("nop", 0, aptb0NOP ) \ + X("not", 1, (P) aptb1NOT ) \ + X("or", 2, (P) aptb2OR ) \ + X("orpd", 2, (P) aptb2ORPD ) \ + X("orps", 2, (P) aptb2ORPS ) \ + X("out", 2, (P) aptb2OUT ) \ + X("outs", 2, (P) aptb2OUTS ) \ + X("outsb", 0, aptb0OUTSB ) \ + X("outsd", 0, aptb0OUTSD ) \ + X("outsw", 0, aptb0OUTSW ) \ + X("pabsb", 2, (P) aptb2PABSB ) \ + X("pabsd", 2, (P) aptb2PABSD ) \ + X("pabsw", 2, (P) aptb2PABSW ) \ + X("packssdw", 2, (P) aptb2PACKSSDW ) \ + X("packsswb", 2, (P) aptb2PACKSSWB ) \ + X("packusdw", 2, (P) aptb2PACKUSDW ) \ + X("packuswb", 2, (P) aptb2PACKUSWB ) \ + X("paddb", 2, (P) aptb2PADDB ) \ + X("paddd", 2, (P) aptb2PADDD ) \ + X("paddq", 2, (P) aptb2PADDQ ) \ + X("paddsb", 2, (P) aptb2PADDSB ) \ + X("paddsw", 2, (P) aptb2PADDSW ) \ + X("paddusb", 2, (P) aptb2PADDUSB ) \ + X("paddusw", 2, (P) aptb2PADDUSW ) \ + X("paddw", 2, (P) aptb2PADDW ) \ + X("palignr", 3, (P) aptb3PALIGNR ) \ + X("pand", 2, (P) aptb2PAND ) \ + X("pandn", 2, (P) aptb2PANDN ) \ + /* X("pause", 0, aptb0PAUSE) */ \ + X("pavgb", 2, (P) aptb2PAVGB ) \ + X("pavgusb", 2, (P) aptb2PAVGUSB ) \ + X("pavgw", 2, (P) aptb2PAVGW ) \ + X("pblendvb", 3, (P) aptb3PBLENDVB ) \ + X("pblendw", 3, (P) aptb3PBLENDW ) \ + X("pcmpeqb", 2, (P) aptb2PCMPEQB ) \ + X("pcmpeqd", 2, (P) aptb2PCMPEQD ) \ + X("pcmpeqq", 2, (P) aptb2PCMPEQQ ) \ + X("pcmpeqw", 2, (P) aptb2PCMPEQW ) \ + X("pcmpestri", 3, (P) aptb3PCMPESTRI ) \ + X("pcmpestrm", 3, (P) aptb3PCMPESTRM ) \ + X("pcmpgtb", 2, (P) aptb2PCMPGTB ) \ + X("pcmpgtd", 2, (P) aptb2PCMPGTD ) \ + X("pcmpgtq", 2, (P) aptb2PCMPGTQ ) \ + X("pcmpgtw", 2, (P) aptb2PCMPGTW ) \ + X("pcmpistri", 3, (P) aptb3PCMPISTRI ) \ + X("pcmpistrm", 3, (P) aptb3PCMPISTRM ) \ + X("pextrb", 3, (P) aptb3PEXTRB ) \ + X("pextrd", 3, (P) aptb3PEXTRD ) \ + X("pextrq", 3, (P) aptb3PEXTRQ ) \ + X("pextrw", 3, (P) aptb3PEXTRW ) \ + X("pf2id", 2, (P) aptb2PF2ID ) \ + X("pfacc", 2, (P) aptb2PFACC ) \ + X("pfadd", 2, (P) aptb2PFADD ) \ + X("pfcmpeq", 2, (P) aptb2PFCMPEQ ) \ + X("pfcmpge", 2, (P) aptb2PFCMPGE ) \ + X("pfcmpgt", 2, (P) aptb2PFCMPGT ) \ + X("pfmax", 2, (P) aptb2PFMAX ) \ + X("pfmin", 2, (P) aptb2PFMIN ) \ + X("pfmul", 2, (P) aptb2PFMUL ) \ + X("pfnacc", 2, (P) aptb2PFNACC ) \ + X("pfpnacc", 2, (P) aptb2PFPNACC ) \ + X("pfrcp", 2, (P) aptb2PFRCP ) \ + X("pfrcpit1", 2, (P) aptb2PFRCPIT1 ) \ + X("pfrcpit2", 2, (P) aptb2PFRCPIT2 ) \ + X("pfrsqit1", 2, (P) aptb2PFRSQIT1 ) \ + X("pfrsqrt", 2, (P) aptb2PFRSQRT ) \ + X("pfsub", 2, (P) aptb2PFSUB ) \ + X("pfsubr", 2, (P) aptb2PFSUBR ) \ + X("phaddd", 2, (P) aptb2PHADDD ) \ + X("phaddsw", 2, (P) aptb2PHADDSW ) \ + X("phaddw", 2, (P) aptb2PHADDW ) \ + X("phminposuw", 2, (P) aptb2PHMINPOSUW ) \ + X("phsubd", 2, (P) aptb2PHSUBD ) \ + X("phsubsw", 2, (P) aptb2PHSUBSW ) \ + X("phsubw", 2, (P) aptb2PHSUBW ) \ + X("pi2fd", 2, (P) aptb2PI2FD ) \ + X("pinsrb", 3, (P) aptb3PINSRB ) \ + X("pinsrd", 3, (P) aptb3PINSRD ) \ + X("pinsrq", 3, (P) aptb3PINSRQ ) \ + X("pinsrw", 3, (P) aptb3PINSRW ) \ + X("pmaddubsw", 2, (P) aptb2PMADDUBSW ) \ + X("pmaddwd", 2, (P) aptb2PMADDWD ) \ + X("pmaxsb", 2, (P) aptb2PMAXSB ) \ + X("pmaxsd", 2, (P) aptb2PMAXSD ) \ + X("pmaxsw", 2, (P) aptb2PMAXSW ) \ + X("pmaxub", 2, (P) aptb2PMAXUB ) \ + X("pmaxud", 2, (P) aptb2PMAXUD ) \ + X("pmaxuw", 2, (P) aptb2PMAXUW ) \ + X("pminsb", 2, (P) aptb2PMINSB ) \ + X("pminsd", 2, (P) aptb2PMINSD ) \ + X("pminsw", 2, (P) aptb2PMINSW ) \ + X("pminub", 2, (P) aptb2PMINUB ) \ + X("pminud", 2, (P) aptb2PMINUD ) \ + X("pminuw", 2, (P) aptb2PMINUW ) \ + X("pmovmskb", 2, (P) aptb2PMOVMSKB ) \ + X("pmovsxbd", 2, (P) aptb2PMOVSXBD ) \ + X("pmovsxbq", 2, (P) aptb2PMOVSXBQ ) \ + X("pmovsxbw", 2, (P) aptb2PMOVSXBW ) \ + X("pmovsxdq", 2, (P) aptb2PMOVSXDQ ) \ + X("pmovsxwd", 2, (P) aptb2PMOVSXWD ) \ + X("pmovsxwq", 2, (P) aptb2PMOVSXWQ ) \ + X("pmovzxbd", 2, (P) aptb2PMOVZXBD ) \ + X("pmovzxbq", 2, (P) aptb2PMOVZXBQ ) \ + X("pmovzxbw", 2, (P) aptb2PMOVZXBW ) \ + X("pmovzxdq", 2, (P) aptb2PMOVZXDQ ) \ + X("pmovzxwd", 2, (P) aptb2PMOVZXWD ) \ + X("pmovzxwq", 2, (P) aptb2PMOVZXWQ ) \ + X("pmuldq", 2, (P) aptb2PMULDQ ) \ + X("pmulhrsw", 2, (P) aptb2PMULHRSW ) \ + X("pmulhrw", 2, (P) aptb2PMULHRW ) \ + X("pmulhuw", 2, (P) aptb2PMULHUW ) \ + X("pmulhw", 2, (P) aptb2PMULHW ) \ + X("pmulld", 2, (P) aptb2PMULLD ) \ + X("pmullw", 2, (P) aptb2PMULLW ) \ + X("pmuludq", 2, (P) aptb2PMULUDQ ) \ + X("pop", 1, (P) aptb1POP ) \ + X("popa", 0, aptb0POPA ) \ + X("popad", 0, aptb0POPAD ) \ + X("popcnt", 2, (P) aptb2POPCNT ) \ + X("popf", 0, aptb0POPF ) \ + X("popfd", 0, aptb0POPFD ) \ + X("popfq", 0, aptb0POPFQ ) \ + X("por", 2, (P) aptb2POR ) \ + X("prefetchnta", 1, (P) aptb1PREFETCHNTA ) \ + X("prefetcht0", 1, (P) aptb1PREFETCHT0 ) \ + X("prefetcht1", 1, (P) aptb1PREFETCHT1 ) \ + X("prefetcht2", 1, (P) aptb1PREFETCHT2 ) \ + X("psadbw", 2, (P) aptb2PSADBW ) \ + X("pshufb", 2, (P) aptb2PSHUFB ) \ + X("pshufd", 3, (P) aptb3PSHUFD ) \ + X("pshufhw", 3, (P) aptb3PSHUFHW ) \ + X("pshuflw", 3, (P) aptb3PSHUFLW ) \ + X("pshufw", 3, (P) aptb3PSHUFW ) \ + X("psignb", 2, (P) aptb2PSIGNB ) \ + X("psignd", 2, (P) aptb2PSIGND ) \ + X("psignw", 2, (P) aptb2PSIGNW ) \ + X("pslld", 2, (P) aptb2PSLLD ) \ + X("pslldq", 2, (P) aptb2PSLLDQ ) \ + X("psllq", 2, (P) aptb2PSLLQ ) \ + X("psllw", 2, (P) aptb2PSLLW ) \ + X("psrad", 2, (P) aptb2PSRAD ) \ + X("psraw", 2, (P) aptb2PSRAW ) \ + X("psrld", 2, (P) aptb2PSRLD ) \ + X("psrldq", 2, (P) aptb2PSRLDQ ) \ + X("psrlq", 2, (P) aptb2PSRLQ ) \ + X("psrlw", 2, (P) aptb2PSRLW ) \ + X("psubb", 2, (P) aptb2PSUBB ) \ + X("psubd", 2, (P) aptb2PSUBD ) \ + X("psubq", 2, (P) aptb2PSUBQ ) \ + X("psubsb", 2, (P) aptb2PSUBSB ) \ + X("psubsw", 2, (P) aptb2PSUBSW ) \ + X("psubusb", 2, (P) aptb2PSUBUSB ) \ + X("psubusw", 2, (P) aptb2PSUBUSW ) \ + X("psubw", 2, (P) aptb2PSUBW ) \ + X("pswapd", 2, (P) aptb2PSWAPD ) \ + X("ptest", 2, (P) aptb2PTEST ) \ + X("punpckhbw", 2, (P) aptb2PUNPCKHBW ) \ + X("punpckhdq", 2, (P) aptb2PUNPCKHDQ ) \ + X("punpckhqdq", 2, (P) aptb2PUNPCKHQDQ ) \ + X("punpckhwd", 2, (P) aptb2PUNPCKHWD ) \ + X("punpcklbw", 2, (P) aptb2PUNPCKLBW ) \ + X("punpckldq", 2, (P) aptb2PUNPCKLDQ ) \ + X("punpcklqdq", 2, (P) aptb2PUNPCKLQDQ ) \ + X("punpcklwd", 2, (P) aptb2PUNPCKLWD ) \ + X("push", 1, (P) aptb1PUSH ) \ + X("pusha", 0, aptb0PUSHA ) \ + X("pushad", 0, aptb0PUSHAD ) \ + X("pushf", 0, aptb0PUSHF ) \ + X("pushfd", 0, aptb0PUSHFD ) \ + X("pushfq", 0, aptb0PUSHFQ ) \ + X("pxor", 2, (P) aptb2PXOR ) \ + X("rcl", ITshift | 2, (P) aptb2RCL ) \ + X("rcpps", 2, (P) aptb2RCPPS ) \ + X("rcpss", 2, (P) aptb2RCPSS ) \ + X("rcr", ITshift | 2, (P) aptb2RCR ) \ + X("rdfsbase", 1, (P) aptb1RDFSBASE ) \ + X("rdgsbase", 1, (P) aptb1RDGSBASE ) \ + X("rdmsr", 0, aptb0RDMSR ) \ + X("rdpmc", 0, aptb0RDPMC ) \ + X("rdrand", 1, (P) aptb1RDRAND ) \ + X("rdtsc", 0, aptb0RDTSC ) \ + X("rep", ITprefix | 0, aptb0REP ) \ + X("repe", ITprefix | 0, aptb0REP ) \ + X("repne", ITprefix | 0, aptb0REPNE ) \ + X("repnz", ITprefix | 0, aptb0REPNE ) \ + X("repz", ITprefix | 0, aptb0REP ) \ + X("ret", ITopt | 1, (P) aptb1RET ) \ + X("retf", ITopt | 1, (P) aptb1RETF ) \ + X("rol", ITshift | 2, (P) aptb2ROL ) \ + X("ror", ITshift | 2, (P) aptb2ROR ) \ + X("roundpd", 3, (P) aptb3ROUNDPD ) \ + X("roundps", 3, (P) aptb3ROUNDPS ) \ + X("roundsd", 3, (P) aptb3ROUNDSD ) \ + X("roundss", 3, (P) aptb3ROUNDSS ) \ + X("rsm", 0, aptb0RSM ) \ + X("rsqrtps", 2, (P) aptb2RSQRTPS ) \ + X("rsqrtss", 2, (P) aptb2RSQRTSS ) \ + X("sahf", 0, aptb0SAHF ) \ + X("sal", ITshift | 2, (P) aptb2SHL ) \ + X("sar", ITshift | 2, (P) aptb2SAR ) \ + X("sbb", 2, (P) aptb2SBB ) \ + X("scas", 1, (P) aptb1SCAS ) \ + X("scasb", 0, aptb0SCASB ) \ + X("scasd", 0, aptb0SCASD ) \ + X("scasq", 0, aptb0SCASQ ) \ + X("scasw", 0, aptb0SCASW ) \ + X("seta", 1, (P) aptb1SETNBE ) \ + X("setae", 1, (P) aptb1SETNB ) \ + X("setb", 1, (P) aptb1SETB ) \ + X("setbe", 1, (P) aptb1SETBE ) \ + X("setc", 1, (P) aptb1SETB ) \ + X("sete", 1, (P) aptb1SETZ ) \ + X("setg", 1, (P) aptb1SETNLE ) \ + X("setge", 1, (P) aptb1SETNL ) \ + X("setl", 1, (P) aptb1SETL ) \ + X("setle", 1, (P) aptb1SETLE ) \ + X("setna", 1, (P) aptb1SETBE ) \ + X("setnae", 1, (P) aptb1SETB ) \ + X("setnb", 1, (P) aptb1SETNB ) \ + X("setnbe", 1, (P) aptb1SETNBE ) \ + X("setnc", 1, (P) aptb1SETNB ) \ + X("setne", 1, (P) aptb1SETNZ ) \ + X("setng", 1, (P) aptb1SETLE ) \ + X("setnge", 1, (P) aptb1SETL ) \ + X("setnl", 1, (P) aptb1SETNL ) \ + X("setnle", 1, (P) aptb1SETNLE ) \ + X("setno", 1, (P) aptb1SETNO ) \ + X("setnp", 1, (P) aptb1SETNP ) \ + X("setns", 1, (P) aptb1SETNS ) \ + X("setnz", 1, (P) aptb1SETNZ ) \ + X("seto", 1, (P) aptb1SETO ) \ + X("setp", 1, (P) aptb1SETP ) \ + X("setpe", 1, (P) aptb1SETP ) \ + X("setpo", 1, (P) aptb1SETNP ) \ + X("sets", 1, (P) aptb1SETS ) \ + X("setz", 1, (P) aptb1SETZ ) \ + X("sfence", 0, aptb0SFENCE) \ + X("sgdt", 1, (P) aptb1SGDT ) \ + X("shl", ITshift | 2, (P) aptb2SHL ) \ + X("shld", 3, (P) aptb3SHLD ) \ + X("shr", ITshift | 2, (P) aptb2SHR ) \ + X("shrd", 3, (P) aptb3SHRD ) \ + X("shufpd", 3, (P) aptb3SHUFPD ) \ + X("shufps", 3, (P) aptb3SHUFPS ) \ + X("sidt", 1, (P) aptb1SIDT ) \ + X("sldt", 1, (P) aptb1SLDT ) \ + X("smsw", 1, (P) aptb1SMSW ) \ + X("sqrtpd", 2, (P) aptb2SQRTPD ) \ + X("sqrtps", 2, (P) aptb2SQRTPS ) \ + X("sqrtsd", 2, (P) aptb2SQRTSD ) \ + X("sqrtss", 2, (P) aptb2SQRTSS ) \ + X("stc", 0, aptb0STC ) \ + X("std", 0, aptb0STD ) \ + X("sti", 0, aptb0STI ) \ + X("stmxcsr", 1, (P) aptb1STMXCSR ) \ + X("stos", 1, (P) aptb1STOS ) \ + X("stosb", 0, aptb0STOSB ) \ + X("stosd", 0, aptb0STOSD ) \ + X("stosq", 0, aptb0STOSQ ) \ + X("stosw", 0, aptb0STOSW ) \ + X("str", 1, (P) aptb1STR ) \ + X("sub", 2, (P) aptb2SUB ) \ + X("subpd", 2, (P) aptb2SUBPD ) \ + X("subps", 2, (P) aptb2SUBPS ) \ + X("subsd", 2, (P) aptb2SUBSD ) \ + X("subss", 2, (P) aptb2SUBSS ) \ + X("syscall", 0, aptb0SYSCALL ) \ + X("sysenter", 0, aptb0SYSENTER ) \ + X("sysexit", 0, aptb0SYSEXIT ) \ + X("sysret", 0, aptb0SYSRET ) \ + X("test", 2, (P) aptb2TEST ) \ + X("ucomisd", 2, (P) aptb2UCOMISD ) \ + X("ucomiss", 2, (P) aptb2UCOMISS ) \ + X("ud2", 0, aptb0UD2 ) \ + X("unpckhpd", 2, (P) aptb2UNPCKHPD ) \ + X("unpckhps", 2, (P) aptb2UNPCKHPS ) \ + X("unpcklpd", 2, (P) aptb2UNPCKLPD ) \ + X("unpcklps", 2, (P) aptb2UNPCKLPS ) \ + + +#define OPCODETABLE3 \ + X("vaddpd", 3, (P) aptb3VADDPD ) \ + X("vaddps", 3, (P) aptb3VADDPS ) \ + X("vaddsd", 3, (P) aptb3VADDSD ) \ + X("vaddss", 3, (P) aptb3VADDSS ) \ + X("vaddsubpd", 3, (P) aptb3VADDSUBPD ) \ + X("vaddsubps", 3, (P) aptb3VADDSUBPS ) \ + X("vaesdec", 3, (P) aptb3VAESDEC ) \ + X("vaesdeclast", 3, (P) aptb3VAESDECLAST ) \ + X("vaesenc", 3, (P) aptb3VAESENC ) \ + X("vaesenclast", 3, (P) aptb3VAESENCLAST ) \ + X("vaesimc", 2, (P) aptb2VAESIMC ) \ + X("vaeskeygenassist", 3, (P) aptb3VAESKEYGENASSIST ) \ + X("vandnpd", 3, (P) aptb3VANDNPD ) \ + X("vandnps", 3, (P) aptb3VANDNPS ) \ + X("vandpd", 3, (P) aptb3VANDPD ) \ + X("vandps", 3, (P) aptb3VANDPS ) \ + X("vblendpd", 4, (P) aptb4VBLENDPD ) \ + X("vblendps", 4, (P) aptb4VBLENDPS ) \ + X("vblendvpd", 4, (P) aptb4VBLENDVPD ) \ + X("vblendvps", 4, (P) aptb4VBLENDVPS ) \ + X("vbroadcastf128", 2, (P) aptb2VBROADCASTF128 ) \ + X("vbroadcastsd", 2, (P) aptb2VBROADCASTSD ) \ + X("vbroadcastss", 2, (P) aptb2VBROADCASTSS ) \ + X("vcmppd", 4, (P) aptb4VCMPPD ) \ + X("vcmpps", 4, (P) aptb4VCMPPS ) \ + X("vcmpsd", 4, (P) aptb4VCMPSD ) \ + X("vcmpss", 4, (P) aptb4VCMPSS ) \ + X("vcomisd", 2, (P) aptb2VCOMISD ) \ + X("vcomiss", 2, (P) aptb2VCOMISS ) \ + X("vcvtdq2pd", 2, (P) aptb2VCVTDQ2PD ) \ + X("vcvtdq2ps", 2, (P) aptb2VCVTDQ2PS ) \ + X("vcvtpd2dq", 2, (P) aptb2VCVTPD2DQ ) \ + X("vcvtpd2ps", 2, (P) aptb2VCVTPD2PS ) \ + X("vcvtph2ps", 2, (P) aptb2VCVTPH2PS ) \ + X("vcvtps2dq", 2, (P) aptb2VCVTPS2DQ ) \ + X("vcvtps2pd", 2, (P) aptb2VCVTPS2PD ) \ + X("vcvtps2ph", 3, (P) aptb3VCVTPS2PH ) \ + X("vcvtsd2si", 2, (P) aptb2VCVTSD2SI ) \ + X("vcvtsd2ss", 3, (P) aptb3VCVTSD2SS ) \ + X("vcvtsi2sd", 3, (P) aptb3VCVTSI2SD ) \ + X("vcvtsi2ss", 3, (P) aptb3VCVTSI2SS ) \ + X("vcvtss2sd", 3, (P) aptb3VCVTSS2SD ) \ + X("vcvtss2si", 2, (P) aptb2VCVTSS2SI ) \ + X("vcvttpd2dq", 2, (P) aptb2VCVTTPD2DQ ) \ + X("vcvttps2dq", 2, (P) aptb2VCVTTPS2DQ ) \ + X("vcvttsd2si", 2, (P) aptb2VCVTTSD2SI ) \ + X("vcvttss2si", 2, (P) aptb2VCVTTSS2SI ) \ + X("vdivpd", 3, (P) aptb3VDIVPD ) \ + X("vdivps", 3, (P) aptb3VDIVPS ) \ + X("vdivsd", 3, (P) aptb3VDIVSD ) \ + X("vdivss", 3, (P) aptb3VDIVSS ) \ + X("vdppd", 4, (P) aptb4VDPPD ) \ + X("vdpps", 4, (P) aptb4VDPPS ) \ + X("verr", 1, (P) aptb1VERR ) \ + X("verw", 1, (P) aptb1VERW ) \ + X("vextractf128", 3, (P) aptb3VEXTRACTF128 ) \ + X("vextractps", 3, (P) aptb3VEXTRACTPS ) \ + X("vfmadd132pd", 3, (P) aptb3VFMADD132PD ) \ + X("vfmadd132ps", 3, (P) aptb3VFMADD132PS ) \ + X("vfmadd132sd", 3, (P) aptb3VFMADD132SD ) \ + X("vfmadd132ss", 3, (P) aptb3VFMADD132SS ) \ + X("vfmadd213pd", 3, (P) aptb3VFMADD213PD ) \ + X("vfmadd213ps", 3, (P) aptb3VFMADD213PS ) \ + X("vfmadd213sd", 3, (P) aptb3VFMADD213SD ) \ + X("vfmadd213ss", 3, (P) aptb3VFMADD213SS ) \ + X("vfmadd231pd", 3, (P) aptb3VFMADD231PD ) \ + X("vfmadd231ps", 3, (P) aptb3VFMADD231PS ) \ + X("vfmadd231sd", 3, (P) aptb3VFMADD231SD ) \ + X("vfmadd231ss", 3, (P) aptb3VFMADD231SS ) \ + X("vfmaddsub132pd", 3, (P) aptb3VFMADDSUB132PD ) \ + X("vfmaddsub132ps", 3, (P) aptb3VFMADDSUB132PS ) \ + X("vfmaddsub213pd", 3, (P) aptb3VFMADDSUB213PD ) \ + X("vfmaddsub213ps", 3, (P) aptb3VFMADDSUB213PS ) \ + X("vfmaddsub231pd", 3, (P) aptb3VFMADDSUB231PD ) \ + X("vfmaddsub231ps", 3, (P) aptb3VFMADDSUB231PS ) \ + X("vfmsub132pd", 3, (P) aptb3VFMSUB132PD ) \ + X("vfmsub132ps", 3, (P) aptb3VFMSUB132PS ) \ + X("vfmsub132sd", 3, (P) aptb3VFMSUB132SD ) \ + X("vfmsub132ss", 3, (P) aptb3VFMSUB132SS ) \ + X("vfmsub213pd", 3, (P) aptb3VFMSUB213PD ) \ + X("vfmsub213ps", 3, (P) aptb3VFMSUB213PS ) \ + X("vfmsub213sd", 3, (P) aptb3VFMSUB213SD ) \ + X("vfmsub213ss", 3, (P) aptb3VFMSUB213SS ) \ + X("vfmsub231pd", 3, (P) aptb3VFMSUB231PD ) \ + X("vfmsub231ps", 3, (P) aptb3VFMSUB231PS ) \ + X("vfmsub231sd", 3, (P) aptb3VFMSUB231SD ) \ + X("vfmsub231ss", 3, (P) aptb3VFMSUB231SS ) \ + X("vfmsubadd132pd", 3, (P) aptb3VFMSUBADD132PD ) \ + X("vfmsubadd132ps", 3, (P) aptb3VFMSUBADD132PS ) \ + X("vfmsubadd213pd", 3, (P) aptb3VFMSUBADD213PD ) \ + X("vfmsubadd213ps", 3, (P) aptb3VFMSUBADD213PS ) \ + X("vfmsubadd231pd", 3, (P) aptb3VFMSUBADD231PD ) \ + X("vfmsubadd231ps", 3, (P) aptb3VFMSUBADD231PS ) \ + X("vhaddpd", 3, (P) aptb3VHADDPD ) \ + X("vhaddps", 3, (P) aptb3VHADDPS ) \ + X("vinsertf128", 4, (P) aptb4VINSERTF128 ) \ + X("vinsertps", 4, (P) aptb4VINSERTPS ) \ + X("vlddqu", 2, (P) aptb2VLDDQU ) \ + X("vldmxcsr", 1, (P) aptb1VLDMXCSR ) \ + X("vmaskmovdqu", 2, (P) aptb2VMASKMOVDQU ) \ + X("vmaskmovpd", 3, (P) aptb3VMASKMOVPD ) \ + X("vmaskmovps", 3, (P) aptb3VMASKMOVPS ) \ + X("vmaxpd", 3, (P) aptb3VMAXPD ) \ + X("vmaxps", 3, (P) aptb3VMAXPS ) \ + X("vmaxsd", 3, (P) aptb3VMAXSD ) \ + X("vmaxss", 3, (P) aptb3VMAXSS ) \ + X("vminpd", 3, (P) aptb3VMINPD ) \ + X("vminps", 3, (P) aptb3VMINPS ) \ + X("vminsd", 3, (P) aptb3VMINSD ) \ + X("vminss", 3, (P) aptb3VMINSS ) \ + X("vmovapd", 2, (P) aptb2VMOVAPD ) \ + X("vmovaps", 2, (P) aptb2VMOVAPS ) \ + X("vmovd", 2, (P) aptb2VMOVD ) \ + X("vmovddup", 2, (P) aptb2VMOVDDUP ) \ + X("vmovdqa", 2, (P) aptb2VMOVDQA ) \ + X("vmovdqu", 2, (P) aptb2VMOVDQU ) \ + X("vmovhlps", 3, (P) aptb3VMOVLHPS ) \ + X("vmovhpd", ITopt | 3, (P) aptb3VMOVHPD ) \ + X("vmovhps", ITopt | 3, (P) aptb3VMOVHPS ) \ + X("vmovlhps", 3, (P) aptb3VMOVHLPS ) \ + X("vmovlpd", ITopt | 3, (P) aptb3VMOVLPD ) \ + X("vmovlps", ITopt | 3, (P) aptb3VMOVLPS ) \ + X("vmovmskpd", 2, (P) aptb2VMOVMSKPD ) \ + X("vmovmskps", 2, (P) aptb2VMOVMSKPS ) \ + X("vmovntdq", 2, (P) aptb2VMOVNTDQ ) \ + X("vmovntdqa", 2, (P) aptb2VMOVNTDQA ) \ + X("vmovntpd", 2, (P) aptb2VMOVNTPD ) \ + X("vmovntps", 2, (P) aptb2VMOVNTPS ) \ + X("vmovq", 2, (P) aptb2VMOVQ ) \ + X("vmovsd", ITopt | 3, (P) aptb3VMOVSD ) \ + X("vmovshdup", 2, (P) aptb2VMOVSHDUP ) \ + X("vmovsldup", 2, (P) aptb2VMOVSLDUP ) \ + X("vmovss", ITopt | 3, (P) aptb3VMOVSS ) \ + X("vmovupd", 2, (P) aptb2VMOVUPD ) \ + X("vmovups", 2, (P) aptb2VMOVUPS ) \ + X("vmpsadbw", 4, (P) aptb4VMPSADBW ) \ + X("vmulpd", 3, (P) aptb3VMULPD ) \ + X("vmulps", 3, (P) aptb3VMULPS ) \ + X("vmulsd", 3, (P) aptb3VMULSD ) \ + X("vmulss", 3, (P) aptb3VMULSS ) \ + X("vorpd", 3, (P) aptb3VORPD ) \ + X("vorps", 3, (P) aptb3VORPS ) \ + X("vpabsb", 2, (P) aptb2VPABSB ) \ + X("vpabsd", 2, (P) aptb2VPABSD ) \ + X("vpabsw", 2, (P) aptb2VPABSW ) \ + X("vpackssdw", 3, (P) aptb3VPACKSSDW ) \ + X("vpacksswb", 3, (P) aptb3VPACKSSWB ) \ + X("vpackusdw", 3, (P) aptb3VPACKUSDW ) \ + X("vpackuswb", 3, (P) aptb3VPACKUSWB ) \ + X("vpaddb", 3, (P) aptb3VPADDB ) \ + X("vpaddd", 3, (P) aptb3VPADDD ) \ + X("vpaddq", 3, (P) aptb3VPADDQ ) \ + X("vpaddsb", 3, (P) aptb3VPADDSB ) \ + X("vpaddsw", 3, (P) aptb3VPADDSW ) \ + X("vpaddusb", 3, (P) aptb3VPADDUSB ) \ + X("vpaddusw", 3, (P) aptb3VPADDUSW ) \ + X("vpaddw", 3, (P) aptb3VPADDW ) \ + X("vpalignr", 4, (P) aptb4VPALIGNR ) \ + X("vpand", 3, (P) aptb3VPAND ) \ + X("vpandn", 3, (P) aptb3VPANDN ) \ + X("vpavgb", 3, (P) aptb3VPAVGB ) \ + X("vpavgw", 3, (P) aptb3VPAVGW ) \ + X("vpblendvb", 4, (P) aptb4VPBLENDVB ) \ + X("vpblendw", 4, (P) aptb4VPBLENDW ) \ + X("vpclmulqdq", 4, (P) aptb4VPCLMULQDQ ) \ + X("vpcmpeqb", 3, (P) aptb3VPCMPEQB ) \ + X("vpcmpeqd", 3, (P) aptb3VPCMPEQD ) \ + X("vpcmpeqq", 3, (P) aptb3VPCMPEQQ ) \ + X("vpcmpeqw", 3, (P) aptb3VPCMPEQW ) \ + X("vpcmpestri", 3, (P) aptb3VPCMPESTRI ) \ + X("vpcmpestrm", 3, (P) aptb3VPCMPESTRM ) \ + X("vpcmpgtb", 3, (P) aptb3VPCMPGTB ) \ + X("vpcmpgtd", 3, (P) aptb3VPCMPGTD ) \ + X("vpcmpgtq", 3, (P) aptb3VPCMPGTQ ) \ + X("vpcmpgtw", 3, (P) aptb3VPCMPGTW ) \ + X("vpcmpistri", 3, (P) aptb3VPCMPISTRI ) \ + X("vpcmpistrm", 3, (P) aptb3VPCMPISTRM ) \ + X("vperm2f128", 4, (P) aptb3VPERM2F128 ) \ + X("vpermilpd", 3, (P) aptb3VPERMILPD ) \ + X("vpermilps", 3, (P) aptb3VPERMILPS ) \ + X("vpextrb", 3, (P) aptb3VPEXTRB ) \ + X("vpextrd", 3, (P) aptb3VPEXTRD ) \ + X("vpextrq", 3, (P) aptb3VPEXTRQ ) \ + X("vpextrw", 3, (P) aptb3VPEXTRW ) \ + X("vphaddd", 3, (P) aptb3VPHADDD ) \ + X("vphaddsw", 3, (P) aptb3VPHADDSW ) \ + X("vphaddw", 3, (P) aptb3VPHADDW ) \ + X("vphminposuw", 2, (P) aptb2VPHMINPOSUW ) \ + X("vphsubd", 3, (P) aptb3VPHSUBD ) \ + X("vphsubsw", 3, (P) aptb3VPHSUBSW ) \ + X("vphsubw", 3, (P) aptb3VPHSUBW ) \ + X("vpinsrb", 4, (P) aptb4VPINSRB ) \ + X("vpinsrd", 4, (P) aptb4VPINSRD ) \ + X("vpinsrq", 4, (P) aptb4VPINSRQ ) \ + X("vpinsrw", 4, (P) aptb4VPINSRW ) \ + X("vpmaddubsw", 3, (P) aptb3VPMADDUBSW ) \ + X("vpmaddwd", 3, (P) aptb3VPMADDWD ) \ + X("vpmaxsb", 3, (P) aptb3VPMAXSB ) \ + X("vpmaxsd", 3, (P) aptb3VPMAXSD ) \ + X("vpmaxsw", 3, (P) aptb3VPMAXSW ) \ + X("vpmaxub", 3, (P) aptb3VPMAXUB ) \ + X("vpmaxud", 3, (P) aptb3VPMAXUD ) \ + X("vpmaxuw", 3, (P) aptb3VPMAXUW ) \ + X("vpminsb", 3, (P) aptb3VPMINSB ) \ + X("vpminsd", 3, (P) aptb3VPMINSD ) \ + X("vpminsw", 3, (P) aptb3VPMINSW ) \ + X("vpminub", 3, (P) aptb3VPMINUB ) \ + X("vpminud", 3, (P) aptb3VPMINUD ) \ + X("vpminuw", 3, (P) aptb3VPMINUW ) \ + X("vpmovmskb", 2, (P) aptb2VPMOVMSKB ) \ + X("vpmovsxbd", 2, (P) aptb2VPMOVSXBD ) \ + X("vpmovsxbq", 2, (P) aptb2VPMOVSXBQ ) \ + X("vpmovsxbw", 2, (P) aptb2VPMOVSXBW ) \ + X("vpmovsxdq", 2, (P) aptb2VPMOVSXDQ ) \ + X("vpmovsxwd", 2, (P) aptb2VPMOVSXWD ) \ + X("vpmovsxwq", 2, (P) aptb2VPMOVSXWQ ) \ + X("vpmovzxbd", 2, (P) aptb2VPMOVZXBD ) \ + X("vpmovzxbq", 2, (P) aptb2VPMOVZXBQ ) \ + X("vpmovzxbw", 2, (P) aptb2VPMOVZXBW ) \ + X("vpmovzxdq", 2, (P) aptb2VPMOVZXDQ ) \ + X("vpmovzxwd", 2, (P) aptb2VPMOVZXWD ) \ + X("vpmovzxwq", 2, (P) aptb2VPMOVZXWQ ) \ + X("vpmulhrsw", 3, (P) aptb3VPMULHRSW ) \ + X("vpmulhuw", 3, (P) aptb3VPMULHUW ) \ + X("vpmulhw", 3, (P) aptb3VPMULHW ) \ + X("vpmulld", 3, (P) aptb3VPMULLD ) \ + X("vpmullw", 3, (P) aptb3VPMULLW ) \ + X("vpmuludq", 3, (P) aptb3VPMULUDQ ) \ + X("vpor", 3, (P) aptb3VPOR ) \ + X("vpsadbw", 3, (P) aptb3VPSADBW ) \ + X("vpshufb", 3, (P) aptb3VPSHUFB ) \ + X("vpshufd", 3, (P) aptb3VPSHUFD ) \ + X("vpshufhw", 3, (P) aptb3VPSHUFHW ) \ + X("vpshuflw", 3, (P) aptb3VPSHUFLW ) \ + X("vpsignb", 3, (P) aptb3VPSIGNB ) \ + X("vpsignd", 3, (P) aptb3VPSIGND ) \ + X("vpsignw", 3, (P) aptb3VPSIGNW ) \ + X("vpslld", 3, (P) aptb3VPSLLD ) \ + X("vpslldq", 3, (P) aptb3VPSLLDQ ) \ + X("vpsllq", 3, (P) aptb3VPSLLQ ) \ + X("vpsllw", 3, (P) aptb3VPSLLW ) \ + X("vpsrad", 3, (P) aptb3VPSRAD ) \ + X("vpsraw", 3, (P) aptb3VPSRAW ) \ + X("vpsrld", 3, (P) aptb3VPSRLD ) \ + X("vpsrldq", 3, (P) aptb3VPSRLDQ ) \ + X("vpsrlq", 3, (P) aptb3VPSRLQ ) \ + X("vpsrlw", 3, (P) aptb3VPSRLW ) \ + X("vpsubb", 3, (P) aptb3VPSUBB ) \ + X("vpsubd", 3, (P) aptb3VPSUBD ) \ + X("vpsubq", 3, (P) aptb3VPSUBQ ) \ + X("vpsubsb", 3, (P) aptb3VPSUBSB ) \ + X("vpsubsw", 3, (P) aptb3VPSUBSW ) \ + X("vpsubusb", 3, (P) aptb3VPSUBUSB ) \ + X("vpsubusw", 3, (P) aptb3VPSUBUSW ) \ + X("vpsubw", 3, (P) aptb3VPSUBW ) \ + X("vptest", 2, (P) aptb2VPTEST ) \ + X("vpunpckhbw", 3, (P) aptb3VPUNPCKHBW ) \ + X("vpunpckhdq", 3, (P) aptb3VPUNPCKHDQ ) \ + X("vpunpckhqdq", 3, (P) aptb3VPUNPCKHQDQ ) \ + X("vpunpckhwd", 3, (P) aptb3VPUNPCKHWD ) \ + X("vpunpcklbw", 3, (P) aptb3VPUNPCKLBW ) \ + X("vpunpckldq", 3, (P) aptb3VPUNPCKLDQ ) \ + X("vpunpcklqdq", 3, (P) aptb3VPUNPCKLQDQ ) \ + X("vpunpcklwd", 3, (P) aptb3VPUNPCKLWD ) \ + X("vpxor", 3, (P) aptb3VPXOR ) \ + X("vrcpps", 2, (P) aptb2VRCPPS ) \ + X("vrcpss", 3, (P) aptb3VRCPSS ) \ + X("vroundpd", 3, (P) aptb3VROUNDPD ) \ + X("vroundps", 3, (P) aptb3VROUNDPS ) \ + X("vroundsd", 4, (P) aptb4VROUNDSD ) \ + X("vroundss", 4, (P) aptb4VROUNDSS ) \ + X("vshufpd", 4, (P) aptb4VSHUFPD ) \ + X("vshufps", 4, (P) aptb4VSHUFPS ) \ + X("vsqrtpd", 2, (P) aptb2VSQRTPD ) \ + X("vsqrtps", 2, (P) aptb2VSQRTPS ) \ + X("vsqrtsd", 3, (P) aptb3VSQRTSD ) \ + X("vsqrtss", 3, (P) aptb3VSQRTSS ) \ + X("vstmxcsr", 1, (P) aptb1VSTMXCSR ) \ + X("vsubpd", 3, (P) aptb3VSUBPD ) \ + X("vsubps", 3, (P) aptb3VSUBPS ) \ + X("vsubsd", 3, (P) aptb3VSUBSD ) \ + X("vsubss", 3, (P) aptb3VSUBSS ) \ + X("vucomisd", 2, (P) aptb2VUCOMISD ) \ + X("vucomiss", 2, (P) aptb2VUCOMISS ) \ + X("vunpckhpd", 3, (P) aptb3VUNPCKHPD ) \ + X("vunpckhps", 3, (P) aptb3VUNPCKHPS ) \ + X("vunpcklpd", 3, (P) aptb3VUNPCKLPD ) \ + X("vunpcklps", 3, (P) aptb3VUNPCKLPS ) \ + X("vxorpd", 3, (P) aptb3VXORPD ) \ + X("vxorps", 3, (P) aptb3VXORPS ) \ + X("vzeroall", 0, aptb0VZEROALL ) \ + X("vzeroupper", 0, aptb0VZEROUPPER ) \ + X("wait", 0, aptb0WAIT ) \ + X("wbinvd", 0, aptb0WBINVD ) \ + X("wrfsbase", 1, (P) aptb1WRFSBASE ) \ + X("wrgsbase", 1, (P) aptb1WRGSBASE ) \ + X("wrmsr", 0, aptb0WRMSR ) \ + X("xadd", 2, (P) aptb2XADD ) \ + X("xchg", 2, (P) aptb2XCHG ) \ + X("xgetbv", 0, aptb0XGETBV) \ + X("xlat", ITopt | 1, (P) aptb1XLAT ) \ + X("xlatb", 0, aptb0XLATB ) \ + X("xor", 2, (P) aptb2XOR ) \ + X("xorpd", 2, (P) aptb2XORPD ) \ + X("xorps", 2, (P) aptb2XORPS ) \ + X("xrstor", ITfloat | 1, (P) aptb1XRSTOR ) \ + X("xrstor64", ITfloat | 1, (P) aptb1XRSTOR64 ) \ + X("xsave", ITfloat | 1, (P) aptb1XSAVE ) \ + X("xsave64", ITfloat | 1, (P) aptb1XSAVE64 ) \ + X("xsaveopt", ITfloat | 1, (P) aptb1XSAVEOPT ) \ + X("xsaveopt64", ITfloat | 1, (P) aptb1XSAVEOPT64 ) \ + X("xsetbv", 0, aptb0XSETBV) \ + +#endif + +static const char *opcodestr[] = +{ + #define X(a,b,c) a, + OPCODETABLE1 + OPCODETABLE2 + OPCODETABLE3 + #undef X +}; + +static OP optab[] = +{ + #define X(a,b,c) b,c, + OPCODETABLE1 + OPCODETABLE2 + OPCODETABLE3 + #undef X +}; + + +/******************************* + */ + +const char *asm_opstr(OP *pop) +{ + return opcodestr[pop - optab]; +} + +/******************************* + */ + +OP *asm_op_lookup(const char *s) +{ + int i; + char szBuf[20]; + + //dbg_printf("asm_op_lookup('%s')\n",s); + if (strlen(s) >= sizeof(szBuf)) + return NULL; + strcpy(szBuf,s); +#if SCPP + strlwr(szBuf); +#endif + + i = binary(szBuf,opcodestr,sizeof(opcodestr)/sizeof(opcodestr[0])); + return (i == -1) ? NULL : &optab[i]; +} + +/******************************* + */ + +void init_optab() +{ int i; + +#ifdef DEBUG + for (i = 0; i < arraysize(opcodestr) - 1; i++) + { + if (strcmp(opcodestr[i],opcodestr[i + 1]) >= 0) + { + dbg_printf("opcodestr[%d] = '%s', [%d] = '%s'\n",i,opcodestr[i],i + 1,opcodestr[i + 1]); + assert(0); + } + } +#endif +} + + + +#endif // !SPP diff --git a/backend/rtlsym.c b/backend/rtlsym.c new file mode 100644 index 00000000..3cc54b8e --- /dev/null +++ b/backend/rtlsym.c @@ -0,0 +1,125 @@ +// Copyright (C) 1996-1998 by Symantec +// Copyright (C) 2000-2010 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include "cc.h" +#include "type.h" +#include "oper.h" +#include "global.h" +#include "code.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +Symbol *rtlsym[RTLSYM_MAX]; + +#if MARS +// This varies depending on C ABI +#define FREGSAVED fregsaved +#else +#define FREGSAVED (mBP | mBX | mSI | mDI) +#endif + +static Symbol rtlsym2[RTLSYM_MAX]; + +/****************************************** + * Initialize rtl symbols. + */ + +void rtlsym_init() +{ + static int inited; + + if (!inited) + { inited++; + + //printf("rtlsym_init(%s)\n", regm_str(FREGSAVED)); + + for (int i = 0; i < RTLSYM_MAX; i++) + { + rtlsym[i] = &rtlsym2[i]; +#ifdef DEBUG + rtlsym[i]->id = IDsymbol; +#endif + rtlsym[i]->Stype = tsclib; + rtlsym[i]->Ssymnum = -1; + rtlsym[i]->Sclass = SCextern; + rtlsym[i]->Sfl = FLfunc; +#if ELFOBJ || MACHOBJ + rtlsym[i]->obj_si = (unsigned)-1; + rtlsym[i]->dwarf_off = (unsigned)-1; +#endif + rtlsym[i]->Sregsaved = FREGSAVED; + } + +#if MARS + type *t = type_fake(TYnfunc); + t->Tmangle = mTYman_c; + t->Tcount++; + + // Variadic function + type *tv = type_fake(TYnfunc); + tv->Tmangle = mTYman_c; + tv->Tcount++; +#endif + +#if MACHOBJ + type *tw = type_fake(TYnpfunc); + tw->Tmangle = mTYman_sys; + tw->Tcount++; +#else + type *tw = NULL; +#endif + +#undef SYMBOL_Z +#define SYMBOL_Z(e, fl, saved, n, flags, ty) \ + if (ty) rtlsym[RTLSYM_##e]->Stype = (ty); \ + if ((fl) != FLfunc) rtlsym[RTLSYM_##e]->Sfl = (fl); \ + if (flags) rtlsym[RTLSYM_##e]->Sflags = (flags); \ + if ((saved) != FREGSAVED) rtlsym[RTLSYM_##e]->Sregsaved = (saved); \ + strcpy(rtlsym[RTLSYM_##e]->Sident, (n)); \ + + RTLSYMS + } +} + +/******************************* + * Reset the symbols for the case when we are generating multiple + * .OBJ files from one compile. + */ + +#if MARS + +void rtlsym_reset() +{ int i; + + clib_inited = 0; + for (i = 0; i < RTLSYM_MAX; i++) + { rtlsym[i]->Sxtrnnum = 0; + rtlsym[i]->Stypidx = 0; + } +} + +#endif + +/******************************* + */ + +void rtlsym_term() +{ +} + +#endif diff --git a/backend/rtlsym.h b/backend/rtlsym.h new file mode 100644 index 00000000..89a14bed --- /dev/null +++ b/backend/rtlsym.h @@ -0,0 +1,155 @@ +// Copyright (C) 1994-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +/* + ty + ------------------------------------ + 0 tsclib TYnpfunc, C mangling + t TYnfunc, C mangling + tsjlib TYjfunc, C mangling + tsdlib TYjfunc, C mangling + */ + +#if SCPP +#define SYMBOL_SCPP(e, fl, saved, n, flags, ty) SYMBOL_Z(e,fl,saved,n,flags,ty) +#else +#define SYMBOL_SCPP(e, fl, saved, n, flags, ty) +#endif + +#if SCPP && TX86 +#define SYMBOL_SCPP_TX86(e, fl, saved, n, flags, ty) SYMBOL_Z(e,fl,saved,n,flags,ty) +#else +#define SYMBOL_SCPP_TX86(e, fl, saved, n, flags, ty) +#endif + +#if MARS +#define SYMBOL_MARS(e, fl, saved, n, flags, ty) SYMBOL_Z(e,fl,saved,n,flags,ty) +#else +#define SYMBOL_MARS(e, fl, saved, n, flags, ty) +#endif + + +#define RTLSYMS \ +\ +SYMBOL_MARS(THROW, FLfunc,(mES | mBP),"_d_throw@4", SFLexit, tw) \ +SYMBOL_MARS(THROWC, FLfunc,(mES | mBP),"_d_throwc", SFLexit, t) \ +SYMBOL_MARS(MONITOR_HANDLER, FLfunc,FREGSAVED,"_d_monitor_handler", 0, 0) \ +SYMBOL_MARS(MONITOR_PROLOG, FLfunc,FREGSAVED,"_d_monitor_prolog",0,t) \ +SYMBOL_MARS(MONITOR_EPILOG, FLfunc,FREGSAVED,"_d_monitor_epilog",0,t) \ +SYMBOL_MARS(DCOVER, FLfunc,FREGSAVED,"_d_cover_register", 0, t) \ +SYMBOL_MARS(DASSERT, FLfunc,FREGSAVED,"_d_assert", SFLexit, t) \ +SYMBOL_MARS(DASSERTM, FLfunc,FREGSAVED,"_d_assertm", SFLexit, t) \ +SYMBOL_MARS(DASSERT_MSG, FLfunc,FREGSAVED,"_d_assert_msg", SFLexit, t) \ +SYMBOL_MARS(DUNITTEST, FLfunc,FREGSAVED,"_d_unittest", 0, t) \ +SYMBOL_MARS(DUNITTESTM, FLfunc,FREGSAVED,"_d_unittestm", 0, t) \ +SYMBOL_MARS(DUNITTEST_MSG, FLfunc,FREGSAVED,"_d_unittest_msg", 0, t) \ +SYMBOL_MARS(DARRAY, FLfunc,FREGSAVED,"_d_array_bounds", SFLexit, t) \ +SYMBOL_MARS(DINVARIANT, FLfunc,FREGSAVED,"D9invariant12_d_invariantFC6ObjectZv", 0, tsdlib) \ +SYMBOL_MARS(_DINVARIANT, FLfunc,FREGSAVED,"_D9invariant12_d_invariantFC6ObjectZv", 0, tsdlib) \ +SYMBOL_MARS(MEMCPY, FLfunc,FREGSAVED,"memcpy", 0, t) \ +SYMBOL_MARS(MEMSET8, FLfunc,FREGSAVED,"memset", 0, t) \ +SYMBOL_MARS(MEMSET16, FLfunc,FREGSAVED,"_memset16", 0, t) \ +SYMBOL_MARS(MEMSET32, FLfunc,FREGSAVED,"_memset32", 0, t) \ +SYMBOL_MARS(MEMSET64, FLfunc,FREGSAVED,"_memset64", 0, t) \ +SYMBOL_MARS(MEMSET128, FLfunc,FREGSAVED,"_memset128",0, t) \ +SYMBOL_MARS(MEMSET80, FLfunc,FREGSAVED,"_memset80", 0, t) \ +SYMBOL_MARS(MEMSET160, FLfunc,FREGSAVED,"_memset160",0, t) \ +SYMBOL_MARS(MEMSETFLOAT, FLfunc,FREGSAVED,"_memsetFloat", 0, t) \ +SYMBOL_MARS(MEMSETDOUBLE, FLfunc,FREGSAVED,"_memsetDouble", 0, t) \ +SYMBOL_MARS(MEMSETN, FLfunc,FREGSAVED,"_memsetn", 0, t) \ +SYMBOL_MARS(MODULO, FLfunc,FREGSAVED,"_modulo", 0, t) \ +SYMBOL_MARS(MONITORENTER, FLfunc,FREGSAVED,"_d_monitorenter",0, t) \ +SYMBOL_MARS(MONITOREXIT, FLfunc,FREGSAVED,"_d_monitorexit",0, t) \ +SYMBOL_MARS(CRITICALENTER, FLfunc,FREGSAVED,"_d_criticalenter",0, t) \ +SYMBOL_MARS(CRITICALEXIT, FLfunc,FREGSAVED,"_d_criticalexit",0, t) \ +SYMBOL_MARS(SWITCH_STRING, FLfunc,FREGSAVED,"_d_switch_string", 0, t) \ +SYMBOL_MARS(SWITCH_USTRING,FLfunc,FREGSAVED,"_d_switch_ustring", 0, t) \ +SYMBOL_MARS(SWITCH_DSTRING,FLfunc,FREGSAVED,"_d_switch_dstring", 0, t) \ +SYMBOL_MARS(DSWITCHERR, FLfunc,FREGSAVED,"_d_switch_error", SFLexit, t) \ +SYMBOL_MARS(DHIDDENFUNC, FLfunc,FREGSAVED,"_d_hidden_func", 0, t) \ +SYMBOL_MARS(NEWCLASS, FLfunc,FREGSAVED,"_d_newclass", 0, t) \ +SYMBOL_MARS(NEWARRAYT, FLfunc,FREGSAVED,"_d_newarrayT", 0, t) \ +SYMBOL_MARS(NEWARRAYIT, FLfunc,FREGSAVED,"_d_newarrayiT", 0, t) \ +SYMBOL_MARS(NEWARRAYMT, FLfunc,FREGSAVED,"_d_newarraymT", 0, tv) \ +SYMBOL_MARS(NEWARRAYMIT, FLfunc,FREGSAVED,"_d_newarraymiT", 0, tv) \ +SYMBOL_MARS(ARRAYLITERALT, FLfunc,FREGSAVED,"_d_arrayliteralT", 0, tv) \ +SYMBOL_MARS(ARRAYLITERALTX, FLfunc,FREGSAVED,"_d_arrayliteralTX", 0, t) \ +SYMBOL_MARS(ASSOCARRAYLITERALT, FLfunc,FREGSAVED,"_d_assocarrayliteralT", 0, tv) \ +SYMBOL_MARS(ASSOCARRAYLITERALTX, FLfunc,FREGSAVED,"_d_assocarrayliteralTX", 0, t) \ +SYMBOL_MARS(CALLFINALIZER, FLfunc,FREGSAVED,"_d_callfinalizer", 0, t) \ +SYMBOL_MARS(CALLINTERFACEFINALIZER, FLfunc,FREGSAVED,"_d_callinterfacefinalizer", 0, t) \ +SYMBOL_MARS(DELCLASS, FLfunc,FREGSAVED,"_d_delclass", 0, t) \ +SYMBOL_MARS(DELINTERFACE, FLfunc,FREGSAVED,"_d_delinterface", 0, t) \ +SYMBOL_MARS(ALLOCMEMORY, FLfunc,FREGSAVED,"_d_allocmemory", 0, t) \ +SYMBOL_MARS(DELARRAY, FLfunc,FREGSAVED,"_d_delarray", 0, t) \ +SYMBOL_MARS(DELARRAYT, FLfunc,FREGSAVED,"_d_delarray_t", 0, t) \ +SYMBOL_MARS(DELMEMORY, FLfunc,FREGSAVED,"_d_delmemory", 0, t) \ +SYMBOL_MARS(INTERFACE, FLfunc,FREGSAVED,"_d_interface_vtbl", 0, t) \ +SYMBOL_MARS(DYNAMIC_CAST, FLfunc,FREGSAVED,"_d_dynamic_cast", 0, t) \ +SYMBOL_MARS(INTERFACE_CAST,FLfunc,FREGSAVED,"_d_interface_cast", 0, t) \ +SYMBOL_MARS(FATEXIT, FLfunc,FREGSAVED,"_fatexit", 0, t) \ +SYMBOL_MARS(ARRAYCATT, FLfunc,FREGSAVED,"_d_arraycatT", 0, t) \ +SYMBOL_MARS(ARRAYCATNT, FLfunc,FREGSAVED,"_d_arraycatnT", 0, tv) \ +SYMBOL_MARS(ARRAYAPPENDT, FLfunc,FREGSAVED,"_d_arrayappendT", 0, t) \ +SYMBOL_MARS(ARRAYAPPENDCT, FLfunc,FREGSAVED,"_d_arrayappendcT", 0, tv) \ +SYMBOL_MARS(ARRAYAPPENDCTX, FLfunc,FREGSAVED,"_d_arrayappendcTX", 0, t) \ +SYMBOL_MARS(ARRAYAPPENDCD, FLfunc,FREGSAVED,"_d_arrayappendcd", 0, t) \ +SYMBOL_MARS(ARRAYAPPENDWD, FLfunc,FREGSAVED,"_d_arrayappendwd", 0, t) \ +SYMBOL_MARS(ARRAYSETLENGTHT,FLfunc,FREGSAVED,"_d_arraysetlengthT", 0, t) \ +SYMBOL_MARS(ARRAYSETLENGTHIT,FLfunc,FREGSAVED,"_d_arraysetlengthiT", 0, t) \ +SYMBOL_MARS(ARRAYCOPY, FLfunc,FREGSAVED,"_d_arraycopy", 0, t) \ +SYMBOL_MARS(ARRAYASSIGN, FLfunc,FREGSAVED,"_d_arrayassign", 0, t) \ +SYMBOL_MARS(ARRAYCTOR, FLfunc,FREGSAVED,"_d_arrayctor", 0, t) \ +SYMBOL_MARS(ARRAYSETASSIGN, FLfunc,FREGSAVED,"_d_arraysetassign", 0, t) \ +SYMBOL_MARS(ARRAYSETCTOR, FLfunc,FREGSAVED,"_d_arraysetctor", 0, t) \ +SYMBOL_MARS(ARRAYCAST, FLfunc,FREGSAVED,"_d_arraycast", 0, t) \ +SYMBOL_MARS(ARRAYCAST_FROMBIT, FLfunc,FREGSAVED,"_d_arraycast_frombit", 0, t) \ +SYMBOL_MARS(ARRAYEQ, FLfunc,FREGSAVED,"_adEq", 0, t) \ +SYMBOL_MARS(ARRAYEQ2, FLfunc,FREGSAVED,"_adEq2", 0, t) \ +SYMBOL_MARS(ARRAYEQBIT, FLfunc,FREGSAVED,"_adEqBit", 0, t) \ +SYMBOL_MARS(ARRAYCMP, FLfunc,FREGSAVED,"_adCmp", 0, t) \ +SYMBOL_MARS(ARRAYCMP2, FLfunc,FREGSAVED,"_adCmp2", 0, t) \ +SYMBOL_MARS(ARRAYCMPCHAR, FLfunc,FREGSAVED,"_adCmpChar", 0, t) \ +SYMBOL_MARS(ARRAYCMPBIT, FLfunc,FREGSAVED,"_adCmpBit", 0, t) \ +SYMBOL_MARS(OBJ_EQ, FLfunc,FREGSAVED,"_d_obj_eq", 0, t) \ +SYMBOL_MARS(OBJ_CMP, FLfunc,FREGSAVED,"_d_obj_cmp", 0, t) \ +\ +SYMBOL_Z(EXCEPT_HANDLER2, FLfunc,fregsaved,"_except_handler2", 0, 0) \ +SYMBOL_Z(EXCEPT_HANDLER3, FLfunc,fregsaved,"_except_handler3", 0, 0) \ +SYMBOL_SCPP(CPP_HANDLER, FLfunc,FREGSAVED,"_cpp_framehandler", 0, 0) \ +SYMBOL_MARS(CPP_HANDLER, FLfunc,FREGSAVED,"_d_framehandler", 0, 0) \ +SYMBOL_MARS(D_LOCAL_UNWIND2, FLfunc,FREGSAVED,"_d_local_unwind2", 0, 0) \ +SYMBOL_SCPP(LOCAL_UNWIND2, FLfunc,FREGSAVED,"_local_unwind2", 0, 0) \ +\ +SYMBOL_Z(TLS_INDEX, FLextern,0,"_tls_index",0,tsint) \ +SYMBOL_Z(TLS_ARRAY, FLextern,0,"_tls_array",0,tsint) \ +SYMBOL_SCPP(AHSHIFT, FLfunc,0,"_AHSHIFT",0,tstrace) \ +\ +SYMBOL_SCPP_TX86(HDIFFN, FLfunc,mBX|mCX|mSI|mDI|mBP|mES,"_aNahdiff", 0, 0) \ +SYMBOL_SCPP_TX86(HDIFFF, FLfunc,mBX|mCX|mSI|mDI|mBP|mES,"_aFahdiff", 0, 0) \ +\ +SYMBOL_Z(EXCEPT_LIST, FLextern,0,"_except_list",0,tsint) \ +SYMBOL_Z(SETJMP3, FLfunc,FREGSAVED,"_setjmp3", 0, 0) \ +SYMBOL_Z(LONGJMP, FLfunc,FREGSAVED,"_seh_longjmp_unwind@4", 0, 0) \ +SYMBOL_Z(INTONLY, FLfunc,mSI|mDI,"_intonly", 0, 0) \ +SYMBOL_Z(ALLOCA, FLfunc,fregsaved,"__alloca", 0, 0) \ +SYMBOL_Z(CPP_LONGJMP, FLfunc,FREGSAVED,"_cpp_longjmp_unwind@4", 0, 0) \ +SYMBOL_Z(PTRCHK, FLfunc,fregsaved,"_ptrchk", 0, 0) \ +SYMBOL_Z(CHKSTK, FLfunc,fregsaved,"_chkstk", 0, 0) \ +SYMBOL_Z(TRACE_PRO_N, FLfunc,ALLREGS|mBP|mES,"_trace_pro_n",0,tstrace) \ +SYMBOL_Z(TRACE_PRO_F, FLfunc,ALLREGS|mBP|mES,"_trace_pro_f",0,tstrace) \ +SYMBOL_Z(TRACE_EPI_N, FLfunc,ALLREGS|mBP|mES,"_trace_epi_n",0,tstrace) \ +SYMBOL_Z(TRACE_EPI_F, FLfunc,ALLREGS|mBP|mES,"_trace_epi_f",0,tstrace) \ + + + diff --git a/backend/strtold.c b/backend/strtold.c new file mode 100644 index 00000000..190d5577 --- /dev/null +++ b/backend/strtold.c @@ -0,0 +1,712 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#include +#include +#include +#include +#include +#include +#if _WIN32 +#include +#include +#endif +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#include +#endif + +#if _WIN32 +// from \sc\src\include\setlocal.h +extern char * __cdecl __locale_decpoint; +void __pascal __set_errno (int an_errno); +#endif + +#if _WIN32 || linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + +#if 0 +/* This is for compilers that don't support hex float literals, + * and also makes it clearer what constants we're trying to use. + */ + +static long double negtab[] = + {1e-4096L,1e-2048L,1e-1024L,1e-512L, + 1e-256L,1e-128L,1e-64L,1e-32L,1e-16L,1e-8L,1e-4L,1e-2L,1e-1L,1.0L}; + +static long double postab[] = + {1e+4096L,1e+2048L,1e+1024L,1e+512L, + 1e+256L,1e+128L,1e+64L,1e+32L,1e+16L,1e+8L,1e+4L,1e+2L,1e+1L}; + +#elif defined(__GNUC__) && __FreeBSD__ && __i386__ + +// GCC on FreeBSD/i386 incorrectly rounds long double constants to double precision. Workaround: + +// Note that the [sizeof(long double)] takes care of whatever the 0 padding is for the +// target platform + +static unsigned char _negtab_bytes[][sizeof(long double)] = + { { 0xDE,0x9F,0xCE,0xD2,0xC8,0x04,0xDD,0xA6,0xD8,0x0A,0xBF,0xBF }, + { 0xE4,0x2D,0x36,0x34,0x4F,0x53,0xAE,0xCE,0x6B,0x25,0xBF,0xBF }, + { 0xBE,0xC0,0x57,0xDA,0xA5,0x82,0xA6,0xA2,0xB5,0x32,0xBF,0xBF }, + { 0x1C,0xD2,0x23,0xDB,0x32,0xEE,0x49,0x90,0x5A,0x39,0xBF,0xBF }, + { 0x3A,0x19,0x7A,0x63,0x25,0x43,0x31,0xC0,0xAC,0x3C,0xBF,0xBF }, + { 0xA1,0xE4,0xBC,0x64,0x7C,0x46,0xD0,0xDD,0x55,0x3E,0xBF,0xBF }, + { 0xA5,0xE9,0x39,0xA5,0x27,0xEA,0x7F,0xA8,0x2A,0x3F,0xBF,0xBF }, + { 0xBA,0x94,0x39,0x45,0xAD,0x1E,0xB1,0xCF,0x94,0x3F,0xBF,0xBF }, + { 0x5B,0xE1,0x4D,0xC4,0xBE,0x94,0x95,0xE6,0xC9,0x3F,0xBF,0xBF }, + { 0xFD,0xCE,0x61,0x84,0x11,0x77,0xCC,0xAB,0xE4,0x3F,0xBF,0xBF }, + { 0x2C,0x65,0x19,0xE2,0x58,0x17,0xB7,0xD1,0xF1,0x3F,0xBF,0xBF }, + { 0x0A,0xD7,0xA3,0x70,0x3D,0x0A,0xD7,0xA3,0xF8,0x3F,0xBF,0xBF }, + { 0xCD,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xFB,0x3F,0xBF,0xBF }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFF,0x3F,0xBF,0xBF } }; + +static unsigned char _postab_bytes[][sizeof(long double)] = + { { 0x9B,0x97,0x20,0x8A,0x02,0x52,0x60,0xC4,0x25,0x75,0x18,0x28 }, + { 0xE5,0x5D,0x3D,0xC5,0x5D,0x3B,0x8B,0x9E,0x92,0x5A,0x18,0x28 }, + { 0x17,0x0C,0x75,0x81,0x86,0x75,0x76,0xC9,0x48,0x4D,0x18,0x28 }, + { 0xC7,0x91,0x0E,0xA6,0xAE,0xA0,0x19,0xE3,0xA3,0x46,0x18,0x28 }, + { 0x8E,0xDE,0xF9,0x9D,0xFB,0xEB,0x7E,0xAA,0x51,0x43,0x18,0x28 }, + { 0xE0,0x8C,0xE9,0x80,0xC9,0x47,0xBA,0x93,0xA8,0x41,0x18,0x28 }, + { 0xD5,0xA6,0xCF,0xFF,0x49,0x1F,0x78,0xC2,0xD3,0x40,0x18,0x28 }, + { 0x9E,0xB5,0x70,0x2B,0xA8,0xAD,0xC5,0x9D,0x69,0x40,0x18,0x28 }, + { 0x00,0x00,0x00,0x04,0xBF,0xC9,0x1B,0x8E,0x34,0x40,0x18,0x28 }, + { 0x00,0x00,0x00,0x00,0x00,0x20,0xBC,0xBE,0x19,0x40,0x18,0x28 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x9C,0x0C,0x40,0x18,0x28 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC8,0x05,0x40,0x18,0x28 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xA0,0x02,0x40,0x18,0x28 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFF,0x3F,0x18,0x28 } }; + +static long double *negtab = (long double *) _negtab_bytes; +static long double *postab = (long double *) _postab_bytes; + +#else + +// Use exact values, computed separately, to bootstrap. +// The digits here past 17 are just for amusement value, they +// only contribute to the 'sticky' bit. + +static long double negtab[] = +{ + 1 / 0x62.30290145104bcd64a60a9fc025254932bb0fd922271133eeae7be4a2f9151ffff868e970c234d8f51c5563f48bd2b496d868b27518ae42404964046f87cc1d213d5d0b54f74eb9281bb6c6e435fcb457200c03a5bca35f7792959da22e8d623b3e7b21e2b6100fab123cd8a1a75409f23956d4b941c759f83557de068edd2d00bcdd9d4a52ec8721ac7867f9e974996fb03d7ecd2fdc6349af06940d48741a6c2ed4684e5ab8d9c7bd7991dc03b4f63b8afd6b25ff66e42caeee333b7000a51987ec7038aec29e6ee8cac982a4ba47440496fcbe00d313d584e857fd214495bbdf373f41fd86fe49b70a5c7d2b17e0b2544f10cd4d8bfa89d0d73df29d0176cca7c234f4e6d2767113fd01c8c1a08a138c4ef80456c02d9a0ff4f1d4e3e51cb9255858325ed8d2399faddd9e9985a2df904ff6bf5c4f2ef0650ebc692c5508c2cbd6667097aced8e437b3d7fe03b2b6341a4c954108b89bc108f19ade5b533458e0dd75a53400d03119534074e89541bae9641fdd6266a3fdcbf778900fc509ba674343dd6769f3b72b882e7282566fbc6cc3f8d6b0dd9bc96119b31a96ddeff35e836b5d298f9994b8c90918e7b9a73491260806f233b7c94ab6feba2ebd6c1d9960e2d73a130d84c4a74fde9ce4724ed5bf546a03f40a8fb126ab1c32da38338eb3acc1a67778cfbe8b12acf1b23504dcd6cd995aca6a8b492ed8aa19adb95484971870239f4cea6e9cfda20c33857b32c450c3fecb534b71bd1a45b060904788f6e50fe78d6823613c8509ee3352c90ca19cfe90afb779eea37c8ab8db59a0a80627ce41d3cc425971d582dfe6d97ee63302b8e13e25feeaf19e63d326a7eb6d1c7bf2608c4cf1cc939c1307641d9b2c39497a8fcd8e0cd9e8d7c3172826ac9df13cb3d04e8d2fca26a9ff7d8b57e27ecf57bbb9373f46fee7aab86deb3f078787e2ab608b89572dac789bf627ede440b3f251f2b2322ab312bb95893d4b850be10e02d2408206e7bb8272181327ec8fa2e8a37a2d4390caea134c53c0adf9462ea75ecf9b5d0ed4d542dc19e1faf7a872e74f984d83e2dd8d92580152f18390a2b295138753d1fa8fd5d59c89f1b095edc162e2690f3cd8f62ff42923bbd87d1cde840b464a0e137d5e9a4eb8f8cde35c88baf63b71292baf1deeca19beb77fb8af6176ca776743074fa7021b97a1e0a68173c20ee69e79dadf7eb83cadbdfea5242a8329761ffe062053ccb5b92ac50b9c175a697b2b5341743c994a4503b9af26b398c6fed037d19eef4090ee8ae0725b1655fec303297cd0c2bd9cc1110c4e9968738b909454eb2a0dcfe388f15b8c898d3967a1b6dc3a5b4811a4f04f3618ac0280f4d3295a842bcfd82373a3f8ec72af2acd5071a8309cb2130504dd97d9556a1ebcad7947e0d0e30c7ae41eb659fb878f061814f6cea9c441c2d473bfe167b1a1c304e7613b22454ab9c41ff0b0905bc13176168dde6d488052f8cf8169c84cb4bf982870097012c23481161959127142e0e80cab3e6d7af6a25743dbeabcd0f237f1a016b67b2c2dfae78e341be10d6bfdf759b8ba1e81d1f4cce7c4823da7e1e7c34c0591cc245155e93b86ae5be806c0ed3f0da6146e599574efb29b172506be82913b1bb5154e05154ef084117f89a1e908efe7ae7d4724e8d2a67c001p+13600L, + 1 / 0x9.e8b3b5dc53d5de4a74d28ce329ace526a3197bbebe3034f77154ce2bcba19648b21c11eb962b1b61b93cf2ee5ca6f7e928e61d08e2d694222771e50f30278c9836230af908b40a753b7d77cd8c6be7151aab4efac5dcd83e49d6907855eeb028af623f6f7024d2c36fa9ce9d04a487fa1fb992be221ef1bd0ad5f775677ce0de08402ad3fa140eac7d56c7c9dee0bedd8a6c038f9245b2e87c348ad803ecca8f0070f8dbb57a6a445f278b3d5cf42915e818415c7f3ef82df84658ccf45cfad379433f3389a4408f43c513ef5a83fb8886fbf56d9d4bd5f860792e55ecee70beb1810d76ce39de9ec24bcf99d01953761abd9d7389c0a244de3c195355d84eeebeee6f46eadb56c6815b785ce6b7b125ac8edb0708fd8f6cae5f5715f7915b33eb417bf03c19d7917c7ba1fc6b9681428c85744695f0e866d7efc9ac375d77c1a42f40660460944545ff87a7dc62d752f7a66a57b1ab730f203c1aa9f44484d80e2e5fc5a04779c56b8a9e110c7bcbea4ca7982da4663cfe491d0dbd21feab49869733554c36685e5510c4a656654419bd438e48ff35d6c7d6ab91bac974fb1264b4f111821fa2bca416afe609c313b41e449952fbed5a151440967abbb3a8281ed6a8f16f9210c17f94e3892ee98074ff01e3cb64f32dbb6643a7a8289c8c6c54de34c101349713b44938209ce1f3861ce0fb7fedcc235552eb57a7842d71c7fd8f66912e4ad2f869c29279498719342c12866ed6f1c850dabc98342c9e51b78db2ea50d142fd8277732ed56d55a5e5a191368b8abbb6067584ee87e354ec2e472149e28dcfb27d4d3fe30968651333e001p+6800L, + 1 / 0x3.25d9d61a05d4305d9434f4a3c62d433949ae6209d4926c3f5bd2db49ef47187094c1a6970ca7e6bd2a73c5534936a8de061e8d4649f4f3235e005b80411640114a88bc491b9fc4ed520190fba035faaba6c356e38a31b5653f445975836cb0b6c975a351a28e4262ce3ce3a0b8df68368ae26a7b7e976a3310fc8f1f9031eb0f669a20288280bda5a580d98089dc1a47fe6b7595fb101a3616b6f4654b31fb6bfdf56deeecb1b896bc8fc51a16bf3fdeb3d814b505ba34c4118ad822a51abe1de3045b7a748e1042c462be695a9f9f2a07a7e89431922bbb9fc96359861c5cd134f451218b65dc60d7233e55c7231d2b9c9fce837d1e43f61f7de16cfb896634ee0ed1440ecc2cd8194c7d1e1a140ac53515c51a88991c4e871ec29f866e7c215bf55b2b722919f001p+3400L, + 1 / 0x1c.633415d4c1d238d98cab8a978a0b1f138cb07303a269974845a71d46b099bc817343afac69be5b0e9449775c1366732a93abade4b2908ee0f95f635e85a91924c3fc0695e7fc7153329c57aebfa3edac96e14f5dbc51fb2eb21a2f221e25cfea703ed321aa1da1bf28f8733b4475b579c88976c194e6574746c40513c31e1ad9b83a8a975d96976f8f9546dc77f27267fc6cf801p+1696L, + 1 / 0x5.53f75fdcefcef46eeddc80dcc7f755bc28f265f9ef17cc5573c063ff540e3c42d35a1d153624adc666b026b2716ed595d80fcf4a6e706bde50c612152f87d8d99f72bed3875b982e7c01p+848L, + 1 / 0x2.4ee91f2603a6337f19bccdb0dac404dc08d3cff5ec2374e42f0f1538fd03df99092e953e01p+424L, + 1 / 0x18.4f03e93ff9f4daa797ed6e38ed64bf6a1f01p+208L, + 1 / 0x4.ee2d6d415b85acef81p+104L, + 1 / 0x23.86f26fc1p+48L, + 1 / 0x5.f5e1p+24L, + 1 / 0x27.10p+8L, + 1 / 0x64.p+0L, + 1 / 0xa.p+0L, +}; + +static long double postab[] = +{ + 0x62.30290145104bcd64a60a9fc025254932bb0fd922271133eeae7be4a2f9151ffff868e970c234d8f51c5563f48bd2b496d868b27518ae42404964046f87cc1d213d5d0b54f74eb9281bb6c6e435fcb457200c03a5bca35f7792959da22e8d623b3e7b21e2b6100fab123cd8a1a75409f23956d4b941c759f83557de068edd2d00bcdd9d4a52ec8721ac7867f9e974996fb03d7ecd2fdc6349af06940d48741a6c2ed4684e5ab8d9c7bd7991dc03b4f63b8afd6b25ff66e42caeee333b7000a51987ec7038aec29e6ee8cac982a4ba47440496fcbe00d313d584e857fd214495bbdf373f41fd86fe49b70a5c7d2b17e0b2544f10cd4d8bfa89d0d73df29d0176cca7c234f4e6d2767113fd01c8c1a08a138c4ef80456c02d9a0ff4f1d4e3e51cb9255858325ed8d2399faddd9e9985a2df904ff6bf5c4f2ef0650ebc692c5508c2cbd6667097aced8e437b3d7fe03b2b6341a4c954108b89bc108f19ade5b533458e0dd75a53400d03119534074e89541bae9641fdd6266a3fdcbf778900fc509ba674343dd6769f3b72b882e7282566fbc6cc3f8d6b0dd9bc96119b31a96ddeff35e836b5d298f9994b8c90918e7b9a73491260806f233b7c94ab6feba2ebd6c1d9960e2d73a130d84c4a74fde9ce4724ed5bf546a03f40a8fb126ab1c32da38338eb3acc1a67778cfbe8b12acf1b23504dcd6cd995aca6a8b492ed8aa19adb95484971870239f4cea6e9cfda20c33857b32c450c3fecb534b71bd1a45b060904788f6e50fe78d6823613c8509ee3352c90ca19cfe90afb779eea37c8ab8db59a0a80627ce41d3cc425971d582dfe6d97ee63302b8e13e25feeaf19e63d326a7eb6d1c7bf2608c4cf1cc939c1307641d9b2c39497a8fcd8e0cd9e8d7c3172826ac9df13cb3d04e8d2fca26a9ff7d8b57e27ecf57bbb9373f46fee7aab86deb3f078787e2ab608b89572dac789bf627ede440b3f251f2b2322ab312bb95893d4b850be10e02d2408206e7bb8272181327ec8fa2e8a37a2d4390caea134c53c0adf9462ea75ecf9b5d0ed4d542dc19e1faf7a872e74f984d83e2dd8d92580152f18390a2b295138753d1fa8fd5d59c89f1b095edc162e2690f3cd8f62ff42923bbd87d1cde840b464a0e137d5e9a4eb8f8cde35c88baf63b71292baf1deeca19beb77fb8af6176ca776743074fa7021b97a1e0a68173c20ee69e79dadf7eb83cadbdfea5242a8329761ffe062053ccb5b92ac50b9c175a697b2b5341743c994a4503b9af26b398c6fed037d19eef4090ee8ae0725b1655fec303297cd0c2bd9cc1110c4e9968738b909454eb2a0dcfe388f15b8c898d3967a1b6dc3a5b4811a4f04f3618ac0280f4d3295a842bcfd82373a3f8ec72af2acd5071a8309cb2130504dd97d9556a1ebcad7947e0d0e30c7ae41eb659fb878f061814f6cea9c441c2d473bfe167b1a1c304e7613b22454ab9c41ff0b0905bc13176168dde6d488052f8cf8169c84cb4bf982870097012c23481161959127142e0e80cab3e6d7af6a25743dbeabcd0f237f1a016b67b2c2dfae78e341be10d6bfdf759b8ba1e81d1f4cce7c4823da7e1e7c34c0591cc245155e93b86ae5be806c0ed3f0da6146e599574efb29b172506be82913b1bb5154e05154ef084117f89a1e908efe7ae7d4724e8d2a67c001p+13600L, + 0x9.e8b3b5dc53d5de4a74d28ce329ace526a3197bbebe3034f77154ce2bcba19648b21c11eb962b1b61b93cf2ee5ca6f7e928e61d08e2d694222771e50f30278c9836230af908b40a753b7d77cd8c6be7151aab4efac5dcd83e49d6907855eeb028af623f6f7024d2c36fa9ce9d04a487fa1fb992be221ef1bd0ad5f775677ce0de08402ad3fa140eac7d56c7c9dee0bedd8a6c038f9245b2e87c348ad803ecca8f0070f8dbb57a6a445f278b3d5cf42915e818415c7f3ef82df84658ccf45cfad379433f3389a4408f43c513ef5a83fb8886fbf56d9d4bd5f860792e55ecee70beb1810d76ce39de9ec24bcf99d01953761abd9d7389c0a244de3c195355d84eeebeee6f46eadb56c6815b785ce6b7b125ac8edb0708fd8f6cae5f5715f7915b33eb417bf03c19d7917c7ba1fc6b9681428c85744695f0e866d7efc9ac375d77c1a42f40660460944545ff87a7dc62d752f7a66a57b1ab730f203c1aa9f44484d80e2e5fc5a04779c56b8a9e110c7bcbea4ca7982da4663cfe491d0dbd21feab49869733554c36685e5510c4a656654419bd438e48ff35d6c7d6ab91bac974fb1264b4f111821fa2bca416afe609c313b41e449952fbed5a151440967abbb3a8281ed6a8f16f9210c17f94e3892ee98074ff01e3cb64f32dbb6643a7a8289c8c6c54de34c101349713b44938209ce1f3861ce0fb7fedcc235552eb57a7842d71c7fd8f66912e4ad2f869c29279498719342c12866ed6f1c850dabc98342c9e51b78db2ea50d142fd8277732ed56d55a5e5a191368b8abbb6067584ee87e354ec2e472149e28dcfb27d4d3fe30968651333e001p+6800L, + 0x3.25d9d61a05d4305d9434f4a3c62d433949ae6209d4926c3f5bd2db49ef47187094c1a6970ca7e6bd2a73c5534936a8de061e8d4649f4f3235e005b80411640114a88bc491b9fc4ed520190fba035faaba6c356e38a31b5653f445975836cb0b6c975a351a28e4262ce3ce3a0b8df68368ae26a7b7e976a3310fc8f1f9031eb0f669a20288280bda5a580d98089dc1a47fe6b7595fb101a3616b6f4654b31fb6bfdf56deeecb1b896bc8fc51a16bf3fdeb3d814b505ba34c4118ad822a51abe1de3045b7a748e1042c462be695a9f9f2a07a7e89431922bbb9fc96359861c5cd134f451218b65dc60d7233e55c7231d2b9c9fce837d1e43f61f7de16cfb896634ee0ed1440ecc2cd8194c7d1e1a140ac53515c51a88991c4e871ec29f866e7c215bf55b2b722919f001p+3400L, + 0x1c.633415d4c1d238d98cab8a978a0b1f138cb07303a269974845a71d46b099bc817343afac69be5b0e9449775c1366732a93abade4b2908ee0f95f635e85a91924c3fc0695e7fc7153329c57aebfa3edac96e14f5dbc51fb2eb21a2f221e25cfea703ed321aa1da1bf28f8733b4475b579c88976c194e6574746c40513c31e1ad9b83a8a975d96976f8f9546dc77f27267fc6cf801p+1696L, + 0x5.53f75fdcefcef46eeddc80dcc7f755bc28f265f9ef17cc5573c063ff540e3c42d35a1d153624adc666b026b2716ed595d80fcf4a6e706bde50c612152f87d8d99f72bed3875b982e7c01p+848L, + 0x2.4ee91f2603a6337f19bccdb0dac404dc08d3cff5ec2374e42f0f1538fd03df99092e953e01p+424L, + 0x18.4f03e93ff9f4daa797ed6e38ed64bf6a1f01p+208L, + 0x4.ee2d6d415b85acef81p+104L, + 0x23.86f26fc1p+48L, + 0x5.f5e1p+24L, + 0x27.10p+8L, + 0x64.p+0L, + 0xa.p+0L, +}; + +#endif + +/************************* + * Convert string to double. + * Terminates on first unrecognized character. + */ + +long double strtold(const char *p,char **endp) +{ + long double ldval; + int exp; + long long msdec,lsdec; + unsigned long msscale; + char dot,sign; + int pow; + int ndigits; + const char *pinit = p; + static char infinity[] = "infinity"; + static char nans[] = "nans"; + unsigned int old_cw; + unsigned int old_status; + +#if _WIN32 + fenv_t flagp; + fegetenv(&flagp); /* Store all exceptions, and current status word */ + if (_8087) + { + // disable exceptions from occurring, set max precision, and round to nearest +#if __DMC__ + __asm + { + fstcw word ptr old_cw + mov EAX,old_cw + mov ECX,EAX + and EAX,0xf0c0 + or EAX,033fh + mov old_cw,EAX + fldcw word ptr old_cw + mov old_cw,ECX + } +#else + old_cw = _control87(_MCW_EM | _PC_64 | _RC_NEAR, + _MCW_EM | _MCW_PC | _MCW_RC); +#endif + } +#endif + + while (isspace(*p)) + p++; + sign = 0; /* indicating + */ + switch (*p) + { case '-': + sign++; + /* FALL-THROUGH */ + case '+': + p++; + } + ldval = 0.0; + dot = 0; /* if decimal point has been seen */ + exp = 0; + msdec = lsdec = 0; + msscale = 1; + ndigits = 0; + +#if __DMC__ + switch (*p) + { case 'i': + case 'I': + if (memicmp(p,infinity,8) == 0) + { p += 8; + goto L4; + } + if (memicmp(p,infinity,3) == 0) /* is it "inf"? */ + { p += 3; + L4: + ldval = HUGE_VAL; + goto L3; + } + break; + case 'n': + case 'N': + if (memicmp(p,nans,4) == 0) /* "nans"? */ + { p += 4; + ldval = NANS; + goto L5; + } + if (memicmp(p,nans,3) == 0) /* "nan"? */ + { p += 3; + ldval = NAN; + L5: + if (*p == '(') /* if (n-char-sequence) */ + goto Lerr; /* invalid input */ + goto L3; + } + } +#endif + + if (*p == '0' && (p[1] == 'x' || p[1] == 'X')) + { int guard = 0; + int anydigits = 0; + + p += 2; + while (1) + { int i = *p; + + while (isxdigit(i)) + { + anydigits = 1; + i = isalpha(i) ? ((i & ~0x20) - ('A' - 10)) : i - '0'; + if (ndigits < 16) + { + msdec = msdec * 16 + i; + if (msdec) + ndigits++; + } + else if (ndigits == 16) + { + while (msdec >= 0) + { + exp--; + msdec <<= 1; + i <<= 1; + if (i & 0x10) + msdec |= 1; + } + guard = i << 4; + ndigits++; + exp += 4; + } + else + { + guard |= i; + exp += 4; + } + exp -= dot; + i = *++p; + } +#ifdef _WIN32 + if (i == *__locale_decpoint && !dot) +#else + if (i == '.' && !dot) +#endif + { p++; + dot = 4; + } + else + break; + } + + // Round up if (guard && (sticky || odd)) + if (guard & 0x80 && (guard & 0x7F || msdec & 1)) + { + msdec++; + if (msdec == 0) // overflow + { msdec = 0x8000000000000000LL; + exp++; + } + } + + if (anydigits == 0) // if error (no digits seen) + goto Lerr; + if (*p == 'p' || *p == 'P') + { + char sexp; + int e; + + sexp = 0; + switch (*++p) + { case '-': sexp++; + case '+': p++; + } + ndigits = 0; + e = 0; + while (isdigit(*p)) + { + if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow + { + e = e * 10 + *p - '0'; + } + p++; + ndigits = 1; + } + exp += (sexp) ? -e : e; + if (!ndigits) // if no digits in exponent + goto Lerr; + + if (msdec) + { +#if __DMC__ + // The 8087 has no instruction to load an + // unsigned long long + if (msdec < 0) + { + *(long long *)&ldval = msdec; + ((unsigned short *)&ldval)[4] = 0x3FFF + 63; + } + else + { // But does for a signed one + __asm + { + fild qword ptr msdec + fstp tbyte ptr ldval + } + } +#else + int e2 = 0x3FFF + 63; + + // left justify mantissa + while (msdec >= 0) + { msdec <<= 1; + e2--; + } + + // Stuff mantissa directly into long double + *(long long *)&ldval = msdec; + ((unsigned short *)&ldval)[4] = e2; +#endif + +#if 0 + if (0) + { int i; + printf("msdec = x%llx, ldval = %Lg\n", msdec, ldval); + for (i = 0; i < 5; i++) + printf("%04x ",((unsigned short *)&ldval)[i]); + printf("\n"); + printf("%llx\n",ldval); + } +#endif + // Exponent is power of 2, not power of 10 +#if _WIN32 && __DMC__ + __asm + { + fild dword ptr exp + fld tbyte ptr ldval + fscale // ST(0) = ST(0) * (2**ST(1)) + fstp ST(1) + fstp tbyte ptr ldval + } +#else + ldval = ldexpl(ldval,exp); +#endif + } + goto L6; + } + else + goto Lerr; // exponent is required + } + else + { + while (1) + { int i = *p; + + while (isdigit(i)) + { + ndigits = 1; /* must have at least 1 digit */ + if (msdec < (0x7FFFFFFFFFFFLL-10)/10) + msdec = msdec * 10 + (i - '0'); + else if (msscale < (0xFFFFFFFF-10)/10) + { lsdec = lsdec * 10 + (i - '0'); + msscale *= 10; + } + else + { + exp++; + } + exp -= dot; + i = *++p; + } +#if _WIN32 + if (i == *__locale_decpoint && !dot) +#else + if (i == '.' && !dot) +#endif + { p++; + dot++; + } + else + break; + } + if (!ndigits) // if error (no digits seen) + goto Lerr; // return 0.0 + } + if (*p == 'e' || *p == 'E') + { + char sexp; + int e; + + sexp = 0; + switch (*++p) + { case '-': sexp++; + case '+': p++; + } + ndigits = 0; + e = 0; + while (isdigit(*p)) + { + if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow + { + e = e * 10 + *p - '0'; + } + p++; + ndigits = 1; + } + exp += (sexp) ? -e : e; + if (!ndigits) // if no digits in exponent + goto Lerr; // return 0.0 + } + +#if _WIN32 + __asm + { + fild qword ptr msdec + mov EAX,msscale + cmp EAX,1 + je La1 + fild long ptr msscale + fmul + fild qword ptr lsdec + fadd + La1: + fstp tbyte ptr ldval + } +#else + ldval = msdec; + if (msscale != 1) /* if stuff was accumulated in lsdec */ + ldval = ldval * msscale + lsdec; +#endif + if (ldval) + { unsigned u; + + u = 0; + pow = 4096; + +#if _WIN32 + //printf("msdec = x%x, lsdec = x%x, msscale = x%x\n",msdec,lsdec,msscale); + //printf("dval = %g, x%llx, exp = %d\n",dval,dval,exp); + __asm fld tbyte ptr ldval +#endif + + while (exp > 0) + { + while (exp >= pow) + { +#if _WIN32 + __asm + { + mov EAX,u + imul EAX,10 + fld tbyte ptr postab[EAX] + fmul + } +#else + ldval *= postab[u]; +#endif + exp -= pow; + } + pow >>= 1; + u++; + } +#if _WIN32 + __asm fstp tbyte ptr ldval +#endif + while (exp < 0) + { while (exp <= -pow) + { +#if _WIN32 + __asm + { + mov EAX,u + imul EAX,10 + fld tbyte ptr ldval + fld tbyte ptr negtab[EAX] + fmul + fstp tbyte ptr ldval + } +#else + ldval *= negtab[u]; +#endif + if (ldval == 0) +#if _WIN32 + __set_errno (ERANGE); +#else + errno = ERANGE; +#endif + exp += pow; + } + pow >>= 1; + u++; + } +#if 0 + if (0) + { int i; + for (i = 0; i < 5; i++) + printf("%04x ",ldval.value[i]); + printf("\n"); + printf("%llx\n",dval); + } +#endif + } + L6: // if overflow occurred + if (ldval == HUGE_VAL) +#if _WIN32 + __set_errno (ERANGE); // range error +#else + errno = ERANGE; +#endif + + L1: + if (endp) + { + *endp = (char *) p; + } + L3: +#if _WIN32 + fesetenv(&flagp); // reset floating point environment + if (_8087) + { + __asm + { + xor EAX,EAX + fstsw AX + fclex + fldcw word ptr old_cw + } + } +#endif + + return (sign) ? -ldval : ldval; + + Lerr: + p = pinit; + goto L1; +} + +#else + +long double strtold(const char *p,char **endp) +{ + return strtod(p, endp); +} + +#endif + +/************************* Test ************************************/ + +#if 0 + +#include +#include +#include + +extern "C" long double strtold(const char *p,char **endp); + +struct longdouble +{ + unsigned short value[5]; +}; + +void main() +{ + long double ld; + struct longdouble x; + int i; + + errno = 0; +// ld = strtold("0x1.FFFFFFFFFFFFFFFEp16383", NULL); + ld = strtold("0x1.FFFFFFFFFFFFFFFEp-16382", NULL); + x = *(struct longdouble *)&ld; + for (i = 4; i >= 0; i--) + { + printf("%04x ", x.value[i]); + } + printf("\t%d\n", errno); + + ld = strtold("1.0e5", NULL); + x = *(struct longdouble *)&ld; + for (i = 4; i >= 0; i--) + { + printf("%04x ", x.value[i]); + } + printf("\n"); +} + +#endif + +/************************* Bigint ************************************/ + +#if 0 + +/* This program computes powers of 10 exactly. + * Used to generate postab[]. + */ + + +#include +#include +#include + +#define NDIGITS 4096 + +void times10(unsigned *a) +{ + int i; + + for (i = 0; i < NDIGITS; i++) + { + a[i] *= 10; + if (i) + { + a[i] += a[i - 1] >> 8; + a[i - 1] &= 0xFF; + } + } +} + +void print(unsigned *a) +{ + int i; + int p; + int j; + + for (i = NDIGITS; i; ) + { + --i; + if (a[i]) + break; + } + + printf("0x%x.", a[i]); + p = i * 8; + i--; + for (j = 0; j < i; j++) + if (a[j]) + break; + for (; i >= j; i--) + { + printf("%02x", a[i]); + } + printf("p+%d", p); +} + +void main() +{ + unsigned a[NDIGITS]; + int i; + int j; + + static long double tab[] = + { + 0x62.30290145104bcd64a60a9fc025254932bb0fd922271133eeae7be4a2f9151ffff868e970c234d8f51c5563f48bd2b496d868b27518ae42404964046f87cc1d213d5d0b54f74eb9281bb6c6e435fcb457200c03a5bca35f7792959da22e8d623b3e7b21e2b6100fab123cd8a1a75409f23956d4b941c759f83557de068edd2d00bcdd9d4a52ec8721ac7867f9e974996fb03d7ecd2fdc6349af06940d48741a6c2ed4684e5ab8d9c7bd7991dc03b4f63b8afd6b25ff66e42caeee333b7000a51987ec7038aec29e6ee8cac982a4ba47440496fcbe00d313d584e857fd214495bbdf373f41fd86fe49b70a5c7d2b17e0b2544f10cd4d8bfa89d0d73df29d0176cca7c234f4e6d2767113fd01c8c1a08a138c4ef80456c02d9a0ff4f1d4e3e51cb9255858325ed8d2399faddd9e9985a2df904ff6bf5c4f2ef0650ebc692c5508c2cbd6667097aced8e437b3d7fe03b2b6341a4c954108b89bc108f19ade5b533458e0dd75a53400d03119534074e89541bae9641fdd6266a3fdcbf778900fc509ba674343dd6769f3b72b882e7282566fbc6cc3f8d6b0dd9bc96119b31a96ddeff35e836b5d298f9994b8c90918e7b9a73491260806f233b7c94ab6feba2ebd6c1d9960e2d73a130d84c4a74fde9ce4724ed5bf546a03f40a8fb126ab1c32da38338eb3acc1a67778cfbe8b12acf1b23504dcd6cd995aca6a8b492ed8aa19adb95484971870239f4cea6e9cfda20c33857b32c450c3fecb534b71bd1a45b060904788f6e50fe78d6823613c8509ee3352c90ca19cfe90afb779eea37c8ab8db59a0a80627ce41d3cc425971d582dfe6d97ee63302b8e13e25feeaf19e63d326a7eb6d1c7bf2608c4cf1cc939c1307641d9b2c39497a8fcd8e0cd9e8d7c3172826ac9df13cb3d04e8d2fca26a9ff7d8b57e27ecf57bbb9373f46fee7aab86deb3f078787e2ab608b89572dac789bf627ede440b3f251f2b2322ab312bb95893d4b850be10e02d2408206e7bb8272181327ec8fa2e8a37a2d4390caea134c53c0adf9462ea75ecf9b5d0ed4d542dc19e1faf7a872e74f984d83e2dd8d92580152f18390a2b295138753d1fa8fd5d59c89f1b095edc162e2690f3cd8f62ff42923bbd87d1cde840b464a0e137d5e9a4eb8f8cde35c88baf63b71292baf1deeca19beb77fb8af6176ca776743074fa7021b97a1e0a68173c20ee69e79dadf7eb83cadbdfea5242a8329761ffe062053ccb5b92ac50b9c175a697b2b5341743c994a4503b9af26b398c6fed037d19eef4090ee8ae0725b1655fec303297cd0c2bd9cc1110c4e9968738b909454eb2a0dcfe388f15b8c898d3967a1b6dc3a5b4811a4f04f3618ac0280f4d3295a842bcfd82373a3f8ec72af2acd5071a8309cb2130504dd97d9556a1ebcad7947e0d0e30c7ae41eb659fb878f061814f6cea9c441c2d473bfe167b1a1c304e7613b22454ab9c41ff0b0905bc13176168dde6d488052f8cf8169c84cb4bf982870097012c23481161959127142e0e80cab3e6d7af6a25743dbeabcd0f237f1a016b67b2c2dfae78e341be10d6bfdf759b8ba1e81d1f4cce7c4823da7e1e7c34c0591cc245155e93b86ae5be806c0ed3f0da6146e599574efb29b172506be82913b1bb5154e05154ef084117f89a1e908efe7ae7d4724e8d2a67c001p+13600L, + 0x9.e8b3b5dc53d5de4a74d28ce329ace526a3197bbebe3034f77154ce2bcba19648b21c11eb962b1b61b93cf2ee5ca6f7e928e61d08e2d694222771e50f30278c9836230af908b40a753b7d77cd8c6be7151aab4efac5dcd83e49d6907855eeb028af623f6f7024d2c36fa9ce9d04a487fa1fb992be221ef1bd0ad5f775677ce0de08402ad3fa140eac7d56c7c9dee0bedd8a6c038f9245b2e87c348ad803ecca8f0070f8dbb57a6a445f278b3d5cf42915e818415c7f3ef82df84658ccf45cfad379433f3389a4408f43c513ef5a83fb8886fbf56d9d4bd5f860792e55ecee70beb1810d76ce39de9ec24bcf99d01953761abd9d7389c0a244de3c195355d84eeebeee6f46eadb56c6815b785ce6b7b125ac8edb0708fd8f6cae5f5715f7915b33eb417bf03c19d7917c7ba1fc6b9681428c85744695f0e866d7efc9ac375d77c1a42f40660460944545ff87a7dc62d752f7a66a57b1ab730f203c1aa9f44484d80e2e5fc5a04779c56b8a9e110c7bcbea4ca7982da4663cfe491d0dbd21feab49869733554c36685e5510c4a656654419bd438e48ff35d6c7d6ab91bac974fb1264b4f111821fa2bca416afe609c313b41e449952fbed5a151440967abbb3a8281ed6a8f16f9210c17f94e3892ee98074ff01e3cb64f32dbb6643a7a8289c8c6c54de34c101349713b44938209ce1f3861ce0fb7fedcc235552eb57a7842d71c7fd8f66912e4ad2f869c29279498719342c12866ed6f1c850dabc98342c9e51b78db2ea50d142fd8277732ed56d55a5e5a191368b8abbb6067584ee87e354ec2e472149e28dcfb27d4d3fe30968651333e001p+6800L, + 0x3.25d9d61a05d4305d9434f4a3c62d433949ae6209d4926c3f5bd2db49ef47187094c1a6970ca7e6bd2a73c5534936a8de061e8d4649f4f3235e005b80411640114a88bc491b9fc4ed520190fba035faaba6c356e38a31b5653f445975836cb0b6c975a351a28e4262ce3ce3a0b8df68368ae26a7b7e976a3310fc8f1f9031eb0f669a20288280bda5a580d98089dc1a47fe6b7595fb101a3616b6f4654b31fb6bfdf56deeecb1b896bc8fc51a16bf3fdeb3d814b505ba34c4118ad822a51abe1de3045b7a748e1042c462be695a9f9f2a07a7e89431922bbb9fc96359861c5cd134f451218b65dc60d7233e55c7231d2b9c9fce837d1e43f61f7de16cfb896634ee0ed1440ecc2cd8194c7d1e1a140ac53515c51a88991c4e871ec29f866e7c215bf55b2b722919f001p+3400L, + 0x1c.633415d4c1d238d98cab8a978a0b1f138cb07303a269974845a71d46b099bc817343afac69be5b0e9449775c1366732a93abade4b2908ee0f95f635e85a91924c3fc0695e7fc7153329c57aebfa3edac96e14f5dbc51fb2eb21a2f221e25cfea703ed321aa1da1bf28f8733b4475b579c88976c194e6574746c40513c31e1ad9b83a8a975d96976f8f9546dc77f27267fc6cf801p+1696L, + 0x5.53f75fdcefcef46eeddc80dcc7f755bc28f265f9ef17cc5573c063ff540e3c42d35a1d153624adc666b026b2716ed595d80fcf4a6e706bde50c612152f87d8d99f72bed3875b982e7c01p+848L, + 0x2.4ee91f2603a6337f19bccdb0dac404dc08d3cff5ec2374e42f0f1538fd03df99092e953e01p+424L, + 0x18.4f03e93ff9f4daa797ed6e38ed64bf6a1f01p+208L, + 0x4.ee2d6d415b85acef81p+104L, + 0x23.86f26fc1p+48L, + 0x5.f5e1p+24L, + 0x27.10p+8L, + 0x64.p+0L, + 0xa.p+0L, + }; + + for (j = 1; j <= 4096; j *= 2) + { + printf("%4d: ", j); + memset(a, 0, sizeof(a)); + a[0] = 1; + for (i = 0; i < j; i++) + times10(a); + print(a); + printf("L,\n"); + } + + for (i = 0; i < 13; i++) + { + printf("tab[%d] = %Lg\n", i, tab[i]); + } +} + +#endif + diff --git a/backend/symbol.c b/backend/symbol.c new file mode 100644 index 00000000..8c413631 --- /dev/null +++ b/backend/symbol.c @@ -0,0 +1,2307 @@ +// Copyright (C) 1984-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include +#include + +#include "cc.h" +#include "global.h" +#include "type.h" +#include "dt.h" +#if TX86 +#include "cgcv.h" +#endif + +#include "el.h" +#include "oper.h" /* for OPMAX */ +#include "token.h" + +#if SCPP +#include "parser.h" +#include "cpp.h" +#endif + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +//STATIC void symbol_undef(symbol *s); +STATIC void symbol_freemember(symbol *s); +STATIC void mptr_hydrate(mptr_t **); +STATIC void mptr_dehydrate(mptr_t **); +STATIC void baseclass_hydrate(baseclass_t **); +STATIC void baseclass_dehydrate(baseclass_t **); + +/********************************* + * Allocate/free symbol table. + */ + +symbol **symtab_realloc(symbol **tab, size_t symmax) +{ symbol **newtab; + + if (config.flags2 & (CFG2phgen | CFG2phuse | CFG2phauto | CFG2phautoy)) + { + newtab = (symbol **) MEM_PH_REALLOC(tab, symmax * sizeof(symbol *)); + } + else + { + newtab = (symbol **) realloc(tab, symmax * sizeof(symbol *)); + if (!newtab) + err_nomem(); + } + return newtab; +} + +symbol **symtab_malloc(size_t symmax) +{ symbol **newtab; + + if (config.flags2 & (CFG2phgen | CFG2phuse | CFG2phauto | CFG2phautoy)) + { + newtab = (symbol **) MEM_PH_MALLOC(symmax * sizeof(symbol *)); + } + else + { + newtab = (symbol **) malloc(symmax * sizeof(symbol *)); + if (!newtab) + err_nomem(); + } + return newtab; +} + +symbol **symtab_calloc(size_t symmax) +{ symbol **newtab; + + if (config.flags2 & (CFG2phgen | CFG2phuse | CFG2phauto | CFG2phautoy)) + { + newtab = (symbol **) MEM_PH_CALLOC(symmax * sizeof(symbol *)); + } + else + { + newtab = (symbol **) calloc(symmax, sizeof(symbol *)); + if (!newtab) + err_nomem(); + } + return newtab; +} + +void symtab_free(symbol **tab) +{ + if (config.flags2 & (CFG2phgen | CFG2phuse | CFG2phauto | CFG2phautoy)) + MEM_PH_FREE(tab); + else if (tab) + free(tab); +} + +/******************************* + * Type out symbol information. + */ + +#ifdef DEBUG + +void symbol_print(symbol *s) +{ +#if !SPP + if (!s) return; + dbg_printf("symbol %p '%s'\n ",s,s->Sident); + dbg_printf(" Sclass = "); WRclass((enum SC) s->Sclass); + dbg_printf(" Ssymnum = %d",s->Ssymnum); + dbg_printf(" Sfl = "); WRFL((enum FL) s->Sfl); + dbg_printf(" Sseg = %d\n",s->Sseg); +// dbg_printf(" Ssize = x%02x\n",s->Ssize); + dbg_printf(" Soffset = x%04llx",(unsigned long long)s->Soffset); + dbg_printf(" Sweight = %d",s->Sweight); + dbg_printf(" Sflags = x%04lx",(unsigned long)s->Sflags); + dbg_printf(" Sxtrnnum = %d\n",s->Sxtrnnum); + dbg_printf(" Stype = %p",s->Stype); +#if SCPP + dbg_printf(" Ssequence = %x", s->Ssequence); + dbg_printf(" Scover = %p", s->Scover); +#endif + dbg_printf(" Sl = %p",s->Sl); + dbg_printf(" Sr = %p\n",s->Sr); +#if SCPP + if (s->Sscope) + dbg_printf(" Sscope = '%s'\n",s->Sscope->Sident); +#endif + if (s->Stype) + type_print(s->Stype); + if (s->Sclass == SCmember || s->Sclass == SCfield) + { + dbg_printf(" Smemoff =%5lld", (long long)s->Smemoff); + dbg_printf(" Sbit =%3d",s->Sbit); + dbg_printf(" Swidth =%3d\n",s->Swidth); + } +#if SCPP + if (s->Sclass == SCstruct) + { +#if VBTABLES + dbg_printf(" Svbptr = %p, Svptr = %p\n",s->Sstruct->Svbptr,s->Sstruct->Svptr); +#endif + } +#endif +#endif +} + +#endif + +/********************************* + * Terminate use of symbol table. + */ + +static symbol *keep; + +void symbol_term() +{ + symbol_free(keep); +} + +/**************************************** + * Keep symbol around until symbol_term(). + */ + +#if TERMCODE + +void symbol_keep(symbol *s) +{ + symbol_debug(s); + s->Sr = keep; // use Sr so symbol_free() doesn't nest + keep = s; +} + +#endif + +/*********************************** + * Get user name of symbol. + */ + +char *symbol_ident(symbol *s) +{ +#if SCPP + static char noname[] = "__unnamed"; + switch (s->Sclass) + { case SCstruct: + if (s->Sstruct->Salias) + s = s->Sstruct->Salias; + else if (s->Sstruct->Sflags & STRnotagname) + return noname; + break; + case SCenum: + if (CPP) + { if (s->Senum->SEalias) + s = s->Senum->SEalias; + else if (s->Senum->SEflags & SENnotagname) + return noname; + } + break; + + case SCnamespace: + if (s->Sident[0] == '?' && s->Sident[1] == '%') + return "unique"; // an unnamed namespace + break; + } +#endif + return s->Sident; +} + +/**************************************** + * Create a new symbol. + */ + +symbol * symbol_calloc(const char *id) +{ symbol *s; + int len; + + len = strlen(id); + //printf("sizeof(symbol)=%d, sizeof(s->Sident)=%d, len=%d\n",sizeof(symbol),sizeof(s->Sident),len); + s = (symbol *) mem_fmalloc(sizeof(symbol) - sizeof(s->Sident) + len + 1 + 5); + memset(s,0,sizeof(symbol) - sizeof(s->Sident)); +#if SCPP + s->Ssequence = pstate.STsequence; + pstate.STsequence += 1; + //if (s->Ssequence == 0x21) *(char*)0=0; +#endif +#ifdef DEBUG + if (debugy) + dbg_printf("symbol_calloc('%s') = %p\n",id,s); + s->id = IDsymbol; +#endif + memcpy(s->Sident,id,len + 1); + s->Ssymnum = -1; + return s; +} + +/**************************************** + * Create a symbol, given a name and type. + */ + +symbol * symbol_name(const char *name,int sclass,type *t) +{ + type_debug(t); + symbol *s = symbol_calloc(name); + s->Sclass = (enum SC) sclass; + s->Stype = t; + s->Stype->Tcount++; +#if ELFOBJ || MACHOBJ // Burton + s->Sseg = CDATA; +#endif + + if (tyfunc(t->Tty)) + symbol_func(s); + return s; +} + +/**************************************** + * Create a symbol that is an alias to another function symbol. + */ + +Funcsym *symbol_funcalias(Funcsym *sf) +{ + Funcsym *s; + + symbol_debug(sf); + assert(tyfunc(sf->Stype->Tty)); + if (sf->Sclass == SCfuncalias) + sf = sf->Sfunc->Falias; + s = (Funcsym *)symbol_name(sf->Sident,SCfuncalias,sf->Stype); + s->Sfunc->Falias = sf; +#if SCPP + s->Scover = sf->Scover; +#endif + return s; +} + +/**************************************** + * Create a symbol, give it a name, storage class and type. + */ + +symbol * symbol_generate(int sclass,type *t) +{ char name[10]; + static int tmpnum; + + sprintf(name,"_TMP%d",tmpnum++); +#ifdef DEBUG + symbol *s = symbol_name(name,sclass,t); + //symbol_print(s); + return s; +#else + return symbol_name(name,sclass,t); +#endif +} + +/**************************************** + * Generate an auto symbol, and add it to the symbol table. + */ + +symbol * symbol_genauto(type *t) +{ symbol *s; + + s = symbol_generate(SCauto,t); +#if SCPP + //printf("symbol_genauto(t) '%s'\n", s->Sident); + if (pstate.STdefertemps) + { symbol_keep(s); + s->Ssymnum = -1; + } + else + { s->Sflags |= SFLfree; + if (init_staticctor) + { // variable goes into _STI_xxxx + s->Ssymnum = -1; // deferred allocation +//printf("test2\n"); +//if (s->Sident[4] == '2') *(char*)0=0; + } + else + { + symbol_add(s); + } + } +#else + s->Sflags |= SFLfree; + symbol_add(s); +#endif + return s; +} + +/****************************************** + * Generate symbol into which we can copy the contents of expression e. + */ + +Symbol *symbol_genauto(elem *e) +{ + return symbol_genauto(type_fake(e->Ety)); +} + +/****************************************** + * Generate symbol into which we can copy the contents of expression e. + */ + +Symbol *symbol_genauto(tym_t ty) +{ + return symbol_genauto(type_fake(ty)); +} + +/**************************************** + * Add in the variants for a function symbol. + */ + +void symbol_func(symbol *s) +{ + //printf("symbol_func(%s, x%x)\n", s->Sident, fregsaved); + symbol_debug(s); + s->Sfl = FLfunc; + // Interrupt functions modify all registers +#if TX86 + // BUG: do interrupt functions really save BP? + #define mBP 0x20 + // Note that fregsaved may not be set yet + s->Sregsaved = (s->Stype && tybasic(s->Stype->Tty) == TYifunc) ? mBP : fregsaved; + s->Sseg = UNKNOWN; // don't know what segment it is in +#endif + if (!s->Sfunc) + s->Sfunc = func_calloc(); +} + +/******************************** + * Define symbol in specified symbol table. + * Returns: + * pointer to symbol + */ + +#if SCPP + +symbol * defsy(const char *p,symbol **parent) +{ + symbol *s = symbol_calloc(p); + symbol_addtotree(parent,s); + return s; +} + +#endif + +/******************************** + * Check integrity of symbol data structure. + */ + +#ifdef DEBUG + +void symbol_check(symbol *s) +{ + //dbg_printf("symbol_check('%s',%p)\n",s->Sident,s); + symbol_debug(s); + if (s->Stype) type_debug(s->Stype); + assert((unsigned)s->Sclass < (unsigned)SCMAX); +#if SCPP + if (s->Sscope) + symbol_check(s->Sscope); + if (s->Scover) + symbol_check(s->Scover); +#endif +} + +void symbol_tree_check(symbol *s) +{ + while (s) + { symbol_check(s); + symbol_tree_check(s->Sl); + s = s->Sr; + } +} + +#endif + +/******************************** + * Insert symbol in specified symbol table. + */ + +#if SCPP + +void symbol_addtotree(symbol **parent,symbol *s) +{ symbol *rover; + signed char cmp; + size_t len; + const char *p; + char c; + + //dbg_printf("symbol_addtotree('%s',%p)\n",s->Sident,*parent); +#ifdef DEBUG + symbol_tree_check(*parent); + assert(!s->Sl && !s->Sr); +#endif + symbol_debug(s); + p = s->Sident; + c = *p; + len = strlen(p); + p++; + rover = *parent; + while (rover != NULL) // while we haven't run out of tree + { symbol_debug(rover); + if ((cmp = c - rover->Sident[0]) == 0) + { cmp = memcmp(p,rover->Sident + 1,len); // compare identifier strings + if (cmp == 0) // found it if strings match + { + if (CPP) + { symbol *s2; + + switch (rover->Sclass) + { case SCstruct: + s2 = rover; + goto case_struct; + + case_struct: + if (s2->Sstruct->Sctor && + !(s2->Sstruct->Sctor->Sfunc->Fflags & Fgen)) + cpperr(EM_ctor_disallowed,p); // no ctor allowed for class rover + s2->Sstruct->Sflags |= STRnoctor; + goto case_cover; + + case_cover: + // Replace rover with the new symbol s, and + // have s 'cover' the tag symbol s2. + // BUG: memory leak on rover if s2!=rover + assert(!s2->Scover); + s->Sl = rover->Sl; + s->Sr = rover->Sr; + s->Scover = s2; + *parent = s; + rover->Sl = rover->Sr = NULL; + return; + + case SCenum: + s2 = rover; + goto case_cover; + + case SCtemplate: + s2 = rover; + s2->Stemplate->TMflags |= STRnoctor; + goto case_cover; + + case SCalias: + s2 = rover->Smemalias; + if (s2->Sclass == SCstruct) + goto case_struct; + if (s2->Sclass == SCenum) + goto case_cover; + break; + } + } + synerr(EM_multiple_def,p - 1); // symbol is already defined + //symbol_undef(s); // undefine the symbol + return; + } + } + parent = (cmp < 0) ? /* if we go down left side */ + &(rover->Sl) : /* then get left child */ + &(rover->Sr); /* else get right child */ + rover = *parent; /* get child */ + } + /* not in table, so insert into table */ + *parent = s; /* link new symbol into tree */ +L1: + ; +} + +#endif + +/************************************* + * Search for symbol in multiple symbol tables, + * starting with most recently nested one. + * Input: + * p -> identifier string + * Returns: + * pointer to symbol + * NULL if couldn't find it + */ + +#if 0 +symbol * lookupsym(const char *p) +{ + return scope_search(p,SCTglobal | SCTlocal); +} +#endif + +/************************************* + * Search for symbol in symbol table. + * Input: + * p -> identifier string + * rover -> where to start looking + * Returns: + * pointer to symbol (NULL if not found) + */ + +#if SCPP + +symbol * findsy(const char *p,symbol *rover) +{ +#if __INTSIZE == 2 && TX86 && !defined(_MSC_VER) + volatile int len; + __asm + { + push DS + mov DS,word ptr p+2 + les DI,p + mov DX,word ptr p + mov BX,ES:[DI] + xor AL,AL + mov CX,0FFFFh + repne scasb + not CX + sub CX,2 + mov len,CX + add DX,2 + mov AX,BX + les BX,rover + jmp short L1 + +L38C: les BX,ES:symbol.Sl[BX] +L1: test BX,BX + je L3A5 + cmp AL,ES:symbol.Sident[BX] + js L38C + je L2 + les BX,ES:symbol.Sr[BX] + jmp L1 + +L2: cmp AH,ES:symbol.Sident+1[BX] + js L38C + je L3 + les BX,ES:symbol.Sr[BX] + jmp L1 + +L3: mov SI,DX + lea DI,symbol.Sident+2[BX] + mov CX,len + rep cmpsb + js L38C + je L3A5 + les BX,ES:symbol.Sr[BX] + jmp L1 + +L3A5: mov DX,ES + mov AX,BX + pop DS + } +#elif __INTSIZE == 4 && TX86 && !defined(_MSC_VER) && !M_UNIX + volatile int len; + __asm + { +#if !_WIN32 + push DS + pop ES +#endif + mov EDI,p + xor AL,AL + + mov BL,[EDI] + mov ECX,-1 + + repne scasb + + not ECX + mov EDX,p + + dec ECX + inc EDX + + mov len,ECX + mov AL,BL + + mov EBX,rover + mov ESI,EDX + + test EBX,EBX + je L6 + + cmp AL,symbol.Sident[EBX] + js L2 + + lea EDI,symbol.Sident+1[EBX] + je L5 + + mov EBX,symbol.Sr[EBX] + jmp L3 + +L1: mov ECX,len +L2: mov EBX,symbol.Sl[EBX] + +L3: test EBX,EBX + je L6 + +L4: cmp AL,symbol.Sident[EBX] + js L2 + + lea EDI,symbol.Sident+1[EBX] + je L5 + + mov EBX,symbol.Sr[EBX] + jmp L3 + +L5: rep cmpsb + + mov ESI,EDX + js L1 + + je L6 + + mov EBX,symbol.Sr[EBX] + mov ECX,len + + test EBX,EBX + jne L4 + +L6: mov EAX,EBX + } +#else + size_t len; + signed char cmp; /* set to value of strcmp */ + char c = *p; + + len = strlen(p); + p++; // will pick up 0 on memcmp + while (rover != NULL) // while we haven't run out of tree + { symbol_debug(rover); + if ((cmp = c - rover->Sident[0]) == 0) + { cmp = memcmp(p,rover->Sident + 1,len); /* compare identifier strings */ + if (cmp == 0) + return rover; /* found it if strings match */ + } + rover = (cmp < 0) ? rover->Sl : rover->Sr; + } + return rover; // failed to find it +#endif +} + +#endif + +/*********************************** + * Create a new symbol table. + */ + +#if SCPP + +void createglobalsymtab() +{ + assert(!scope_end); + if (CPP) + scope_push(NULL,(scope_fp)findsy, SCTcglobal); + else + scope_push(NULL,(scope_fp)findsy, SCTglobaltag); + scope_push(NULL,(scope_fp)findsy, SCTglobal); +} + + +void createlocalsymtab() +{ + assert(scope_end); + if (!CPP) + scope_push(NULL,(scope_fp)findsy, SCTtag); + scope_push(NULL,(scope_fp)findsy, SCTlocal); +} + + +/*********************************** + * Delete current symbol table and back up one. + */ + +void deletesymtab() +{ symbol *root; + + root = (symbol *)scope_pop(); + if (root) + { + if (funcsym_p) + list_prepend(&funcsym_p->Sfunc->Fsymtree,root); + else + symbol_free(root); // free symbol table + } + + if (!CPP) + { + root = (symbol *)scope_pop(); + if (root) + { + if (funcsym_p) + list_prepend(&funcsym_p->Sfunc->Fsymtree,root); + else + symbol_free(root); // free symbol table + } + } +} + +#endif + +/********************************* + * Delete symbol from symbol table, taking care to delete + * all children of a symbol. + * Make sure there are no more forward references (labels, tags). + * Input: + * pointer to a symbol + */ + +void meminit_free(meminit_t *m) /* helper for symbol_free() */ +{ + list_free(&m->MIelemlist,(list_free_fp)el_free); + MEM_PARF_FREE(m); +} + +void symbol_free(symbol *s) +{ + while (s) /* if symbol exists */ + { symbol *sr; + +#ifdef DEBUG + if (debugy) + dbg_printf("symbol_free('%s',%p)\n",s->Sident,s); + symbol_debug(s); + assert(/*s->Sclass != SCunde &&*/ (int) s->Sclass < (int) SCMAX); +#endif + { type *t = s->Stype; + + if (t) + type_debug(t); + if (t && tyfunc(t->Tty) && s->Sfunc) + { + func_t *f = s->Sfunc; + + debug(assert(f)); + blocklist_free(&f->Fstartblock); + freesymtab(f->Flocsym.tab,0,f->Flocsym.top); + + symtab_free(f->Flocsym.tab); + if (CPP) + { + if (f->Fflags & Fnotparent) + { debug(debugy && dbg_printf("not parent, returning\n")); + return; + } + + /* We could be freeing the symbol before it's class is */ + /* freed, so remove it from the class's field list */ +#if 1 + if (f->Fclass) + { list_t tl; + + symbol_debug(f->Fclass); + tl = list_inlist(f->Fclass->Sstruct->Sfldlst,s); + if (tl) + list_setsymbol(tl,0); + } +#endif + if (f->Foversym && f->Foversym->Sfunc) + { f->Foversym->Sfunc->Fflags &= ~Fnotparent; + f->Foversym->Sfunc->Fclass = NULL; + symbol_free(f->Foversym); + } + + if (f->Fexplicitspec) + symbol_free(f->Fexplicitspec); + + /* If operator function, remove from list of such functions */ + if (f->Fflags & Foperator) + { assert(f->Foper && f->Foper < OPMAX); + //if (list_inlist(cpp_operfuncs[f->Foper],s)) + // list_subtract(&cpp_operfuncs[f->Foper],s); + } + + list_free(&f->Fclassfriends,FPNULL); + list_free(&f->Ffwdrefinstances,FPNULL); + param_free(&f->Farglist); + param_free(&f->Fptal); + list_free(&f->Fexcspec,(list_free_fp)type_free); +#if SCPP + token_free(f->Fbody); +#endif + el_free(f->Fbaseinit); + if (f->Fthunk && !(f->Fflags & Finstance)) + MEM_PH_FREE(f->Fthunk); + list_free(&f->Fthunks,(list_free_fp)symbol_free); + } + list_free(&f->Fsymtree,(list_free_fp)symbol_free); + func_free(f); + } + switch (s->Sclass) + { +#if SCPP + case SClabel: + if (!s->Slabel) + synerr(EM_unknown_label,s->Sident); + break; +#endif + case SCstruct: +#if SCPP + if (CPP) + { + struct_t *st = s->Sstruct; + assert(st); + list_free(&st->Sclassfriends,FPNULL); + list_free(&st->Sfriendclass,FPNULL); + list_free(&st->Sfriendfuncs,FPNULL); + list_free(&st->Scastoverload,FPNULL); + list_free(&st->Sopoverload,FPNULL); + list_free(&st->Svirtual,MEM_PH_FREEFP); + list_free(&st->Sfldlst,FPNULL); + symbol_free(st->Sroot); + baseclass_t *b,*bn; + + for (b = st->Sbase; b; b = bn) + { bn = b->BCnext; + list_free(&b->BCpublics,FPNULL); + baseclass_free(b); + } + for (b = st->Svirtbase; b; b = bn) + { bn = b->BCnext; + baseclass_free(b); + } + for (b = st->Smptrbase; b; b = bn) + { bn = b->BCnext; + list_free(&b->BCmptrlist,MEM_PH_FREEFP); + baseclass_free(b); + } +#if VBTABLES + for (b = st->Svbptrbase; b; b = bn) + { bn = b->BCnext; + baseclass_free(b); + } +#endif + param_free(&st->Sarglist); + param_free(&st->Spr_arglist); + struct_free(st); + } + else +#endif + { +#ifdef DEBUG + if (debugy) + dbg_printf("freeing members %p\n",s->Sstruct->Sfldlst); +#endif + list_free(&s->Sstruct->Sfldlst,FPNULL); + symbol_free(s->Sstruct->Sroot); + struct_free(s->Sstruct); + } +#if 0 /* Don't complain anymore about these, ANSI C says */ + /* it's ok */ + if (t && t->Tflags & TFsizeunknown) + synerr(EM_unknown_tag,s->Sident); +#endif + break; + case SCenum: + /* The actual member symbols are either in a local */ + /* table or on the member list of a class, so we */ + /* don't free them here. */ + assert(s->Senum); + list_free(&s->Senumlist,FPNULL); + MEM_PH_FREE(s->Senum); + s->Senum = NULL; + break; + +#if SCPP + case SCtemplate: + { template_t *tm = s->Stemplate; + + list_free(&tm->TMinstances,FPNULL); + list_free(&tm->TMmemberfuncs,(list_free_fp)tmf_free); + list_free(&tm->TMexplicit,(list_free_fp)tme_free); + list_free(&tm->TMnestedexplicit,(list_free_fp)tmne_free); + list_free(&tm->TMnestedfriends,(list_free_fp)tmnf_free); + param_free(&tm->TMptpl); + param_free(&tm->TMptal); + token_free(tm->TMbody); + symbol_free(tm->TMpartial); + list_free(&tm->TMfriends,FPNULL); + MEM_PH_FREE(tm); + break; + } + case SCnamespace: + symbol_free(s->Snameroot); + list_free(&s->Susing,FPNULL); + break; + + case SCmemalias: + case SCfuncalias: + case SCadl: + list_free(&s->Spath,FPNULL); + break; +#endif + case SCparameter: + case SCregpar: + case SCfastpar: + case SCregister: + case SCtmp: + case SCauto: + vec_free(s->Srange); + /* FALL-THROUGH */ +#if 0 + case SCconst: + if (s->Sflags & (SFLvalue | SFLdtorexp)) + el_free(s->Svalue); +#endif + break; + default: + break; + } + if (s->Sflags & (SFLvalue | SFLdtorexp)) + el_free(s->Svalue); + if (s->Sdt) + dt_free(s->Sdt); + type_free(t); + symbol_free(s->Sl); +#if SCPP + if (s->Scover) + symbol_free(s->Scover); +#endif + sr = s->Sr; +#ifdef DEBUG + s->id = 0; +#endif + mem_ffree(s); + } + s = sr; + } +} + +/******************************** + * Undefine a symbol. + * Assume error msg was already printed. + */ + +#if 0 +STATIC void symbol_undef(symbol *s) +{ + s->Sclass = SCunde; + s->Ssymnum = -1; + type_free(s->Stype); /* free type data */ + s->Stype = NULL; +} +#endif + +/***************************** + * Add symbol to current symbol array. + */ + +SYMIDX symbol_add(symbol *s) +{ SYMIDX sitop; + + //printf("symbol_add('%s')\n", s->Sident); +#ifdef DEBUG + if (!s || !s->Sident[0]) + { dbg_printf("bad symbol\n"); + assert(0); + } +#endif + symbol_debug(s); + if (pstate.STinsizeof) + { symbol_keep(s); + return -1; + } + debug(assert(cstate.CSpsymtab)); + sitop = cstate.CSpsymtab->top; + assert(sitop <= cstate.CSpsymtab->symmax); + if (sitop == cstate.CSpsymtab->symmax) + { +#ifdef DEBUG +#define SYMINC 1 /* flush out reallocation bugs */ +#else +#define SYMINC 99 +#endif + cstate.CSpsymtab->symmax += (cstate.CSpsymtab == &globsym) ? SYMINC : 1; + //assert(cstate.CSpsymtab->symmax * sizeof(symbol *) < 4096 * 4); + cstate.CSpsymtab->tab = symtab_realloc(cstate.CSpsymtab->tab, cstate.CSpsymtab->symmax); + } + cstate.CSpsymtab->tab[sitop] = s; +#if AUTONEST + if (pushcount) + { s->Spush = pushcount; + pushcount = 0; + } +#endif +#ifdef DEBUG + if (debugy) + dbg_printf("symbol_add(%p '%s') = %d\n",s,s->Sident,cstate.CSpsymtab->top); +#endif + assert(s->Ssymnum == -1); + return s->Ssymnum = cstate.CSpsymtab->top++; +} + +/**************************** + * Free up the symbol table, from symbols n1 through n2, not + * including n2. + */ + +void freesymtab(symbol **stab,SYMIDX n1,SYMIDX n2) +{ SYMIDX si; + + if (!stab) + return; +#ifdef DEBUG + if (debugy) + dbg_printf("freesymtab(from %d to %d)\n",n1,n2); +#endif + assert(stab != globsym.tab || (n1 <= n2 && n2 <= globsym.top)); + for (si = n1; si < n2; si++) + { symbol *s; + + s = stab[si]; + if (s && s->Sflags & SFLfree) + { stab[si] = NULL; +#ifdef DEBUG + if (debugy) + dbg_printf("Freeing %p '%s' (%d)\n",s,s->Sident,si); + symbol_debug(s); +#endif + s->Sl = s->Sr = NULL; + s->Ssymnum = -1; + symbol_free(s); + } + } +} + +/**************************** + * Create a copy of a symbol. + */ + +symbol * symbol_copy(symbol *s) +{ symbol *scopy; + type *t; + + symbol_debug(s); + /*dbg_printf("symbol_copy(%s)\n",s->Sident);*/ + scopy = symbol_calloc(s->Sident); + memcpy(scopy,s,sizeof(symbol) - sizeof(s->Sident)); + scopy->Sl = scopy->Sr = scopy->Snext = NULL; + scopy->Ssymnum = -1; + if (scopy->Sdt) + dtsymsize(scopy); + if (scopy->Sflags & (SFLvalue | SFLdtorexp)) + scopy->Svalue = el_copytree(s->Svalue); + t = scopy->Stype; + if (t) + { t->Tcount++; /* one more parent of the type */ + type_debug(t); + } + return scopy; +} + +/******************************* + * Search list for a symbol with an identifier that matches. + * Returns: + * pointer to matching symbol + * NULL if not found + */ + +#if SCPP + +symbol * symbol_searchlist(symlist_t sl,const char *vident) +{ symbol *s; +#ifdef DEBUG + int count = 0; +#endif + + //dbg_printf("searchlist(%s)\n",vident); + for (; sl; sl = list_next(sl)) + { s = list_symbol(sl); + symbol_debug(s); + /*dbg_printf("\tcomparing with %s\n",s->Sident);*/ + if (strcmp(vident,s->Sident) == 0) + return s; +#ifdef DEBUG + assert(++count < 300); /* prevent infinite loops */ +#endif + } + return NULL; +} + +/*************************************** + * Search for symbol in sequence of symbol tables. + * Input: + * glbl !=0 if global symbol table only + */ + +symbol *symbol_search(const char *id) +{ + Scope *sc; + if (CPP) + { unsigned sct; + + sct = pstate.STclasssym ? SCTclass : 0; + sct |= SCTmfunc | SCTlocal | SCTwith | SCTglobal | SCTnspace | SCTtemparg | SCTtempsym; + return scope_searchx(id,sct,&sc); + } + else + return scope_searchx(id,SCTglobal | SCTlocal,&sc); +} + +#endif + +/******************************************* + * Hydrate a symbol tree. + */ + +#if HYDRATE +void symbol_tree_hydrate(symbol **ps) +{ symbol *s; + + while (isdehydrated(*ps)) /* if symbol is dehydrated */ + { + s = symbol_hydrate(ps); + symbol_debug(s); + if (s->Scover) + symbol_hydrate(&s->Scover); + symbol_tree_hydrate(&s->Sl); + ps = &s->Sr; + } + +} +#endif + +/******************************************* + * Dehydrate a symbol tree. + */ + +#if DEHYDRATE +void symbol_tree_dehydrate(symbol **ps) +{ symbol *s; + + while ((s = *ps) != NULL && !isdehydrated(s)) /* if symbol exists */ + { + symbol_debug(s); + symbol_dehydrate(ps); +#if DEBUG_XSYMGEN + if (xsym_gen && ph_in_head(s)) + return; +#endif + symbol_dehydrate(&s->Scover); + symbol_tree_dehydrate(&s->Sl); + ps = &s->Sr; + } +} +#endif + +/******************************************* + * Hydrate a symbol. + */ + +#if HYDRATE +symbol *symbol_hydrate(symbol **ps) +{ symbol *s; + + s = *ps; + if (isdehydrated(s)) /* if symbol is dehydrated */ + { type *t; + struct_t *st; + + s = (symbol *) ph_hydrate(ps); +#ifdef DEBUG + debugy && dbg_printf("symbol_hydrate('%s')\n",s->Sident); +#endif + symbol_debug(s); + if (!isdehydrated(s->Stype)) // if this symbol is already dehydrated + return s; // no need to do it again +#if SOURCE_4SYMS + s->Ssrcpos.Sfilnum += File_Hydrate_Num; /* file number relative header build */ +#endif + if (pstate.SThflag != FLAG_INPLACE && s->Sfl != FLreg) + s->Sxtrnnum = 0; // not written to .OBJ file yet + type_hydrate(&s->Stype); + //dbg_printf("symbol_hydrate(%p, '%s', t = %p)\n",s,s->Sident,s->Stype); + t = s->Stype; + if (t) + type_debug(t); + + if (t && tyfunc(t->Tty) && ph_hydrate(&s->Sfunc)) + { + func_t *f = s->Sfunc; + SYMIDX si; + + debug(assert(f)); + + list_hydrate(&f->Fsymtree,(list_free_fp)symbol_tree_hydrate); + blocklist_hydrate(&f->Fstartblock); + + ph_hydrate(&f->Flocsym.tab); + for (si = 0; si < f->Flocsym.top; si++) + symbol_hydrate(&f->Flocsym.tab[si]); + + srcpos_hydrate(&f->Fstartline); + srcpos_hydrate(&f->Fendline); + + symbol_hydrate(&f->F__func__); + + if (CPP) + { + symbol_hydrate(&f->Fparsescope); + Classsym_hydrate(&f->Fclass); + symbol_hydrate(&f->Foversym); + symbol_hydrate(&f->Fexplicitspec); + symbol_hydrate(&f->Fsurrogatesym); + + list_hydrate(&f->Fclassfriends,(list_free_fp)symbol_hydrate); + el_hydrate(&f->Fbaseinit); + token_hydrate(&f->Fbody); + symbol_hydrate(&f->Falias); + list_hydrate(&f->Fthunks,(list_free_fp)symbol_hydrate); + if (f->Fflags & Finstance) + symbol_hydrate(&f->Ftempl); + else + thunk_hydrate(&f->Fthunk); + param_hydrate(&f->Farglist); + param_hydrate(&f->Fptal); + list_hydrate(&f->Ffwdrefinstances,(list_free_fp)symbol_hydrate); + list_hydrate(&f->Fexcspec,(list_free_fp)type_hydrate); + } + } + if (CPP) + symbol_hydrate(&s->Sscope); + switch (s->Sclass) + { + case SCstruct: + if (CPP) + { + st = (struct_t *) ph_hydrate(&s->Sstruct); + assert(st); + symbol_tree_hydrate(&st->Sroot); + ph_hydrate(&st->Spvirtder); + list_hydrate(&st->Sfldlst,(list_free_fp)symbol_hydrate); + list_hydrate(&st->Svirtual,(list_free_fp)mptr_hydrate); + list_hydrate(&st->Sopoverload,(list_free_fp)symbol_hydrate); + list_hydrate(&st->Scastoverload,(list_free_fp)symbol_hydrate); + list_hydrate(&st->Sclassfriends,(list_free_fp)symbol_hydrate); + list_hydrate(&st->Sfriendclass,(list_free_fp)symbol_hydrate); + list_hydrate(&st->Sfriendfuncs,(list_free_fp)symbol_hydrate); + assert(!st->Sinlinefuncs); + + baseclass_hydrate(&st->Sbase); + baseclass_hydrate(&st->Svirtbase); + baseclass_hydrate(&st->Smptrbase); + baseclass_hydrate(&st->Sprimary); +#if VBTABLES + baseclass_hydrate(&st->Svbptrbase); +#endif + + ph_hydrate(&st->Svecctor); + ph_hydrate(&st->Sctor); + ph_hydrate(&st->Sdtor); +#if VBTABLES + ph_hydrate(&st->Sprimdtor); + ph_hydrate(&st->Spriminv); + ph_hydrate(&st->Sscaldeldtor); +#endif + ph_hydrate(&st->Sinvariant); + ph_hydrate(&st->Svptr); + ph_hydrate(&st->Svtbl); + ph_hydrate(&st->Sopeq); + ph_hydrate(&st->Sopeq2); + ph_hydrate(&st->Scpct); + ph_hydrate(&st->Sveccpct); + ph_hydrate(&st->Salias); + ph_hydrate(&st->Stempsym); + param_hydrate(&st->Sarglist); + param_hydrate(&st->Spr_arglist); +#if VBTABLES + ph_hydrate(&st->Svbptr); + ph_hydrate(&st->Svbptr_parent); + ph_hydrate(&st->Svbtbl); +#endif + } + else + { + ph_hydrate(&s->Sstruct); + symbol_tree_hydrate(&s->Sstruct->Sroot); + list_hydrate(&s->Sstruct->Sfldlst,(list_free_fp)symbol_hydrate); + } + break; + + case SCenum: + assert(s->Senum); + ph_hydrate(&s->Senum); + if (CPP) + { ph_hydrate(&s->Senum->SEalias); + list_hydrate(&s->Senumlist,(list_free_fp)symbol_hydrate); + } + break; + + case SCtemplate: + { template_t *tm; + + tm = (template_t *) ph_hydrate(&s->Stemplate); + list_hydrate(&tm->TMinstances,(list_free_fp)symbol_hydrate); + list_hydrate(&tm->TMfriends,(list_free_fp)symbol_hydrate); + param_hydrate(&tm->TMptpl); + param_hydrate(&tm->TMptal); + token_hydrate(&tm->TMbody); + list_hydrate(&tm->TMmemberfuncs,(list_free_fp)tmf_hydrate); + list_hydrate(&tm->TMexplicit,(list_free_fp)tme_hydrate); + list_hydrate(&tm->TMnestedexplicit,(list_free_fp)tmne_hydrate); + list_hydrate(&tm->TMnestedfriends,(list_free_fp)tmnf_hydrate); + ph_hydrate(&tm->TMnext); + symbol_hydrate(&tm->TMpartial); + symbol_hydrate(&tm->TMprimary); + break; + } + + case SCnamespace: + symbol_tree_hydrate(&s->Snameroot); + list_hydrate(&s->Susing,(list_free_fp)symbol_hydrate); + break; + + case SCmemalias: + case SCfuncalias: + case SCadl: + list_hydrate(&s->Spath,(list_free_fp)symbol_hydrate); + case SCalias: + ph_hydrate(&s->Smemalias); + break; + + default: + if (s->Sflags & (SFLvalue | SFLdtorexp)) + el_hydrate(&s->Svalue); + break; + } + { dt_t **pdt,*dt; + + for (pdt = &s->Sdt; isdehydrated(*pdt); pdt = &dt->DTnext) + { + dt = (dt_t *) ph_hydrate(pdt); + switch (dt->dt) + { case DT_abytes: + case DT_nbytes: + ph_hydrate(&dt->DTpbytes); + break; + case DT_xoff: + symbol_hydrate(&dt->DTsym); + break; + } + } + } + if (s->Scover) + symbol_hydrate(&s->Scover); + } + return s; +} +#endif + +/******************************************* + * Dehydrate a symbol. + */ + +#if DEHYDRATE +void symbol_dehydrate(symbol **ps) +{ + symbol *s; + + if ((s = *ps) != NULL && !isdehydrated(s)) /* if symbol exists */ + { type *t; + struct_t *st; + +#ifdef DEBUG + if (debugy) + dbg_printf("symbol_dehydrate('%s')\n",s->Sident); +#endif + ph_dehydrate(ps); +#if DEBUG_XSYMGEN + if (xsym_gen && ph_in_head(s)) + return; +#endif + symbol_debug(s); + t = s->Stype; + if (isdehydrated(t)) + return; + type_dehydrate(&s->Stype); + + if (tyfunc(t->Tty) && !isdehydrated(s->Sfunc)) + { + func_t *f = s->Sfunc; + SYMIDX si; + + debug(assert(f)); + ph_dehydrate(&s->Sfunc); + + list_dehydrate(&f->Fsymtree,(list_free_fp)symbol_tree_dehydrate); + blocklist_dehydrate(&f->Fstartblock); + assert(!isdehydrated(&f->Flocsym.tab)); + +#if DEBUG_XSYMGEN + if (!xsym_gen || !ph_in_head(f->Flocsym.tab)) + +#endif + for (si = 0; si < f->Flocsym.top; si++) + symbol_dehydrate(&f->Flocsym.tab[si]); + ph_dehydrate(&f->Flocsym.tab); + + srcpos_dehydrate(&f->Fstartline); + srcpos_dehydrate(&f->Fendline); + symbol_dehydrate(&f->F__func__); + if (CPP) + { + symbol_dehydrate(&f->Fparsescope); + ph_dehydrate(&f->Fclass); + symbol_dehydrate(&f->Foversym); + symbol_dehydrate(&f->Fexplicitspec); + symbol_dehydrate(&f->Fsurrogatesym); + + list_dehydrate(&f->Fclassfriends,FPNULL); + el_dehydrate(&f->Fbaseinit); +#if DEBUG_XSYMGEN + if (xsym_gen && s->Sclass == SCfunctempl) + ph_dehydrate(&f->Fbody); + else +#endif + token_dehydrate(&f->Fbody); + symbol_dehydrate(&f->Falias); + list_dehydrate(&f->Fthunks,(list_free_fp)symbol_dehydrate); + if (f->Fflags & Finstance) + symbol_dehydrate(&f->Ftempl); + else + thunk_dehydrate(&f->Fthunk); +#if !TX86 && DEBUG_XSYMGEN + if (xsym_gen && s->Sclass == SCfunctempl) + ph_dehydrate(&f->Farglist); + else +#endif + param_dehydrate(&f->Farglist); + param_dehydrate(&f->Fptal); + list_dehydrate(&f->Ffwdrefinstances,(list_free_fp)symbol_dehydrate); + list_dehydrate(&f->Fexcspec,(list_free_fp)type_dehydrate); + } + } + if (CPP) + ph_dehydrate(&s->Sscope); + switch (s->Sclass) + { + case SCstruct: + if (CPP) + { + st = s->Sstruct; + if (isdehydrated(st)) + break; + ph_dehydrate(&s->Sstruct); + assert(st); + symbol_tree_dehydrate(&st->Sroot); + ph_dehydrate(&st->Spvirtder); + list_dehydrate(&st->Sfldlst,(list_free_fp)symbol_dehydrate); + list_dehydrate(&st->Svirtual,(list_free_fp)mptr_dehydrate); + list_dehydrate(&st->Sopoverload,(list_free_fp)symbol_dehydrate); + list_dehydrate(&st->Scastoverload,(list_free_fp)symbol_dehydrate); + list_dehydrate(&st->Sclassfriends,(list_free_fp)symbol_dehydrate); + list_dehydrate(&st->Sfriendclass,(list_free_fp)ph_dehydrate); + list_dehydrate(&st->Sfriendfuncs,(list_free_fp)ph_dehydrate); + assert(!st->Sinlinefuncs); + + baseclass_dehydrate(&st->Sbase); + baseclass_dehydrate(&st->Svirtbase); + baseclass_dehydrate(&st->Smptrbase); + baseclass_dehydrate(&st->Sprimary); +#if VBTABLES + baseclass_dehydrate(&st->Svbptrbase); +#endif + + ph_dehydrate(&st->Svecctor); + ph_dehydrate(&st->Sctor); + ph_dehydrate(&st->Sdtor); +#if VBTABLES + ph_dehydrate(&st->Sprimdtor); + ph_dehydrate(&st->Spriminv); + ph_dehydrate(&st->Sscaldeldtor); +#endif + ph_dehydrate(&st->Sinvariant); + ph_dehydrate(&st->Svptr); + ph_dehydrate(&st->Svtbl); + ph_dehydrate(&st->Sopeq); + ph_dehydrate(&st->Sopeq2); + ph_dehydrate(&st->Scpct); + ph_dehydrate(&st->Sveccpct); + ph_dehydrate(&st->Salias); + ph_dehydrate(&st->Stempsym); + param_dehydrate(&st->Sarglist); + param_dehydrate(&st->Spr_arglist); +#if VBTABLES + ph_dehydrate(&st->Svbptr); + ph_dehydrate(&st->Svbptr_parent); + ph_dehydrate(&st->Svbtbl); +#endif + } + else + { + symbol_tree_dehydrate(&s->Sstruct->Sroot); + list_dehydrate(&s->Sstruct->Sfldlst,(list_free_fp)symbol_dehydrate); + ph_dehydrate(&s->Sstruct); + } + break; + + case SCenum: + assert(s->Senum); + if (!isdehydrated(s->Senum)) + { + if (CPP) + { ph_dehydrate(&s->Senum->SEalias); + list_dehydrate(&s->Senumlist,(list_free_fp)ph_dehydrate); + } + ph_dehydrate(&s->Senum); + } + break; + + case SCtemplate: + { template_t *tm; + + tm = s->Stemplate; + if (!isdehydrated(tm)) + { + ph_dehydrate(&s->Stemplate); + list_dehydrate(&tm->TMinstances,(list_free_fp)symbol_dehydrate); + list_dehydrate(&tm->TMfriends,(list_free_fp)symbol_dehydrate); + list_dehydrate(&tm->TMnestedfriends,(list_free_fp)tmnf_dehydrate); + param_dehydrate(&tm->TMptpl); + param_dehydrate(&tm->TMptal); + token_dehydrate(&tm->TMbody); + list_dehydrate(&tm->TMmemberfuncs,(list_free_fp)tmf_dehydrate); + list_dehydrate(&tm->TMexplicit,(list_free_fp)tme_dehydrate); + list_dehydrate(&tm->TMnestedexplicit,(list_free_fp)tmne_dehydrate); + ph_dehydrate(&tm->TMnext); + symbol_dehydrate(&tm->TMpartial); + symbol_dehydrate(&tm->TMprimary); + } + break; + } + + case SCnamespace: + symbol_tree_dehydrate(&s->Snameroot); + list_dehydrate(&s->Susing,(list_free_fp)symbol_dehydrate); + break; + + case SCmemalias: + case SCfuncalias: + case SCadl: + list_dehydrate(&s->Spath,(list_free_fp)symbol_dehydrate); + case SCalias: + ph_dehydrate(&s->Smemalias); + break; + + default: + if (s->Sflags & (SFLvalue | SFLdtorexp)) + el_dehydrate(&s->Svalue); + break; + } + { dt_t **pdt,*dt; + + for (pdt = &s->Sdt; + (dt = *pdt) != NULL && !isdehydrated(dt); + pdt = &dt->DTnext) + { + ph_dehydrate(pdt); + switch (dt->dt) + { case DT_abytes: + case DT_nbytes: + ph_dehydrate(&dt->DTpbytes); + break; + case DT_xoff: + symbol_dehydrate(&dt->DTsym); + break; + } + } + } + if (s->Scover) + symbol_dehydrate(&s->Scover); + } +} +#endif + +/*************************** + * Dehydrate threaded list of symbols. + */ + +#if DEHYDRATE +void symbol_symdefs_dehydrate(symbol **ps) +{ + symbol *s; + + for (; *ps; ps = &s->Snext) + { + s = *ps; + symbol_debug(s); + //dbg_printf("symbol_symdefs_dehydrate(%p, '%s')\n",s,s->Sident); + symbol_dehydrate(ps); + } +} +#endif + +/*************************** + * Hydrate threaded list of symbols. + * Input: + * *ps start of threaded list + * *parent root of symbol table to add symbol into + * flag !=0 means add onto existing stuff + * 0 means hydrate in place + */ + +#if SCPP + +void symbol_symdefs_hydrate(symbol **ps,symbol **parent,int flag) +{ symbol *s; + + //printf("symbol_symdefs_hydrate(flag = %d)\n",flag); +#ifdef DEBUG + int count = 0; + + if (flag) symbol_tree_check(*parent); +#endif + for (; *ps; ps = &s->Snext) + { + //dbg_printf("%p ",*ps); +#ifdef DEBUG + count++; +#endif + s = dohydrate ? symbol_hydrate(ps) : *ps; + + //if (s->Sclass == SCstruct) + //dbg_printf("symbol_symdefs_hydrate(%p, '%s')\n",s,s->Sident); + symbol_debug(s); +#if 0 + if (tyfunc(s->Stype->Tty)) + { Outbuffer buf; + char *p1; + + p1 = param_tostring(&buf,s->Stype); + dbg_printf("'%s%s'\n",cpp_prettyident(s),p1); + } +#endif + type_debug(s->Stype); + if (flag) + { char *p; + symbol **ps; + symbol *rover; + char c; + size_t len; + + p = s->Sident; + c = *p; + + // Put symbol s into symbol table + +#if MMFIO + if (s->Sl || s->Sr) // avoid writing to page if possible +#endif + s->Sl = s->Sr = NULL; + len = strlen(p); + p++; + ps = parent; + while ((rover = *ps) != NULL) + { signed char cmp; + + if ((cmp = c - rover->Sident[0]) == 0) + { cmp = memcmp(p,rover->Sident + 1,len); // compare identifier strings + if (cmp == 0) + { + if (CPP && tyfunc(s->Stype->Tty) && tyfunc(rover->Stype->Tty)) + { symbol **ps; + symbol *sn; + symbol *so; + + so = s; + do + { + // Tack onto end of overloaded function list + for (ps = &rover; *ps; ps = &(*ps)->Sfunc->Foversym) + { if (cpp_funccmp(so, *ps)) + { //printf("function '%s' already in list\n",so->Sident); + goto L2; + } + } + //printf("appending '%s' to rover\n",so->Sident); + *ps = so; + L2: + sn = so->Sfunc->Foversym; + so->Sfunc->Foversym = NULL; + so = sn; + } while (so); + //printf("overloading...\n"); + } + else if (s->Sclass == SCstruct) + { + if (CPP && rover->Scover) + { ps = &rover->Scover; + rover = *ps; + } + else + if (rover->Sclass == SCstruct) + { + if (!(s->Stype->Tflags & TFforward)) + { // Replace rover with s in symbol table + //printf("Replacing '%s'\n",s->Sident); + *ps = s; + s->Sl = rover->Sl; + s->Sr = rover->Sr; + rover->Sl = rover->Sr = NULL; + rover->Stype->Ttag = (Classsym *)s; + symbol_keep(rover); + } + else + s->Stype->Ttag = (Classsym *)rover; + } + } + goto L1; + } + } + ps = (cmp < 0) ? /* if we go down left side */ + &rover->Sl : + &rover->Sr; + } + *ps = s; + if (s->Sclass == SCcomdef) + { s->Sclass = SCglobal; + outcommon(s,type_size(s->Stype)); + } + } + L1: ; + } // for +#ifdef DEBUG + if (flag) symbol_tree_check(*parent); + printf("%d symbols hydrated\n",count); +#endif +} + +#endif + +#if 0 + +/************************************* + * Put symbol table s into parent symbol table. + */ + +void symboltable_hydrate(symbol *s,symbol **parent) +{ + while (s) + { symbol *sl,*sr; + char *p; + + symbol_debug(s); + + sl = s->Sl; + sr = s->Sr; + p = s->Sident; + + //dbg_printf("symboltable_hydrate('%s')\n",p); + + /* Put symbol s into symbol table */ + { symbol **ps; + symbol *rover; + int c = *p; + + ps = parent; + while ((rover = *ps) != NULL) + { int cmp; + + if ((cmp = c - rover->Sident[0]) == 0) + { cmp = strcmp(p,rover->Sident); /* compare identifier strings */ + if (cmp == 0) + { + if (CPP && tyfunc(s->Stype->Tty) && tyfunc(rover->Stype->Tty)) + { symbol **ps; + symbol *sn; + + do + { + // Tack onto end of overloaded function list + for (ps = &rover; *ps; ps = &(*ps)->Sfunc->Foversym) + { if (cpp_funccmp(s, *ps)) + goto L2; + } + s->Sl = s->Sr = NULL; + *ps = s; + L2: + sn = s->Sfunc->Foversym; + s->Sfunc->Foversym = NULL; + s = sn; + } while (s); + } + else + { + if (!typematch(s->Stype,rover->Stype,0)) + { + // cpp_predefine() will define this again + if (type_struct(rover->Stype) && + rover->Sstruct->Sflags & STRpredef) + { s->Sl = s->Sr = NULL; + symbol_keep(s); + } + else + synerr(EM_multiple_def,p); // already defined + } + } + goto L1; + } + } + ps = (cmp < 0) ? /* if we go down left side */ + &rover->Sl : + &rover->Sr; + } + { + s->Sl = s->Sr = NULL; + *ps = s; + } + } + L1: + symboltable_hydrate(sl,parent); + s = sr; + } +} + +#endif + + +/************************************ + * Hydrate/dehydrate an mptr_t. + */ + +#if HYDRATE +STATIC void mptr_hydrate(mptr_t **pm) +{ mptr_t *m; + + m = (mptr_t *) ph_hydrate(pm); + symbol_hydrate(&m->MPf); + symbol_hydrate(&m->MPparent); +} +#endif + +#if DEHYDRATE +STATIC void mptr_dehydrate(mptr_t **pm) +{ mptr_t *m; + + m = *pm; + if (m && !isdehydrated(m)) + { + ph_dehydrate(pm); +#if DEBUG_XSYMGEN + if (xsym_gen && ph_in_head(m->MPf)) + ph_dehydrate(&m->MPf); + else +#endif + symbol_dehydrate(&m->MPf); + symbol_dehydrate(&m->MPparent); + } +} +#endif + +/************************************ + * Hydrate/dehydrate a baseclass_t. + */ + +#if HYDRATE +STATIC void baseclass_hydrate(baseclass_t **pb) +{ baseclass_t *b; + + assert(pb); + while (isdehydrated(*pb)) + { + b = (baseclass_t *) ph_hydrate(pb); + + ph_hydrate(&b->BCbase); + ph_hydrate(&b->BCpbase); + list_hydrate(&b->BCpublics,(list_free_fp)symbol_hydrate); +#if VBTABLES +#else + symbol_hydrate(&b->param); +#endif + list_hydrate(&b->BCmptrlist,(list_free_fp)mptr_hydrate); + symbol_hydrate(&b->BCvtbl); + Classsym_hydrate(&b->BCparent); + + pb = &b->BCnext; + } +} +#endif + +/********************************** + * Dehydrate a baseclass_t. + */ + +#if DEHYDRATE +STATIC void baseclass_dehydrate(baseclass_t **pb) +{ baseclass_t *b; + + while ((b = *pb) != NULL && !isdehydrated(b)) + { + ph_dehydrate(pb); + +#if DEBUG_XSYMGEN + if (xsym_gen && ph_in_head(b)) + return; +#endif + + ph_dehydrate(&b->BCbase); + ph_dehydrate(&b->BCpbase); + list_dehydrate(&b->BCpublics,(list_free_fp)symbol_dehydrate); +#if VBTABLES +#else + symbol_dehydrate(&b->param); +#endif + list_dehydrate(&b->BCmptrlist,(list_free_fp)mptr_dehydrate); + symbol_dehydrate(&b->BCvtbl); + Classsym_dehydrate(&b->BCparent); + + pb = &b->BCnext; + } +} +#endif + +/*************************** + * Look down baseclass list to find sbase. + * Returns: + * NULL not found + * pointer to baseclass + */ + +baseclass_t *baseclass_find(baseclass_t *bm,Classsym *sbase) +{ + symbol_debug(sbase); + for (; bm; bm = bm->BCnext) + if (bm->BCbase == sbase) + break; + return bm; +} + +baseclass_t *baseclass_find_nest(baseclass_t *bm,Classsym *sbase) +{ + symbol_debug(sbase); + for (; bm; bm = bm->BCnext) + { + if (bm->BCbase == sbase || + baseclass_find_nest(bm->BCbase->Sstruct->Sbase, sbase)) + break; + } + return bm; +} + +/****************************** + * Calculate number of baseclasses in list. + */ + +#if VBTABLES + +int baseclass_nitems(baseclass_t *b) +{ int i; + + for (i = 0; b; b = b->BCnext) + i++; + return i; +} + +#endif + + +/***************************** + * Go through symbol table preparing it to be written to a precompiled + * header. That means removing references to things in the .OBJ file. + */ + +#if SCPP + +void symboltable_clean(symbol *s) +{ + while (s) + { + struct_t *st; + + //printf("clean('%s')\n",s->Sident); + if (config.fulltypes != CVTDB && s->Sxtrnnum && s->Sfl != FLreg) + s->Sxtrnnum = 0; // eliminate debug info type index + switch (s->Sclass) + { + case SCstruct: + s->Stypidx = 0; + st = s->Sstruct; + assert(st); + symboltable_clean(st->Sroot); + //list_apply(&st->Sfldlst,(list_free_fp)symboltable_clean); + break; + + case SCtypedef: + case SCenum: + s->Stypidx = 0; + break; +#if 1 + case SCtemplate: + { template_t *tm = s->Stemplate; + + list_apply(&tm->TMinstances,(list_free_fp)symboltable_clean); + break; + } +#endif + case SCnamespace: + symboltable_clean(s->Snameroot); + break; + + default: + if (s->Sxtrnnum && s->Sfl != FLreg) + s->Sxtrnnum = 0; // eliminate external symbol index + if (tyfunc(s->Stype->Tty)) + { + func_t *f = s->Sfunc; + SYMIDX si; + + debug(assert(f)); + + list_apply(&f->Fsymtree,(list_free_fp)symboltable_clean); + for (si = 0; si < f->Flocsym.top; si++) + symboltable_clean(f->Flocsym.tab[si]); + if (f->Foversym) + symboltable_clean(f->Foversym); + if (f->Fexplicitspec) + symboltable_clean(f->Fexplicitspec); + } + break; + } + if (s->Sl) + symboltable_clean(s->Sl); + if (s->Scover) + symboltable_clean(s->Scover); + s = s->Sr; + } +} + +#endif + +#if SCPP + +/* + * Balance our symbol tree in place. This is nice for precompiled headers, since they + * will typically be written out once, but read in many times. We balance the tree in + * place by traversing the tree inorder and writing the pointers out to an ordered + * list. Once we have a list of symbol pointers, we can create a tree by recursively + * dividing the list, using the midpoint of each division as the new root for that + * subtree. + */ + +struct Balance +{ + unsigned nsyms; + symbol **array; + unsigned index; +}; + +static Balance balance; + +STATIC void count_symbols(symbol *s) +{ + while (s) + { + balance.nsyms++; + switch (s->Sclass) + { + case SCnamespace: + symboltable_balance(&s->Snameroot); + break; + + case SCstruct: + symboltable_balance(&s->Sstruct->Sroot); + break; + } + count_symbols(s->Sl); + s = s->Sr; + } +} + +STATIC void place_in_array(symbol *s) +{ + while (s) + { + place_in_array(s->Sl); + balance.array[balance.index++] = s; + s = s->Sr; + } +} + +/* + * Create a tree in place by subdividing between lo and hi inclusive, using i + * as the root for the tree. When the lo-hi interval is one, we've either + * reached a leaf or an empty node. We subdivide below i by halving the interval + * between i and lo, and using i-1 as our new hi point. A similar subdivision + * is created above i. + */ +STATIC symbol * create_tree(int i, int lo, int hi) +{ + symbol *s = balance.array[i]; + + if (i < lo || i > hi) /* empty node ? */ + return NULL; + + assert((unsigned) i < balance.nsyms); + if (i == lo && i == hi) { /* leaf node ? */ + s->Sl = NULL; + s->Sr = NULL; + return s; + } + + s->Sl = create_tree((i + lo) / 2, lo, i - 1); + s->Sr = create_tree((i + hi + 1) / 2, i + 1, hi); + + return s; +} + +#define METRICS 0 + +#if METRICS +void symbol_table_metrics(void); +#endif + +void symboltable_balance(symbol **ps) +{ + Balance balancesave; +#if METRICS + long ticks; + + dbg_printf("symbol table before balance:\n"); + symbol_table_metrics(); + ticks = clock(); +#endif + balancesave = balance; // so we can nest + balance.nsyms = 0; + count_symbols(*ps); + //dbg_printf("Number of global symbols = %d\n",balance.nsyms); + +#if __INTSIZE == 2 + // Don't balance tree if we get 16 bit overflow + if (balance.nsyms >= (unsigned)(0x10000 / sizeof(symbol *))) + goto Lret; +#endif + + // Use malloc instead of mem because of pagesize limits + balance.array = (symbol **) malloc(balance.nsyms * sizeof(symbol *)); + if (!balance.array) + goto Lret; // no error, just don't balance + + balance.index = 0; + place_in_array(*ps); + + *ps = create_tree(balance.nsyms / 2, 0, balance.nsyms - 1); + + free(balance.array); +#if METRICS + dbg_printf("time to balance: %ld\n", clock() - ticks); + dbg_printf("symbol table after balance:\n"); + symbol_table_metrics(); +#endif +Lret: + balance = balancesave; +} + +#endif + +/***************************************** + * Symbol table search routine for members of structs, given that + * we don't know which struct it is in. + * Give error message if it appears more than once. + * Returns: + * NULL member not found + * symbol* symbol matching member + */ + +#if SCPP + +struct Paramblock // to minimize stack usage in helper function +{ const char *id; // identifier we are looking for + symbol *sm; // where to put result + symbol *s; +}; + +STATIC void membersearchx(struct Paramblock *p,symbol *s) +{ symbol *sm; + list_t sl; + + while (s) + { symbol_debug(s); + + switch (s->Sclass) + { case SCstruct: + for (sl = s->Sstruct->Sfldlst; sl; sl = list_next(sl)) + { sm = list_symbol(sl); + symbol_debug(sm); + if ((sm->Sclass == SCmember || sm->Sclass == SCfield) && + strcmp(p->id,sm->Sident) == 0) + { + if (p->sm && p->sm->Smemoff != sm->Smemoff) + synerr(EM_ambig_member,p->id,s->Sident,p->s->Sident); // ambiguous reference to id + p->s = s; + p->sm = sm; + break; + } + } + break; + } + + if (s->Sl) + membersearchx(p,s->Sl); + s = s->Sr; + } +} + +symbol *symbol_membersearch(const char *id) +{ + list_t sl; + struct Paramblock pb; + Scope *sc; + + pb.id = id; + pb.sm = NULL; + for (sc = scope_end; sc; sc = sc->next) + { + if (sc->sctype & (CPP ? (SCTglobal | SCTlocal) : (SCTglobaltag | SCTtag))) + membersearchx((struct Paramblock *)&pb,(symbol *)sc->root); + } + return pb.sm; +} + +/******************************************* + * Generate debug info for global struct tag symbols. + */ + +STATIC void symbol_gendebuginfox(symbol *s) +{ + for (; s; s = s->Sr) + { + if (s->Sl) + symbol_gendebuginfox(s->Sl); + if (s->Scover) + symbol_gendebuginfox(s->Scover); + switch (s->Sclass) + { + case SCenum: + if (CPP && s->Senum->SEflags & SENnotagname) + break; + goto Lout; + case SCstruct: + if (s->Sstruct->Sflags & STRanonymous) + break; + goto Lout; + case SCtypedef: + Lout: + if (!s->Stypidx) + cv_outsym(s); + break; + } + } +} + +void symbol_gendebuginfo() +{ Scope *sc; + + for (sc = scope_end; sc; sc = sc->next) + { + if (sc->sctype & (SCTglobaltag | SCTglobal)) + symbol_gendebuginfox((symbol *)sc->root); + } +} + +#endif + +#endif /* !SPP */ + diff --git a/backend/tassert.h b/backend/tassert.h new file mode 100644 index 00000000..3e9e8410 --- /dev/null +++ b/backend/tassert.h @@ -0,0 +1,54 @@ +// Copyright (C) 1989-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +//#pragma once +#ifndef TASSERT_H +#define TASSERT_H 1 + +/***************************** + * Define a local assert function. + */ + +#undef assert +#define assert(e) ((e) || (local_assert(__LINE__), 0)) + +#if __clang__ + +void util_assert ( char * , int ) __attribute__((analyzer_noreturn)); + +static void local_assert(int line) +{ + util_assert(__file__,line); + __builtin_unreachable(); +} + +#else + +#if _MSC_VER +__declspec(noreturn) +#endif +void util_assert ( char * , int ); + +static void local_assert(int line) +{ + util_assert(__file__,line); +} + +#if __DMC__ +#pragma noreturn(util_assert) +#pragma noreturn(local_assert) +#endif + +#endif + + +#endif /* TASSERT_H */ diff --git a/backend/ti_achar.c b/backend/ti_achar.c new file mode 100644 index 00000000..f59dc2e7 --- /dev/null +++ b/backend/ti_achar.c @@ -0,0 +1,61 @@ + +// Copyright (c) 2006-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gpl.txt. +// See the included readme.txt for details. + + +#include +#include + +#include "tinfo.h" + + +// char* + +TypeInfo_Achar ti_achar; + +const char* TypeInfo_Achar::toString() +{ + return "char*"; +} + +hash_t TypeInfo_Achar::getHash(void *p) +{ char* s; + hash_t hash = 0; + + for (s = *(char**)p; *s; s++) + { + hash = hash * 11 + *s; + } + return hash; +} + +int TypeInfo_Achar::equals(void *p1, void *p2) +{ + char* s1 = *(char**)p1; + char* s2 = *(char**)p2; + + return strcmp(s1, s2) == 0; +} + +int TypeInfo_Achar::compare(void *p1, void *p2) +{ + char* s1 = *(char**)p1; + char* s2 = *(char**)p2; + + return strcmp(s1, s2); +} + +size_t TypeInfo_Achar::tsize() +{ + return sizeof(char*); +} + +void TypeInfo_Achar::swap(void *p1, void *p2) +{ +} + diff --git a/backend/ti_pvoid.c b/backend/ti_pvoid.c new file mode 100644 index 00000000..205cdbc7 --- /dev/null +++ b/backend/ti_pvoid.c @@ -0,0 +1,54 @@ + +// Copyright (c) 2011-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gpl.txt. +// See the included readme.txt for details. + + +#include + +#include "tinfo.h" + + +// void* + +TypeInfo_Pvoid ti_pvoid; + +const char* TypeInfo_Pvoid::toString() +{ + return "void*"; +} + +hash_t TypeInfo_Pvoid::getHash(void *p) +{ void* s = *(void **)p; + return (hash_t)s; +} + +int TypeInfo_Pvoid::equals(void *p1, void *p2) +{ + void* s1 = *(void**)p1; + void* s2 = *(void**)p2; + + return s1 == s2; +} + +int TypeInfo_Pvoid::compare(void *p1, void *p2) +{ + void* s1 = *(void**)p1; + void* s2 = *(void**)p2; + + return (s1 < s2) ? -1 : ((s1 == s2) ? 0 : 1); +} + +size_t TypeInfo_Pvoid::tsize() +{ + return sizeof(void*); +} + +void TypeInfo_Pvoid::swap(void *p1, void *p2) +{ +} + diff --git a/backend/tinfo.h b/backend/tinfo.h new file mode 100644 index 00000000..45c5a380 --- /dev/null +++ b/backend/tinfo.h @@ -0,0 +1,52 @@ + +// Copyright (c) 2000-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gpl.txt. +// See the included readme.txt for details. + + +#ifndef TYPEINFO_H +#define TYPEINFO_H + +#include + +typedef size_t hash_t; + +struct TypeInfo +{ + virtual const char* toString() = 0; + virtual hash_t getHash(void *p) = 0; + virtual int equals(void *p1, void *p2) = 0; + virtual int compare(void *p1, void *p2) = 0; + virtual size_t tsize() = 0; + virtual void swap(void *p1, void *p2) = 0; +}; + +struct TypeInfo_Achar : TypeInfo +{ + const char* toString(); + hash_t getHash(void *p); + int equals(void *p1, void *p2); + int compare(void *p1, void *p2); + size_t tsize(); + void swap(void *p1, void *p2); +}; + +extern TypeInfo_Achar ti_achar; + +struct TypeInfo_Pvoid : TypeInfo +{ + const char* toString(); + hash_t getHash(void *p); + int equals(void *p1, void *p2); + int compare(void *p1, void *p2); + size_t tsize(); + void swap(void *p1, void *p2); +}; + +extern TypeInfo_Pvoid ti_pvoid; + +#endif diff --git a/backend/token.h b/backend/token.h new file mode 100644 index 00000000..b00d0da2 --- /dev/null +++ b/backend/token.h @@ -0,0 +1,458 @@ +// Copyright (C) 1984-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +/********************************** + * Symbol tokens: + * + * TKstar * TKdot . TKeq = + * TKand & TKlbra [ TKaddass += + * TKmin - TKrbra ] TKminass -= + * TKnot ! TKarrow -> TKmulass *= + * TKcom ~ TKdiv / TKdivass /= + * TKplpl ++ TKmod % TKmodass %= + * TKlpar ( TKxor ^ TKshrass >>= + * TKrpar ) TKor | TKshlass <<= + * TKques ? TKoror || TKandass &= + * TKcolon : TKandand && TKxorass ^= + * TKcomma , TKshl << TKorass |= + * TKmimi -- TKshr >> TKsemi ; + * TKlcur { TKrcur } TKlt < + * TKle <= TKgt > TKge >= + * TKeqeq == TKne != TKadd + + * TKellipsis ... TKcolcol :: TKdollar $ + * + * Other tokens: + * + * TKstring string + * TKfilespec + */ + +//#pragma once +#ifndef TOKEN_H +#define TOKEN_H 1 + +#if !defined(TOKENS_ONLY) || TOKENS_ONLY +// Keyword tokens. Needn't be ascii sorted +typedef unsigned char enum_TK; +enum TK { + TKauto, + TKbreak, + TKcase, + TKchar, + TKconst, + TKcontinue, + TKdefault, + TKdo, + TKdouble, + TKelse, + TKenum, + TKextern, + TKfloat, + TKfor, + TKgoto, + TKif, + TKint, + TKlong, + TKregister, + TKreturn, + TKshort, + TKsigned, + TKsizeof, + TKstatic, + TKstruct, + TKswitch, + TKtypedef, + TKunion, + TKunsigned, + TKvoid, + TKvolatile, + TKwhile, + + // ANSI C99 + TK_Complex, + TK_Imaginary, + TKrestrict, + +//#if CPP + TKbool, + TKcatch, + TKclass, + TKconst_cast, + TKdelete, + TKdynamic_cast, + TKexplicit, + TKfalse, + TKfriend, + TKinline, + TKmutable, + TKnamespace, + TKnew, + TKoperator, + TKoverload, + TKprivate, + TKprotected, + TKpublic, + TKreinterpret_cast, + TKstatic_cast, + TKtemplate, + TKthis, + TKthrow, + TKtrue, + TKtry, + TKtypeid, + TKtypename, + TKusing, + TKvirtual, + TKwchar_t, + TK_typeinfo, + TK_typemask, +//#endif + +#if CPP0X + TKalignof, + TKchar16_t, + TKchar32_t, + TKconstexpr, + TKdecltype, + TKnoexcept, + TKnullptr, + TKstatic_assert, + TKthread_local, +#endif + + TKasm, + TK_inf, + TK_nan, + TK_nans, + TK_i, // imaginary constant i + TK_with, + TK_istype, + TK_cdecl, + TK_fortran, + TK_pascal, + + TK_debug, + TK_in, + TK_out, + TK_body, + TK_invariant, +#if TX86 + TK_Seg16, + TK_System, + TK__emit__, + TK_far, + TK_huge, + TK_near, + + TK_asm, + TK_based, + TK_cs, + TK_declspec, + TK_except, + TK_export, + TK_far16, + TK_fastcall, + TK_finally, + TK_handle, + TK_java, + TK_int64, + TK_interrupt, + TK_leave, + TK_loadds, + TK_real80, + TK_saveregs, + TK_segname, + TK_ss, + TK_stdcall, + TK_syscall, + TK_try, +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + TK_attribute, + TK_extension, + TK_format, + TK_restrict, + TK_bltin_const, +#endif +#else + TKcomp, + TKextended, + TK_handle, + TK_machdl, + TK_pasobj, +//#if CPP + TK__class, + TKinherited, +//#endif +#endif + TK_unaligned, + TKsymbol, // special internal token + +#define KWMAX (TK_unaligned + 1) // number of keywords + + TKcolcol, // :: + TKarrowstar, // ->* + TKdotstar, // .* + + TKstar,TKand,TKmin,TKnot,TKcom,TKplpl,TKlpar,TKrpar,TKques,TKcolon,TKcomma, + TKmimi,TKlcur,TKdot,TKlbra,TKrbra,TKarrow,TKdiv,TKmod,TKxor,TKor,TKoror, + TKandand,TKshl,TKshr,TKrcur,TKeq,TKaddass,TKminass,TKmulass,TKdivass, + TKmodass,TKshrass,TKshlass,TKandass,TKxorass,TKorass,TKsemi, + TKadd,TKellipsis, +#if !TX86 || TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + TKdollar, +#endif + + /* The following relational tokens must be in the same order as the + corresponding operators. + */ + /* == != */ + TKle,TKgt,TKlt,TKge,TKeqeq,TKne, + + /* !<>= <> <>= !> !>= !< !<= !<> */ + TKunord,TKlg,TKleg,TKule,TKul,TKuge,TKug,TKue, + + TKstring, + TKfilespec, /* */ + TKpragma, + TKnum, /* integral number */ + TKreal_f, + TKreal_d, + TKreal_da, + TKreal_ld, + TKident, /* identifier */ + TKeol, /* end of line */ + TKeof, /* end of file */ + TKnone, /* no token */ + TKMAX /* number of tokens */ +}; +#endif + +#if !defined(TOKENS_ONLY) || !TOKENS_ONLY +struct token_t +{ + enum_TK TKval; // what the token is + unsigned char TKflags; // Token flags +#define TKFfree 1 // free the token after it's scanned +#define TKFinherited 2 // keyword INHERITED prior to token +#define TKFpasstr 4 // pascal string + unsigned char TKty; // TYxxxx for TKstring and TKnum + union _TKutok + { + // Scheme for short IDs avoids malloc/frees + struct _ident // TKident + { char *ident; // pointer to identifier + char idtext[4]; // if short identifier + } _idx; + + struct _uts /* TKstring and TKfilespec */ + { + char *string;/* for strings (not null terminated) */ + int lenstr; /* length of string */ + } uts; + symbol *sym; // TKsymbol + int pragma; // TKpragma: PRxxxx, pragma number + // -1 if unrecognized pragma + targ_long Vlong; /* integer when TKnum */ +#if LONGLONG + targ_llong Vllong; +#else +#define Vllong Vlong +#endif + targ_float Vfloat; + targ_double Vdouble; + targ_ldouble Vldouble; + } TKutok; + Srcpos TKsrcpos; // line number from where it was taken + token_t *TKnext; // to create a list of tokens + +#ifdef DEBUG + unsigned short id; +#define IDtoken 0xA745 +#define token_debug(e) assert((e)->id == IDtoken) +#else +#define token_debug(e) +#endif + + void setSymbol(symbol *s); + void print(); +}; + +#define TKstr TKutok.uts.string +#define TKlenstr TKutok.uts.lenstr +#define TKid TKutok._idx.ident +#define TKsym TKutok.sym + +// Use this for fast scans +#define _IDS 1 // start of identifier +#define _ID 2 // identifier +#define _TOK 4 // single character token +#define _EOL 8 // end of line +#define _MUL 0x10 // start of multibyte character sequence +#define _BCS 0x20 // in basic-source-character-set +#define _MTK 0x40 // could be multi-character token +#define _ZFF 0x80 // 0 or 0xFF (must be sign bit) + +#define istok(x) (_chartype[(x) + 1] & _TOK) +#define iseol(x) (_chartype[(x) + 1] & _EOL) +#define isidstart(x) (_chartype[(x) + 1] & _IDS) +#define isidchar(x) (_chartype[(x) + 1] & (_IDS | _ID)) +#define ismulti(x) (_chartype[(x) + 1] & _MUL) +#define isbcs(x) (_chartype[(x) + 1] & _BCS) + +/* from token.c */ +extern int igncomment; +extern char *tok_arg; +extern unsigned argmax; +extern token_t tok; +extern int ininclude; +CEXTERN char tok_ident[]; // identifier +extern unsigned char _chartype[]; +extern token_t *toklist; + +void token_setdbcs(int); +void token_setlocale(const char *); +token_t *token_copy(void); +void token_free(token_t *tl); +void token_hydrate(token_t **ptl); +void token_dehydrate(token_t **ptl); +token_t *token_funcbody(int bFlag); +token_t *token_defarg(void); +void token_funcbody_print(token_t *t); +void token_setlist(token_t *t); +void token_poplist(void); +void token_unget(void); +void token_markfree(token_t *t); +void token_setident(char *); +void token_semi(void); +Srcpos token_linnum(void); +enum_TK token_peek(); + +enum_TK rtoken(int); +#if SPP +#define stoken() rtoken(1) +#else +enum_TK stokenx(void); +inline enum_TK stoken() { return toklist ? stokenx() : rtoken(1); } +#endif + +void token_init(void); +void removext(void); +void __near comment(void); +void __near cppcomment(void); +char *combinestrings(targ_size_t *plen); +char *combinestrings(targ_size_t *plen, tym_t *pty); +void __near inident(void); +void inidentX(char *p); +unsigned comphash(const char *p); +int insertSpace(unsigned char xclast, unsigned char xcnext); +void panic(enum_TK ptok); +void chktok(enum_TK toknum , unsigned errnum); +void chktok(enum_TK toknum , unsigned errnum, const char *str); +void opttok(enum_TK toknum); +bool iswhite(int c); +void token_term(void); + +#define ptoken() rtoken(1) +#define token() rtoken(0) + +#if !MARS +/* from pragma.c */ +//enum_TK ptoken(void); +void pragma_process(); +int __near pragma_search(char *id); +macro_t * __near macfind(void); +void __near listident(void); +void pragma_term(void); +macro_t *defmac(const char *name , const char *text); +int pragma_defined(void); +#endif + +#if SPP && TX86 +#define token_linnum() getlinnum() +#endif + +// listing control +// Listings can be produce via -l and SCpre +// -l expand all characters not if'd out including +// comments +// SCpre list only characters to be compiled +// i.e. exclude comments and # preprocess lines + +#if SPP +#define SCPRE_LISTING_ON() expflag--; assert(expflag >= 0) +#define SCPRE_LISTING_OFF() assert(expflag >= 0); expflag++ +#define EXPANDED_LISTING_ON() expflag--; assert(expflag >= 0) +#define EXPANDED_LISTING_OFF() assert(expflag >= 0); expflag++ +#else +#define SCPRE_LISTING_OFF() +#define SCPRE_LISTING_ON() +#define EXPANDED_LISTING_ON() expflag--; assert(expflag >= 0) +#define EXPANDED_LISTING_OFF() assert(expflag >= 0); expflag++ +#endif + +#define EXPANDING_LISTING() (expflag == 0) +#define NOT_EXPANDING_LISTING() (expflag) +#endif + +/*********************************************** + * This is the token lookahead API, which enables us to + * look an arbitrary number of tokens ahead and then + * be able to 'unget' all of them. + */ + +struct Token_lookahead +{ + int inited; // 1 if initialized + token_t *toks; // list of tokens + token_t **pend; // pointer to end of that list + + void init() + { + toks = NULL; + pend = &toks; + inited = 1; + } + + enum_TK lookahead() + { + #ifdef DEBUG + //assert(inited == 1); + #endif + *pend = token_copy(); + (*pend)->TKflags |= TKFfree; + pend = &(*pend)->TKnext; + return stoken(); + } + + void term() + { +#ifdef DEBUG + //assert(inited == 1); +#endif + inited--; + if (toks) + { + token_unget(); + token_setlist(toks); + stoken(); + } + } + + void discard() + { + inited--; + token_free(toks); + } +}; + + +#endif /* TOKEN_H */ diff --git a/backend/ty.h b/backend/ty.h new file mode 100644 index 00000000..120c7933 --- /dev/null +++ b/backend/ty.h @@ -0,0 +1,377 @@ +// Copyright (C) 1983-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + + +#if __SC__ +#pragma once +#endif + +#ifndef TY_H +#define TY_H 1 + +//#define TYjhandle TYnptr // use for Jupiter handle + +/***************************************** + * Data types. + * (consists of basic type + modifier bits) + */ + +// Basic types. +// casttab[][] in exp2.c depends on the order of this +// typromo[] in cpp.c depends on the order too + +enum TYM +{ + TYbool = 0, + TYchar = 1, + TYschar = 2, // signed char + TYuchar = 3, // unsigned char + TYchar8 = 4, + TYchar16 = 5, + TYshort = 6, + TYwchar_t = 7, + TYushort = 8, // unsigned short + TYenum = 9, // enumeration value + TYint = 0xA, + TYuint = 0xB, // unsigned + TYlong = 0xC, + TYulong = 0xD, // unsigned long + TYdchar = 0xE, // 32 bit Unicode char + TYllong = 0xF, // 64 bit long + TYullong = 0x10, // 64 bit unsigned long + TYfloat = 0x11, // 32 bit real + TYdouble = 0x12, // 64 bit real + + // long double is mapped to either of the following at runtime: + TYdouble_alias = 0x13, // 64 bit real (but distinct for overload purposes) + TYldouble = 0x14, // 80 bit real + + // Add imaginary and complex types for D and C99 + TYifloat = 0x15, + TYidouble = 0x16, + TYildouble = 0x17, + TYcfloat = 0x18, + TYcdouble = 0x19, + TYcldouble = 0x1A, + + TYjhandle = 0x1B, // Jupiter handle type, equals TYnptr except + // that the debug type is different so the + // debugger can distinguish them + TYnullptr = 0x1C, + TYnptr = 0x1D, // data segment relative pointer + TYref = 0x24, // reference to another type + TYvoid = 0x25, + TYstruct = 0x26, // watch tyaggregate() + TYarray = 0x27, // watch tyaggregate() + TYnfunc = 0x28, // near C func + TYnpfunc = 0x2A, // near Cpp func + TYnsfunc = 0x2C, // near stdcall func + TYifunc = 0x2E, // interrupt func + TYptr = 0x33, // generic pointer type + TYmfunc = 0x37, // NT C++ member func + TYjfunc = 0x38, // LINKd D function + TYhfunc = 0x39, // C function with hidden parameter + TYnref = 0x3A, // near reference + + TYcent = 0x3C, // 128 bit signed integer + TYucent = 0x3D, // 128 bit unsigned integer + +#if TARGET_SEGMENTED + TYsptr = 0x1E, // stack segment relative pointer + TYcptr = 0x1F, // code segment relative pointer + TYf16ptr = 0x20, // special OS/2 far16 pointer + TYfptr = 0x21, // far pointer (has segment and offset) + TYhptr = 0x22, // huge pointer (has segment and offset) + TYvptr = 0x23, // __handle pointer (has segment and offset) + TYffunc = 0x29, // far C func + TYfpfunc = 0x2B, // far Cpp func + TYfsfunc = 0x2D, // far stdcall func + TYf16func = 0x34, // _far16 _pascal function + TYnsysfunc = 0x35, // near __syscall func + TYfsysfunc = 0x36, // far __syscall func + TYfref = 0x3B, // far reference +#endif + +#if !MARS + TYmemptr = 0x2F, // pointer to member + TYident = 0x30, // type-argument + TYtemplate = 0x31, // unexpanded class template + TYvtshape = 0x32, // virtual function table +#endif + + // SIMD vector types // D type + TYfloat4 = 0x3E, // float[4] + TYdouble2 = 0x3F, // double[2] + TYschar16 = 0x40, // byte[16] + TYuchar16 = 0x41, // ubyte[16] + TYshort8 = 0x42, // short[8] + TYushort8 = 0x43, // ushort[8] + TYlong4 = 0x44, // int[4] + TYulong4 = 0x45, // uint[4] + TYllong2 = 0x46, // long[2] + TYullong2 = 0x47, // ulong[2] + +#if MARS +#define TYaarray TYnptr +#define TYdelegate (I64 ? TYcent : TYllong) +#define TYdarray (I64 ? TYucent : TYullong) +#endif + + TYMAX = 0x48, +}; + +#define mTYbasic 0xFF /* bit mask for basic types */ +#define tybasic(ty) ((ty) & mTYbasic) + +#if TX86 +// These change depending on memory model +extern int TYptrdiff, TYsize, TYsize_t; + +/* Linkage type */ +#define mTYnear 0x0800 +#if TARGET_SEGMENTED +#define mTYfar 0x1000 +#define mTYcs 0x2000 // in code segment +#endif +#define mTYthread 0x4000 +#define mTYLINK 0x7800 // all linkage bits + +#define mTYloadds 0x08000 +#define mTYexport 0x10000 +#define mTYweak 0x00000 +#define mTYimport 0x20000 +#define mTYnaked 0x40000 +#define mTYMOD 0x78000 // all modifier bits + +#else +#define TYTARG 0x11 +#include "TGty.h" /* Target types */ +#endif + +/* Modifiers to basic types */ + +#ifdef JHANDLE +#define mTYarrayhandle 0x200 +#else +#define mTYarrayhandle 0x0 +#endif +#define mTYconst 0x100 +#define mTYvolatile 0x200 +#define mTYrestrict 0 // BUG: add for C99 +#define mTYmutable 0 // need to add support +#define mTYunaligned 0 // non-zero for PowerPC + +#define mTYimmutable 0x00080000 // immutable data +#define mTYshared 0x00100000 // shared data +#define mTYnothrow 0x00200000 // nothrow function + +#if !MARS +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +#define mTYnoret 0x01000000 // function has no return +#define mTYtransu 0x01000000 // transparent union +#else +#define mTYfar16 0x01000000 +#endif +#define mTYstdcall 0x02000000 +#define mTYfastcall 0x04000000 +#define mTYinterrupt 0x08000000 +#define mTYcdecl 0x10000000 +#define mTYpascal 0x20000000 +#define mTYsyscall 0x40000000 +#define mTYjava 0x80000000 + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +#define mTYTFF 0xFE000000 +#else +#define mTYTFF 0xFF000000 +#endif +#endif + +/* Flags in tytab[] array */ +extern unsigned tytab[]; +#define TYFLptr 1 +#define TYFLreal 2 +#define TYFLintegral 4 +#define TYFLcomplex 8 +#define TYFLimaginary 0x10 +#define TYFLuns 0x20 +#define TYFLmptr 0x40 +#define TYFLfv 0x80 /* TYfptr || TYvptr */ + +#if TX86 +#define TYFLfarfunc 0x100 +#define TYFLpascal 0x200 // callee cleans up stack +#define TYFLrevparam 0x400 // function parameters are reversed +#define TYFLxmmreg 0x10000 // can be put in XMM register +#else +#define TYFLcallstkc 0x100 // callee cleans up stack +#define TYFLrevparam 0x200 // function parameters are reversed +#endif +#define TYFLnullptr 0x800 +#define TYFLshort 0x1000 +#define TYFLaggregate 0x2000 +#define TYFLfunc 0x4000 +#define TYFLref 0x8000 + +/* Groupings of types */ + +#define tyintegral(ty) (tytab[(ty) & 0xFF] & TYFLintegral) + +#define tyarithmetic(ty) (tytab[(ty) & 0xFF] & (TYFLintegral | TYFLreal | TYFLimaginary | TYFLcomplex)) + +#define tyaggregate(ty) (tytab[(ty) & 0xFF] & TYFLaggregate) + +#define tyscalar(ty) (tytab[(ty) & 0xFF] & (TYFLintegral | TYFLreal | TYFLimaginary | TYFLcomplex | TYFLptr | TYFLmptr | TYFLnullptr)) + +#define tyfloating(ty) (tytab[(ty) & 0xFF] & (TYFLreal | TYFLimaginary | TYFLcomplex)) + +#define tyimaginary(ty) (tytab[(ty) & 0xFF] & TYFLimaginary) + +#define tycomplex(ty) (tytab[(ty) & 0xFF] & TYFLcomplex) + +#define tyreal(ty) (tytab[(ty) & 0xFF] & TYFLreal) + +// Fits into 64 bit register +#define ty64reg(ty) (tytab[(ty) & 0xFF] & (TYFLintegral | TYFLptr) && tysize(ty) <= NPTRSIZE) + +// Can go in XMM floating point register +#define tyxmmreg(ty) (tytab[(ty) & 0xFF] & TYFLxmmreg) + +// Is a vector type +#define tyvector(ty) (tybasic(ty) >= TYfloat4 && tybasic(ty) <= TYullong2) + +#ifndef tyshort +/* Types that are chars or shorts */ +#define tyshort(ty) (tytab[(ty) & 0xFF] & TYFLshort) +#endif + +/* Detect TYlong or TYulong */ +#ifndef tylong +#define tylong(ty) (tybasic(ty) == TYlong || tybasic(ty) == TYulong) +#endif + +/* Use to detect a pointer type */ +#ifndef typtr +#define typtr(ty) (tytab[(ty) & 0xFF] & TYFLptr) +#endif + +/* Use to detect a reference type */ +#ifndef tyref +#define tyref(ty) (tytab[(ty) & 0xFF] & TYFLref) +#endif + +/* Use to detect a pointer type or a member pointer */ +#ifndef tymptr +#define tymptr(ty) (tytab[(ty) & 0xFF] & (TYFLptr | TYFLmptr)) +#endif + +// Use to detect a nullptr type or a member pointer +#ifndef tynullptr +#define tynullptr(ty) (tytab[(ty) & 0xFF] & TYFLnullptr) +#endif + +/* Detect TYfptr or TYvptr */ +#ifndef tyfv +#define tyfv(ty) (tytab[(ty) & 0xFF] & TYFLfv) +#endif + +/* Array to give the size in bytes of a type, -1 means error */ +extern signed char tysize[]; +extern signed char tyalignsize[]; + +// Give size of type +#define tysize(ty) tysize[(ty) & 0xFF] +#define tyalignsize(ty) tyalignsize[(ty) & 0xFF] + +/* All data types that fit in exactly 8 bits */ +#ifndef tybyte +#define tybyte(ty) (tysize(ty) == 1) +#endif + +/* Types that fit into a single machine register */ +#ifndef tyreg +#define tyreg(ty) (tysize(ty) <= REGSIZE) +#endif + +/* Detect function type */ +#ifndef tyfunc +#define tyfunc(ty) (tytab[(ty) & 0xFF] & TYFLfunc) +#endif + +/* Detect function type where parameters are pushed left to right */ +#ifndef tyrevfunc +#define tyrevfunc(ty) (tytab[(ty) & 0xFF] & TYFLrevparam) +#endif + +/* Detect unsigned types */ +#ifndef tyuns +#define tyuns(ty) (tytab[(ty) & 0xFF] & (TYFLuns | TYFLptr)) +#endif + +/* Target dependent info */ +#if TX86 +#define TYoffset TYuint /* offset to an address */ + +/* Detect cpp function type (callee cleans up stack) */ +#define typfunc(ty) (tytab[(ty) & 0xFF] & TYFLpascal) + +#else +/* Detect cpp function type (callee cleans up stack) */ +#ifndef typfunc +#define typfunc(ty) (tytab[(ty) & 0xFF] & TYFLcallstkc) +#endif +#endif + +/* Array to convert a type to its unsigned equivalent */ +extern const tym_t tytouns[]; +#ifndef touns +#define touns(ty) (tytouns[(ty) & 0xFF]) +#endif + +/* Determine if TYffunc or TYfpfunc (a far function) */ +#ifndef tyfarfunc +#define tyfarfunc(ty) (tytab[(ty) & 0xFF] & TYFLfarfunc) +#endif + +// Determine if parameter can go in register for TYjfunc +#ifndef tyjparam +#define tyjparam(ty) (tysize(ty) <= NPTRSIZE && !tyfloating(ty) && tybasic(ty) != TYstruct && tybasic(ty) != TYarray) +#endif + +/* Determine relaxed type */ +#ifndef tyrelax +#define tyrelax(ty) (_tyrelax[tybasic(ty)]) +#endif + +/* Array to give the 'relaxed' type for relaxed type checking */ +extern unsigned char _tyrelax[]; +#define type_relax (config.flags3 & CFG3relax) // !=0 if relaxed type checking +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +#define type_semirelax (config.flags3 & CFG3semirelax) // !=0 if semi-relaxed type checking +#else +#define type_semirelax type_relax +#endif + +/* Determine functionally equivalent type */ +extern unsigned char tyequiv[]; + +/* Give an ascii string for a type */ +extern const char *tystring[]; + +#if TX86 +/* Debugger value for type */ +extern unsigned char dttab[]; +extern unsigned short dttab4[]; +#endif + +#endif /* TY_H */ + diff --git a/backend/type.c b/backend/type.c new file mode 100644 index 00000000..071ad7dc --- /dev/null +++ b/backend/type.c @@ -0,0 +1,1480 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if !SPP + +#include +#include +#include + +#include "cc.h" +#include "global.h" +#include "type.h" +#include "el.h" + +#if SCPP +#include "parser.h" +#endif + +#undef MEM_PH_MALLOC +#define MEM_PH_MALLOC mem_fmalloc + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +static type *type_list = NULL; // free list of types +static param_t *param_list = NULL; // free list of params + +#ifdef DEBUG +static int type_num,type_max; /* gather statistics on # of types */ +#endif + +typep_t tstypes[TYMAX]; +typep_t tsptr2types[TYMAX]; + +typep_t tstrace,tsclib,tsjlib,tsdlib, + tslogical; +typep_t tspvoid,tspcvoid; +typep_t tsptrdiff, tssize; + +/******************************* + * Compute size of type in bytes. + */ + +targ_size_t type_size(type *t) +{ targ_size_t s; + unsigned long u; + tym_t tyb; + + type_debug(t); + tyb = tybasic(t->Tty); +#ifdef DEBUG + if (tyb >= TYMAX) + /*type_print(t),*/ + dbg_printf("tyb = x%lx\n",(long)tyb); +#endif + assert(tyb < TYMAX); + s = tysize[tyb]; + if (s == (targ_size_t) -1) + { + switch (tyb) + { + // in case program plays games with function pointers +#if TARGET_SEGMENTED + case TYffunc: + case TYfpfunc: + case TYfsfunc: + case TYf16func: +#endif + case TYhfunc: + case TYnfunc: /* in case program plays games with function pointers */ + case TYnpfunc: + case TYnsfunc: + case TYifunc: + case TYjfunc: +#if SCPP + if (ANSI) + synerr(EM_unknown_size,"function"); /* size of function is not known */ +#endif + s = 1; + break; + case TYarray: + if (t->Tflags & TFsizeunknown) + { +#if SCPP + synerr(EM_unknown_size,"array"); /* size of array is unknown */ +#endif + t->Tflags &= ~TFsizeunknown; + } + if (t->Tflags & TFvla) + { + s = tysize[pointertype]; + break; + } + s = type_size(t->Tnext); + u = t->Tdim * (unsigned long) s; +#if TX86 && SCPP + type_chksize(u); +#endif + s = u; + break; + case TYstruct: + t = t->Ttag->Stype; /* find main instance */ + /* (for const struct X) */ + if (t->Tflags & TFsizeunknown) + { +#if SCPP + template_instantiate_forward(t->Ttag); + if (t->Tflags & TFsizeunknown) + synerr(EM_unknown_size,t->Tty & TYstruct ? prettyident(t->Ttag) : "struct"); + t->Tflags &= ~TFsizeunknown; +#endif + } + assert(t->Ttag); + s = t->Ttag->Sstruct->Sstructsize; + break; +#if SCPP + case TYenum: + if (t->Ttag->Senum->SEflags & SENforward) + synerr(EM_unknown_size, prettyident(t->Ttag)); + s = type_size(t->Tnext); + break; +#endif + case TYvoid: +#if SCPP && TARGET_WINDOS // GNUC allows it, so we will, too + synerr(EM_void_novalue); // voids have no value +#endif + s = 1; + break; +#if SCPP + case TYref: + case TYmemptr: + case TYvtshape: + s = tysize(tym_conv(t)); + break; + + case TYident: + synerr(EM_unknown_size, t->Tident); + s = 1; + break; +#endif +#if MARS + case TYref: + s = tysize(TYnptr); + break; +#endif + default: +#ifdef DEBUG + WRTYxx(t->Tty); +#endif + assert(0); + } + } + return s; +} + +/******************************** + * Return the size of a type for alignment purposes. + */ + +unsigned type_alignsize(type *t) +{ targ_size_t sz; + +L1: + type_debug(t); + + sz = tyalignsize(t->Tty); + if (sz == (targ_size_t)-1) + { + switch (tybasic(t->Tty)) + { + case TYarray: + if (t->Tflags & TFsizeunknown) + goto err1; + t = t->Tnext; + goto L1; + case TYstruct: + t = t->Ttag->Stype; // find main instance + // (for const struct X) + if (t->Tflags & TFsizeunknown) + goto err1; + sz = t->Ttag->Sstruct->Salignsize; + if (sz > t->Ttag->Sstruct->Sstructalign) + sz = t->Ttag->Sstruct->Sstructalign; + break; + + case TYldouble: + assert(0); + + default: + err1: // let type_size() handle error messages + sz = type_size(t); + break; + } + } + + //printf("type_alignsize() = %d\n", sz); + return sz; +} + +/***************************** + * Compute the size of parameters for function call. + * Used for stdcall name mangling. + * Note that hidden parameters do not contribute to size. + */ + +targ_size_t type_paramsize(type *t) +{ + targ_size_t sz = 0; + if (tyfunc(t->Tty)) + { + for (param_t *p = t->Tparamtypes; p; p = p->Pnext) + { + size_t n = type_size(p->Ptype); + n = align(REGSIZE,n); // align to REGSIZE boundary + sz += n; + } + } + return sz; +} + +/***************************** + * Create a type & initialize it. + * Input: + * ty = TYxxxx + * Returns: + * pointer to newly created type. + */ + +type *type_alloc(tym_t ty) +{ type *t; + static type tzero; + +#if TARGET_SEGMENTED + assert(tybasic(ty) != TYtemplate); +#endif + if (type_list) + { t = type_list; + type_list = t->Tnext; + } + else + t = (type *) mem_fmalloc(sizeof(type)); + tzero.Tty = ty; + *t = tzero; +#if SRCPOS_4TYPES + if (PARSER && config.fulltypes) + t->Tsrcpos = getlinnum(); +#endif +#ifdef DEBUG + t->id = IDtype; + type_num++; + if (type_num > type_max) + type_max = type_num; +#endif + //dbg_printf("type_alloc() = %p ",t); WRTYxx(t->Tty); dbg_printf("\n"); + //if (t == (type*)0xB6B744) *(char*)0=0; + return t; +} + +/************************************* + * Allocate a TYtemplate. + */ + +#if !MARS +type *type_alloc_template(symbol *s) +{ type *t; + + t = (type *) mem_fcalloc(sizeof(typetemp_t)); + t->Tty = TYtemplate; + if (s->Stemplate->TMprimary) + s = s->Stemplate->TMprimary; + ((typetemp_t *)t)->Tsym = s; +#if SRCPOS_4TYPES + if (PARSER && config.fulltypes) + t->Tsrcpos = getlinnum(); +#endif +#ifdef DEBUG + t->id = IDtype; + type_num++; + if (type_num > type_max) + type_max = type_num; + //dbg_printf("Alloc'ing template type %p ",t); WRTYxx(t->Tty); dbg_printf("\n"); +#endif + return t; +} +#endif + +/***************************** + * Fake a type & initialize it. + * Input: + * ty = TYxxxx + * Returns: + * pointer to newly created type. + */ + +type *type_fake(tym_t ty) +{ type *t; + +#if MARS + assert(ty != TYstruct); +#endif + t = type_alloc(ty); + if (typtr(ty) || tyfunc(ty)) + { t->Tnext = type_alloc(TYvoid); /* fake with pointer to void */ + t->Tnext->Tcount = 1; + } + return t; +} + +/***************************** + * Allocate a type of ty with a Tnext of tn. + */ + +type *type_allocn(tym_t ty,type *tn) +{ type *t; + + //printf("type_allocn(ty = x%x, tn = %p)\n", ty, tn); + assert(tn); + type_debug(tn); + t = type_alloc(ty); + t->Tnext = tn; + tn->Tcount++; + //printf("\tt = %p\n", t); + return t; +} + +/****************************** + * Allocate a TYmemptr type. + */ + +#if !MARS +type *type_allocmemptr(Classsym *stag,type *tn) +{ type *t; + + symbol_debug(stag); + assert(stag->Sclass == SCstruct || tybasic(stag->Stype->Tty) == TYident); + t = type_allocn(TYmemptr,tn); + t->Ttag = stag; + //printf("type_allocmemptr() = %p\n", t); + //type_print(t); + return t; +} +#endif + +/***************************** + * Free up data type. + */ + +void type_free(type *t) +{ type *tn; + tym_t ty; + + while (t) + { + //dbg_printf("type_free(%p, Tcount = %d)\n", t, t->Tcount); + type_debug(t); + assert((int)t->Tcount != -1); + if (--t->Tcount) /* if usage count doesn't go to 0 */ + break; + ty = tybasic(t->Tty); + if (tyfunc(ty)) + { param_free(&t->Tparamtypes); + list_free(&t->Texcspec, (list_free_fp)type_free); + } +#if !MARS + else if (ty == TYtemplate) + param_free(&t->Tparamtypes); + else if (ty == TYident) + MEM_PH_FREE(t->Tident); +#endif + else if (t->Tflags & TFvla && t->Tel) + el_free(t->Tel); +#if SCPP + else if (t->Talternate && typtr(ty)) + type_free(t->Talternate); +#endif +#if MARS + else if (t->Tkey && typtr(ty)) + type_free(t->Tkey); +#endif +#ifdef DEBUG + type_num--; + //dbg_printf("Free'ing type %p ",t); WRTYxx(t->Tty); dbg_printf("\n"); + t->id = 0; /* no longer a valid type */ +#endif + tn = t->Tnext; + t->Tnext = type_list; + type_list = t; /* link into free list */ + t = tn; + } +} + +#ifdef STATS +/* count number of free types available on type list */ +type_count_free() + { + type *t; + int count; + + for(t=type_list;t;t=t->Tnext) + count++; + dbg_printf("types on free list %d with max of %d\n",count,type_max); + } +#endif + +/********************************** + * Initialize type package. + */ + +STATIC type * __near type_allocbasic(tym_t ty) +{ type *t; + + t = type_alloc(ty); + t->Tmangle = mTYman_c; + t->Tcount = 1; /* so it is not inadvertantly free'd */ + return t; +} + +void type_init() +{ + tsbool = type_allocbasic(TYbool); + tswchar_t = type_allocbasic(TYwchar_t); + tsdchar = type_allocbasic(TYdchar); + tsvoid = type_allocbasic(TYvoid); + tsnullptr = type_allocbasic(TYnullptr); + tschar16 = type_allocbasic(TYchar16); + tsuchar = type_allocbasic(TYuchar); + tsschar = type_allocbasic(TYschar); + tschar = type_allocbasic(TYchar); + tsshort = type_allocbasic(TYshort); + tsushort = type_allocbasic(TYushort); + tsint = type_allocbasic(TYint); + tsuns = type_allocbasic(TYuint); + tslong = type_allocbasic(TYlong); + tsulong = type_allocbasic(TYulong); + tsllong = type_allocbasic(TYllong); + tsullong = type_allocbasic(TYullong); + tsfloat = type_allocbasic(TYfloat); + tsdouble = type_allocbasic(TYdouble); + tsreal64 = type_allocbasic(TYdouble_alias); + tsldouble = type_allocbasic(TYldouble); + tsifloat = type_allocbasic(TYifloat); + tsidouble = type_allocbasic(TYidouble); + tsildouble = type_allocbasic(TYildouble); + tscfloat = type_allocbasic(TYcfloat); + tscdouble = type_allocbasic(TYcdouble); + tscldouble = type_allocbasic(TYcldouble); + + if (I64) + { + TYptrdiff = TYllong; + TYsize = TYullong; + tsptrdiff = tsllong; + tssize = tsullong; + } + else + { + TYptrdiff = TYint; + TYsize = TYuint; + tsptrdiff = tsint; + tssize = tsuns; + } + + // Type of trace function +#if TARGET_SEGMENTED + tstrace = type_fake(I16 ? TYffunc : TYnfunc); +#else + tstrace = type_fake(TYnfunc); +#endif + tstrace->Tmangle = mTYman_c; + tstrace->Tcount++; + + chartype = (config.flags3 & CFG3ju) ? tsuchar : tschar; + + // Type of far library function +#if TARGET_SEGMENTED + tsclib = type_fake(LARGECODE ? TYfpfunc : TYnpfunc); +#else + tsclib = type_fake(TYnpfunc); +#endif + tsclib->Tmangle = mTYman_c; + tsclib->Tcount++; + + tspvoid = type_allocn(pointertype,tsvoid); + tspvoid->Tmangle = mTYman_c; + tspvoid->Tcount++; + + // Type of far library function + tsjlib = type_fake(TYjfunc); + tsjlib->Tmangle = mTYman_c; + tsjlib->Tcount++; + + tsdlib = tsjlib; + +#if SCPP + tspcvoid = type_alloc(mTYconst | TYvoid); + tspcvoid = newpointer(tspcvoid); + tspcvoid->Tmangle = mTYman_c; + tspcvoid->Tcount++; +#endif + + // Type of logical expression + tslogical = (config.flags4 & CFG4bool) ? tsbool : tsint; + + for (int i = 0; i < TYMAX; i++) + { + if (tstypes[i]) + { tsptr2types[i] = type_allocn(pointertype,tstypes[i]); + tsptr2types[i]->Tcount++; + } + } +} + +/********************************** + * Free type_list. + */ + +#if TERMCODE +void type_term() +{ type *tn; + param_t *pn; + int i; + + for (i = 0; i < arraysize(tstypes); i++) + { type *t = tsptr2types[i]; + + if (t) + { assert(!(t->Tty & (mTYconst | mTYvolatile | mTYimmutable | mTYshared))); + assert(!(t->Tflags)); + assert(!(t->Tmangle)); + type_free(t); + } + type_free(tstypes[i]); + } + + type_free(tsclib); + type_free(tspvoid); + type_free(tspcvoid); + type_free(tsjlib); + type_free(tstrace); + + while (type_list) + { tn = type_list->Tnext; + mem_ffree(type_list); + type_list = tn; + } + + while (param_list) + { pn = param_list->Pnext; + mem_ffree(param_list); + param_list = pn; + } + +#ifdef DEBUG + dbg_printf("Max # of types = %d\n",type_max); + if (type_num != 0) + dbg_printf("type_num = %d\n",type_num); +/* assert(type_num == 0);*/ +#endif +} +#endif // TERMCODE + +/******************************* + * Type type information. + */ + +/************************** + * Make copy of a type. + */ + +type *type_copy(type *t) +{ type *tn; + param_t *p; + + type_debug(t); +#if !MARS + if (tybasic(t->Tty) == TYtemplate) + { + tn = type_alloc_template(((typetemp_t *)t)->Tsym); + } + else +#endif + tn = type_alloc(t->Tty); + *tn = *t; + switch (tybasic(tn->Tty)) + { +#if !MARS + case TYtemplate: + ((typetemp_t *)tn)->Tsym = ((typetemp_t *)t)->Tsym; + goto L1; + + case TYident: + tn->Tident = (char *)MEM_PH_STRDUP(t->Tident); + break; +#endif + + case TYarray: + if (tn->Tflags & TFvla) + tn->Tel = el_copytree(tn->Tel); + break; + + default: + if (tyfunc(tn->Tty)) + { + L1: + tn->Tparamtypes = NULL; + for (p = t->Tparamtypes; p; p = p->Pnext) + { param_t *pn; + + pn = param_append_type(&tn->Tparamtypes,p->Ptype); + if (p->Pident) + { + pn->Pident = (char *)MEM_PH_STRDUP(p->Pident); + } + assert(!p->Pelem); + } + } +#if SCPP + else if (tn->Talternate && typtr(tn->Tty)) + tn->Talternate->Tcount++; +#endif +#if MARS + else if (tn->Tkey && typtr(tn->Tty)) + tn->Tkey->Tcount++; +#endif + break; + } + if (tn->Tnext) + { type_debug(tn->Tnext); + tn->Tnext->Tcount++; + } + tn->Tcount = 0; + return tn; +} + +/************************************ + */ + +#if SCPP + +elem *type_vla_fix(type **pt) +{ + type *t; + elem *e = NULL; + + for (t = *pt; t; t = t->Tnext) + { + type_debug(t); + if (tybasic(t->Tty) == TYarray && t->Tflags & TFvla && t->Tel) + { symbol *s; + elem *ec; + + s = symbol_genauto(tsuns); + ec = el_var(s); + ec = el_bint(OPeq, tsuns, ec, t->Tel); + e = el_combine(e, ec); + t->Tel = el_var(s); + } + } + return e; +} + +#endif + +/**************************** + * Modify the tym_t field of a type. + */ + +type *type_setty(type **pt,long newty) +{ type *t; + + t = *pt; + type_debug(t); + if ((tym_t)newty != t->Tty) + { if (t->Tcount > 1) /* if other people pointing at t */ + { type *tn; + + tn = type_copy(t); + tn->Tcount++; + type_free(t); + t = tn; + *pt = t; + } + t->Tty = newty; + } + return t; +} + +/****************************** + * Set type field of some object to t. + */ + +type *type_settype(type **pt, type *t) +{ + if (t) + { type_debug(t); + t->Tcount++; + } + type_free(*pt); + return *pt = t; +} + +/**************************** + * Modify the Tmangle field of a type. + */ + +type *type_setmangle(type **pt,mangle_t mangle) +{ type *t; + + t = *pt; + type_debug(t); + if (mangle != type_mangle(t)) + { + if (t->Tcount > 1) // if other people pointing at t + { type *tn; + + tn = type_copy(t); + tn->Tcount++; + type_free(t); + t = tn; + *pt = t; + } + t->Tmangle = mangle; + } + return t; +} + +/****************************** + * Set/clear const and volatile bits in *pt according to the settings + * in cv. + */ + +type *type_setcv(type **pt,tym_t cv) +{ unsigned long ty; + + type_debug(*pt); + ty = (*pt)->Tty & ~(mTYconst | mTYvolatile | mTYimmutable | mTYshared); + return type_setty(pt,ty | (cv & (mTYconst | mTYvolatile | mTYimmutable | mTYshared))); +} + +/***************************** + * Set dimension of array. + */ + +type *type_setdim(type **pt,targ_size_t dim) +{ type *t = *pt; + + type_debug(t); + if (t->Tcount > 1) /* if other people pointing at t */ + { type *tn; + + tn = type_copy(t); + tn->Tcount++; + type_free(t); + t = tn; + } + t->Tflags &= ~TFsizeunknown; /* we have determined its size */ + t->Tdim = dim; /* index of array */ + return *pt = t; +} + + +/***************************** + * Create a 'dependent' version of type t. + */ + +type *type_setdependent(type *t) +{ + type_debug(t); + if (t->Tcount > 0 && /* if other people pointing at t */ + !(t->Tflags & TFdependent)) + { + t = type_copy(t); + } + t->Tflags |= TFdependent; + return t; +} + +/************************************ + * Determine if type t is a dependent type. + */ + +int type_isdependent(type *t) +{ + Symbol *stempl; + type *tstart; + + //printf("type_isdependent(%p)\n", t); + //type_print(t); + for (tstart = t; t; t = t->Tnext) + { + type_debug(t); + if (t->Tflags & TFdependent) + goto Lisdependent; + if (tyfunc(t->Tty) +#if TARGET_SEGMENTED + || tybasic(t->Tty) == TYtemplate +#endif + ) + { + for (param_t *p = t->Tparamtypes; p; p = p->Pnext) + { + if (p->Ptype && type_isdependent(p->Ptype)) + goto Lisdependent; + if (p->Pelem && el_isdependent(p->Pelem)) + goto Lisdependent; + } + } + else if (type_struct(t) && + (stempl = t->Ttag->Sstruct->Stempsym) != NULL) + { + for (param_t *p = t->Ttag->Sstruct->Sarglist; p; p = p->Pnext) + { + if (p->Ptype && type_isdependent(p->Ptype)) + goto Lisdependent; + if (p->Pelem && el_isdependent(p->Pelem)) + goto Lisdependent; + } + } + } + //printf("\tis not dependent\n"); + return 0; + +Lisdependent: + //printf("\tis dependent\n"); + // Dependence on a dependent type makes this type dependent as well + tstart->Tflags |= TFdependent; + return 1; +} + + +/******************************* + * Recursively check if type u is embedded in type t. + * Returns: + * != 0 if embedded + */ + +int type_embed(type *t,type *u) +{ param_t *p; + + for (; t; t = t->Tnext) + { + type_debug(t); + if (t == u) + return 1; + if (tyfunc(t->Tty)) + { + for (p = t->Tparamtypes; p; p = p->Pnext) + if (type_embed(p->Ptype,u)) + return 1; + } + } + return 0; +} + + +/*********************************** + * Determine if type is a VLA. + */ + +int type_isvla(type *t) +{ + while (t) + { + if (tybasic(t->Tty) != TYarray) + break; + if (t->Tflags & TFvla) + return 1; + t = t->Tnext; + } + return 0; +} + +/************************************* + * Determine if type can be passed in a register. + */ + +int type_jparam(type *t) +{ + targ_size_t sz; + type_debug(t); + return tyjparam(t->Tty) || + + ((tybasic(t->Tty) == TYstruct || tybasic(t->Tty) == TYarray) && + (sz = type_size(t)) <= NPTRSIZE && + (sz == 1 || sz == 2 || sz == 4 || sz == 8)) || + + tybasic(t->Tty) == TYfloat4; +} + + +/********************************** + * Pretty-print a type. + */ + +#ifdef DEBUG + +void type_print(type *t) +{ + type_debug(t); + dbg_printf("Tty="); WRTYxx(t->Tty); + dbg_printf(" Tmangle=%d",t->Tmangle); + dbg_printf(" Tflags=x%x",t->Tflags); + dbg_printf(" Tcount=%d",t->Tcount); + if (!(t->Tflags & TFsizeunknown) && + tybasic(t->Tty) != TYvoid && +#if !MARS + tybasic(t->Tty) != TYident && + tybasic(t->Tty) != TYtemplate && +#endif + tybasic(t->Tty) != TYmfunc && + tybasic(t->Tty) != TYarray) + dbg_printf(" Tsize=%lld",(long long)type_size(t)); + dbg_printf(" Tnext=%p",t->Tnext); + switch (tybasic(t->Tty)) + { case TYstruct: +#if !MARS + case TYmemptr: +#endif + dbg_printf(" Ttag=%p,'%s'",t->Ttag,t->Ttag->Sident); + //dbg_printf(" Sfldlst=%p",t->Ttag->Sstruct->Sfldlst); + break; + case TYarray: + dbg_printf(" Tdim=%ld",(long)t->Tdim); + break; +#if !MARS + case TYident: + dbg_printf(" Tident='%s'",t->Tident); + break; + case TYtemplate: + dbg_printf(" Tsym='%s'",((typetemp_t *)t)->Tsym->Sident); + { param_t *p; + int i; + + i = 1; + for (p = t->Tparamtypes; p; p = p->Pnext) + { dbg_printf("\nTP%d (%p): ",i++,p); + fflush(stdout); + +dbg_printf("Pident=%p,Ptype=%p,Pelem=%p,Pnext=%p ",p->Pident,p->Ptype,p->Pelem,p->Pnext); + param_debug(p); + if (p->Pident) + printf("'%s' ", p->Pident); + if (p->Ptype) + type_print(p->Ptype); + if (p->Pelem) + elem_print(p->Pelem); + } + } + break; +#endif + default: + if (tyfunc(t->Tty)) + { param_t *p; + int i; + + i = 1; + for (p = t->Tparamtypes; p; p = p->Pnext) + { dbg_printf("\nP%d (%p): ",i++,p); + fflush(stdout); + +dbg_printf("Pident=%p,Ptype=%p,Pelem=%p,Pnext=%p ",p->Pident,p->Ptype,p->Pelem,p->Pnext); + param_debug(p); + if (p->Pident) + printf("'%s' ", p->Pident); + type_print(p->Ptype); + } + } + break; + } + dbg_printf("\n"); + if (t->Tnext) type_print(t->Tnext); +} + +/******************************* + * Pretty-print a param_t + */ + +void param_t::print() +{ + dbg_printf("Pident=%p,Ptype=%p,Pelem=%p,Psym=%p,Pnext=%p\n",Pident,Ptype,Pelem,Psym,Pnext); + if (Pident) + dbg_printf("\tPident = '%s'\n", Pident); + if (Ptype) + { dbg_printf("\tPtype =\n"); + type_print(Ptype); + } + if (Pelem) + { dbg_printf("\tPelem =\n"); + elem_print(Pelem); + } + if (Pdeftype) + { dbg_printf("\tPdeftype =\n"); + type_print(Pdeftype); + } + if (Psym) + { dbg_printf("\tPsym = '%s'\n", Psym->Sident); + } + if (Pptpl) + { dbg_printf("\tPptpl = %p\n", Pptpl); + } +} + +void param_t::print_list() +{ + for (param_t *p = this; p; p = p->Pnext) + p->print(); +} + +#endif /* DEBUG */ + +/********************************** + * Hydrate a type. + */ + +#if HYDRATE +void type_hydrate(type **pt) +{ + type *t; + + assert(pt); + while (isdehydrated(*pt)) + { + t = (type *) ph_hydrate(pt); + type_debug(t); + switch (tybasic(t->Tty)) + { + case TYstruct: + case TYenum: + case TYmemptr: + case TYvtshape: + // Cannot assume symbol is hydrated, because entire HX file + // may not have been hydrated. + Classsym_hydrate(&t->Ttag); + symbol_debug(t->Ttag); + break; + case TYident: + ph_hydrate(&t->Tident); + break; + case TYtemplate: + symbol_hydrate(&((typetemp_t *)t)->Tsym); + param_hydrate(&t->Tparamtypes); + break; + case TYarray: + if (t->Tflags & TFvla) + el_hydrate(&t->Tel); + break; + default: + if (tyfunc(t->Tty)) + { param_hydrate(&t->Tparamtypes); + list_hydrate(&t->Texcspec, (list_free_fp)type_hydrate); + } +#if SCPP + else if (t->Talternate && typtr(t->Tty)) + type_hydrate(&t->Talternate); +#endif +#if MARS + else if (t->Tkey && typtr(t->Tty)) + type_hydrate(&t->Tkey); +#endif + break; + } + pt = &t->Tnext; + } +} +#endif + +/********************************** + * Dehydrate a type. + */ + +#if DEHYDRATE +void type_dehydrate(type **pt) +{ + type *t; + + while ((t = *pt) != NULL && !isdehydrated(t)) + { + ph_dehydrate(pt); +#if DEBUG_XSYMGEN + /* don't dehydrate types in HEAD when creating XSYM */ + if (xsym_gen && (t->Tflags & TFhydrated)) + return; +#endif + type_debug(t); + switch (tybasic(t->Tty)) + { + case TYstruct: + case TYenum: + case TYmemptr: + case TYvtshape: + Classsym_dehydrate(&t->Ttag); + break; + case TYident: + ph_dehydrate(&t->Tident); + break; + case TYtemplate: + symbol_dehydrate(&((typetemp_t *)t)->Tsym); + param_dehydrate(&t->Tparamtypes); + break; + case TYarray: + if (t->Tflags & TFvla) + el_dehydrate(&t->Tel); + break; + default: + if (tyfunc(t->Tty)) + { param_dehydrate(&t->Tparamtypes); + list_dehydrate(&t->Texcspec, (list_free_fp)type_dehydrate); + } +#if SCPP + else if (t->Talternate && typtr(t->Tty)) + type_dehydrate(&t->Talternate); +#endif +#if MARS + else if (t->Tkey && typtr(t->Tty)) + type_dehydrate(&t->Tkey); +#endif + break; + } + pt = &t->Tnext; + } +} +#endif + +/**************************** + * Allocate a param_t. + */ + +param_t *param_calloc() +{ + static param_t pzero; + param_t *p; + +#if !TX86 + debug_assert(PARSER); +#endif + if (param_list) + { + p = param_list; + param_list = p->Pnext; + } + else + { + p = (param_t *) mem_fmalloc(sizeof(param_t)); + } + *p = pzero; +#ifdef DEBUG + p->id = IDparam; +#endif + return p; +} + +/*************************** + * Allocate a param_t of type t, and append it to parameter list. + */ + +param_t *param_append_type(param_t **pp,type *t) +{ param_t *p; + + p = param_calloc(); + while (*pp) + { param_debug(*pp); + pp = &((*pp)->Pnext); /* find end of list */ + } + *pp = p; /* append p to list */ + type_debug(t); + p->Ptype = t; + t->Tcount++; + return p; +} + +/************************ + * Version of param_free() suitable for list_free(). + */ + +void param_free_l(param_t *p) +{ + param_free(&p); +} + +/*********************** + * Free parameter list. + * Output: + * paramlst = NULL + */ + +void param_free(param_t **pparamlst) +{ param_t *p,*pn; + +#if !TX86 + debug_assert(PARSER); +#endif + for (p = *pparamlst; p; p = pn) + { param_debug(p); + pn = p->Pnext; + type_free(p->Ptype); + mem_free(p->Pident); + el_free(p->Pelem); + type_free(p->Pdeftype); + if (p->Pptpl) + param_free(&p->Pptpl); +#ifdef DEBUG + p->id = 0; +#endif + p->Pnext = param_list; + param_list = p; + } + *pparamlst = NULL; +} + +/*********************************** + * Compute number of parameters + */ + +unsigned param_t::length() +{ + unsigned nparams = 0; + param_t *p; + + for (p = this; p; p = p->Pnext) + nparams++; + return nparams; +} + +/************************************* + * Create template-argument-list blank from + * template-parameter-list + * Input: + * ptali initial template-argument-list + */ + +param_t *param_t::createTal(param_t *ptali) +{ +#if SCPP + param_t *ptalistart = ptali; +#endif + param_t *ptal = NULL; + param_t **pp = &ptal; + param_t *p; + + for (p = this; p; p = p->Pnext) + { + *pp = param_calloc(); + if (p->Pident) + { + // Should find a way to just point rather than dup + (*pp)->Pident = (char *)MEM_PH_STRDUP(p->Pident); + } + if (ptali) + { + if (ptali->Ptype) + { (*pp)->Ptype = ptali->Ptype; + (*pp)->Ptype->Tcount++; + } + if (ptali->Pelem) + { + elem *e = el_copytree(ptali->Pelem); +#if SCPP + if (p->Ptype) + { type *t = p->Ptype; + t = template_tyident(t, ptalistart, this, 1); + e = poptelem3(typechk(e, t)); + type_free(t); + } +#endif + (*pp)->Pelem = e; + } + (*pp)->Psym = ptali->Psym; + (*pp)->Pflags = ptali->Pflags; + assert(!ptali->Pptpl); + ptali = ptali->Pnext; + } + pp = &(*pp)->Pnext; + } + return ptal; +} + +/********************************** + * Look for Pident matching id + */ + +param_t *param_t::search(char *id) +{ param_t *p; + + for (p = this; p; p = p->Pnext) + { + if (p->Pident && strcmp(p->Pident, id) == 0) + break; + } + return p; +} + +/********************************** + * Look for Pident matching id + */ + +int param_t::searchn(char *id) +{ param_t *p; + int n = 0; + + for (p = this; p; p = p->Pnext) + { + if (p->Pident && strcmp(p->Pident, id) == 0) + return n; + n++; + } + return -1; +} + +/************************************* + * Search for member, create symbol as needed. + * Used for symbol tables for VLA's such as: + * void func(int n, int a[n]); + */ + +symbol *param_search(const char *name, param_t **pp) +{ symbol *s = NULL; + param_t *p; + + p = (*pp)->search((char *)name); + if (p) + { + s = p->Psym; + if (!s) + { + s = symbol_calloc(p->Pident); + s->Sclass = SCparameter; + s->Stype = p->Ptype; + s->Stype->Tcount++; +#if SOURCE_4PARAMS + s->Ssrcpos = p->Psrcpos; +#endif + p->Psym = s; + } + } + return s; +} + +/********************************** + * Hydrate/dehydrate a type. + */ + +#if HYDRATE +void param_hydrate(param_t **pp) +{ + param_t *p; + + assert(pp); + if (isdehydrated(*pp)) + { while (*pp) + { assert(isdehydrated(*pp)); + p = (param_t *) ph_hydrate(pp); +#if SOURCE_4PARAMS + p->Psrcpos.Sfilnum += File_Hydrate_Num; /* file number relative header build */ +#endif + param_debug(p); + + type_hydrate(&p->Ptype); + if (p->Ptype) + type_debug(p->Ptype); + ph_hydrate(&p->Pident); + if (CPP) + { + el_hydrate(&p->Pelem); + if (p->Pelem) + elem_debug(p->Pelem); + type_hydrate(&p->Pdeftype); + if (p->Pptpl) + param_hydrate(&p->Pptpl); + if (p->Psym) + symbol_hydrate(&p->Psym); + if (p->PelemToken) + token_hydrate(&p->PelemToken); + } + + pp = &p->Pnext; + } + } +} +#endif + +#if DEHYDRATE +void param_dehydrate(param_t **pp) +{ + param_t *p; + + assert(pp); + while ((p = *pp) != NULL && !isdehydrated(p)) + { param_debug(p); + + ph_dehydrate(pp); + if (p->Ptype && !isdehydrated(p->Ptype)) + type_debug(p->Ptype); + type_dehydrate(&p->Ptype); + ph_dehydrate(&p->Pident); + if (CPP) + { + el_dehydrate(&p->Pelem); + type_dehydrate(&p->Pdeftype); + if (p->Pptpl) + param_dehydrate(&p->Pptpl); + if (p->Psym) + symbol_dehydrate(&p->Psym); + if (p->PelemToken) + token_dehydrate(&p->PelemToken); + } + pp = &p->Pnext; + } +} +#endif + +#if MARS + +int typematch(type *t1, type *t2, int relax); + +// Return TRUE if type lists match. +static int paramlstmatch(param_t *p1,param_t *p2) +{ + return p1 == p2 || + p1 && p2 && typematch(p1->Ptype,p2->Ptype,0) && + paramlstmatch(p1->Pnext,p2->Pnext) + ; +} + +/************************************************* + * A cheap version of exp2.typematch() and exp2.paramlstmatch(), + * so that we can get cpp_mangle() to work for MARS. + * It's less complex because it doesn't do templates and + * can rely on strict typechecking. + * Returns: + * !=0 if types match. + */ + +int typematch(type *t1,type *t2,int relax) +{ tym_t t1ty, t2ty; + tym_t tym; + + tym = ~(mTYimport | mTYnaked); + + return t1 == t2 || + t1 && t2 && + + ( + /* ignore name mangling */ + (t1ty = (t1->Tty & tym)) == (t2ty = (t2->Tty & tym)) + ) + && + + (tybasic(t1ty) != TYarray || t1->Tdim == t2->Tdim || + t1->Tflags & TFsizeunknown || t2->Tflags & TFsizeunknown) + && + + (tybasic(t1ty) != TYstruct + && tybasic(t1ty) != TYenum +#if !MARS + && tybasic(t1ty) != TYmemptr +#endif + || t1->Ttag == t2->Ttag) + && + + typematch(t1->Tnext,t2->Tnext, 0) + && + + (!tyfunc(t1ty) || + ((t1->Tflags & TFfixed) == (t2->Tflags & TFfixed) && + paramlstmatch(t1->Tparamtypes,t2->Tparamtypes) )) + ; +} + +#endif + +#endif /* !SPP */ diff --git a/backend/type.h b/backend/type.h new file mode 100644 index 00000000..8f92566d --- /dev/null +++ b/backend/type.h @@ -0,0 +1,209 @@ +// Copyright (C) 1985-1994 by Symantec +// Copyright (C) 2000-2009 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#if __SC__ +#pragma once +#endif + +#ifndef __TYPE_H +#define __TYPE_H + +#include + +typedef unsigned char mangle_t; + +#define mTYman_c 1 // C mangling +#define mTYman_cpp 2 // C++ mangling +#define mTYman_pas 3 // Pascal mangling +#define mTYman_for 4 // FORTRAN mangling +#define mTYman_sys 5 // _syscall mangling +#define mTYman_std 6 // _stdcall mangling +#define mTYman_d 7 // D mangling + +/********************************* + * Data type. + */ + +#define list_type(tl) ((struct TYPE *) list_ptr(tl)) + +struct TYPE +{ +#ifdef DEBUG + unsigned short id; +#define IDtype 0x1234 +#define type_debug(t) assert((t)->id == IDtype) +#else +#define type_debug(t) +#endif + + tym_t Tty; /* mask (TYxxx) */ + unsigned short Tflags; // TFxxxxx + + mangle_t Tmangle; // name mangling +// Return name mangling of type +#define type_mangle(t) ((t)->Tmangle) + + unsigned Tcount; // # pointing to this type + struct TYPE *Tnext; // next in list + // TYenum: gives base type + union + { + targ_size_t Tdim; // TYarray: # of elements in array + struct elem *Tel; // TFvla: gives dimension (NULL if '*') + struct PARAM *Tparamtypes; // TYfunc, TYtemplate: types of function parameters + struct Classsym *Ttag; // TYstruct,TYmemptr: tag symbol + // TYenum,TYvtshape: tag symbol + char *Tident; // TYident: identifier +#if SCPP + struct TYPE *Talternate; // typtr: type of parameter before converting +#endif +#if MARS + struct TYPE *Tkey; // typtr: key type for associative arrays +#endif + }; + list_t Texcspec; // tyfunc(): list of types of exception specification +#if 0 + unsigned short Tstabidx; // Index into stab types +#endif +#if SOURCE_4TYPES + Srcpos Tsrcpos; /* position of type definition */ +#endif +#if HTOD + Symbol *Ttypedef; // if this type came from a typedef, this is + // the typedef symbol +#endif +}; + +typedef struct TYPETEMP +{ struct TYPE Ttype; + + /* Tsym should really be part of a derived class, as we only + allocate room for it if TYtemplate + */ + Symbol *Tsym; // primary class template symbol +} typetemp_t; + +/* Values for Tflags: */ +#define TFprototype 1 /* if this function is prototyped */ +#define TFfixed 2 /* if prototype has a fixed # of parameters */ +#define TFforward 8 // TYstruct: if forward reference of tag name +#define TFsizeunknown 0x10 // TYstruct,TYarray: if size of type is unknown + // TYmptr: the Stag is TYident type +#define TFfuncret 0x20 // C++,tyfunc(): overload based on function return value +#define TFfuncparam 0x20 // TYarray: top level function parameter +#define TFstatic 0x40 // TYarray: static dimension +#define TFvla 0x80 // TYarray: variable length array +#define TFemptyexc 0x100 // tyfunc(): empty exception specification + +// C +#define TFgenerated 4 // if we generated the prototype ourselves + +// CPP +#define TFdependent 4 // template dependent type + +#if DEHYDRATE +#define TFhydrated 0x20 // type data already hydrated +#endif + +/* Return !=0 if function type has a variable number of arguments */ +#define variadic(t) (((t)->Tflags & (TFprototype | TFfixed)) == TFprototype) + +/* Data */ + +typedef type *typep_t; + +extern typep_t tstypes[TYMAX]; +extern typep_t tsptr2types[TYMAX]; + +#define tsbool tstypes[TYbool] +#define tschar tstypes[TYchar] +#define tsschar tstypes[TYschar] +#define tsuchar tstypes[TYuchar] +#define tschar16 tstypes[TYchar16] +#define tsshort tstypes[TYshort] +#define tsushort tstypes[TYushort] +#define tswchar_t tstypes[TYwchar_t] +#define tsint tstypes[TYint] +#define tsuns tstypes[TYuint] +#define tslong tstypes[TYlong] +#define tsulong tstypes[TYulong] +#define tsdchar tstypes[TYdchar] +#define tsllong tstypes[TYllong] +#define tsullong tstypes[TYullong] +#define tsfloat tstypes[TYfloat] +#define tsdouble tstypes[TYdouble] +#define tsreal64 tstypes[TYdouble_alias] +#define tsldouble tstypes[TYldouble] +#define tsvoid tstypes[TYvoid] + +#define tsifloat tstypes[TYifloat] +#define tsidouble tstypes[TYidouble] +#define tsildouble tstypes[TYildouble] +#define tscfloat tstypes[TYcfloat] +#define tscdouble tstypes[TYcdouble] +#define tscldouble tstypes[TYcldouble] + +#define tsnullptr tstypes[TYnullptr] + +extern typep_t tslogical; +extern typep_t chartype; +extern typep_t tsclib; +extern typep_t tsdlib; +extern typep_t tspvoid,tspcvoid; +extern typep_t tsptrdiff, tssize; +extern typep_t tstrace; + +#define tserr tsint /* error type */ + +// Return !=0 if type is a struct, class or union +#define type_struct(t) (tybasic((t)->Tty) == TYstruct) + +/* Functions */ +void type_print(type *t); +void type_free(type *); +void type_init(void); +void type_term(void); +type *type_copy(type *); +elem *type_vla_fix(type **pt); +type *type_setdim(type **,targ_size_t); +type *type_setdependent(type *t); +int type_isdependent(type *t); +type *type_copy(type *); +void type_hydrate(type **); +void type_dehydrate(type **); + +targ_size_t type_size(type *); +unsigned type_alignsize(type *); +targ_size_t type_paramsize(type *t); +type *type_alloc(tym_t); +type *type_alloc_template(symbol *s); +type *type_allocn(tym_t,type *tn); +type *type_allocmemptr(Classsym *stag,type *tn); +type *type_fake(tym_t); +type *type_setty(type **,long); +type *type_settype(type **pt, type *t); +type *type_setmangle(type **pt,mangle_t mangle); +type *type_setcv(type **pt,tym_t cv); +int type_embed(type *t,type *u); +int type_isvla(type *t); +int type_jparam(type *t); + +param_t *param_calloc(void); +param_t *param_append_type(param_t **,type *); +void param_free_l(param_t *); +void param_free(param_t **); +symbol *param_search(const char *name, param_t **pp); +void param_hydrate(param_t **); +void param_dehydrate(param_t **); +int typematch(type *t1, type *t2, int relax); + +#endif diff --git a/backend/var.c b/backend/var.c new file mode 100644 index 00000000..b4f81f99 --- /dev/null +++ b/backend/var.c @@ -0,0 +1,235 @@ +// Copyright (C) 1985-1998 by Symantec +// Copyright (C) 2000-2010 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * or /dm/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +/* Global variables for PARSER */ + +#include +#include + +#include "cc.h" +#include "global.h" +#include "oper.h" +#include "type.h" +#include "go.h" +#include "ty.h" + +#include "optab.c" +#include "tytab.c" + +#if __SC__ && _MSDOS +#if __INTSIZE == 4 +unsigned __cdecl _stack = 100000; // set default stack size +#else +unsigned __cdecl _stack = 60000; // set default stack size +#endif +#endif + +/* Global flags: + */ + +char PARSER; // indicate we're in the parser +char OPTIMIZER; // indicate we're in the optimizer +int structalign; /* alignment for members of structures */ +char dbcs; // current double byte character set + +int TYptrdiff = TYint; +int TYsize = TYuint; +int TYsize_t = TYuint; + +#ifdef DEBUG +char debuga,debugb,debugc,debugd,debuge,debugf,debugr,debugs,debugt,debugu,debugw,debugx,debugy; +#endif + +#if !MARS +linkage_t linkage; +int linkage_spec = 0; /* using the default */ + +/* Function types */ +/* LINK_MAXDIM = C,C++,Pascal,FORTRAN,syscall,stdcall,Jupiter */ +#if MEMMODELS == 1 +tym_t functypetab[LINK_MAXDIM] = +{ +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + TYnfunc, + TYnpfunc, + TYnpfunc, + TYnfunc, +#endif +}; +#else +tym_t functypetab[LINK_MAXDIM][MEMMODELS] = +{ + TYnfunc, TYffunc, TYnfunc, TYffunc, TYffunc, +#if VBTABLES + TYnfunc, TYffunc, TYnfunc, TYffunc, TYffunc, +#else + TYnpfunc, TYfpfunc, TYnpfunc, TYfpfunc, TYfpfunc, +#endif + TYnpfunc, TYfpfunc, TYnpfunc, TYfpfunc, TYfpfunc, + TYnpfunc, TYfpfunc, TYnpfunc, TYfpfunc, TYfpfunc, + TYnfunc, TYffunc, TYnfunc, TYffunc, TYffunc, + TYnsfunc, TYfsfunc, TYnsfunc, TYfsfunc, TYfsfunc, + TYjfunc, TYfpfunc, TYnpfunc, TYfpfunc, TYfpfunc, +}; +#endif + +/* Function mangling */ +/* LINK_MAXDIM = C,C++,Pascal,FORTRAN,syscall,stdcall */ +mangle_t funcmangletab[LINK_MAXDIM] = +{ + mTYman_c, + mTYman_cpp, + mTYman_pas, + mTYman_for, + mTYman_sys, + mTYman_std, + mTYman_d, +}; + +/* Name mangling for global variables */ +mangle_t varmangletab[LINK_MAXDIM] = +{ + mTYman_c, +#if NEWMANGLE + mTYman_cpp, +#else + mTYman_c, +#endif + mTYman_pas,mTYman_for,mTYman_sys,mTYman_std,mTYman_d +}; +#endif + +targ_size_t dsout = 0; /* # of bytes actually output to data */ + /* segment, used to pad for alignment */ + +/* File variables: */ + +char *argv0; // argv[0] (program name) +FILE *fdep = NULL; // dependency file stream pointer +FILE *flst = NULL; // list file stream pointer +FILE *fin = NULL; // input file +#if SPP +FILE *fout; +#endif +#if HTOD +char *fdmodulename = NULL; +FILE *fdmodule = NULL; +#endif +char *foutdir = NULL, // directory to place output files in + *finname = NULL, + *foutname = NULL, + *fsymname = NULL, + *fphreadname = NULL, + *ftdbname = NULL, + *fdepname = NULL, + *flstname = NULL; /* the filename strings */ + +list_t pathlist; /* include paths */ +list_t headers; /* pre-include files */ + +/* Data from lexical analyzer: */ + +unsigned idhash = 0; // hash value of identifier +int xc = ' '; // character last read + +/* Data for pragma processor: + */ + +int colnumber = 0; /* current column number */ + +/* Other variables: */ + +int level = 0; /* declaration level */ + /* 0: top level */ + /* 1: function parameter declarations */ + /* 2: function local declarations */ + /* 3+: compound statement decls */ + +param_t *paramlst = NULL; /* function parameter list */ +tym_t pointertype = TYnptr; /* default data pointer type */ + +/************************ + * Bit masks + */ + +const unsigned mask[32] = + {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,0x8000, + 0x10000,0x20000,0x40000,0x80000,0x100000,0x200000,0x400000,0x800000, + 0x1000000,0x2000000,0x4000000,0x8000000, + 0x10000000,0x20000000,0x40000000,0x80000000}; + +#if 0 +const unsigned long maskl[32] = + {1,2,4,8,0x10,0x20,0x40,0x80, + 0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000,0x8000, + 0x10000,0x20000,0x40000,0x80000,0x100000,0x200000,0x400000,0x800000, + 0x1000000,0x2000000,0x4000000,0x8000000, + 0x10000000,0x20000000,0x40000000,0x80000000}; +#endif + +/* From util.c */ + +/***************************** + * SCxxxx types. + */ + +#if !SPP + +char sytab[SCMAX] = +{ + #define X(a,b) b, + ENUMSCMAC + #undef X +}; + +#endif /* !SPP */ +volatile int controlc_saw; /* a control C was seen */ +symtab_t globsym; /* global symbol table */ +Pstate pstate; // parser state +Cstate cstate; // compiler state + +/* From go.c */ +mftype mfoptim = 0; // mask of optimizations to perform + +unsigned changes; // # of optimizations performed +struct DN *defnod = NULL; // array of definition elems + +elem **expnod = NULL; /* array of expression elems */ +block **expblk = NULL; /* parallel array of block pointers */ + +unsigned + maxblks = 0, /* array max for all block stuff */ + /* dfoblks <= numblks <= maxblks */ + numcse, /* number of common subexpressions */ + deftop = 0, /* # of entries in defnod[] */ + exptop = 0; /* top of expnod[] */ + +vec_t defkill = NULL, /* vector of AEs killed by an ambiguous */ + /* definition */ + starkill = NULL, /* vector of AEs killed by a definition */ + /* of something that somebody could be */ + /* pointing to */ + vptrkill = NULL; /* vector of AEs killed by an access */ + /* to a vptr */ + +/* From debug.c */ +#if DEBUG +const char *regstring[32] = {"AX","CX","DX","BX","SP","BP","SI","DI", + "R8","R9","R10","R11","R12","R13","R14","R15", + "XMM0","XMM1","XMM2","XMM3","XMM4","XMM5","XMM6","XMM7", + "ES","PSW","STACK","ST0","ST01","NOREG","RMload","RMstore"}; +#endif + +/* From nwc.c */ + +type *chartype; /* default 'char' type */ + diff --git a/backend/xmm.h b/backend/xmm.h new file mode 100644 index 00000000..5dcd10c1 --- /dev/null +++ b/backend/xmm.h @@ -0,0 +1,314 @@ +// XMM opcodes + +enum +{ + ADDSS = 0xF30F58, + ADDSD = 0xF20F58, + ADDPS = 0x000F58, + ADDPD = 0x660F58, + PADDB = 0x660FFC, + PADDW = 0x660FFD, + PADDD = 0x660FFE, + PADDQ = 0x660FD4, + + SUBSS = 0xF30F5C, + SUBSD = 0xF20F5C, + SUBPS = 0x000F5C, + SUBPD = 0x660F5C, + PSUBB = 0x660FF8, + PSUBW = 0x660FF9, + PSUBD = 0x660FFA, + PSUBQ = 0x660FFB, + + MULSS = 0xF30F59, + MULSD = 0xF20F59, + MULPS = 0x000F59, + MULPD = 0x660F59, + PMULLW = 0x660FD5, + + DIVSS = 0xF30F5E, + DIVSD = 0xF20F5E, + DIVPS = 0x000F5E, + DIVPD = 0x660F5E, + + PAND = 0x660FDB, + POR = 0x660FEB, + + UCOMISS = 0x000F2E, + UCOMISD = 0x660F2E, + + XORPS = 0x000F57, + XORPD = 0x660F57, + + // Use STO and LOD instead of MOV to distinguish the direction + STOSS = 0xF30F11, // MOVSS + STOSD = 0xF20F11, + STOAPS = 0x000F29, + STOAPD = 0x660F29, + STODQA = 0x660F7F, + STOD = 0x660F7E, + STOQ = 0x660FD6, + + LODSS = 0xF30F10, // MOVSS + LODSD = 0xF20F10, + LODAPS = 0x000F28, + LODAPD = 0x660F28, + LODDQA = 0x660F6F, + LODD = 0x660F6E, + LODQ = 0xF30F7E, + + LODDQU = 0xF30F6F, + STODQU = 0xF30F7F, + MOVDQ2Q = 0xF20FD6, + MOVHLPS = 0x0F12, + LODHPD = 0x660F16, + STOHPD = 0x660F17, + LODHPS = 0x0F16, + STOHPS = 0x0F17, + MOVLHPS = 0x0F16, + LODLPD = 0x660F12, + STOLPD = 0x660F13, + LODLPS = 0x0F12, + STOLPS = 0x0F13, + MOVMSKPD = 0x660F50, + MOVMSKPS = 0x0F50, + MOVNTDQ = 0x660FE7, + MOVNTI = 0x0FC3, + MOVNTPD = 0x660F2B, + MOVNTPS = 0x0F2B, + MOVNTQ = 0x0FE7, + MOVQ2DQ = 0xF30FD6, + LODUPD = 0x660F10, + STOUPD = 0x660F11, + LODUPS = 0x0F10, + STOUPS = 0x0F11, + + PACKSSDW = 0x660F6B, + PACKSSWB = 0x660F63, + PACKUSWB = 0x660F67, + PADDSB = 0x660FEC, + PADDSW = 0x660FED, + PADDUSB = 0x660FDC, + PADDUSW = 0x660FDD, + PANDN = 0x660FDF, + PCMPEQB = 0x660F74, + PCMPEQD = 0x660F76, + PCMPEQW = 0x660F75, + PCMPGTB = 0x660F64, + PCMPGTD = 0x660F66, + PCMPGTW = 0x660F65, + PMADDWD = 0x660FF5, + PSLLW = 0x660FF1, + PSLLD = 0x660FF2, + PSLLQ = 0x660FF3, + PSRAW = 0x660FE1, + PSRAD = 0x660FE2, + PSRLW = 0x660FD1, + PSRLD = 0x660FD2, + PSRLQ = 0x660FD3, + PSUBSB = 0x660FE8, + PSUBSW = 0x660FE9, + PSUBUSB = 0x660FD8, + PSUBUSW = 0x660FD9, + PUNPCKHBW = 0x660F68, + PUNPCKHDQ = 0x660F6A, + PUNPCKHWD = 0x660F69, + PUNPCKLBW = 0x660F60, + PUNPCKLDQ = 0x660F62, + PUNPCKLWD = 0x660F61, + PXOR = 0x660FEF, + ANDPD = 0x660F54, + ANDPS = 0x0F54, + ANDNPD = 0x660F55, + ANDNPS = 0x0F55, + CMPPS = 0x0FC2, + CMPPD = 0x660FC2, + CMPSD = 0xF20FC2, + CMPSS = 0xF30FC2, + COMISD = 0x660F2F, + COMISS = 0x0F2F, + CVTDQ2PD = 0xF30FE6, + CVTDQ2PS = 0x0F5B, + CVTPD2DQ = 0xF20FE6, + CVTPD2PI = 0x660F2D, + CVTPD2PS = 0x660F5A, + CVTPI2PD = 0x660F2A, + CVTPI2PS = 0x0F2A, + CVTPS2DQ = 0x660F5B, + CVTPS2PD = 0x0F5A, + CVTPS2PI = 0x0F2D, + CVTSD2SI = 0xF20F2D, + CVTSD2SS = 0xF20F5A, + CVTSI2SD = 0xF20F2A, + CVTSI2SS = 0xF30F2A, + CVTSS2SD = 0xF30F5A, + CVTSS2SI = 0xF30F2D, + CVTTPD2PI = 0x660F2C, + CVTTPD2DQ = 0x660FE6, + CVTTPS2DQ = 0xF30F5B, + CVTTPS2PI = 0x0F2C, + CVTTSD2SI = 0xF20F2C, + CVTTSS2SI = 0xF30F2C, + MASKMOVDQU = 0x660FF7, + MASKMOVQ = 0x0FF7, + MAXPD = 0x660F5F, + MAXPS = 0x0F5F, + MAXSD = 0xF20F5F, + MAXSS = 0xF30F5F, + MINPD = 0x660F5D, + MINPS = 0x0F5D, + MINSD = 0xF20F5D, + MINSS = 0xF30F5D, + ORPD = 0x660F56, + ORPS = 0x0F56, + PAVGB = 0x660FE0, + PAVGW = 0x660FE3, + PMAXSW = 0x660FEE, + PINSRW = 0x660FC4, + PMAXUB = 0x660FDE, + PMINSW = 0x660FEA, + PMINUB = 0x660FDA, + PMOVMSKB = 0x660FD7, + PMULHUW = 0x660FE4, + PMULHW = 0x660FE5, + PMULUDQ = 0x660FF4, + PSADBW = 0x660FF6, + PUNPCKHQDQ = 0x660F6D, + PUNPCKLQDQ = 0x660F6C, + RCPPS = 0x0F53, + RCPSS = 0xF30F53, + RSQRTPS = 0x0F52, + RSQRTSS = 0xF30F52, + SQRTPD = 0x660F51, + SHUFPD = 0x660FC6, + SHUFPS = 0x0FC6, + SQRTPS = 0x0F51, + SQRTSD = 0xF20F51, + SQRTSS = 0xF30F51, + UNPCKHPD = 0x660F15, + UNPCKHPS = 0x0F15, + UNPCKLPD = 0x660F14, + UNPCKLPS = 0x0F14, + + PSHUFD = 0x660F70, + PSHUFHW = 0xF30F70, + PSHUFLW = 0xF20F70, + PSHUFW = 0x0F70, + PSLLDQ = 0x660F73, + PSRLDQ = 0x660F73, + + PREFETCH = 0x0F18, + +// SSE3 Pentium 4 (Prescott) + + ADDSUBPD = 0x660FD0, + ADDSUBPS = 0xF20FD0, + HADDPD = 0x660F7C, + HADDPS = 0xF20F7C, + HSUBPD = 0x660F7D, + HSUBPS = 0xF20F7D, + MOVDDUP = 0xF20F12, + MOVSHDUP = 0xF30F16, + MOVSLDUP = 0xF30F12, + LDDQU = 0xF20FF0, + MONITOR = 0x0F01C8, + MWAIT = 0x0F01C9, + +// SSSE3 + PALIGNR = 0x660F3A0F, + PHADDD = 0x660F3802, + PHADDW = 0x660F3801, + PHADDSW = 0x660F3803, + PABSB = 0x660F381C, + PABSD = 0x660F381E, + PABSW = 0x660F381D, + PSIGNB = 0x660F3808, + PSIGND = 0x660F380A, + PSIGNW = 0x660F3809, + PSHUFB = 0x660F3800, + PMADDUBSW = 0x660F3804, + PMULHRSW = 0x660F380B, + PHSUBD = 0x660F3806, + PHSUBW = 0x660F3805, + PHSUBSW = 0x660F3807, + +// SSE4.1 + + BLENDPD = 0x660F3A0D, + BLENDPS = 0x660F3A0C, + BLENDVPD = 0x660F3815, + BLENDVPS = 0x660F3814, + DPPD = 0x660F3A41, + DPPS = 0x660F3A40, + EXTRACTPS = 0x660F3A17, + INSERTPS = 0x660F3A21, + MPSADBW = 0x660F3A42, + PBLENDVB = 0x660F3810, + PBLENDW = 0x660F3A0E, + PEXTRD = 0x660F3A16, + PEXTRQ = 0x660F3A16, + PINSRB = 0x660F3A20, + PINSRD = 0x660F3A22, + PINSRQ = 0x660F3A22, + + MOVNTDQA = 0x660F382A, + PACKUSDW = 0x660F382B, + PCMPEQQ = 0x660F3829, + PEXTRB = 0x660F3A14, + PHMINPOSUW = 0x660F3841, + PMAXSB = 0x660F383C, + PMAXSD = 0x660F383D, + PMAXUD = 0x660F383F, + PMAXUW = 0x660F383E, + PMINSB = 0x660F3838, + PMINSD = 0x660F3839, + PMINUD = 0x660F383B, + PMINUW = 0x660F383A, + PMOVSXBW = 0x660F3820, + PMOVSXBD = 0x660F3821, + PMOVSXBQ = 0x660F3822, + PMOVSXWD = 0x660F3823, + PMOVSXWQ = 0x660F3824, + PMOVSXDQ = 0x660F3825, + PMOVZXBW = 0x660F3830, + PMOVZXBD = 0x660F3831, + PMOVZXBQ = 0x660F3832, + PMOVZXWD = 0x660F3833, + PMOVZXWQ = 0x660F3834, + PMOVZXDQ = 0x660F3835, + PMULDQ = 0x660F3828, + PMULLD = 0x660F3840, + PTEST = 0x660F3817, + + ROUNDPD = 0x660F3A09, + ROUNDPS = 0x660F3A08, + ROUNDSD = 0x660F3A0B, + ROUNDSS = 0x660F3A0A, + +// SSE4.2 + PCMPESTRI = 0x660F3A61, + PCMPESTRM = 0x660F3A60, + PCMPISTRI = 0x660F3A63, + PCMPISTRM = 0x660F3A62, + PCMPGTQ = 0x660F3837, + // CRC32 + +// SSE4a (AMD only) + // EXTRQ,INSERTQ,MOVNTSD,MOVNTSS + +// POPCNT and LZCNT (have their own CPUID bits) + POPCNT = 0xF30FB8, + // LZCNT + +// AVX + XGETBV = 0x0F01D0, + XSETBV = 0x0F01D1, + +// AES + AESENC = 0x660F38DC, + AESENCLAST = 0x660F38DD, + AESDEC = 0x660F38DE, + AESDECLAST = 0x660F38DF, + AESIMC = 0x660F38DB, + AESKEYGENASSIST = 0x660F3ADF, +}; diff --git a/backendlicense.txt b/backendlicense.txt new file mode 100644 index 00000000..bdb69f49 --- /dev/null +++ b/backendlicense.txt @@ -0,0 +1,42 @@ + +The Software is not generally available software. It has not undergone +testing and may contain errors. The Software was not designed to operate +after December 31, 1999. It may be incomplete and it may not function +properly. No support or maintenance is provided with this Software. Do +not install or distribute the Software if +you are not accustomed to using or distributing experimental software. +Do not use this software for life critical applications, or applications +that could cause significant harm or property damage. + +Digital Mars licenses the Software to you on an "AS IS" basis, without +warranty of any kind. DIGITAL MARS AND SYMANTEC HEREBY EXPRESSLY DISCLAIM +ALL WARRANTIES AND CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, +NONINFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. You are solely +responsible for determining the appropriateness of using this Software and +assume all risks associated with the use of this Software, including but not +limited to the risks of program errors, damage +to or loss of data, programs or equipment, unavailability or interruption of +operations and third party claims. You agree to defend, indemnify and hold +Digital Mars and Symantec, its subsidiaries, affiliates, directors, officers, +employees and agents harmless from all claims or demands made against them +(and any related losses, damages, expenses +and costs) arising out of your use of the Software. DIGITAL MARS AND SYMANTEC +WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, INCIDENTAL, OR +INDIRECT DAMAGES OR FOR ANY ECONOMIC CONSEQUENTIAL DAMAGES (INCLUDING +LOST PROFITS OR SAVINGS), EVEN IF DIGITAL MARS OR SYMANTEC HAS BEEN ADVISED +OF THE POSSIBILITY OF SUCH DAMAGES. +Digital Mars and Symantec will not be liable for the loss of, or damage to, +your records or data, the records or +data of any third party, or any damages claimed by you based on a third party +claim. + +If you send any messages to Digital Mars, on either the Digital Mars +newsgroups, the Digital Mars mailing list, or via email, you agree not +to make any claims of intellectual +property rights over the contents of those messages. + +The Software is copyrighted and comes with a single user license, +and may not be redistributed. If you wish to obtain a redistribution license, +please contact Digital Mars. + diff --git a/builtin.c b/builtin.c new file mode 100644 index 00000000..4cfee71f --- /dev/null +++ b/builtin.c @@ -0,0 +1,234 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include + +#if __FreeBSD__ +extern "C" +{ + long double sinl(long double); + long double cosl(long double); + long double tanl(long double); + long double sqrtl(long double); +} +#endif + +#include "mars.h" +#include "declaration.h" +#include "attrib.h" +#include "expression.h" +#include "scope.h" +#include "mtype.h" +#include "aggregate.h" +#include "identifier.h" +#include "id.h" +#include "module.h" + +#if DMDV2 + +/********************************** + * Determine if function is a builtin one that we can + * evaluate at compile time. + */ +enum BUILTIN FuncDeclaration::isBuiltin() +{ + static const char FeZe [] = "FNaNbNfeZe"; // @safe pure nothrow real function(real) + static const char FeZe2[] = "FNaNbNeeZe"; // @trusted pure nothrow real function(real) + static const char FuintZint[] = "FNaNbkZi"; // pure nothrow int function(uint) + static const char FuintZuint[] = "FNaNbkZk"; // pure nothrow uint function(uint) + static const char FulongZulong[] = "FNaNbkZk"; // pure nothrow int function(ulong) + static const char FulongZint[] = "FNaNbmZi"; // pure nothrow int function(uint) + static const char FrealrealZreal [] = "FNaNbNfeeZe"; // @safe pure nothrow real function(real, real) + static const char FrealZlong [] = "FNaNbNfeZl"; // @safe pure nothrow long function(real) + + //printf("FuncDeclaration::isBuiltin() %s, %d\n", toChars(), builtin); + if (builtin == BUILTINunknown) + { + builtin = BUILTINnot; + if (parent && parent->isModule()) + { + // If it's in the std.math package + if (parent->ident == Id::math && + parent->parent && (parent->parent->ident == Id::std || parent->parent->ident == Id::core) && + !parent->parent->parent) + { + //printf("deco = %s\n", type->deco); + if (strcmp(type->deco, FeZe) == 0 || strcmp(type->deco, FeZe2) == 0) + { + if (ident == Id::sin) + builtin = BUILTINsin; + else if (ident == Id::cos) + builtin = BUILTINcos; + else if (ident == Id::tan) + builtin = BUILTINtan; + else if (ident == Id::_sqrt) + builtin = BUILTINsqrt; + else if (ident == Id::fabs) + builtin = BUILTINfabs; + else if (ident == Id::expm1) + builtin = BUILTINexpm1; + else if (ident == Id::exp2) + builtin = BUILTINexp2; + //printf("builtin = %d\n", builtin); + } + // if float or double versions + else if (strcmp(type->deco, "FNaNbNfdZd") == 0 || + strcmp(type->deco, "FNaNbNffZf") == 0) + { + if (ident == Id::_sqrt) + builtin = BUILTINsqrt; + } + else if (strcmp(type->deco, FrealrealZreal) == 0) + { + if (ident == Id::atan2) + builtin = BUILTINatan2; + else if (ident == Id::yl2x) + builtin = BUILTINyl2x; + else if (ident == Id::yl2xp1) + builtin = BUILTINyl2xp1; + } + else if (strcmp(type->deco, FrealZlong) == 0 && ident == Id::rndtol) + builtin = BUILTINrndtol; + } + if (parent->ident == Id::bitop && + parent->parent && parent->parent->ident == Id::core && + !parent->parent->parent) + { + //printf("deco = %s\n", type->deco); + if (strcmp(type->deco, FuintZint) == 0 || strcmp(type->deco, FulongZint) == 0) + { + if (ident == Id::bsf) + builtin = BUILTINbsf; + else if (ident == Id::bsr) + builtin = BUILTINbsr; + } + else if (strcmp(type->deco, FuintZuint) == 0) + { + if (ident == Id::bswap) + builtin = BUILTINbswap; + } + } + } + } + return builtin; +} + +int eval_bsf(uinteger_t n) +{ + n = (n ^ (n - 1)) >> 1; // convert trailing 0s to 1, and zero rest + int k = 0; + while( n ) + { ++k; + n >>=1; + } + return k; +} + +int eval_bsr(uinteger_t n) +{ int k= 0; + while(n>>=1) + { + ++k; + } + return k; +} + +uinteger_t eval_bswap(Expression *arg0) +{ uinteger_t n = arg0->toInteger(); + #define BYTEMASK 0x00FF00FF00FF00FFLL + #define SHORTMASK 0x0000FFFF0000FFFFLL + #define INTMASK 0x0000FFFF0000FFFFLL + // swap adjacent ubytes + n = ((n >> 8 ) & BYTEMASK) | ((n & BYTEMASK) << 8 ); + // swap adjacent ushorts + n = ((n >> 16) & SHORTMASK) | ((n & SHORTMASK) << 16); + TY ty = arg0->type->toBasetype()->ty; + // If 64 bits, we need to swap high and low uints + if (ty == Tint64 || ty == Tuns64) + n = ((n >> 32) & INTMASK) | ((n & INTMASK) << 32); + return n; +} + +/************************************** + * Evaluate builtin function. + * Return result; NULL if cannot evaluate it. + */ + +Expression *eval_builtin(Loc loc, enum BUILTIN builtin, Expressions *arguments) +{ + assert(arguments && arguments->dim); + Expression *arg0 = arguments->tdata()[0]; + Expression *e = NULL; + switch (builtin) + { + case BUILTINsin: + if (arg0->op == TOKfloat64) + e = new RealExp(0, sinl(arg0->toReal()), arg0->type); + break; + + case BUILTINcos: + if (arg0->op == TOKfloat64) + e = new RealExp(0, cosl(arg0->toReal()), arg0->type); + break; + + case BUILTINtan: + if (arg0->op == TOKfloat64) + e = new RealExp(0, tanl(arg0->toReal()), arg0->type); + break; + + case BUILTINsqrt: + if (arg0->op == TOKfloat64) + e = new RealExp(0, sqrtl(arg0->toReal()), arg0->type); + break; + + case BUILTINfabs: + if (arg0->op == TOKfloat64) + e = new RealExp(0, fabsl(arg0->toReal()), arg0->type); + break; + // These math intrinsics are not yet implemented + case BUILTINatan2: + break; + case BUILTINrndtol: + break; + case BUILTINexpm1: + break; + case BUILTINexp2: + break; + case BUILTINyl2x: + break; + case BUILTINyl2xp1: + break; + case BUILTINbsf: + if (arg0->op == TOKint64) + { if (arg0->toInteger()==0) + error(loc, "bsf(0) is undefined"); + else + e = new IntegerExp(loc, eval_bsf(arg0->toInteger()), Type::tint32); + } + break; + case BUILTINbsr: + if (arg0->op == TOKint64) + { if (arg0->toInteger()==0) + error(loc, "bsr(0) is undefined"); + else + e = new IntegerExp(loc, eval_bsr(arg0->toInteger()), Type::tint32); + } + break; + case BUILTINbswap: + if (arg0->op == TOKint64) + e = new IntegerExp(loc, eval_bswap(arg0), arg0->type); + break; + } + return e; +} + +#endif diff --git a/canthrow.c b/canthrow.c new file mode 100644 index 00000000..885b241a --- /dev/null +++ b/canthrow.c @@ -0,0 +1,189 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "mars.h" +#include "init.h" +#include "expression.h" +#include "template.h" +#include "statement.h" +#include "mtype.h" +#include "utf.h" +#include "declaration.h" +#include "aggregate.h" +#include "scope.h" +#include "attrib.h" + +int Dsymbol_canThrow(Dsymbol *s, bool mustNotThrow); +int lambdaCanThrow(Expression *e, void *param); + +/******************************************** + * Convert from expression to delegate that returns the expression, + * i.e. convert: + * expr + * to: + * t delegate() { return expr; } + */ + +struct CanThrow +{ + bool can; + bool mustnot; +}; + +int Expression::canThrow(bool mustNotThrow) +{ + CanThrow ct; + ct.can = FALSE; + ct.mustnot = mustNotThrow; + apply(&lambdaCanThrow, &ct); + return ct.can; +} + +int lambdaCanThrow(Expression *e, void *param) +{ + CanThrow *pct = (CanThrow *)param; + switch (e->op) + { + case TOKdeclaration: + { DeclarationExp *de = (DeclarationExp *)e; + pct->can = Dsymbol_canThrow(de->declaration, pct->mustnot); + break; + } + + case TOKcall: + { CallExp *ce = (CallExp *)e; + + if (global.errors && !ce->e1->type) + break; // error recovery + + /* If calling a function or delegate that is typed as nothrow, + * then this expression cannot throw. + * Note that pure functions can throw. + */ + Type *t = ce->e1->type->toBasetype(); + if (t->ty == Tfunction && ((TypeFunction *)t)->isnothrow) + ; + else if (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow) + ; + else + { + if (pct->mustnot) + e->error("%s is not nothrow", ce->e1->toChars()); + pct->can = TRUE; + } + break; + } + + case TOKnew: + { NewExp *ne = (NewExp *)e; + if (ne->member) + { + // See if constructor call can throw + Type *t = ne->member->type->toBasetype(); + if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) + { + if (pct->mustnot) + e->error("constructor %s is not nothrow", ne->member->toChars()); + pct->can = TRUE; + } + } + // regard storage allocation failures as not recoverable + break; + } + + case TOKnewanonclass: + assert(0); // should have been lowered by semantic() + break; + + default: + break; + } + return pct->can; // stop walking if we determine this expression can throw +} + +/************************************** + * Does symbol, when initialized, throw? + * Mirrors logic in Dsymbol_toElem(). + */ + +int Dsymbol_canThrow(Dsymbol *s, bool mustNotThrow) +{ + AttribDeclaration *ad; + VarDeclaration *vd; + TemplateMixin *tm; + TupleDeclaration *td; + + //printf("Dsymbol_toElem() %s\n", s->toChars()); + ad = s->isAttribDeclaration(); + if (ad) + { + Dsymbols *decl = ad->include(NULL, NULL); + if (decl && decl->dim) + { + for (size_t i = 0; i < decl->dim; i++) + { + s = decl->tdata()[i]; + if (Dsymbol_canThrow(s, mustNotThrow)) + return 1; + } + } + } + else if ((vd = s->isVarDeclaration()) != NULL) + { + s = s->toAlias(); + if (s != vd) + return Dsymbol_canThrow(s, mustNotThrow); + if (vd->storage_class & STCmanifest) + ; + else if (vd->isStatic() || vd->storage_class & (STCextern | STCtls | STCgshared)) + ; + else + { + if (vd->init) + { ExpInitializer *ie = vd->init->isExpInitializer(); + if (ie && ie->exp->canThrow(mustNotThrow)) + return 1; + } + if (vd->edtor && !vd->noscope) + return vd->edtor->canThrow(mustNotThrow); + } + } + else if ((tm = s->isTemplateMixin()) != NULL) + { + //printf("%s\n", tm->toChars()); + if (tm->members) + { + for (size_t i = 0; i < tm->members->dim; i++) + { + Dsymbol *sm = tm->members->tdata()[i]; + if (Dsymbol_canThrow(sm, mustNotThrow)) + return 1; + } + } + } + else if ((td = s->isTupleDeclaration()) != NULL) + { + for (size_t i = 0; i < td->objects->dim; i++) + { Object *o = td->objects->tdata()[i]; + if (o->dyncast() == DYNCAST_EXPRESSION) + { Expression *eo = (Expression *)o; + if (eo->op == TOKdsymbol) + { DsymbolExp *se = (DsymbolExp *)eo; + if (Dsymbol_canThrow(se->s, mustNotThrow)) + return 1; + } + } + } + } + return 0; +} diff --git a/cast.c b/cast.c new file mode 100644 index 00000000..74c67f92 --- /dev/null +++ b/cast.c @@ -0,0 +1,2574 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "rmem.h" + +#include "expression.h" +#include "mtype.h" +#include "utf.h" +#include "declaration.h" +#include "aggregate.h" +#include "scope.h" + +//#define DUMP .dump(__PRETTY_FUNCTION__, this) +#define DUMP + +/* ==================== implicitCast ====================== */ + +/************************************** + * Do an implicit cast. + * Issue error if it can't be done. + */ + +Expression *Expression::implicitCastTo(Scope *sc, Type *t) +{ + //printf("Expression::implicitCastTo(%s of type %s) => %s\n", toChars(), type->toChars(), t->toChars()); + + MATCH match = implicitConvTo(t); + if (match) + { TY tyfrom = type->toBasetype()->ty; + TY tyto = t->toBasetype()->ty; +#if DMDV1 + if (global.params.warnings && + Type::impcnvWarn[tyfrom][tyto] && + op != TOKint64) + { + Expression *e = optimize(WANTflags | WANTvalue); + + if (e->op == TOKint64) + return e->implicitCastTo(sc, t); + if (tyfrom == Tint32 && + (op == TOKadd || op == TOKmin || + op == TOKand || op == TOKor || op == TOKxor) + ) + { + /* This is really only a semi-kludge fix, + * we really should look at the operands of op + * and see if they are narrower types. + * For example, b=b|b and b=b|7 and s=b+b should be allowed, + * but b=b|i should be an error. + */ + ; + } + else + { + warning("implicit conversion of expression (%s) of type %s to %s can cause loss of data", + toChars(), type->toChars(), t->toChars()); + } + } +#endif +#if DMDV2 + if (match == MATCHconst && t == type->constOf()) + { + Expression *e = copy(); + e->type = t; + return e; + } +#endif + return castTo(sc, t); + } + + Expression *e = optimize(WANTflags | WANTvalue); + if (e != this) + return e->implicitCastTo(sc, t); + +#if 0 +printf("ty = %d\n", type->ty); +print(); +type->print(); +printf("to:\n"); +t->print(); +printf("%p %p type: %s to: %s\n", type->deco, t->deco, type->deco, t->deco); +//printf("%p %p %p\n", type->nextOf()->arrayOf(), type, t); +fflush(stdout); +#endif + if (t->ty != Terror && type->ty != Terror) + { + if (!t->deco) + { /* Can happen with: + * enum E { One } + * class A + * { static void fork(EDG dg) { dg(E.One); } + * alias void delegate(E) EDG; + * } + * Should eventually make it work. + */ + error("forward reference to type %s", t->toChars()); + } + else if (t->reliesOnTident()) + error("forward reference to type %s", t->reliesOnTident()->toChars()); + + error("cannot implicitly convert expression (%s) of type %s to %s", + toChars(), type->toChars(), t->toChars()); + } + return new ErrorExp(); +} + +Expression *StringExp::implicitCastTo(Scope *sc, Type *t) +{ + //printf("StringExp::implicitCastTo(%s of type %s) => %s\n", toChars(), type->toChars(), t->toChars()); + unsigned char committed = this->committed; + Expression *e = Expression::implicitCastTo(sc, t); + if (e->op == TOKstring) + { + // Retain polysemous nature if it started out that way + ((StringExp *)e)->committed = committed; + } + return e; +} + +Expression *ErrorExp::implicitCastTo(Scope *sc, Type *t) +{ + return this; +} + +/******************************************* + * Return !=0 if we can implicitly convert this to type t. + * Don't do the actual cast. + */ + +MATCH Expression::implicitConvTo(Type *t) +{ +#if 0 + printf("Expression::implicitConvTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + //static int nest; if (++nest == 10) halt(); + if (t == Type::terror) + return MATCHnomatch; + if (!type) + { error("%s is not an expression", toChars()); + type = Type::terror; + } + Expression *e = optimize(WANTvalue | WANTflags); + if (e->type == t) + return MATCHexact; + if (e != this) + { //printf("\toptimized to %s of type %s\n", e->toChars(), e->type->toChars()); + return e->implicitConvTo(t); + } + MATCH match = type->implicitConvTo(t); + if (match != MATCHnomatch) + return match; + + /* See if we can do integral narrowing conversions + */ + if (type->isintegral() && t->isintegral() && + type->isTypeBasic() && t->isTypeBasic()) + { IntRange src = this->getIntRange() DUMP; + IntRange targetUnsigned = IntRange::fromType(t, /*isUnsigned*/true) DUMP; + IntRange targetSigned = IntRange::fromType(t, /*isUnsigned*/false) DUMP; + if (targetUnsigned.contains(src) || targetSigned.contains(src)) + return MATCHconvert; + } + +#if 0 + Type *tb = t->toBasetype(); + if (tb->ty == Tdelegate) + { TypeDelegate *td = (TypeDelegate *)tb; + TypeFunction *tf = (TypeFunction *)td->nextOf(); + + if (!tf->varargs && + !(tf->arguments && tf->arguments->dim) + ) + { + match = type->implicitConvTo(tf->nextOf()); + if (match) + return match; + if (tf->nextOf()->toBasetype()->ty == Tvoid) + return MATCHconvert; + } + } +#endif + return MATCHnomatch; +} + + +MATCH IntegerExp::implicitConvTo(Type *t) +{ +#if 0 + printf("IntegerExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + MATCH m = type->implicitConvTo(t); + if (m >= MATCHconst) + return m; + + TY ty = type->toBasetype()->ty; + TY toty = t->toBasetype()->ty; + + if (m == MATCHnomatch && t->ty == Tenum) + goto Lno; + + if (t->ty == Tvector) + { TypeVector *tv = (TypeVector *)t; + TypeBasic *tb = tv->elementType(); + toty = tb->ty; + } + + switch (ty) + { + case Tbool: + value &= 1; + ty = Tint32; + break; + + case Tint8: + value = (signed char)value; + ty = Tint32; + break; + + case Tchar: + case Tuns8: + value &= 0xFF; + ty = Tint32; + break; + + case Tint16: + value = (short)value; + ty = Tint32; + break; + + case Tuns16: + case Twchar: + value &= 0xFFFF; + ty = Tint32; + break; + + case Tint32: + value = (int)value; + break; + + case Tuns32: + case Tdchar: + value &= 0xFFFFFFFF; + ty = Tuns32; + break; + + default: + break; + } + + // Only allow conversion if no change in value + switch (toty) + { + case Tbool: + if ((value & 1) != value) + goto Lno; + goto Lyes; + + case Tint8: + if ((signed char)value != value) + goto Lno; + goto Lyes; + + case Tchar: + case Tuns8: + //printf("value = %llu %llu\n", (dinteger_t)(unsigned char)value, value); + if ((unsigned char)value != value) + goto Lno; + goto Lyes; + + case Tint16: + if ((short)value != value) + goto Lno; + goto Lyes; + + case Tuns16: + if ((unsigned short)value != value) + goto Lno; + goto Lyes; + + case Tint32: + if (ty == Tuns32) + { + } + else if ((int)value != value) + goto Lno; + goto Lyes; + + case Tuns32: + if (ty == Tint32) + { + } + else if ((unsigned)value != value) + goto Lno; + goto Lyes; + + case Tdchar: + if (value > 0x10FFFFUL) + goto Lno; + goto Lyes; + + case Twchar: + if ((unsigned short)value != value) + goto Lno; + goto Lyes; + + case Tfloat32: + { + volatile float f; + if (type->isunsigned()) + { + f = (float)value; + if (f != value) + goto Lno; + } + else + { + f = (float)(long long)value; + if (f != (long long)value) + goto Lno; + } + goto Lyes; + } + + case Tfloat64: + { + volatile double f; + if (type->isunsigned()) + { + f = (double)value; + if (f != value) + goto Lno; + } + else + { + f = (double)(long long)value; + if (f != (long long)value) + goto Lno; + } + goto Lyes; + } + + case Tfloat80: + { + volatile long double f; + if (type->isunsigned()) + { + f = (long double)value; + if (f != value) + goto Lno; + } + else + { + f = (long double)(long long)value; + if (f != (long long)value) + goto Lno; + } + goto Lyes; + } + + case Tpointer: +//printf("type = %s\n", type->toBasetype()->toChars()); +//printf("t = %s\n", t->toBasetype()->toChars()); + if (ty == Tpointer && + type->toBasetype()->nextOf()->ty == t->toBasetype()->nextOf()->ty) + { /* Allow things like: + * const char* P = cast(char *)3; + * char* q = P; + */ + goto Lyes; + } + break; + } + return Expression::implicitConvTo(t); + +Lyes: + //printf("MATCHconvert\n"); + return MATCHconvert; + +Lno: + //printf("MATCHnomatch\n"); + return MATCHnomatch; +} + +MATCH NullExp::implicitConvTo(Type *t) +{ +#if 0 + printf("NullExp::implicitConvTo(this=%s, type=%s, t=%s, committed = %d)\n", + toChars(), type->toChars(), t->toChars(), committed); +#endif + if (this->type->equals(t)) + return MATCHexact; + + /* Allow implicit conversions from immutable to mutable|const, + * and mutable to immutable. It works because, after all, a null + * doesn't actually point to anything. + */ + if (t->invariantOf()->equals(type->invariantOf())) + return MATCHconst; + + return Expression::implicitConvTo(t); +} + +#if DMDV2 +MATCH StructLiteralExp::implicitConvTo(Type *t) +{ +#if 0 + printf("StructLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + MATCH m = Expression::implicitConvTo(t); + if (m != MATCHnomatch) + return m; + if (type->ty == t->ty && type->ty == Tstruct && + ((TypeStruct *)type)->sym == ((TypeStruct *)t)->sym) + { + m = MATCHconst; + for (size_t i = 0; i < elements->dim; i++) + { Expression *e = (*elements)[i]; + Type *te = e->type; + te = te->castMod(t->mod); + MATCH m2 = e->implicitConvTo(te); + //printf("\t%s => %s, match = %d\n", e->toChars(), te->toChars(), m2); + if (m2 < m) + m = m2; + } + } + return m; +} +#endif + +MATCH StringExp::implicitConvTo(Type *t) +{ +#if 0 + printf("StringExp::implicitConvTo(this=%s, committed=%d, type=%s, t=%s)\n", + toChars(), committed, type->toChars(), t->toChars()); +#endif + if (!committed && t->ty == Tpointer && t->nextOf()->ty == Tvoid) + { + return MATCHnomatch; + } + if (type->ty == Tsarray || type->ty == Tarray || type->ty == Tpointer) + { + TY tyn = type->nextOf()->ty; + if (tyn == Tchar || tyn == Twchar || tyn == Tdchar) + { Type *tn; + MATCH m; + + switch (t->ty) + { + case Tsarray: + if (type->ty == Tsarray) + { + if (((TypeSArray *)type)->dim->toInteger() != + ((TypeSArray *)t)->dim->toInteger()) + return MATCHnomatch; + TY tynto = t->nextOf()->ty; + if (tynto == tyn) + return MATCHexact; + if (!committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar)) + return MATCHexact; + } + else if (type->ty == Tarray) + { + if (length() > + ((TypeSArray *)t)->dim->toInteger()) + return MATCHnomatch; + TY tynto = t->nextOf()->ty; + if (tynto == tyn) + return MATCHexact; + if (!committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar)) + return MATCHexact; + } + case Tarray: + case Tpointer: + tn = t->nextOf(); + m = MATCHexact; + if (type->nextOf()->mod != tn->mod) + { if (!tn->isConst()) + return MATCHnomatch; + m = MATCHconst; + } + switch (tn->ty) + { + case Tchar: + case Twchar: + case Tdchar: + if (!committed) + return m; + break; + } + break; + } + } + } + return Expression::implicitConvTo(t); +#if 0 + m = (MATCH)type->implicitConvTo(t); + if (m) + { + return m; + } + + return MATCHnomatch; +#endif +} + +MATCH ArrayLiteralExp::implicitConvTo(Type *t) +{ MATCH result = MATCHexact; + +#if 0 + printf("ArrayLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + Type *typeb = type->toBasetype(); + Type *tb = t->toBasetype(); + if ((tb->ty == Tarray || tb->ty == Tsarray) && + (typeb->ty == Tarray || typeb->ty == Tsarray)) + { + if (tb->ty == Tsarray) + { TypeSArray *tsa = (TypeSArray *)tb; + if (elements->dim != tsa->dim->toInteger()) + result = MATCHnomatch; + } + + for (size_t i = 0; i < elements->dim; i++) + { Expression *e = (*elements)[i]; + MATCH m = (MATCH)e->implicitConvTo(tb->nextOf()); + if (m < result) + result = m; // remember worst match + if (result == MATCHnomatch) + break; // no need to check for worse + } + + if (!result) + result = type->implicitConvTo(t); + + return result; + } + else + return Expression::implicitConvTo(t); +} + +MATCH AssocArrayLiteralExp::implicitConvTo(Type *t) +{ MATCH result = MATCHexact; + + Type *typeb = type->toBasetype(); + Type *tb = t->toBasetype(); + if (tb->ty == Taarray && typeb->ty == Taarray) + { + for (size_t i = 0; i < keys->dim; i++) + { Expression *e = keys->tdata()[i]; + MATCH m = (MATCH)e->implicitConvTo(((TypeAArray *)tb)->index); + if (m < result) + result = m; // remember worst match + if (result == MATCHnomatch) + break; // no need to check for worse + e = values->tdata()[i]; + m = (MATCH)e->implicitConvTo(tb->nextOf()); + if (m < result) + result = m; // remember worst match + if (result == MATCHnomatch) + break; // no need to check for worse + } + return result; + } + else + return Expression::implicitConvTo(t); +} + +MATCH CallExp::implicitConvTo(Type *t) +{ +#if 0 + printf("CalLExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + + MATCH m = Expression::implicitConvTo(t); + if (m) + return m; + + /* Allow the result of strongly pure functions to + * convert to immutable + */ + if (f && f->isPure() == PUREstrong && !f->type->hasWild()) + return type->invariantOf()->implicitConvTo(t); + + return MATCHnomatch; +} + +MATCH AddrExp::implicitConvTo(Type *t) +{ +#if 0 + printf("AddrExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + MATCH result; + + result = type->implicitConvTo(t); + //printf("\tresult = %d\n", result); + + if (result == MATCHnomatch) + { + // Look for pointers to functions where the functions are overloaded. + + t = t->toBasetype(); + + if (e1->op == TOKoverloadset && + (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) + { OverExp *eo = (OverExp *)e1; + FuncDeclaration *f = NULL; + for (size_t i = 0; i < eo->vars->a.dim; i++) + { Dsymbol *s = eo->vars->a[i]; + FuncDeclaration *f2 = s->isFuncDeclaration(); + assert(f2); + if (f2->overloadExactMatch(t->nextOf())) + { if (f) + /* Error if match in more than one overload set, + * even if one is a 'better' match than the other. + */ + ScopeDsymbol::multiplyDefined(loc, f, f2); + else + f = f2; + result = MATCHexact; + } + } + } + + if (type->ty == Tpointer && type->nextOf()->ty == Tfunction && + t->ty == Tpointer && t->nextOf()->ty == Tfunction && + e1->op == TOKvar) + { + /* I don't think this can ever happen - + * it should have been + * converted to a SymOffExp. + */ + assert(0); + VarExp *ve = (VarExp *)e1; + FuncDeclaration *f = ve->var->isFuncDeclaration(); + if (f && f->overloadExactMatch(t->nextOf())) + result = MATCHexact; + } + } + //printf("\tresult = %d\n", result); + return result; +} + +MATCH SymOffExp::implicitConvTo(Type *t) +{ +#if 0 + printf("SymOffExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + MATCH result; + + result = type->implicitConvTo(t); + //printf("\tresult = %d\n", result); + + if (result == MATCHnomatch) + { + // Look for pointers to functions where the functions are overloaded. + FuncDeclaration *f; + + t = t->toBasetype(); + if (type->ty == Tpointer && type->nextOf()->ty == Tfunction && + (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) + { + f = var->isFuncDeclaration(); + if (f) + { f = f->overloadExactMatch(t->nextOf()); + if (f) + { if ((t->ty == Tdelegate && (f->needThis() || f->isNested())) || + (t->ty == Tpointer && !(f->needThis() || f->isNested()))) + { + result = MATCHexact; + } + } + } + } + } + //printf("\tresult = %d\n", result); + return result; +} + +MATCH DelegateExp::implicitConvTo(Type *t) +{ +#if 0 + printf("DelegateExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + MATCH result; + + result = type->implicitConvTo(t); + + if (result == MATCHnomatch) + { + // Look for pointers to functions where the functions are overloaded. + + t = t->toBasetype(); + if (type->ty == Tdelegate && + t->ty == Tdelegate) + { + if (func && func->overloadExactMatch(t->nextOf())) + result = MATCHexact; + } + } + return result; +} + +MATCH FuncExp::implicitConvTo(Type *t) +{ + //printf("FuncExp::implicitCastTo type = %p %s, t = %s\n", type, type ? type->toChars() : NULL, t->toChars()); + if (type && type != Type::tvoid && tok == TOKreserved && type->ty == Tpointer + && (t->ty == Tpointer || t->ty == Tdelegate)) + { // Allow implicit function to delegate conversion + if (type->nextOf()->covariant(t->nextOf()) == 1) + return t->ty == Tpointer ? MATCHconst : MATCHconvert; + } + return Expression::implicitConvTo(t); +} + +MATCH OrExp::implicitConvTo(Type *t) +{ + MATCH result = Expression::implicitConvTo(t); + + if (result == MATCHnomatch) + { + MATCH m1 = e1->implicitConvTo(t); + MATCH m2 = e2->implicitConvTo(t); + + // Pick the worst match + result = (m1 < m2) ? m1 : m2; + } + return result; +} + +MATCH XorExp::implicitConvTo(Type *t) +{ + MATCH result = Expression::implicitConvTo(t); + + if (result == MATCHnomatch) + { + MATCH m1 = e1->implicitConvTo(t); + MATCH m2 = e2->implicitConvTo(t); + + // Pick the worst match + result = (m1 < m2) ? m1 : m2; + } + return result; +} + +MATCH CondExp::implicitConvTo(Type *t) +{ + MATCH m1 = e1->implicitConvTo(t); + MATCH m2 = e2->implicitConvTo(t); + //printf("CondExp: m1 %d m2 %d\n", m1, m2); + + // Pick the worst match + return (m1 < m2) ? m1 : m2; +} + +MATCH CommaExp::implicitConvTo(Type *t) +{ + return e2->implicitConvTo(t); +} + +MATCH CastExp::implicitConvTo(Type *t) +{ +#if 0 + printf("CastExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + MATCH result; + + result = type->implicitConvTo(t); + + if (result == MATCHnomatch) + { + if (t->isintegral() && + e1->type->isintegral() && + e1->implicitConvTo(t) != MATCHnomatch) + result = MATCHconvert; + else + result = Expression::implicitConvTo(t); + } + return result; +} + +/* ==================== castTo ====================== */ + +/************************************** + * Do an explicit cast. + */ + +Expression *Expression::castTo(Scope *sc, Type *t) +{ + //printf("Expression::castTo(this=%s, t=%s)\n", toChars(), t->toChars()); +#if 0 + printf("Expression::castTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + if (type == t) + return this; + Expression *e = this; + Type *tb = t->toBasetype(); + Type *typeb = type->toBasetype(); + if (tb != typeb) + { + // Do (type *) cast of (type [dim]) + if (tb->ty == Tpointer && + typeb->ty == Tsarray + ) + { + //printf("Converting [dim] to *\n"); + + if (typeb->size(loc) == 0) + e = new NullExp(loc); + else + e = new AddrExp(loc, e); + } +#if 0 + else if (tb->ty == Tdelegate && type->ty != Tdelegate) + { + TypeDelegate *td = (TypeDelegate *)tb; + TypeFunction *tf = (TypeFunction *)td->nextOf(); + return toDelegate(sc, tf->nextOf()); + } +#endif + else + { + if (typeb->ty == Tstruct) + { TypeStruct *ts = (TypeStruct *)typeb; + if (!(tb->ty == Tstruct && ts->sym == ((TypeStruct *)tb)->sym) && + ts->sym->aliasthis) + { /* Forward the cast to our alias this member, rewrite to: + * cast(to)e1.aliasthis + */ + Expression *e1 = new DotIdExp(loc, this, ts->sym->aliasthis->ident); + Expression *e2 = new CastExp(loc, e1, tb); + e2 = e2->semantic(sc); + return e2; + } + } + else if (typeb->ty == Tclass) + { TypeClass *ts = (TypeClass *)typeb; + if (ts->sym->aliasthis) + { + if (tb->ty == Tclass) + { + ClassDeclaration *cdfrom = typeb->isClassHandle(); + ClassDeclaration *cdto = tb->isClassHandle(); + int offset; + if (cdto->isBaseOf(cdfrom, &offset)) + goto L1; + } + /* Forward the cast to our alias this member, rewrite to: + * cast(to)e1.aliasthis + */ + Expression *e1 = new DotIdExp(loc, this, ts->sym->aliasthis->ident); + Expression *e2 = new CastExp(loc, e1, tb); + e2 = e2->semantic(sc); + return e2; + } + L1: ; + } + else if (tb->ty == Tvector && typeb->ty != Tvector) + { + e = new VectorExp(loc, e, tb); + e = e->semantic(sc); + return e; + } + e = new CastExp(loc, e, tb); + } + } + else + { + e = e->copy(); // because of COW for assignment to e->type + } + assert(e != this); + e->type = t; + //printf("Returning: %s\n", e->toChars()); + return e; +} + + +Expression *ErrorExp::castTo(Scope *sc, Type *t) +{ + return this; +} + + +Expression *RealExp::castTo(Scope *sc, Type *t) +{ Expression *e = this; + if (type != t) + { + if ((type->isreal() && t->isreal()) || + (type->isimaginary() && t->isimaginary()) + ) + { e = copy(); + e->type = t; + } + else + e = Expression::castTo(sc, t); + } + return e; +} + + +Expression *ComplexExp::castTo(Scope *sc, Type *t) +{ Expression *e = this; + if (type != t) + { + if (type->iscomplex() && t->iscomplex()) + { e = copy(); + e->type = t; + } + else + e = Expression::castTo(sc, t); + } + return e; +} + + +Expression *NullExp::castTo(Scope *sc, Type *t) +{ + //printf("NullExp::castTo(t = %p)\n", t); + if (type == t) + { + committed = 1; + return this; + } + + NullExp *e = (NullExp *)copy(); + e->committed = 1; + Type *tb = t->toBasetype(); +#if 0 + e->type = type->toBasetype(); + if (tb != e->type) + { + // NULL implicitly converts to any pointer type or dynamic array + if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tvoid && + (tb->ty == Tpointer || tb->ty == Tarray || tb->ty == Taarray || + tb->ty == Tdelegate)) + { + if (tb->ty == Tdelegate) + { TypeDelegate *td = (TypeDelegate *)tb; + TypeFunction *tf = (TypeFunction *)td->nextOf(); + + if (!tf->varargs && + !(tf->arguments && tf->arguments->dim) + ) + { + return Expression::castTo(sc, t); + } + } + } + else + { + //return e->Expression::castTo(sc, t); + } + } +#else + if (tb->ty == Tvoid) + { + e->type = type->toBasetype(); + return e->Expression::castTo(sc, t); + } +#endif + e->type = t; + return e; +} + +Expression *StringExp::castTo(Scope *sc, Type *t) +{ + /* This follows copy-on-write; any changes to 'this' + * will result in a copy. + * The this->string member is considered immutable. + */ + int copied = 0; + + //printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t->toChars(), toChars(), committed); + + if (!committed && t->ty == Tpointer && t->nextOf()->ty == Tvoid) + { + error("cannot convert string literal to void*"); + return new ErrorExp(); + } + + StringExp *se = this; + if (!committed) + { se = (StringExp *)copy(); + se->committed = 1; + copied = 1; + } + + if (type == t) + { + return se; + } + + Type *tb = t->toBasetype(); + //printf("\ttype = %s\n", type->toChars()); + if (tb->ty == Tdelegate && type->toBasetype()->ty != Tdelegate) + return Expression::castTo(sc, t); + + Type *typeb = type->toBasetype(); + if (typeb == tb) + { + if (!copied) + { se = (StringExp *)copy(); + copied = 1; + } + se->type = t; + return se; + } + + if (committed && tb->ty == Tsarray && typeb->ty == Tarray) + { + se = (StringExp *)copy(); + d_uns64 szx = tb->nextOf()->size(); + assert(szx <= 255); + se->sz = (unsigned char)szx; + se->len = (len * sz) / se->sz; + se->committed = 1; + se->type = t; + return se; + } + + if (tb->ty != Tsarray && tb->ty != Tarray && tb->ty != Tpointer) + { if (!copied) + { se = (StringExp *)copy(); + copied = 1; + } + goto Lcast; + } + if (typeb->ty != Tsarray && typeb->ty != Tarray && typeb->ty != Tpointer) + { if (!copied) + { se = (StringExp *)copy(); + copied = 1; + } + goto Lcast; + } + + if (typeb->nextOf()->size() == tb->nextOf()->size()) + { + if (!copied) + { se = (StringExp *)copy(); + copied = 1; + } + if (tb->ty == Tsarray) + goto L2; // handle possible change in static array dimension + se->type = t; + return se; + } + + if (committed) + goto Lcast; + +#define X(tf,tt) ((tf) * 256 + (tt)) + { + OutBuffer buffer; + size_t newlen = 0; + int tfty = typeb->nextOf()->toBasetype()->ty; + int ttty = tb->nextOf()->toBasetype()->ty; + switch (X(tfty, ttty)) + { + case X(Tchar, Tchar): + case X(Twchar,Twchar): + case X(Tdchar,Tdchar): + break; + + case X(Tchar, Twchar): + for (size_t u = 0; u < len;) + { unsigned c; + const char *p = utf_decodeChar((unsigned char *)se->string, len, &u, &c); + if (p) + error("%s", p); + else + buffer.writeUTF16(c); + } + newlen = buffer.offset / 2; + buffer.writeUTF16(0); + goto L1; + + case X(Tchar, Tdchar): + for (size_t u = 0; u < len;) + { unsigned c; + const char *p = utf_decodeChar((unsigned char *)se->string, len, &u, &c); + if (p) + error("%s", p); + buffer.write4(c); + newlen++; + } + buffer.write4(0); + goto L1; + + case X(Twchar,Tchar): + for (size_t u = 0; u < len;) + { unsigned c; + const char *p = utf_decodeWchar((unsigned short *)se->string, len, &u, &c); + if (p) + error("%s", p); + else + buffer.writeUTF8(c); + } + newlen = buffer.offset; + buffer.writeUTF8(0); + goto L1; + + case X(Twchar,Tdchar): + for (size_t u = 0; u < len;) + { unsigned c; + const char *p = utf_decodeWchar((unsigned short *)se->string, len, &u, &c); + if (p) + error("%s", p); + buffer.write4(c); + newlen++; + } + buffer.write4(0); + goto L1; + + case X(Tdchar,Tchar): + for (size_t u = 0; u < len; u++) + { + unsigned c = ((unsigned *)se->string)[u]; + if (!utf_isValidDchar(c)) + error("invalid UCS-32 char \\U%08x", c); + else + buffer.writeUTF8(c); + newlen++; + } + newlen = buffer.offset; + buffer.writeUTF8(0); + goto L1; + + case X(Tdchar,Twchar): + for (size_t u = 0; u < len; u++) + { + unsigned c = ((unsigned *)se->string)[u]; + if (!utf_isValidDchar(c)) + error("invalid UCS-32 char \\U%08x", c); + else + buffer.writeUTF16(c); + newlen++; + } + newlen = buffer.offset / 2; + buffer.writeUTF16(0); + goto L1; + + L1: + if (!copied) + { se = (StringExp *)copy(); + copied = 1; + } + se->string = buffer.extractData(); + se->len = newlen; + + { + d_uns64 szx = tb->nextOf()->size(); + assert(szx <= 255); + se->sz = (unsigned char)szx; + } + break; + + default: + assert(typeb->nextOf()->size() != tb->nextOf()->size()); + goto Lcast; + } + } +#undef X +L2: + assert(copied); + + // See if need to truncate or extend the literal + if (tb->ty == Tsarray) + { + dinteger_t dim2 = ((TypeSArray *)tb)->dim->toInteger(); + + //printf("dim from = %d, to = %d\n", (int)se->len, (int)dim2); + + // Changing dimensions + if (dim2 != se->len) + { + // Copy when changing the string literal + unsigned newsz = se->sz; + void *s; + int d; + + d = (dim2 < se->len) ? dim2 : se->len; + s = (unsigned char *)mem.malloc((dim2 + 1) * newsz); + memcpy(s, se->string, d * newsz); + // Extend with 0, add terminating 0 + memset((char *)s + d * newsz, 0, (dim2 + 1 - d) * newsz); + se->string = s; + se->len = dim2; + } + } + se->type = t; + return se; + +Lcast: + Expression *e = new CastExp(loc, se, t); + e->type = t; // so semantic() won't be run on e + return e; +} + +Expression *AddrExp::castTo(Scope *sc, Type *t) +{ + Type *tb; + +#if 0 + printf("AddrExp::castTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + Expression *e = this; + + tb = t->toBasetype(); + type = type->toBasetype(); + if (tb != type) + { + // Look for pointers to functions where the functions are overloaded. + + if (e1->op == TOKoverloadset && + (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) + { OverExp *eo = (OverExp *)e1; + FuncDeclaration *f = NULL; + for (size_t i = 0; i < eo->vars->a.dim; i++) + { Dsymbol *s = eo->vars->a[i]; + FuncDeclaration *f2 = s->isFuncDeclaration(); + assert(f2); + if (f2->overloadExactMatch(t->nextOf())) + { if (f) + /* Error if match in more than one overload set, + * even if one is a 'better' match than the other. + */ + ScopeDsymbol::multiplyDefined(loc, f, f2); + else + f = f2; + } + } + if (f) + { f->tookAddressOf++; + SymOffExp *se = new SymOffExp(loc, f, 0, 0); + se->semantic(sc); + // Let SymOffExp::castTo() do the heavy lifting + return se->castTo(sc, t); + } + } + + + if (type->ty == Tpointer && type->nextOf()->ty == Tfunction && + tb->ty == Tpointer && tb->nextOf()->ty == Tfunction && + e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e1; + FuncDeclaration *f = ve->var->isFuncDeclaration(); + if (f) + { + assert(0); // should be SymOffExp instead + f = f->overloadExactMatch(tb->nextOf()); + if (f) + { + e = new VarExp(loc, f); + e->type = f->type; + e = new AddrExp(loc, e); + e->type = t; + return e; + } + } + } + e = Expression::castTo(sc, t); + } + e->type = t; + return e; +} + + +Expression *TupleExp::castTo(Scope *sc, Type *t) +{ TupleExp *e = (TupleExp *)copy(); + e->exps = (Expressions *)exps->copy(); + for (size_t i = 0; i < e->exps->dim; i++) + { Expression *ex = e->exps->tdata()[i]; + ex = ex->castTo(sc, t); + e->exps->tdata()[i] = ex; + } + return e; +} + + +Expression *ArrayLiteralExp::castTo(Scope *sc, Type *t) +{ +#if 0 + printf("ArrayLiteralExp::castTo(this=%s, type=%s, => %s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + if (type == t) + return this; + ArrayLiteralExp *e = this; + Type *typeb = type->toBasetype(); + Type *tb = t->toBasetype(); + if ((tb->ty == Tarray || tb->ty == Tsarray) && + (typeb->ty == Tarray || typeb->ty == Tsarray) && + // Not trying to convert non-void[] to void[] + !(tb->nextOf()->toBasetype()->ty == Tvoid && typeb->nextOf()->toBasetype()->ty != Tvoid)) + { + if (tb->ty == Tsarray) + { TypeSArray *tsa = (TypeSArray *)tb; + if (elements->dim != tsa->dim->toInteger()) + goto L1; + } + + e = (ArrayLiteralExp *)copy(); + e->elements = (Expressions *)elements->copy(); + for (size_t i = 0; i < elements->dim; i++) + { Expression *ex = (*elements)[i]; + ex = ex->castTo(sc, tb->nextOf()); + (*e->elements)[i] = ex; + } + e->type = t; + return e; + } + if (tb->ty == Tpointer && typeb->ty == Tsarray) + { + Type *tp = typeb->nextOf()->pointerTo(); + if (!tp->equals(e->type)) + { e = (ArrayLiteralExp *)copy(); + e->type = tp; + } + } +L1: + return e->Expression::castTo(sc, t); +} + +Expression *AssocArrayLiteralExp::castTo(Scope *sc, Type *t) +{ + if (type == t) + return this; + AssocArrayLiteralExp *e = this; + Type *typeb = type->toBasetype(); + Type *tb = t->toBasetype(); + if (tb->ty == Taarray && typeb->ty == Taarray && + tb->nextOf()->toBasetype()->ty != Tvoid) + { + e = (AssocArrayLiteralExp *)copy(); + e->keys = (Expressions *)keys->copy(); + e->values = (Expressions *)values->copy(); + assert(keys->dim == values->dim); + for (size_t i = 0; i < keys->dim; i++) + { Expression *ex = values->tdata()[i]; + ex = ex->castTo(sc, tb->nextOf()); + e->values->tdata()[i] = ex; + + ex = keys->tdata()[i]; + ex = ex->castTo(sc, ((TypeAArray *)tb)->index); + e->keys->tdata()[i] = ex; + } + e->type = t; + return e; + } + return e->Expression::castTo(sc, t); +} + +Expression *SymOffExp::castTo(Scope *sc, Type *t) +{ +#if 0 + printf("SymOffExp::castTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + if (type == t && hasOverloads == 0) + return this; + Expression *e; + Type *tb = t->toBasetype(); + Type *typeb = type->toBasetype(); + if (tb != typeb) + { + // Look for pointers to functions where the functions are overloaded. + FuncDeclaration *f; + + if (hasOverloads && + typeb->ty == Tpointer && typeb->nextOf()->ty == Tfunction && + (tb->ty == Tpointer || tb->ty == Tdelegate) && tb->nextOf()->ty == Tfunction) + { + f = var->isFuncDeclaration(); + if (f) + { + f = f->overloadExactMatch(tb->nextOf()); + if (f) + { + if (tb->ty == Tdelegate) + { + if (f->needThis() && hasThis(sc)) + { + e = new DelegateExp(loc, new ThisExp(loc), f); + e = e->semantic(sc); + } + else if (f->isNested()) + { + e = new DelegateExp(loc, new IntegerExp(0), f); + e = e->semantic(sc); + } + else if (f->needThis()) + { error("no 'this' to create delegate for %s", f->toChars()); + return new ErrorExp(); + } + else + { error("cannot cast from function pointer to delegate"); + return new ErrorExp(); + } + } + else + { + e = new SymOffExp(loc, f, 0); + e->type = t; + } +#if DMDV2 + f->tookAddressOf++; +#endif + return e; + } + } + } + e = Expression::castTo(sc, t); + } + else + { e = copy(); + e->type = t; + ((SymOffExp *)e)->hasOverloads = 0; + } + return e; +} + +Expression *DelegateExp::castTo(Scope *sc, Type *t) +{ +#if 0 + printf("DelegateExp::castTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + static char msg[] = "cannot form delegate due to covariant return type"; + + Expression *e = this; + Type *tb = t->toBasetype(); + Type *typeb = type->toBasetype(); + if (tb != typeb) + { + // Look for delegates to functions where the functions are overloaded. + FuncDeclaration *f; + + if (typeb->ty == Tdelegate && + tb->ty == Tdelegate) + { + if (func) + { + f = func->overloadExactMatch(tb->nextOf()); + if (f) + { int offset; + if (f->tintro && f->tintro->nextOf()->isBaseOf(f->type->nextOf(), &offset) && offset) + error("%s", msg); + f->tookAddressOf++; + e = new DelegateExp(loc, e1, f); + e->type = t; + return e; + } + if (func->tintro) + error("%s", msg); + } + } + e = Expression::castTo(sc, t); + } + else + { int offset; + + func->tookAddressOf++; + if (func->tintro && func->tintro->nextOf()->isBaseOf(func->type->nextOf(), &offset) && offset) + error("%s", msg); + e = copy(); + e->type = t; + } + return e; +} + +Expression *FuncExp::castTo(Scope *sc, Type *t) +{ + //printf("FuncExp::castTo type = %s, t = %s\n", type->toChars(), t->toChars()); + if (tok == TOKreserved) + { assert(type && type != Type::tvoid); + if (type->ty == Tpointer && t->ty == Tdelegate) + { + Expression *e = copy(); + e->type = new TypeDelegate(fd->type); + e->type = e->type->semantic(loc, sc); + return e; + } + } + return Expression::castTo(sc, t); +} + +Expression *CondExp::castTo(Scope *sc, Type *t) +{ + Expression *e = this; + + if (type != t) + { + if (1 || e1->op == TOKstring || e2->op == TOKstring) + { e = new CondExp(loc, econd, e1->castTo(sc, t), e2->castTo(sc, t)); + e->type = t; + } + else + e = Expression::castTo(sc, t); + } + return e; +} + +Expression *CommaExp::castTo(Scope *sc, Type *t) +{ + Expression *e2c = e2->castTo(sc, t); + Expression *e; + + if (e2c != e2) + { + e = new CommaExp(loc, e1, e2c); + e->type = e2c->type; + } + else + { e = this; + e->type = e2->type; + } + return e; +} + +/* ==================== ====================== */ + +/**************************************** + * Scale addition/subtraction to/from pointer. + */ + +Expression *BinExp::scaleFactor(Scope *sc) +{ + if (sc->func && !sc->intypeof) + { + if (sc->func->setUnsafe()) + { + error("pointer arithmetic not allowed in @safe functions"); + return new ErrorExp(); + } + } + + d_uns64 stride; + Type *t1b = e1->type->toBasetype(); + Type *t2b = e2->type->toBasetype(); + + if (t1b->ty == Tpointer && t2b->isintegral()) + { // Need to adjust operator by the stride + // Replace (ptr + int) with (ptr + (int * stride)) + Type *t = Type::tptrdiff_t; + + stride = t1b->nextOf()->size(loc); + if (!t->equals(t2b)) + e2 = e2->castTo(sc, t); + e2 = new MulExp(loc, e2, new IntegerExp(0, stride, t)); + e2->type = t; + type = e1->type; + } + else if (t2b->ty == Tpointer && t1b->isintegral()) + { // Need to adjust operator by the stride + // Replace (int + ptr) with (ptr + (int * stride)) + Type *t = Type::tptrdiff_t; + Expression *e; + + stride = t2b->nextOf()->size(loc); + if (!t->equals(t1b)) + e = e1->castTo(sc, t); + else + e = e1; + e = new MulExp(loc, e, new IntegerExp(0, stride, t)); + e->type = t; + type = e2->type; + e1 = e2; + e2 = e; + } + return this; +} + +/************************************** + * Return true if e is an empty array literal with dimensionality + * equal to or less than type of other array. + * [], [[]], [[[]]], etc. + * I.e., make sure that [1,2] is compatible with [], + * [[1,2]] is compatible with [[]], etc. + */ +bool isVoidArrayLiteral(Expression *e, Type *other) +{ + while (e->op == TOKarrayliteral && e->type->ty == Tarray + && (((ArrayLiteralExp *)e)->elements->dim == 1)) + { + e = ((ArrayLiteralExp *)e)->elements->tdata()[0]; + if (other->ty == Tsarray || other->ty == Tarray) + other = other->nextOf(); + else + return false; + } + if (other->ty != Tsarray && other->ty != Tarray) + return false; + Type *t = e->type; + return (e->op == TOKarrayliteral && t->ty == Tarray && + t->nextOf()->ty == Tvoid && + ((ArrayLiteralExp *)e)->elements->dim == 0); +} + + +/************************************** + * Combine types. + * Output: + * *pt merged type, if *pt is not NULL + * *pe1 rewritten e1 + * *pe2 rewritten e2 + * Returns: + * !=0 success + * 0 failed + */ + +int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression **pe2) +{ + //printf("typeMerge() %s op %s\n", (*pe1)->toChars(), (*pe2)->toChars()); + //e->dump(0); + + Expression *e1 = *pe1; + Expression *e2 = *pe2; + + if (e->op != TOKquestion || + e1->type->toBasetype()->ty != e2->type->toBasetype()->ty) + { + e1 = e1->integralPromotions(sc); + e2 = e2->integralPromotions(sc); + } + + Type *t1 = e1->type; + Type *t2 = e2->type; + assert(t1); + Type *t = t1; + + //if (t1) printf("\tt1 = %s\n", t1->toChars()); + //if (t2) printf("\tt2 = %s\n", t2->toChars()); +#ifdef DEBUG + if (!t2) printf("\te2 = '%s'\n", e2->toChars()); +#endif + assert(t2); + + Type *t1b = t1->toBasetype(); + Type *t2b = t2->toBasetype(); + + TY ty = (TY)Type::impcnvResult[t1b->ty][t2b->ty]; + if (ty != Terror) + { + TY ty1 = (TY)Type::impcnvType1[t1b->ty][t2b->ty]; + TY ty2 = (TY)Type::impcnvType2[t1b->ty][t2b->ty]; + + if (t1b->ty == ty1) // if no promotions + { + if (t1 == t2) + { + t = t1; + goto Lret; + } + + if (t1b == t2b) + { + t = t1b; + goto Lret; + } + } + + t = Type::basic[ty]; + + t1 = Type::basic[ty1]; + t2 = Type::basic[ty2]; + e1 = e1->castTo(sc, t1); + e2 = e2->castTo(sc, t2); + //printf("after typeCombine():\n"); + //dump(0); + //printf("ty = %d, ty1 = %d, ty2 = %d\n", ty, ty1, ty2); + goto Lret; + } + + t1 = t1b; + t2 = t2b; + +Lagain: + if (t1 == t2) + { + } + else if ((t1->ty == Tpointer && t2->ty == Tpointer) || + (t1->ty == Tdelegate && t2->ty == Tdelegate)) + { + // Bring pointers to compatible type + Type *t1n = t1->nextOf(); + Type *t2n = t2->nextOf(); + + if (t1n == t2n) + ; + else if (t1n->ty == Tvoid) // pointers to void are always compatible + t = t2; + else if (t2n->ty == Tvoid) + ; + else if (t1->implicitConvTo(t2)) + { + goto Lt2; + } + else if (t2->implicitConvTo(t1)) + { + goto Lt1; + } + else if (t1n->ty == Tfunction && t2n->ty == Tfunction) + { + TypeFunction *tf1 = (TypeFunction *)t1n; + TypeFunction *tf2 = (TypeFunction *)t2n; + TypeFunction *d = (TypeFunction *)tf1->syntaxCopy(); + + if (tf1->purity != tf2->purity) + d->purity = PUREimpure; + assert(d->purity != PUREfwdref); + + d->isnothrow = (tf1->isnothrow && tf2->isnothrow); + + if (tf1->trust == tf2->trust) + d->trust = tf1->trust; + else if (tf1->trust <= TRUSTsystem || tf2->trust <= TRUSTsystem) + d->trust = TRUSTsystem; + else + d->trust = TRUSTtrusted; + + Type *tx = NULL; + if (t1->ty == Tdelegate) + { + tx = new TypeDelegate(d); + tx = tx->merge(); + } + else + tx = d->pointerTo(); + + if (t1->implicitConvTo(tx) && t2->implicitConvTo(tx)) + { + t = tx; + e1 = e1->castTo(sc, t); + e2 = e2->castTo(sc, t); + goto Lret; + } + goto Lincompatible; + } + else if (t1n->mod != t2n->mod) + { + if (!t1n->isImmutable() && !t2n->isImmutable() && t1n->isShared() != t2n->isShared()) + goto Lincompatible; + unsigned char mod = MODmerge(t1n->mod, t2n->mod); + t1 = t1n->castMod(mod)->pointerTo(); + t2 = t2n->castMod(mod)->pointerTo(); + t = t1; + goto Lagain; + } + else if (t1n->ty == Tclass && t2n->ty == Tclass) + { ClassDeclaration *cd1 = t1n->isClassHandle(); + ClassDeclaration *cd2 = t2n->isClassHandle(); + int offset; + + if (cd1->isBaseOf(cd2, &offset)) + { + if (offset) + e2 = e2->castTo(sc, t); + } + else if (cd2->isBaseOf(cd1, &offset)) + { + t = t2; + if (offset) + e1 = e1->castTo(sc, t); + } + else + goto Lincompatible; + } + else + { + t1 = t1n->constOf()->pointerTo(); + t2 = t2n->constOf()->pointerTo(); + if (t1->implicitConvTo(t2)) + { + goto Lt2; + } + else if (t2->implicitConvTo(t1)) + { + goto Lt1; + } + goto Lincompatible; + } + } + else if ((t1->ty == Tsarray || t1->ty == Tarray) && + (e2->op == TOKnull && t2->ty == Tpointer && t2->nextOf()->ty == Tvoid || + // if e2 is void[] + e2->op == TOKarrayliteral && t2->ty == Tsarray && t2->nextOf()->ty == Tvoid && ((TypeSArray *)t2)->dim->toInteger() == 0 || + isVoidArrayLiteral(e2, t1)) + ) + { /* (T[n] op void*) => T[] + * (T[] op void*) => T[] + * (T[n] op void[0]) => T[] + * (T[] op void[0]) => T[] + * (T[n] op void[]) => T[] + * (T[] op void[]) => T[] + */ + goto Lx1; + } + else if ((t2->ty == Tsarray || t2->ty == Tarray) && + (e1->op == TOKnull && t1->ty == Tpointer && t1->nextOf()->ty == Tvoid || + e1->op == TOKarrayliteral && t1->ty == Tsarray && t1->nextOf()->ty == Tvoid && ((TypeSArray *)t1)->dim->toInteger() == 0 || + isVoidArrayLiteral(e1, t2)) + ) + { /* (void* op T[n]) => T[] + * (void* op T[]) => T[] + * (void[0] op T[n]) => T[] + * (void[0] op T[]) => T[] + * (void[] op T[n]) => T[] + * (void[] op T[]) => T[] + */ + goto Lx2; + } + else if ((t1->ty == Tsarray || t1->ty == Tarray) && t1->implicitConvTo(t2)) + { + if (t1->ty == Tsarray && e2->op == TOKarrayliteral) + goto Lt1; + goto Lt2; + } + else if ((t2->ty == Tsarray || t2->ty == Tarray) && t2->implicitConvTo(t1)) + { + if (t2->ty == Tsarray && e1->op == TOKarrayliteral) + goto Lt2; + goto Lt1; + } + /* If one is mutable and the other invariant, then retry + * with both of them as const + */ + else if ((t1->ty == Tsarray || t1->ty == Tarray || t1->ty == Tpointer) && + (t2->ty == Tsarray || t2->ty == Tarray || t2->ty == Tpointer) && + t1->nextOf()->mod != t2->nextOf()->mod + ) + { + Type *t1n = t1->nextOf(); + Type *t2n = t2->nextOf(); + unsigned char mod; + if (e1->op == TOKnull && e2->op != TOKnull) + mod = t2n->mod; + else if (e1->op != TOKnull && e2->op == TOKnull) + mod = t1n->mod; + else if (!t1n->isImmutable() && !t2n->isImmutable() && t1n->isShared() != t2n->isShared()) + goto Lincompatible; + else + mod = MODmerge(t1n->mod, t2n->mod); + + if (t1->ty == Tpointer) + t1 = t1n->castMod(mod)->pointerTo(); + else + t1 = t1n->castMod(mod)->arrayOf(); + + if (t2->ty == Tpointer) + t2 = t2n->castMod(mod)->pointerTo(); + else + t2 = t2n->castMod(mod)->arrayOf(); + t = t1; + goto Lagain; + } + else if (t1->ty == Tclass && t2->ty == Tclass) + { + if (t1->mod != t2->mod) + { + unsigned char mod; + if (e1->op == TOKnull && e2->op != TOKnull) + mod = t2->mod; + else if (e1->op != TOKnull && e2->op == TOKnull) + mod = t1->mod; + else if (!t1->isImmutable() && !t2->isImmutable() && t1->isShared() != t2->isShared()) + goto Lincompatible; + else + mod = MODmerge(t1->mod, t2->mod); + t1 = t1->castMod(mod); + t2 = t2->castMod(mod); + t = t1; + goto Lagain; + } + goto Lcc; + } + else if (t1->ty == Tclass || t2->ty == Tclass) + { +Lcc: + while (1) + { + int i1 = e2->implicitConvTo(t1); + int i2 = e1->implicitConvTo(t2); + + if (i1 && i2) + { + // We have the case of class vs. void*, so pick class + if (t1->ty == Tpointer) + i1 = 0; + else if (t2->ty == Tpointer) + i2 = 0; + } + + if (i2) + { + goto Lt2; + } + else if (i1) + { + goto Lt1; + } + else if (t1->ty == Tclass && t2->ty == Tclass) + { TypeClass *tc1 = (TypeClass *)t1; + TypeClass *tc2 = (TypeClass *)t2; + + /* Pick 'tightest' type + */ + ClassDeclaration *cd1 = tc1->sym->baseClass; + ClassDeclaration *cd2 = tc2->sym->baseClass; + + if (cd1 && cd2) + { t1 = cd1->type; + t2 = cd2->type; + } + else if (cd1) + t1 = cd1->type; + else if (cd2) + t2 = cd2->type; + else + goto Lincompatible; + } + else if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis) + { + e1 = new DotIdExp(e1->loc, e1, ((TypeStruct *)t1)->sym->aliasthis->ident); + e1 = e1->semantic(sc); + e1 = resolveProperties(sc, e1); + t1 = e1->type; + continue; + } + else if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis) + { + e2 = new DotIdExp(e2->loc, e2, ((TypeStruct *)t2)->sym->aliasthis->ident); + e2 = e2->semantic(sc); + e2 = resolveProperties(sc, e2); + t2 = e2->type; + continue; + } + else + goto Lincompatible; + } + } + else if (t1->ty == Tstruct && t2->ty == Tstruct) + { + if (t1->mod != t2->mod) + { + if (!t1->isImmutable() && !t2->isImmutable() && t1->isShared() != t2->isShared()) + goto Lincompatible; + unsigned char mod = MODmerge(t1->mod, t2->mod); + t1 = t1->castMod(mod); + t2 = t2->castMod(mod); + t = t1; + goto Lagain; + } + + TypeStruct *ts1 = (TypeStruct *)t1; + TypeStruct *ts2 = (TypeStruct *)t2; + if (ts1->sym != ts2->sym) + { + if (!ts1->sym->aliasthis && !ts2->sym->aliasthis) + goto Lincompatible; + + int i1 = 0; + int i2 = 0; + + Expression *e1b = NULL; + Expression *e2b = NULL; + if (ts2->sym->aliasthis) + { + e2b = new DotIdExp(e2->loc, e2, ts2->sym->aliasthis->ident); + e2b = e2b->semantic(sc); + e2b = resolveProperties(sc, e2b); + i1 = e2b->implicitConvTo(t1); + } + if (ts1->sym->aliasthis) + { + e1b = new DotIdExp(e1->loc, e1, ts1->sym->aliasthis->ident); + e1b = e1b->semantic(sc); + e1b = resolveProperties(sc, e1b); + i2 = e1b->implicitConvTo(t2); + } + if (i1 && i2) + goto Lincompatible; + + if (i1) + goto Lt1; + else if (i2) + goto Lt2; + + if (e1b) + { e1 = e1b; + t1 = e1b->type->toBasetype(); + } + if (e2b) + { e2 = e2b; + t2 = e2b->type->toBasetype(); + } + t = t1; + goto Lagain; + } + } + else if (t1->ty == Tstruct || t2->ty == Tstruct) + { + if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis) + { + e1 = new DotIdExp(e1->loc, e1, ((TypeStruct *)t1)->sym->aliasthis->ident); + e1 = e1->semantic(sc); + e1 = resolveProperties(sc, e1); + t1 = e1->type; + t = t1; + goto Lagain; + } + if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis) + { + e2 = new DotIdExp(e2->loc, e2, ((TypeStruct *)t2)->sym->aliasthis->ident); + e2 = e2->semantic(sc); + e2 = resolveProperties(sc, e2); + t2 = e2->type; + t = t2; + goto Lagain; + } + goto Lincompatible; + } + else if ((e1->op == TOKstring || e1->op == TOKnull) && e1->implicitConvTo(t2)) + { + goto Lt2; + } + else if ((e2->op == TOKstring || e2->op == TOKnull) && e2->implicitConvTo(t1)) + { + goto Lt1; + } + else if (t1->ty == Tsarray && t2->ty == Tsarray && + e2->implicitConvTo(t1->nextOf()->arrayOf())) + { + Lx1: + t = t1->nextOf()->arrayOf(); // T[] + e1 = e1->castTo(sc, t); + e2 = e2->castTo(sc, t); + } + else if (t1->ty == Tsarray && t2->ty == Tsarray && + e1->implicitConvTo(t2->nextOf()->arrayOf())) + { + Lx2: + t = t2->nextOf()->arrayOf(); + e1 = e1->castTo(sc, t); + e2 = e2->castTo(sc, t); + } + else if (t1->ty == Tvector && t2->ty != Tvector && + e2->implicitConvTo(t1)) + { + e2 = e2->castTo(sc, t1); + t2 = t1; + goto Lagain; + } + else if (t2->ty == Tvector && t1->ty != Tvector && + e1->implicitConvTo(t2)) + { + e1 = e1->castTo(sc, t2); + t1 = t2; + goto Lagain; + } + else if (t1->isintegral() && t2->isintegral()) + { + assert(t1->ty == t2->ty); + if (!t1->isImmutable() && !t2->isImmutable() && t1->isShared() != t2->isShared()) + goto Lincompatible; + unsigned char mod = MODmerge(t1->mod, t2->mod); + + t1 = t1->castMod(mod); + t2 = t2->castMod(mod); + t = t1; + e1 = e1->castTo(sc, t); + e2 = e2->castTo(sc, t); + goto Lagain; + } + else if (e1->isArrayOperand() && t1->ty == Tarray && + e2->implicitConvTo(t1->nextOf())) + { // T[] op T + e2 = e2->castTo(sc, t1->nextOf()); + t = t1->nextOf()->arrayOf(); + } + else if (e2->isArrayOperand() && t2->ty == Tarray && + e1->implicitConvTo(t2->nextOf())) + { // T op T[] + e1 = e1->castTo(sc, t2->nextOf()); + t = t2->nextOf()->arrayOf(); + + //printf("test %s\n", e->toChars()); + e1 = e1->optimize(WANTvalue); + if (e && e->isCommutative() && e1->isConst()) + { /* Swap operands to minimize number of functions generated + */ + //printf("swap %s\n", e->toChars()); + Expression *tmp = e1; + e1 = e2; + e2 = tmp; + } + } + else + { + Lincompatible: + return 0; + } +Lret: + if (!*pt) + *pt = t; + *pe1 = e1; + *pe2 = e2; +#if 0 + printf("-typeMerge() %s op %s\n", e1->toChars(), e2->toChars()); + if (e1->type) printf("\tt1 = %s\n", e1->type->toChars()); + if (e2->type) printf("\tt2 = %s\n", e2->type->toChars()); + printf("\ttype = %s\n", t->toChars()); +#endif + //dump(0); + return 1; + + +Lt1: + e2 = e2->castTo(sc, t1); + t = t1; + goto Lret; + +Lt2: + e1 = e1->castTo(sc, t2); + t = t2; + goto Lret; +} + +/************************************ + * Bring leaves to common type. + */ + +Expression *BinExp::typeCombine(Scope *sc) +{ + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + + if (op == TOKmin || op == TOKadd) + { + // struct+struct, and class+class are errors + if (t1->ty == Tstruct && t2->ty == Tstruct) + goto Lerror; + else if (t1->ty == Tclass && t2->ty == Tclass) + goto Lerror; + } + + if (!typeMerge(sc, this, &type, &e1, &e2)) + goto Lerror; + return this; + +Lerror: + incompatibleTypes(); + type = Type::terror; + e1 = new ErrorExp(); + e2 = new ErrorExp(); + return new ErrorExp(); +} + +/*********************************** + * Do integral promotions (convertchk). + * Don't convert to + */ + +Expression *Expression::integralPromotions(Scope *sc) +{ + Expression *e = this; + + //printf("integralPromotions %s %s\n", e->toChars(), e->type->toChars()); + switch (type->toBasetype()->ty) + { + case Tvoid: + error("void has no value"); + return new ErrorExp(); + + case Tint8: + case Tuns8: + case Tint16: + case Tuns16: + case Tbool: + case Tchar: + case Twchar: + e = e->castTo(sc, Type::tint32); + break; + + case Tdchar: + e = e->castTo(sc, Type::tuns32); + break; + } + return e; +} + +/*********************************** + * See if both types are arrays that can be compared + * for equality. Return !=0 if so. + * If they are arrays, but incompatible, issue error. + * This is to enable comparing things like an immutable + * array with a mutable one. + */ + +int arrayTypeCompatible(Loc loc, Type *t1, Type *t2) +{ + t1 = t1->toBasetype(); + t2 = t2->toBasetype(); + + if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) && + (t2->ty == Tarray || t2->ty == Tsarray || t2->ty == Tpointer)) + { + if (t1->nextOf()->implicitConvTo(t2->nextOf()) < MATCHconst && + t2->nextOf()->implicitConvTo(t1->nextOf()) < MATCHconst && + (t1->nextOf()->ty != Tvoid && t2->nextOf()->ty != Tvoid)) + { + error(loc, "array equality comparison type mismatch, %s vs %s", t1->toChars(), t2->toChars()); + } + return 1; + } + return 0; +} + +/*********************************** + * See if both types are arrays that can be compared + * for equality without any casting. Return !=0 if so. + * This is to enable comparing things like an immutable + * array with a mutable one. + */ +int arrayTypeCompatibleWithoutCasting(Loc loc, Type *t1, Type *t2) +{ + t1 = t1->toBasetype(); + t2 = t2->toBasetype(); + + if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) && + t2->ty == t1->ty) + { + if (t1->nextOf()->implicitConvTo(t2->nextOf()) >= MATCHconst || + t2->nextOf()->implicitConvTo(t1->nextOf()) >= MATCHconst) + return 1; + } + return 0; +} + + +/******************************************************************/ + +/* Determine the integral ranges of an expression. + * This is used to determine if implicit narrowing conversions will + * be allowed. + */ + +uinteger_t getMask(uinteger_t v) +{ + // Ref: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + return v /* | 0xff*/; +} +IntRange Expression::getIntRange() +{ + return IntRange::fromType(type) DUMP; +} + +IntRange IntegerExp::getIntRange() +{ + return IntRange(value).cast(type) DUMP; +} + +IntRange CastExp::getIntRange() +{ + return e1->getIntRange().cast(type) DUMP; +} + +IntRange AddExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + return IntRange(ir1.imin + ir2.imin, ir1.imax + ir2.imax).cast(type) DUMP; +} + +IntRange MinExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + return IntRange(ir1.imin - ir2.imax, ir1.imax - ir2.imin).cast(type) DUMP; +} + +IntRange DivExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + + // Should we ignore the possibility of div-by-0??? + if (ir2.containsZero()) + return Expression::getIntRange() DUMP; + + // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)] + SignExtendedNumber bdy[4] = { + ir1.imin / ir2.imin, + ir1.imin / ir2.imax, + ir1.imax / ir2.imin, + ir1.imax / ir2.imax + }; + return IntRange::fromNumbers4(bdy).cast(type) DUMP; +} + +IntRange MulExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + + // [a,b] * [c,d] = [min (ac, ad, bc, bd), max (ac, ad, bc, bd)] + SignExtendedNumber bdy[4] = { + ir1.imin * ir2.imin, + ir1.imin * ir2.imax, + ir1.imax * ir2.imin, + ir1.imax * ir2.imax + }; + return IntRange::fromNumbers4(bdy).cast(type) DUMP; +} + +IntRange ModExp::getIntRange() +{ + IntRange irNum = e1->getIntRange(); + IntRange irDen = e2->getIntRange().absNeg(); + + /* + due to the rules of D (C)'s % operator, we need to consider the cases + separately in different range of signs. + + case 1. [500, 1700] % [7, 23] (numerator is always positive) + = [0, 22] + case 2. [-500, 1700] % [7, 23] (numerator can be negative) + = [-22, 22] + case 3. [-1700, -500] % [7, 23] (numerator is always negative) + = [-22, 0] + + the number 22 is the maximum absolute value in the denomator's range. We + don't care about divide by zero. + */ + + // Modding on 0 is invalid anyway. + if (!irDen.imin.negative) + return Expression::getIntRange() DUMP; + + ++ irDen.imin; + irDen.imax = -irDen.imin; + + if (!irNum.imin.negative) + irNum.imin.value = 0; + else if (irNum.imin < irDen.imin) + irNum.imin = irDen.imin; + + if (irNum.imax.negative) + { + irNum.imax.negative = false; + irNum.imax.value = 0; + } + else if (irNum.imax > irDen.imax) + irNum.imax = irDen.imax; + + return irNum.cast(type) DUMP; +} + +// The algorithms for &, |, ^ are not yet the best! Sometimes they will produce +// not the tightest bound. See +// https://github.com/D-Programming-Language/dmd/pull/116 +// for detail. +static IntRange unsignedBitwiseAnd(const IntRange& a, const IntRange& b) +{ + // the DiffMasks stores the mask of bits which are variable in the range. + uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); + uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); + // Since '&' computes the digitwise-minimum, the we could set all varying + // digits to 0 to get a lower bound, and set all varying digits to 1 to get + // an upper bound. + IntRange result; + result.imin.value = (a.imin.value & ~aDiffMask) & (b.imin.value & ~bDiffMask); + result.imax.value = (a.imax.value | aDiffMask) & (b.imax.value | bDiffMask); + // Sometimes the upper bound is overestimated. The upper bound will never + // exceed the input. + if (result.imax.value > a.imax.value) + result.imax.value = a.imax.value; + if (result.imax.value > b.imax.value) + result.imax.value = b.imax.value; + result.imin.negative = result.imax.negative = a.imin.negative && b.imin.negative; + return result; +} +static IntRange unsignedBitwiseOr(const IntRange& a, const IntRange& b) +{ + // the DiffMasks stores the mask of bits which are variable in the range. + uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); + uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); + // The imax algorithm by Adam D. Ruppe. + // http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=108796 + IntRange result; + result.imin.value = (a.imin.value & ~aDiffMask) | (b.imin.value & ~bDiffMask); + result.imax.value = a.imax.value | b.imax.value | getMask(a.imax.value & b.imax.value); + // Sometimes the lower bound is underestimated. The lower bound will never + // less than the input. + if (result.imin.value < a.imin.value) + result.imin.value = a.imin.value; + if (result.imin.value < b.imin.value) + result.imin.value = b.imin.value; + result.imin.negative = result.imax.negative = a.imin.negative || b.imin.negative; + return result; +} +static IntRange unsignedBitwiseXor(const IntRange& a, const IntRange& b) +{ + // the DiffMasks stores the mask of bits which are variable in the range. + uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); + uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); + IntRange result; + result.imin.value = (a.imin.value ^ b.imin.value) & ~(aDiffMask | bDiffMask); + result.imax.value = (a.imax.value ^ b.imax.value) | (aDiffMask | bDiffMask); + result.imin.negative = result.imax.negative = a.imin.negative != b.imin.negative; + return result; +} + + +IntRange AndExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + + IntRange ir1neg, ir1pos, ir2neg, ir2pos; + bool has1neg, has1pos, has2neg, has2pos; + + ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); + ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); + + IntRange result; + bool hasResult = false; + if (has1pos && has2pos) + result.unionOrAssign(unsignedBitwiseAnd(ir1pos, ir2pos), /*ref*/hasResult); + if (has1pos && has2neg) + result.unionOrAssign(unsignedBitwiseAnd(ir1pos, ir2neg), /*ref*/hasResult); + if (has1neg && has2pos) + result.unionOrAssign(unsignedBitwiseAnd(ir1neg, ir2pos), /*ref*/hasResult); + if (has1neg && has2neg) + result.unionOrAssign(unsignedBitwiseAnd(ir1neg, ir2neg), /*ref*/hasResult); + assert(hasResult); + return result.cast(type) DUMP; +} + +IntRange OrExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + + IntRange ir1neg, ir1pos, ir2neg, ir2pos; + bool has1neg, has1pos, has2neg, has2pos; + + ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); + ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); + + IntRange result; + bool hasResult = false; + if (has1pos && has2pos) + result.unionOrAssign(unsignedBitwiseOr(ir1pos, ir2pos), /*ref*/hasResult); + if (has1pos && has2neg) + result.unionOrAssign(unsignedBitwiseOr(ir1pos, ir2neg), /*ref*/hasResult); + if (has1neg && has2pos) + result.unionOrAssign(unsignedBitwiseOr(ir1neg, ir2pos), /*ref*/hasResult); + if (has1neg && has2neg) + result.unionOrAssign(unsignedBitwiseOr(ir1neg, ir2neg), /*ref*/hasResult); + + assert(hasResult); + return result.cast(type) DUMP; +} + +IntRange XorExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + + IntRange ir1neg, ir1pos, ir2neg, ir2pos; + bool has1neg, has1pos, has2neg, has2pos; + + ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); + ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); + + IntRange result; + bool hasResult = false; + if (has1pos && has2pos) + result.unionOrAssign(unsignedBitwiseXor(ir1pos, ir2pos), /*ref*/hasResult); + if (has1pos && has2neg) + result.unionOrAssign(unsignedBitwiseXor(ir1pos, ir2neg), /*ref*/hasResult); + if (has1neg && has2pos) + result.unionOrAssign(unsignedBitwiseXor(ir1neg, ir2pos), /*ref*/hasResult); + if (has1neg && has2neg) + result.unionOrAssign(unsignedBitwiseXor(ir1neg, ir2neg), /*ref*/hasResult); + + assert(hasResult); + return result.cast(type) DUMP; +} + +IntRange ShlExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + + if (ir2.imin.negative) + ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); + + SignExtendedNumber lower = ir1.imin << (ir1.imin.negative ? ir2.imax : ir2.imin); + SignExtendedNumber upper = ir1.imax << (ir1.imax.negative ? ir2.imin : ir2.imax); + + return IntRange(lower, upper).cast(type) DUMP; +} + +IntRange ShrExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + + if (ir2.imin.negative) + ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); + + SignExtendedNumber lower = ir1.imin >> (ir1.imin.negative ? ir2.imin : ir2.imax); + SignExtendedNumber upper = ir1.imax >> (ir1.imax.negative ? ir2.imax : ir2.imin); + + return IntRange(lower, upper).cast(type) DUMP; +} + +IntRange UshrExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange().castUnsigned(e1->type); + IntRange ir2 = e2->getIntRange(); + + if (ir2.imin.negative) + ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); + + return IntRange(ir1.imin >> ir2.imax, ir1.imax >> ir2.imin).cast(type) DUMP; + +} + +IntRange CommaExp::getIntRange() +{ + return e2->getIntRange() DUMP; +} + +IntRange ComExp::getIntRange() +{ + IntRange ir = e1->getIntRange(); + return IntRange(SignExtendedNumber(~ir.imax.value, !ir.imax.negative), + SignExtendedNumber(~ir.imin.value, !ir.imin.negative)).cast(type) DUMP; +} + +IntRange NegExp::getIntRange() +{ + IntRange ir = e1->getIntRange(); + return IntRange(-ir.imax, -ir.imin).cast(type) DUMP; +} + diff --git a/class.c b/class.c new file mode 100644 index 00000000..4121946d --- /dev/null +++ b/class.c @@ -0,0 +1,1648 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include + +#include "root.h" +#include "rmem.h" + +#include "enum.h" +#include "init.h" +#include "attrib.h" +#include "declaration.h" +#include "aggregate.h" +#include "id.h" +#include "mtype.h" +#include "scope.h" +#include "module.h" +#include "expression.h" +#include "statement.h" + +/********************************* ClassDeclaration ****************************/ + +ClassDeclaration *ClassDeclaration::classinfo; +ClassDeclaration *ClassDeclaration::object; +ClassDeclaration *ClassDeclaration::throwable; +ClassDeclaration *ClassDeclaration::exception; +ClassDeclaration *ClassDeclaration::errorException; + +ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses) + : AggregateDeclaration(loc, id) +{ + static char msg[] = "only object.d can define this reserved class name"; + + if (baseclasses) + // Actually, this is a transfer + this->baseclasses = baseclasses; + else + this->baseclasses = new BaseClasses(); + baseClass = NULL; + + interfaces_dim = 0; + interfaces = NULL; + + vtblInterfaces = NULL; + + //printf("ClassDeclaration(%s), dim = %d\n", id->toChars(), this->baseclasses->dim); + + // For forward references + type = new TypeClass(this); + handle = type; + + staticCtor = NULL; + staticDtor = NULL; + + vtblsym = NULL; + vclassinfo = NULL; + + if (id) + { // Look for special class names + + if (id == Id::__sizeof || id == Id::__xalignof || id == Id::mangleof) + error("illegal class name"); + + // BUG: What if this is the wrong TypeInfo, i.e. it is nested? + if (id->toChars()[0] == 'T') + { + if (id == Id::TypeInfo) + { if (Type::typeinfo) + Type::typeinfo->error("%s", msg); + Type::typeinfo = this; + } + + if (id == Id::TypeInfo_Class) + { if (Type::typeinfoclass) + Type::typeinfoclass->error("%s", msg); + Type::typeinfoclass = this; + } + + if (id == Id::TypeInfo_Interface) + { if (Type::typeinfointerface) + Type::typeinfointerface->error("%s", msg); + Type::typeinfointerface = this; + } + + if (id == Id::TypeInfo_Struct) + { if (Type::typeinfostruct) + Type::typeinfostruct->error("%s", msg); + Type::typeinfostruct = this; + } + + if (id == Id::TypeInfo_Typedef) + { if (Type::typeinfotypedef) + Type::typeinfotypedef->error("%s", msg); + Type::typeinfotypedef = this; + } + + if (id == Id::TypeInfo_Pointer) + { if (Type::typeinfopointer) + Type::typeinfopointer->error("%s", msg); + Type::typeinfopointer = this; + } + + if (id == Id::TypeInfo_Array) + { if (Type::typeinfoarray) + Type::typeinfoarray->error("%s", msg); + Type::typeinfoarray = this; + } + + if (id == Id::TypeInfo_StaticArray) + { //if (Type::typeinfostaticarray) + //Type::typeinfostaticarray->error("%s", msg); + Type::typeinfostaticarray = this; + } + + if (id == Id::TypeInfo_AssociativeArray) + { if (Type::typeinfoassociativearray) + Type::typeinfoassociativearray->error("%s", msg); + Type::typeinfoassociativearray = this; + } + + if (id == Id::TypeInfo_Enum) + { if (Type::typeinfoenum) + Type::typeinfoenum->error("%s", msg); + Type::typeinfoenum = this; + } + + if (id == Id::TypeInfo_Function) + { if (Type::typeinfofunction) + Type::typeinfofunction->error("%s", msg); + Type::typeinfofunction = this; + } + + if (id == Id::TypeInfo_Delegate) + { if (Type::typeinfodelegate) + Type::typeinfodelegate->error("%s", msg); + Type::typeinfodelegate = this; + } + + if (id == Id::TypeInfo_Tuple) + { if (Type::typeinfotypelist) + Type::typeinfotypelist->error("%s", msg); + Type::typeinfotypelist = this; + } + +#if DMDV2 + if (id == Id::TypeInfo_Const) + { if (Type::typeinfoconst) + Type::typeinfoconst->error("%s", msg); + Type::typeinfoconst = this; + } + + if (id == Id::TypeInfo_Invariant) + { if (Type::typeinfoinvariant) + Type::typeinfoinvariant->error("%s", msg); + Type::typeinfoinvariant = this; + } + + if (id == Id::TypeInfo_Shared) + { if (Type::typeinfoshared) + Type::typeinfoshared->error("%s", msg); + Type::typeinfoshared = this; + } + + if (id == Id::TypeInfo_Wild) + { if (Type::typeinfowild) + Type::typeinfowild->error("%s", msg); + Type::typeinfowild = this; + } +#endif + } + + if (id == Id::Object) + { if (object) + object->error("%s", msg); + object = this; + } + + if (id == Id::Throwable) + { if (throwable) + throwable->error("%s", msg); + throwable = this; + } + + if (id == Id::Exception) + { if (exception) + exception->error("%s", msg); + exception = this; + } + + if (id == Id::Error) + { if (errorException) + errorException->error("%s", msg); + errorException = this; + } + + //if (id == Id::ClassInfo) + if (id == Id::TypeInfo_Class) + { if (classinfo) + classinfo->error("%s", msg); + classinfo = this; + } + + if (id == Id::ModuleInfo) + { if (Module::moduleinfo) + Module::moduleinfo->error("%s", msg); + Module::moduleinfo = this; + } + } + + com = 0; + isscope = 0; + isabstract = 0; + inuse = 0; +} + +Dsymbol *ClassDeclaration::syntaxCopy(Dsymbol *s) +{ + ClassDeclaration *cd; + + //printf("ClassDeclaration::syntaxCopy('%s')\n", toChars()); + if (s) + cd = (ClassDeclaration *)s; + else + cd = new ClassDeclaration(loc, ident, NULL); + + cd->storage_class |= storage_class; + + cd->baseclasses->setDim(this->baseclasses->dim); + for (size_t i = 0; i < cd->baseclasses->dim; i++) + { + BaseClass *b = this->baseclasses->tdata()[i]; + BaseClass *b2 = new BaseClass(b->type->syntaxCopy(), b->protection); + cd->baseclasses->tdata()[i] = b2; + } + + ScopeDsymbol::syntaxCopy(cd); + return cd; +} + +void ClassDeclaration::semantic(Scope *sc) +{ + //printf("ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); + //printf("\tparent = %p, '%s'\n", sc->parent, sc->parent ? sc->parent->toChars() : ""); + //printf("sc->stc = %x\n", sc->stc); + + //{ static int n; if (++n == 20) *(char*)0=0; } + + if (!ident) // if anonymous class + { const char *id = "__anonclass"; + + ident = Identifier::generateId(id); + } + + if (!sc) + sc = scope; + if (!parent && sc->parent && !sc->parent->isModule()) + parent = sc->parent; + + type = type->semantic(loc, sc); + handle = type; + + if (!members) // if forward reference + { //printf("\tclass '%s' is forward referenced\n", toChars()); + return; + } + if (symtab) + { if (sizeok == 1 || !scope) + { //printf("\tsemantic for '%s' is already completed\n", toChars()); + return; // semantic() already completed + } + } + else + symtab = new DsymbolTable(); + + Scope *scx = NULL; + if (scope) + { sc = scope; + scx = scope; // save so we don't make redundant copies + scope = NULL; + } + unsigned dprogress_save = Module::dprogress; +#ifdef IN_GCC + methods.setDim(0); +#endif + + int errors = global.gaggedErrors; + + if (sc->stc & STCdeprecated) + { + isdeprecated = true; + } + + if (sc->linkage == LINKcpp) + error("cannot create C++ classes"); + + // Expand any tuples in baseclasses[] + for (size_t i = 0; i < baseclasses->dim; ) + { BaseClass *b = baseclasses->tdata()[i]; + b->type = b->type->semantic(loc, sc); + Type *tb = b->type->toBasetype(); + + if (tb->ty == Ttuple) + { TypeTuple *tup = (TypeTuple *)tb; + enum PROT protection = b->protection; + baseclasses->remove(i); + size_t dim = Parameter::dim(tup->arguments); + for (size_t j = 0; j < dim; j++) + { Parameter *arg = Parameter::getNth(tup->arguments, j); + b = new BaseClass(arg->type, protection); + baseclasses->insert(i + j, b); + } + } + else + i++; + } + + // See if there's a base class as first in baseclasses[] + if (baseclasses->dim) + { TypeClass *tc; + BaseClass *b; + Type *tb; + + b = baseclasses->tdata()[0]; + //b->type = b->type->semantic(loc, sc); + tb = b->type->toBasetype(); + if (tb->ty != Tclass) + { error("base type must be class or interface, not %s", b->type->toChars()); + baseclasses->remove(0); + } + else + { + tc = (TypeClass *)(tb); + + if (tc->sym->isDeprecated()) + { + if (!isDeprecated()) + { + // Deriving from deprecated class makes this one deprecated too + isdeprecated = true; + + tc->checkDeprecated(loc, sc); + } + } + + if (tc->sym->isInterfaceDeclaration()) + ; + else + { + for (ClassDeclaration *cdb = tc->sym; cdb; cdb = cdb->baseClass) + { + if (cdb == this) + { + error("circular inheritance"); + baseclasses->remove(0); + goto L7; + } + } + if (!tc->sym->symtab || tc->sym->sizeok == 0) + { // Try to resolve forward reference + if (/*sc->mustsemantic &&*/ tc->sym->scope) + tc->sym->semantic(NULL); + } + if (!tc->sym->symtab || tc->sym->scope || tc->sym->sizeok == 0) + { + //printf("%s: forward reference of base class %s\n", toChars(), tc->sym->toChars()); + //error("forward reference of base class %s", baseClass->toChars()); + // Forward reference of base class, try again later + //printf("\ttry later, forward reference of base class %s\n", tc->sym->toChars()); + scope = scx ? scx : new Scope(*sc); + scope->setNoFree(); + if (tc->sym->scope) + tc->sym->scope->module->addDeferredSemantic(tc->sym); + scope->module->addDeferredSemantic(this); + return; + } + else + { baseClass = tc->sym; + b->base = baseClass; + } + L7: ; + } + } + } + + // Treat the remaining entries in baseclasses as interfaces + // Check for errors, handle forward references + for (size_t i = (baseClass ? 1 : 0); i < baseclasses->dim; ) + { TypeClass *tc; + BaseClass *b; + Type *tb; + + b = baseclasses->tdata()[i]; + b->type = b->type->semantic(loc, sc); + tb = b->type->toBasetype(); + if (tb->ty == Tclass) + tc = (TypeClass *)tb; + else + tc = NULL; + if (!tc || !tc->sym->isInterfaceDeclaration()) + { + error("base type must be interface, not %s", b->type->toChars()); + baseclasses->remove(i); + continue; + } + else + { + if (tc->sym->isDeprecated()) + { + if (!isDeprecated()) + { + // Deriving from deprecated class makes this one deprecated too + isdeprecated = true; + + tc->checkDeprecated(loc, sc); + } + } + + // Check for duplicate interfaces + for (size_t j = (baseClass ? 1 : 0); j < i; j++) + { + BaseClass *b2 = baseclasses->tdata()[j]; + if (b2->base == tc->sym) + error("inherits from duplicate interface %s", b2->base->toChars()); + } + + if (!tc->sym->symtab) + { // Try to resolve forward reference + if (/*sc->mustsemantic &&*/ tc->sym->scope) + tc->sym->semantic(NULL); + } + + b->base = tc->sym; + if (!b->base->symtab || b->base->scope) + { + //error("forward reference of base class %s", baseClass->toChars()); + // Forward reference of base, try again later + //printf("\ttry later, forward reference of base %s\n", baseClass->toChars()); + scope = scx ? scx : new Scope(*sc); + scope->setNoFree(); + if (tc->sym->scope) + tc->sym->scope->module->addDeferredSemantic(tc->sym); + scope->module->addDeferredSemantic(this); + return; + } + } + i++; + } + + + // If no base class, and this is not an Object, use Object as base class + if (!baseClass && ident != Id::Object) + { + // BUG: what if Object is redefined in an inner scope? + Type *tbase = new TypeIdentifier(0, Id::Object); + BaseClass *b; + TypeClass *tc; + Type *bt; + + if (!object) + { + error("missing or corrupt object.d"); + fatal(); + } + bt = tbase->semantic(loc, sc)->toBasetype(); + b = new BaseClass(bt, PROTpublic); + baseclasses->shift(b); + assert(b->type->ty == Tclass); + tc = (TypeClass *)(b->type); + baseClass = tc->sym; + assert(!baseClass->isInterfaceDeclaration()); + b->base = baseClass; + } + + interfaces_dim = baseclasses->dim; + interfaces = baseclasses->tdata(); + + + if (baseClass) + { + if (baseClass->storage_class & STCfinal) + error("cannot inherit from final class %s", baseClass->toChars()); + + interfaces_dim--; + interfaces++; + + // Copy vtbl[] from base class + vtbl.setDim(baseClass->vtbl.dim); + memcpy(vtbl.tdata(), baseClass->vtbl.tdata(), sizeof(void *) * vtbl.dim); + + // Inherit properties from base class + com = baseClass->isCOMclass(); + isscope = baseClass->isscope; + vthis = baseClass->vthis; + storage_class |= baseClass->storage_class & STC_TYPECTOR; + } + else + { + // No base class, so this is the root of the class hierarchy + vtbl.setDim(0); + vtbl.push(this); // leave room for classinfo as first member + } + + protection = sc->protection; + storage_class |= sc->stc; + + if (sizeok == 0) + { + interfaceSemantic(sc); + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->addMember(sc, this, 1); + } + + /* If this is a nested class, add the hidden 'this' + * member which is a pointer to the enclosing scope. + */ + if (vthis) // if inheriting from nested class + { // Use the base class's 'this' member + isnested = 1; + if (storage_class & STCstatic) + error("static class cannot inherit from nested class %s", baseClass->toChars()); + if (toParent2() != baseClass->toParent2()) + { + if (toParent2()) + { + error("is nested within %s, but super class %s is nested within %s", + toParent2()->toChars(), + baseClass->toChars(), + baseClass->toParent2()->toChars()); + } + else + { + error("is not nested, but super class %s is nested within %s", + baseClass->toChars(), + baseClass->toParent2()->toChars()); + } + isnested = 0; + } + } + else if (!(storage_class & STCstatic)) + { Dsymbol *s = toParent2(); + if (s) + { + AggregateDeclaration *ad = s->isClassDeclaration(); + FuncDeclaration *fd = s->isFuncDeclaration(); + + + if (ad || fd) + { isnested = 1; + Type *t; + if (ad) + t = ad->handle; + else if (fd) + { AggregateDeclaration *ad2 = fd->isMember2(); + if (ad2) + t = ad2->handle; + else + { + t = Type::tvoidptr; + } + } + else + assert(0); + if (t->ty == Tstruct) // ref to struct + t = Type::tvoidptr; + assert(!vthis); + vthis = new ThisDeclaration(loc, t); + members->push(vthis); + } + } + } + } + + if (storage_class & STCauto) + error("storage class 'auto' is invalid when declaring a class, did you mean to use 'scope'?"); + if (storage_class & STCscope) + isscope = 1; + if (storage_class & STCabstract) + isabstract = 1; + + if (storage_class & STCimmutable) + type = type->addMod(MODimmutable); + if (storage_class & STCconst) + type = type->addMod(MODconst); + if (storage_class & STCshared) + type = type->addMod(MODshared); + + sc = sc->push(this); + //sc->stc &= ~(STCfinal | STCauto | STCscope | STCstatic | STCabstract | STCdeprecated | STC_TYPECTOR | STCtls | STCgshared); + //sc->stc |= storage_class & STC_TYPECTOR; + sc->stc &= STCsafe | STCtrusted | STCsystem; + sc->parent = this; + sc->inunion = 0; + + if (isCOMclass()) + { +#if _WIN32 + sc->linkage = LINKwindows; +#else + /* This enables us to use COM objects under Linux and + * work with things like XPCOM + */ + sc->linkage = LINKc; +#endif + } + sc->protection = PROTpublic; + sc->explicitProtection = 0; + sc->structalign = 8; + structalign = sc->structalign; + if (baseClass) + { sc->offset = baseClass->structsize; + alignsize = baseClass->alignsize; +// if (isnested) +// sc->offset += PTRSIZE; // room for uplevel context pointer + } + else + { sc->offset = PTRSIZE * 2; // allow room for __vptr and __monitor + alignsize = PTRSIZE; + } + structsize = sc->offset; + Scope scsave = *sc; + size_t members_dim = members->dim; + sizeok = 0; + + /* Set scope so if there are forward references, we still might be able to + * resolve individual members like enums. + */ + for (size_t i = 0; i < members_dim; i++) + { Dsymbol *s = members->tdata()[i]; + /* There are problems doing this in the general case because + * Scope keeps track of things like 'offset' + */ + if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident)) + { + //printf("setScope %s %s\n", s->kind(), s->toChars()); + s->setScope(sc); + } + } + + for (size_t i = 0; i < members_dim; i++) + { Dsymbol *s = members->tdata()[i]; + s->semantic(sc); + } + + if (global.gag && global.gaggedErrors != errors) + { // The type is no good, yet the error messages were gagged. + type = Type::terror; + } + + if (sizeok == 2) // failed due to forward references + { // semantic() failed due to forward references + // Unwind what we did, and defer it for later + + fields.setDim(0); + structsize = 0; + alignsize = 0; + structalign = 0; + + sc = sc->pop(); + + scope = scx ? scx : new Scope(*sc); + scope->setNoFree(); + scope->module->addDeferredSemantic(this); + + Module::dprogress = dprogress_save; + + //printf("\tsemantic('%s') failed due to forward references\n", toChars()); + return; + } + + //printf("\tsemantic('%s') successful\n", toChars()); + + structsize = sc->offset; + //members->print(); + + /* Look for special member functions. + * They must be in this class, not in a base class. + */ + ctor = (CtorDeclaration *)search(0, Id::ctor, 0); + if (ctor && (ctor->toParent() != this || !ctor->isCtorDeclaration())) + ctor = NULL; + +// dtor = (DtorDeclaration *)search(Id::dtor, 0); +// if (dtor && dtor->toParent() != this) +// dtor = NULL; + +// inv = (InvariantDeclaration *)search(Id::classInvariant, 0); +// if (inv && inv->toParent() != this) +// inv = NULL; + + // Can be in base class + aggNew = (NewDeclaration *)search(0, Id::classNew, 0); + aggDelete = (DeleteDeclaration *)search(0, Id::classDelete, 0); + + // If this class has no constructor, but base class does, create + // a constructor: + // this() { } + if (!ctor && baseClass && baseClass->ctor) + { + //printf("Creating default this(){} for class %s\n", toChars()); + Type *tf = new TypeFunction(NULL, NULL, 0, LINKd, 0); + CtorDeclaration *ctor = new CtorDeclaration(loc, 0, 0, tf); + ctor->fbody = new CompoundStatement(0, new Statements()); + members->push(ctor); + ctor->addMember(sc, this, 1); + *sc = scsave; // why? What about sc->nofree? + sc->offset = structsize; + ctor->semantic(sc); + this->ctor = ctor; + defaultCtor = ctor; + } + +#if 0 + if (baseClass) + { if (!aggDelete) + aggDelete = baseClass->aggDelete; + if (!aggNew) + aggNew = baseClass->aggNew; + } +#endif + + // Allocate instance of each new interface + for (size_t i = 0; i < vtblInterfaces->dim; i++) + { + BaseClass *b = vtblInterfaces->tdata()[i]; + unsigned thissize = PTRSIZE; + + alignmember(structalign, thissize, &sc->offset); + assert(b->offset == 0); + b->offset = sc->offset; + + // Take care of single inheritance offsets + while (b->baseInterfaces_dim) + { + b = &b->baseInterfaces[0]; + b->offset = sc->offset; + } + + sc->offset += thissize; + if (alignsize < thissize) + alignsize = thissize; + } + structsize = sc->offset; + sizeok = 1; + Module::dprogress++; + + dtor = buildDtor(sc); + + sc->pop(); + +#if 0 // Do not call until toObjfile() because of forward references + // Fill in base class vtbl[]s + for (i = 0; i < vtblInterfaces->dim; i++) + { + BaseClass *b = vtblInterfaces->tdata()[i]; + + //b->fillVtbl(this, &b->vtbl, 1); + } +#endif + //printf("-ClassDeclaration::semantic(%s), type = %p\n", toChars(), type); + + if (deferred && !global.gag) + { + deferred->semantic2(sc); + deferred->semantic3(sc); + } +} + +void ClassDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (!isAnonymous()) + { + buf->printf("%s ", kind()); + buf->writestring(toChars()); + if (baseclasses->dim) + buf->writestring(" : "); + } + for (size_t i = 0; i < baseclasses->dim; i++) + { + BaseClass *b = baseclasses->tdata()[i]; + + if (i) + buf->writeByte(','); + //buf->writestring(b->base->ident->toChars()); + b->type->toCBuffer(buf, NULL, hgs); + } + if (members) + { + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + + buf->writestring(" "); + s->toCBuffer(buf, hgs); + } + buf->writestring("}"); + } + else + buf->writeByte(';'); + buf->writenl(); +} + +#if 0 +void ClassDeclaration::defineRef(Dsymbol *s) +{ + ClassDeclaration *cd; + + AggregateDeclaration::defineRef(s); + cd = s->isClassDeclaration(); + baseType = cd->baseType; + cd->baseType = NULL; +} +#endif + +/********************************************* + * Determine if 'this' is a base class of cd. + * This is used to detect circular inheritance only. + */ + +int ClassDeclaration::isBaseOf2(ClassDeclaration *cd) +{ + if (!cd) + return 0; + //printf("ClassDeclaration::isBaseOf2(this = '%s', cd = '%s')\n", toChars(), cd->toChars()); + for (size_t i = 0; i < cd->baseclasses->dim; i++) + { BaseClass *b = cd->baseclasses->tdata()[i]; + + if (b->base == this || isBaseOf2(b->base)) + return 1; + } + return 0; +} + +/******************************************* + * Determine if 'this' is a base class of cd. + */ + +int ClassDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) +{ + //printf("ClassDeclaration::isBaseOf(this = '%s', cd = '%s')\n", toChars(), cd->toChars()); + if (poffset) + *poffset = 0; + while (cd) + { + /* cd->baseClass might not be set if cd is forward referenced. + */ + if (!cd->baseClass && cd->baseclasses->dim && !cd->isInterfaceDeclaration()) + { + cd->semantic(NULL); + if (!cd->baseClass) + cd->error("base class is forward referenced by %s", toChars()); + } + + if (this == cd->baseClass) + return 1; + + cd = cd->baseClass; + } + return 0; +} + +/********************************************* + * Determine if 'this' has complete base class information. + * This is used to detect forward references in covariant overloads. + */ + +int ClassDeclaration::isBaseInfoComplete() +{ + if (!baseClass) + return ident == Id::Object; + for (size_t i = 0; i < baseclasses->dim; i++) + { BaseClass *b = baseclasses->tdata()[i]; + if (!b->base || !b->base->isBaseInfoComplete()) + return 0; + } + return 1; +} + +Dsymbol *ClassDeclaration::search(Loc loc, Identifier *ident, int flags) +{ + Dsymbol *s; + //printf("%s.ClassDeclaration::search('%s')\n", toChars(), ident->toChars()); + + if (scope && !symtab) + { Scope *sc = scope; + sc->mustsemantic++; + semantic(sc); + sc->mustsemantic--; + } + + if (!members || !symtab) + { + error("is forward referenced when looking for '%s'", ident->toChars()); + //*(char*)0=0; + return NULL; + } + + s = ScopeDsymbol::search(loc, ident, flags); + if (!s) + { + // Search bases classes in depth-first, left to right order + + for (size_t i = 0; i < baseclasses->dim; i++) + { + BaseClass *b = baseclasses->tdata()[i]; + + if (b->base) + { + if (!b->base->symtab) + error("base %s is forward referenced", b->base->ident->toChars()); + else + { + s = b->base->search(loc, ident, flags); + if (s == this) // happens if s is nested in this and derives from this + s = NULL; + else if (s) + break; + } + } + } + } + return s; +} + +Dsymbol *ClassDeclaration::searchBase(Loc loc, Identifier *ident) +{ + // Search bases classes in depth-first, left to right order + + for (size_t i = 0; i < baseclasses->dim; i++) + { + BaseClass *b = (*baseclasses)[i]; + Dsymbol *cdb = b->type->isClassHandle(); + if (cdb->ident->equals(ident)) + return cdb; + cdb = ((ClassDeclaration *)cdb)->searchBase(loc, ident); + if (cdb) + return cdb; + } + return NULL; +} + +/********************************************************** + * fd is in the vtbl[] for this class. + * Return 1 if function is hidden (not findable through search). + */ + +#if DMDV2 +int isf(void *param, FuncDeclaration *fd) +{ + //printf("param = %p, fd = %p %s\n", param, fd, fd->toChars()); + return param == fd; +} + +int ClassDeclaration::isFuncHidden(FuncDeclaration *fd) +{ + //printf("ClassDeclaration::isFuncHidden(class = %s, fd = %s)\n", toChars(), fd->toChars()); + Dsymbol *s = search(0, fd->ident, 4|2); + if (!s) + { //printf("not found\n"); + /* Because, due to a hack, if there are multiple definitions + * of fd->ident, NULL is returned. + */ + return 0; + } + s = s->toAlias(); + OverloadSet *os = s->isOverloadSet(); + if (os) + { + for (size_t i = 0; i < os->a.dim; i++) + { Dsymbol *s2 = os->a.tdata()[i]; + FuncDeclaration *f2 = s2->isFuncDeclaration(); + if (f2 && overloadApply(f2, &isf, fd)) + return 0; + } + return 1; + } + else + { + FuncDeclaration *fdstart = s->isFuncDeclaration(); + //printf("%s fdstart = %p\n", s->kind(), fdstart); + if (overloadApply(fdstart, &isf, fd)) + return 0; + + return !fd->parent->isTemplateMixin(); + } +} +#endif + +/**************** + * Find virtual function matching identifier and type. + * Used to build virtual function tables for interface implementations. + */ + +FuncDeclaration *ClassDeclaration::findFunc(Identifier *ident, TypeFunction *tf) +{ + //printf("ClassDeclaration::findFunc(%s, %s) %s\n", ident->toChars(), tf->toChars(), toChars()); + FuncDeclaration *fdmatch = NULL; + FuncDeclaration *fdambig = NULL; + + ClassDeclaration *cd = this; + Dsymbols *vtbl = &cd->vtbl; + while (1) + { + for (size_t i = 0; i < vtbl->dim; i++) + { + FuncDeclaration *fd = vtbl->tdata()[i]->isFuncDeclaration(); + if (!fd) + continue; // the first entry might be a ClassInfo + + //printf("\t[%d] = %s\n", i, fd->toChars()); + if (ident == fd->ident && + fd->type->covariant(tf) == 1) + { //printf("fd->parent->isClassDeclaration() = %p", fd->parent->isClassDeclaration()); + if (!fdmatch) + goto Lfd; + + { + // Function type matcing: exact > covariant + int m1 = tf->equals(fd ->type) ? MATCHexact : MATCHnomatch; + int m2 = tf->equals(fdmatch->type) ? MATCHexact : MATCHnomatch; + if (m1 > m2) + goto Lfd; + else if (m1 < m2) + goto Lfdmatch; + } + + { + // The way of definition: non-mixin > mixin + int m1 = fd ->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch; + int m2 = fdmatch->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch; + if (m1 > m2) + goto Lfd; + else if (m1 < m2) + goto Lfdmatch; + } + + Lambig: + fdambig = fd; + //printf("Lambig fdambig = %s %s [%s]\n", fdambig->toChars(), fdambig->type->toChars(), fdambig->loc.toChars()); + continue; + + Lfd: + fdmatch = fd, fdambig = NULL; + //printf("Lfd fdmatch = %s %s [%s]\n", fdmatch->toChars(), fdmatch->type->toChars(), fdmatch->loc.toChars()); + continue; + + Lfdmatch: + continue; + } + //else printf("\t\t%d\n", fd->type->covariant(tf)); + } + if (!cd) + break; + vtbl = &cd->vtblFinal; + cd = cd->baseClass; + } + + if (fdambig) + error("ambiguous virtual function %s", fdambig->toChars()); + return fdmatch; +} + +void ClassDeclaration::interfaceSemantic(Scope *sc) +{ + InterfaceDeclaration *id = isInterfaceDeclaration(); + + vtblInterfaces = new BaseClasses(); + vtblInterfaces->reserve(interfaces_dim); + + for (size_t i = 0; i < interfaces_dim; i++) + { + BaseClass *b = interfaces[i]; + + // If this is an interface, and it derives from a COM interface, + // then this is a COM interface too. + if (b->base->isCOMinterface()) + com = 1; + + if (b->base->isCPPinterface() && id) + id->cpp = 1; + + vtblInterfaces->push(b); + b->copyBaseInterfaces(vtblInterfaces); + } +} + +/**************************************** + */ + +int ClassDeclaration::isCOMclass() +{ + return com; +} + +int ClassDeclaration::isCOMinterface() +{ + return 0; +} + +#if DMDV2 +int ClassDeclaration::isCPPinterface() +{ + return 0; +} +#endif + + +/**************************************** + */ + +int ClassDeclaration::isAbstract() +{ + if (isabstract) + return TRUE; + for (size_t i = 1; i < vtbl.dim; i++) + { + FuncDeclaration *fd = vtbl.tdata()[i]->isFuncDeclaration(); + + //printf("\tvtbl[%d] = %p\n", i, fd); + if (!fd || fd->isAbstract()) + { + isabstract |= 1; + return TRUE; + } + } + return FALSE; +} + + +/**************************************** + * Determine if slot 0 of the vtbl[] is reserved for something else. + * For class objects, yes, this is where the classinfo ptr goes. + * For COM interfaces, no. + * For non-COM interfaces, yes, this is where the Interface ptr goes. + */ + +int ClassDeclaration::vtblOffset() +{ + return 1; +} + +/**************************************** + */ + +const char *ClassDeclaration::kind() +{ + return "class"; +} + +/**************************************** + */ + +void ClassDeclaration::addLocalClass(ClassDeclarations *aclasses) +{ + aclasses->push(this); +} + +/********************************* InterfaceDeclaration ****************************/ + +InterfaceDeclaration::InterfaceDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses) + : ClassDeclaration(loc, id, baseclasses) +{ + com = 0; + cpp = 0; + if (id == Id::IUnknown) // IUnknown is the root of all COM interfaces + { com = 1; + cpp = 1; // IUnknown is also a C++ interface + } +} + +Dsymbol *InterfaceDeclaration::syntaxCopy(Dsymbol *s) +{ + InterfaceDeclaration *id; + + if (s) + id = (InterfaceDeclaration *)s; + else + id = new InterfaceDeclaration(loc, ident, NULL); + + ClassDeclaration::syntaxCopy(id); + return id; +} + +void InterfaceDeclaration::semantic(Scope *sc) +{ + //printf("InterfaceDeclaration::semantic(%s), type = %p\n", toChars(), type); + if (inuse) + return; + + if (!sc) + sc = scope; + if (!parent && sc->parent && !sc->parent->isModule()) + parent = sc->parent; + + type = type->semantic(loc, sc); + handle = type; + + if (!members) // if forward reference + { //printf("\tinterface '%s' is forward referenced\n", toChars()); + return; + } + if (symtab) // if already done + { if (!scope) + return; + } + else + symtab = new DsymbolTable(); + + Scope *scx = NULL; + if (scope) + { sc = scope; + scx = scope; // save so we don't make redundant copies + scope = NULL; + } + + int errors = global.gaggedErrors; + + if (sc->stc & STCdeprecated) + { + isdeprecated = true; + } + + // Expand any tuples in baseclasses[] + for (size_t i = 0; i < baseclasses->dim; ) + { BaseClass *b = baseclasses->tdata()[0]; + b->type = b->type->semantic(loc, sc); + Type *tb = b->type->toBasetype(); + + if (tb->ty == Ttuple) + { TypeTuple *tup = (TypeTuple *)tb; + enum PROT protection = b->protection; + baseclasses->remove(i); + size_t dim = Parameter::dim(tup->arguments); + for (size_t j = 0; j < dim; j++) + { Parameter *arg = Parameter::getNth(tup->arguments, j); + b = new BaseClass(arg->type, protection); + baseclasses->insert(i + j, b); + } + } + else + i++; + } + + if (!baseclasses->dim && sc->linkage == LINKcpp) + cpp = 1; + + // Check for errors, handle forward references + for (size_t i = 0; i < baseclasses->dim; ) + { TypeClass *tc; + BaseClass *b; + Type *tb; + + b = baseclasses->tdata()[i]; + b->type = b->type->semantic(loc, sc); + tb = b->type->toBasetype(); + if (tb->ty == Tclass) + tc = (TypeClass *)tb; + else + tc = NULL; + if (!tc || !tc->sym->isInterfaceDeclaration()) + { + error("base type must be interface, not %s", b->type->toChars()); + baseclasses->remove(i); + continue; + } + else + { + // Check for duplicate interfaces + for (size_t j = 0; j < i; j++) + { + BaseClass *b2 = baseclasses->tdata()[j]; + if (b2->base == tc->sym) + error("inherits from duplicate interface %s", b2->base->toChars()); + } + + b->base = tc->sym; + if (b->base == this || isBaseOf2(b->base)) + { + error("circular inheritance of interface"); + baseclasses->remove(i); + continue; + } + if (!b->base->symtab) + { // Try to resolve forward reference + if (sc->mustsemantic && b->base->scope) + b->base->semantic(NULL); + } + if (!b->base->symtab || b->base->scope || b->base->inuse) + { + //error("forward reference of base class %s", baseClass->toChars()); + // Forward reference of base, try again later + //printf("\ttry later, forward reference of base %s\n", b->base->toChars()); + scope = scx ? scx : new Scope(*sc); + scope->setNoFree(); + scope->module->addDeferredSemantic(this); + return; + } + } +#if 0 + // Inherit const/invariant from base class + storage_class |= b->base->storage_class & STC_TYPECTOR; +#endif + i++; + } + + interfaces_dim = baseclasses->dim; + interfaces = baseclasses->tdata(); + + interfaceSemantic(sc); + + if (vtblOffset()) + vtbl.push(this); // leave room at vtbl[0] for classinfo + + // Cat together the vtbl[]'s from base interfaces + for (size_t i = 0; i < interfaces_dim; i++) + { BaseClass *b = interfaces[i]; + + // Skip if b has already appeared + for (int k = 0; k < i; k++) + { + if (b == interfaces[k]) + goto Lcontinue; + } + + // Copy vtbl[] from base class + if (b->base->vtblOffset()) + { int d = b->base->vtbl.dim; + if (d > 1) + { + vtbl.reserve(d - 1); + for (int j = 1; j < d; j++) + vtbl.push(b->base->vtbl.tdata()[j]); + } + } + else + { + vtbl.append(&b->base->vtbl); + } + + Lcontinue: + ; + } + + protection = sc->protection; + storage_class |= sc->stc & STC_TYPECTOR; + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->addMember(sc, this, 1); + } + + sc = sc->push(this); + sc->stc &= STCsafe | STCtrusted | STCsystem; + sc->parent = this; + if (isCOMinterface()) + sc->linkage = LINKwindows; + else if (isCPPinterface()) + sc->linkage = LINKcpp; + sc->structalign = 8; + sc->protection = PROTpublic; + sc->explicitProtection = 0; + structalign = sc->structalign; + sc->offset = PTRSIZE * 2; + inuse++; + + /* Set scope so if there are forward references, we still might be able to + * resolve individual members like enums. + */ + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = (*members)[i]; + /* There are problems doing this in the general case because + * Scope keeps track of things like 'offset' + */ + if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident)) + { + //printf("setScope %s %s\n", s->kind(), s->toChars()); + s->setScope(sc); + } + } + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->semantic(sc); + } + + if (global.gag && global.gaggedErrors != errors) + { // The type is no good, yet the error messages were gagged. + type = Type::terror; + } + + inuse--; + //members->print(); + sc->pop(); + //printf("-InterfaceDeclaration::semantic(%s), type = %p\n", toChars(), type); +} + + +/******************************************* + * Determine if 'this' is a base class of cd. + * (Actually, if it is an interface supported by cd) + * Output: + * *poffset offset to start of class + * OFFSET_RUNTIME must determine offset at runtime + * Returns: + * 0 not a base + * 1 is a base + */ + +int InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) +{ + unsigned j; + + //printf("%s.InterfaceDeclaration::isBaseOf(cd = '%s')\n", toChars(), cd->toChars()); + assert(!baseClass); + for (j = 0; j < cd->interfaces_dim; j++) + { + BaseClass *b = cd->interfaces[j]; + + //printf("\tbase %s\n", b->base->toChars()); + if (this == b->base) + { + //printf("\tfound at offset %d\n", b->offset); + if (poffset) + { *poffset = b->offset; + if (j && cd->isInterfaceDeclaration()) + *poffset = OFFSET_RUNTIME; + } + return 1; + } + if (isBaseOf(b, poffset)) + { if (j && poffset && cd->isInterfaceDeclaration()) + *poffset = OFFSET_RUNTIME; + return 1; + } + } + + if (cd->baseClass && isBaseOf(cd->baseClass, poffset)) + return 1; + + if (poffset) + *poffset = 0; + return 0; +} + + +int InterfaceDeclaration::isBaseOf(BaseClass *bc, int *poffset) +{ + //printf("%s.InterfaceDeclaration::isBaseOf(bc = '%s')\n", toChars(), bc->base->toChars()); + for (unsigned j = 0; j < bc->baseInterfaces_dim; j++) + { + BaseClass *b = &bc->baseInterfaces[j]; + + if (this == b->base) + { + if (poffset) + { *poffset = b->offset; + if (j && bc->base->isInterfaceDeclaration()) + *poffset = OFFSET_RUNTIME; + } + return 1; + } + if (isBaseOf(b, poffset)) + { if (j && poffset && bc->base->isInterfaceDeclaration()) + *poffset = OFFSET_RUNTIME; + return 1; + } + } + if (poffset) + *poffset = 0; + return 0; +} + +/********************************************* + * Determine if 'this' has clomplete base class information. + * This is used to detect forward references in covariant overloads. + */ + +int InterfaceDeclaration::isBaseInfoComplete() +{ + assert(!baseClass); + for (size_t i = 0; i < baseclasses->dim; i++) + { BaseClass *b = baseclasses->tdata()[i]; + if (!b->base || !b->base->isBaseInfoComplete ()) + return 0; + } + return 1; +} + +/**************************************** + * Determine if slot 0 of the vtbl[] is reserved for something else. + * For class objects, yes, this is where the ClassInfo ptr goes. + * For COM interfaces, no. + * For non-COM interfaces, yes, this is where the Interface ptr goes. + */ + +int InterfaceDeclaration::vtblOffset() +{ + if (isCOMinterface() || isCPPinterface()) + return 0; + return 1; +} + +int InterfaceDeclaration::isCOMinterface() +{ + return com; +} + +#if DMDV2 +int InterfaceDeclaration::isCPPinterface() +{ + return cpp; +} +#endif + +/******************************************* + */ + +const char *InterfaceDeclaration::kind() +{ + return "interface"; +} + + +/******************************** BaseClass *****************************/ + +BaseClass::BaseClass() +{ + memset(this, 0, sizeof(BaseClass)); +} + +BaseClass::BaseClass(Type *type, enum PROT protection) +{ + //printf("BaseClass(this = %p, '%s')\n", this, type->toChars()); + this->type = type; + this->protection = protection; + base = NULL; + offset = 0; + + baseInterfaces_dim = 0; + baseInterfaces = NULL; +} + +/**************************************** + * Fill in vtbl[] for base class based on member functions of class cd. + * Input: + * vtbl if !=NULL, fill it in + * newinstance !=0 means all entries must be filled in by members + * of cd, not members of any base classes of cd. + * Returns: + * !=0 if any entries were filled in by members of cd (not exclusively + * by base classes) + */ + +int BaseClass::fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance) +{ + ClassDeclaration *id = base; + int result = 0; + + //printf("BaseClass::fillVtbl(this='%s', cd='%s')\n", base->toChars(), cd->toChars()); + if (vtbl) + vtbl->setDim(base->vtbl.dim); + + // first entry is ClassInfo reference + for (size_t j = base->vtblOffset(); j < base->vtbl.dim; j++) + { + FuncDeclaration *ifd = base->vtbl.tdata()[j]->isFuncDeclaration(); + FuncDeclaration *fd; + TypeFunction *tf; + + //printf(" vtbl[%d] is '%s'\n", j, ifd ? ifd->toChars() : "null"); + + assert(ifd); + // Find corresponding function in this class + tf = (ifd->type->ty == Tfunction) ? (TypeFunction *)(ifd->type) : NULL; + fd = cd->findFunc(ifd->ident, tf); + if (fd && !fd->isAbstract()) + { + //printf(" found\n"); + // Check that calling conventions match + if (fd->linkage != ifd->linkage) + fd->error("linkage doesn't match interface function"); + + // Check that it is current + if (newinstance && + fd->toParent() != cd && + ifd->toParent() == base) + cd->error("interface function %s.%s is not implemented", + id->toChars(), ifd->ident->toChars()); + + if (fd->toParent() == cd) + result = 1; + } + else + { + //printf(" not found\n"); + // BUG: should mark this class as abstract? + if (!cd->isAbstract()) + cd->error("interface function %s.%s isn't implemented", + id->toChars(), ifd->ident->toChars()); + fd = NULL; + } + if (vtbl) + vtbl->tdata()[j] = fd; + } + + return result; +} + +void BaseClass::copyBaseInterfaces(BaseClasses *vtblInterfaces) +{ + //printf("+copyBaseInterfaces(), %s\n", base->toChars()); +// if (baseInterfaces_dim) +// return; + + baseInterfaces_dim = base->interfaces_dim; + baseInterfaces = (BaseClass *)mem.calloc(baseInterfaces_dim, sizeof(BaseClass)); + + //printf("%s.copyBaseInterfaces()\n", base->toChars()); + for (int i = 0; i < baseInterfaces_dim; i++) + { + BaseClass *b = &baseInterfaces[i]; + BaseClass *b2 = base->interfaces[i]; + + assert(b2->vtbl.dim == 0); // should not be filled yet + memcpy(b, b2, sizeof(BaseClass)); + + if (i) // single inheritance is i==0 + vtblInterfaces->push(b); // only need for M.I. + b->copyBaseInterfaces(vtblInterfaces); + } + //printf("-copyBaseInterfaces\n"); +} diff --git a/clone.c b/clone.c new file mode 100644 index 00000000..2664f864 --- /dev/null +++ b/clone.c @@ -0,0 +1,694 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "root.h" +#include "aggregate.h" +#include "scope.h" +#include "mtype.h" +#include "declaration.h" +#include "module.h" +#include "id.h" +#include "expression.h" +#include "statement.h" +#include "init.h" +#include "template.h" + + +/******************************************* + * We need an opAssign for the struct if + * it has a destructor or a postblit. + * We need to generate one if a user-specified one does not exist. + */ + +int StructDeclaration::needOpAssign() +{ +#define X 0 + if (X) printf("StructDeclaration::needOpAssign() %s\n", toChars()); + if (hasIdentityAssign) + goto Ldontneed; + + if (dtor || postblit) + goto Lneed; + + /* If any of the fields need an opAssign, then we + * need it too. + */ + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol *s = fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v && v->storage_class & STCfield); + if (v->storage_class & STCref) + continue; + Type *tv = v->type->toBasetype(); + while (tv->ty == Tsarray) + { TypeSArray *ta = (TypeSArray *)tv; + tv = tv->nextOf()->toBasetype(); + } + if (tv->ty == Tstruct) + { TypeStruct *ts = (TypeStruct *)tv; + StructDeclaration *sd = ts->sym; + if (sd->needOpAssign()) + goto Lneed; + } + } +Ldontneed: + if (X) printf("\tdontneed\n"); + return 0; + +Lneed: + if (X) printf("\tneed\n"); + return 1; +#undef X +} + +/****************************************** + * Build opAssign for struct. + * S* opAssign(S s) { ... } + * + * Note that s will be constructed onto the stack, probably copy-constructed. + * Then, the body is: + * S tmp = *this; // bit copy + * *this = s; // bit copy + * tmp.dtor(); + * Instead of running the destructor on s, run it on tmp instead. + */ + +FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) +{ + if (!needOpAssign()) + return NULL; + + //printf("StructDeclaration::buildOpAssign() %s\n", toChars()); + + FuncDeclaration *fop = NULL; + + Parameters *fparams = new Parameters; + fparams->push(new Parameter(STCnodtor, type, Id::p, NULL)); + Type *ftype = new TypeFunction(fparams, handle, FALSE, LINKd); +#if STRUCTTHISREF + ((TypeFunction *)ftype)->isref = 1; +#endif + + fop = new FuncDeclaration(loc, 0, Id::assign, STCundefined, ftype); + + Expression *e = NULL; + if (postblit) + { /* Swap: + * tmp = *this; *this = s; tmp.dtor(); + */ + //printf("\tswap copy\n"); + Identifier *idtmp = Lexer::uniqueId("__tmp"); + VarDeclaration *tmp; + AssignExp *ec = NULL; + if (dtor) + { + tmp = new VarDeclaration(0, type, idtmp, new VoidInitializer(0)); + tmp->noscope = 1; + tmp->storage_class |= STCctfe; + e = new DeclarationExp(0, tmp); + ec = new AssignExp(0, + new VarExp(0, tmp), +#if STRUCTTHISREF + new ThisExp(0) +#else + new PtrExp(0, new ThisExp(0)) +#endif + ); + ec->op = TOKblit; + e = Expression::combine(e, ec); + } + ec = new AssignExp(0, +#if STRUCTTHISREF + new ThisExp(0), +#else + new PtrExp(0, new ThisExp(0)), +#endif + new IdentifierExp(0, Id::p)); + ec->op = TOKblit; + e = Expression::combine(e, ec); + if (dtor) + { + /* Instead of running the destructor on s, run it + * on tmp. This avoids needing to copy tmp back in to s. + */ + Expression *ec2 = new DotVarExp(0, new VarExp(0, tmp), dtor, 0); + ec2 = new CallExp(0, ec2); + e = Expression::combine(e, ec2); + } + } + else + { /* Do memberwise copy + */ + //printf("\tmemberwise copy\n"); + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol *s = fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v && v->storage_class & STCfield); + // this.v = s.v; + AssignExp *ec = new AssignExp(0, + new DotVarExp(0, new ThisExp(0), v, 0), + new DotVarExp(0, new IdentifierExp(0, Id::p), v, 0)); + ec->op = TOKblit; + e = Expression::combine(e, ec); + } + } + Statement *s1 = new ExpStatement(0, e); + + /* Add: + * return this; + */ + e = new ThisExp(0); + Statement *s2 = new ReturnStatement(0, e); + + fop->fbody = new CompoundStatement(0, s1, s2); + + members->push(fop); + fop->addMember(sc, this, 1); + + sc = sc->push(); + sc->stc = 0; + sc->linkage = LINKd; + + fop->semantic(sc); + + sc->pop(); + + //printf("-StructDeclaration::buildOpAssign() %s\n", toChars()); + + return fop; +} + +/******************************************* + * We need an opEquals for the struct if + * any fields has an opEquals. + * Generate one if a user-specified one does not exist. + */ + +int StructDeclaration::needOpEquals() +{ +#define X 0 + if (X) printf("StructDeclaration::needOpEquals() %s\n", toChars()); + + if (hasIdentityEquals) + goto Lneed; + + /* If any of the fields has an opEquals, then we + * need it too. + */ + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol *s = fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v && v->storage_class & STCfield); + if (v->storage_class & STCref) + continue; + Type *tv = v->type->toBasetype(); + while (tv->ty == Tsarray) + { TypeSArray *ta = (TypeSArray *)tv; + tv = tv->nextOf()->toBasetype(); + } + if (tv->ty == Tstruct) + { TypeStruct *ts = (TypeStruct *)tv; + StructDeclaration *sd = ts->sym; + if (sd->needOpEquals()) + goto Lneed; + } + } + if (X) printf("\tdontneed\n"); + return 0; + +Lneed: + if (X) printf("\tneed\n"); + return 1; +#undef X +} + +/****************************************** + * Build opEquals for struct. + * const bool opEquals(const S s) { ... } + */ + +FuncDeclaration *StructDeclaration::buildOpEquals(Scope *sc) +{ + Dsymbol *eq = search_function(this, Id::eq); + if (eq) + { + for (size_t i = 0; i <= 1; i++) + { + Expression *e = + i == 0 ? new NullExp(loc, type->constOf()) // dummy rvalue + : type->constOf()->defaultInit(); // dummy lvalue + Expressions *arguments = new Expressions(); + arguments->push(e); + + // check identity opEquals exists + FuncDeclaration *fd = eq->isFuncDeclaration(); + if (fd) + { fd = fd->overloadResolve(loc, e, arguments, 1); + if (fd && !(fd->storage_class & STCdisable)) + return fd; + } + + TemplateDeclaration *td = eq->isTemplateDeclaration(); + if (td) + { fd = td->deduceFunctionTemplate(sc, loc, NULL, e, arguments, 1); + if (fd && !(fd->storage_class & STCdisable)) + return fd; + } + } + return NULL; + } + + if (!needOpEquals()) + return NULL; + + //printf("StructDeclaration::buildOpEquals() %s\n", toChars()); + + Parameters *parameters = new Parameters; + parameters->push(new Parameter(STCin, type, Id::p, NULL)); + TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); + tf->mod = MODconst; + tf = (TypeFunction *)tf->semantic(loc, sc); + + FuncDeclaration *fop = new FuncDeclaration(loc, 0, Id::eq, STCundefined, tf); + + Expression *e = NULL; + /* Do memberwise compare + */ + //printf("\tmemberwise compare\n"); + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol *s = fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v && v->storage_class & STCfield); + if (v->storage_class & STCref) + assert(0); // what should we do with this? + // this.v == s.v; + EqualExp *ec = new EqualExp(TOKequal, loc, + new DotVarExp(loc, new ThisExp(loc), v, 0), + new DotVarExp(loc, new IdentifierExp(loc, Id::p), v, 0)); + if (e) + e = new AndAndExp(loc, e, ec); + else + e = ec; + } + if (!e) + e = new IntegerExp(loc, 1, Type::tbool); + fop->fbody = new ReturnStatement(loc, e); + + members->push(fop); + fop->addMember(sc, this, 1); + + sc = sc->push(); + sc->stc = 0; + sc->linkage = LINKd; + + fop->semantic(sc); + + sc->pop(); + + //printf("-StructDeclaration::buildOpEquals() %s\n", toChars()); + + return fop; +} + +/****************************************** + * Build __xopEquals for TypeInfo_Struct + * bool __xopEquals(in void* p, in void* q) { ... } + */ + +FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) +{ + if (!search_function(this, Id::eq)) + return NULL; + + /* static bool__xopEquals(in void* p, in void* q) { + * return ( *cast(const S*)(p) ).opEquals( *cast(const S*)(q) ); + * } + */ + + Parameters *parameters = new Parameters; + parameters->push(new Parameter(STCin, Type::tvoidptr, Id::p, NULL)); + parameters->push(new Parameter(STCin, Type::tvoidptr, Id::q, NULL)); + TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); + tf = (TypeFunction *)tf->semantic(loc, sc); + + Identifier *id = Lexer::idPool("__xopEquals"); + FuncDeclaration *fop = new FuncDeclaration(loc, 0, id, STCstatic, tf); + + Expression *e = new CallExp(0, + new DotIdExp(0, + new PtrExp(0, new CastExp(0, + new IdentifierExp(0, Id::p), type->pointerTo()->constOf())), + Id::eq), + new PtrExp(0, new CastExp(0, + new IdentifierExp(0, Id::q), type->pointerTo()->constOf()))); + + fop->fbody = new ReturnStatement(loc, e); + + size_t index = members->dim; + members->push(fop); + + sc = sc->push(); + sc->stc = 0; + sc->linkage = LINKd; + + unsigned errors = global.startGagging(); + fop->semantic(sc); + if (errors == global.gaggedErrors) + { fop->semantic2(sc); + if (errors == global.gaggedErrors) + { fop->semantic3(sc); + if (errors == global.gaggedErrors) + fop->addMember(sc, this, 1); + } + } + if (global.endGagging(errors)) // if errors happened + { + members->remove(index); + + if (!xerreq) + { + Expression *e = new IdentifierExp(loc, Id::empty); + e = new DotIdExp(loc, e, Id::object); + e = new DotIdExp(loc, e, Lexer::idPool("_xopEquals")); + e = e->semantic(sc); + Dsymbol *s = getDsymbol(e); + FuncDeclaration *fd = s->isFuncDeclaration(); + + xerreq = fd; + } + fop = xerreq; + } + + sc->pop(); + + return fop; +} + + +/******************************************* + * Build copy constructor for struct. + * Copy constructors are compiler generated only, and are only + * callable from the compiler. They are not user accessible. + * A copy constructor is: + * void cpctpr(ref const S s) const + * { + * (*cast(S*)&this) = *cast(S*)s; + * (*cast(S*)&this).postBlit(); + * } + * This is done so: + * - postBlit() never sees uninitialized data + * - memcpy can be much more efficient than memberwise copy + * - no fields are overlooked + */ + +FuncDeclaration *StructDeclaration::buildCpCtor(Scope *sc) +{ + //printf("StructDeclaration::buildCpCtor() %s\n", toChars()); + FuncDeclaration *fcp = NULL; + + /* Copy constructor is only necessary if there is a postblit function, + * otherwise the code generator will just do a bit copy. + */ + if (postblit) + { + //printf("generating cpctor\n"); + + StorageClass stc = postblit->storage_class & + (STCdisable | STCsafe | STCtrusted | STCsystem | STCpure | STCnothrow); + if (stc & (STCsafe | STCtrusted)) + stc = stc & ~STCsafe | STCtrusted; + + Parameters *fparams = new Parameters; + fparams->push(new Parameter(STCref, type->constOf(), Id::p, NULL)); + Type *ftype = new TypeFunction(fparams, Type::tvoid, FALSE, LINKd, stc); + ftype->mod = MODconst; + + fcp = new FuncDeclaration(loc, 0, Id::cpctor, stc, ftype); + + if (!(fcp->storage_class & STCdisable)) + { + // Build *this = p; + Expression *e = new ThisExp(0); +#if !STRUCTTHISREF + e = new PtrExp(0, e); +#endif + AssignExp *ea = new AssignExp(0, + new PtrExp(0, new CastExp(0, new AddrExp(0, e), type->mutableOf()->pointerTo())), + new PtrExp(0, new CastExp(0, new AddrExp(0, new IdentifierExp(0, Id::p)), type->mutableOf()->pointerTo())) + ); + ea->op = TOKblit; + Statement *s = new ExpStatement(0, ea); + + // Build postBlit(); + e = new ThisExp(0); +#if !STRUCTTHISREF + e = new PtrExp(0, e); +#endif + e = new PtrExp(0, new CastExp(0, new AddrExp(0, e), type->mutableOf()->pointerTo())); + e = new DotVarExp(0, e, postblit, 0); + e = new CallExp(0, e); + + s = new CompoundStatement(0, s, new ExpStatement(0, e)); + fcp->fbody = s; + } + else + fcp->fbody = new ExpStatement(0, (Expression *)NULL); + + members->push(fcp); + + sc = sc->push(); + sc->stc = 0; + sc->linkage = LINKd; + + fcp->semantic(sc); + + sc->pop(); + } + + return fcp; +} + +/***************************************** + * Create inclusive postblit for struct by aggregating + * all the postblits in postblits[] with the postblits for + * all the members. + * Note the close similarity with AggregateDeclaration::buildDtor(), + * and the ordering changes (runs forward instead of backwards). + */ + +#if DMDV2 +FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) +{ + //printf("StructDeclaration::buildPostBlit() %s\n", toChars()); + Expression *e = NULL; + StorageClass stc = 0; + + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol *s = fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v && v->storage_class & STCfield); + if (v->storage_class & STCref) + continue; + Type *tv = v->type->toBasetype(); + dinteger_t dim = (tv->ty == Tsarray ? 1 : 0); + while (tv->ty == Tsarray) + { TypeSArray *ta = (TypeSArray *)tv; + dim *= ((TypeSArray *)tv)->dim->toInteger(); + tv = tv->nextOf()->toBasetype(); + } + if (tv->ty == Tstruct) + { TypeStruct *ts = (TypeStruct *)tv; + StructDeclaration *sd = ts->sym; + if (sd->postblit) + { + stc |= sd->postblit->storage_class & STCdisable; + + if (stc & STCdisable) + { + e = NULL; + break; + } + + // this.v + Expression *ex = new ThisExp(0); + ex = new DotVarExp(0, ex, v, 0); + + if (dim == 0) + { // this.v.postblit() + ex = new DotVarExp(0, ex, sd->postblit, 0); + ex = new CallExp(0, ex); + } + else + { + // Typeinfo.postblit(cast(void*)&this.v); + Expression *ea = new AddrExp(0, ex); + ea = new CastExp(0, ea, Type::tvoid->pointerTo()); + + Expression *et = v->type->getTypeInfo(sc); + et = new DotIdExp(0, et, Id::postblit); + + ex = new CallExp(0, et, ea); + } + e = Expression::combine(e, ex); // combine in forward order + } + } + } + + /* Build our own "postblit" which executes e + */ + if (e || (stc & STCdisable)) + { //printf("Building __fieldPostBlit()\n"); + PostBlitDeclaration *dd = new PostBlitDeclaration(loc, 0, Lexer::idPool("__fieldPostBlit")); + dd->storage_class |= stc; + dd->fbody = new ExpStatement(0, e); + postblits.shift(dd); + members->push(dd); + dd->semantic(sc); + } + + switch (postblits.dim) + { + case 0: + return NULL; + + case 1: + return postblits.tdata()[0]; + + default: + e = NULL; + for (size_t i = 0; i < postblits.dim; i++) + { FuncDeclaration *fd = postblits.tdata()[i]; + stc |= fd->storage_class & STCdisable; + if (stc & STCdisable) + { + e = NULL; + break; + } + Expression *ex = new ThisExp(0); + ex = new DotVarExp(0, ex, fd, 0); + ex = new CallExp(0, ex); + e = Expression::combine(e, ex); + } + PostBlitDeclaration *dd = new PostBlitDeclaration(loc, 0, Lexer::idPool("__aggrPostBlit")); + dd->storage_class |= stc; + dd->fbody = new ExpStatement(0, e); + members->push(dd); + dd->semantic(sc); + return dd; + } +} + +#endif + +/***************************************** + * Create inclusive destructor for struct/class by aggregating + * all the destructors in dtors[] with the destructors for + * all the members. + * Note the close similarity with StructDeclaration::buildPostBlit(), + * and the ordering changes (runs backward instead of forwards). + */ + +FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc) +{ + //printf("AggregateDeclaration::buildDtor() %s\n", toChars()); + Expression *e = NULL; + +#if DMDV2 + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol *s = fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v && v->storage_class & STCfield); + if (v->storage_class & STCref) + continue; + Type *tv = v->type->toBasetype(); + dinteger_t dim = (tv->ty == Tsarray ? 1 : 0); + while (tv->ty == Tsarray) + { TypeSArray *ta = (TypeSArray *)tv; + dim *= ((TypeSArray *)tv)->dim->toInteger(); + tv = tv->nextOf()->toBasetype(); + } + if (tv->ty == Tstruct) + { TypeStruct *ts = (TypeStruct *)tv; + StructDeclaration *sd = ts->sym; + if (sd->dtor) + { Expression *ex; + + // this.v + ex = new ThisExp(0); + ex = new DotVarExp(0, ex, v, 0); + + if (dim == 0) + { // this.v.dtor() + ex = new DotVarExp(0, ex, sd->dtor, 0); + ex = new CallExp(0, ex); + } + else + { + // Typeinfo.destroy(cast(void*)&this.v); + Expression *ea = new AddrExp(0, ex); + ea = new CastExp(0, ea, Type::tvoid->pointerTo()); + + Expression *et = v->type->getTypeInfo(sc); + et = new DotIdExp(0, et, Id::destroy); + + ex = new CallExp(0, et, ea); + } + e = Expression::combine(ex, e); // combine in reverse order + } + } + } + + /* Build our own "destructor" which executes e + */ + if (e) + { //printf("Building __fieldDtor()\n"); + DtorDeclaration *dd = new DtorDeclaration(loc, 0, Lexer::idPool("__fieldDtor")); + dd->fbody = new ExpStatement(0, e); + dtors.shift(dd); + members->push(dd); + dd->semantic(sc); + } +#endif + + switch (dtors.dim) + { + case 0: + return NULL; + + case 1: + return dtors.tdata()[0]; + + default: + e = NULL; + for (size_t i = 0; i < dtors.dim; i++) + { FuncDeclaration *fd = dtors.tdata()[i]; + Expression *ex = new ThisExp(0); + ex = new DotVarExp(0, ex, fd, 0); + ex = new CallExp(0, ex); + e = Expression::combine(ex, e); + } + DtorDeclaration *dd = new DtorDeclaration(loc, 0, Lexer::idPool("__aggrDtor")); + dd->fbody = new ExpStatement(0, e); + members->push(dd); + dd->semantic(sc); + return dd; + } +} + + diff --git a/complex_t.h b/complex_t.h new file mode 100644 index 00000000..a1b4f4ed --- /dev/null +++ b/complex_t.h @@ -0,0 +1,74 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// written by Walter Bright and Burton Radons +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_COMPLEX_T_H +#define DMD_COMPLEX_T_H + +/* Roll our own complex type for compilers that don't support complex + */ + +struct complex_t +{ + long double re; + long double im; + + complex_t() { this->re = 0; this->im = 0; } + complex_t(long double re) { this->re = re; this->im = 0; } + complex_t(long double re, long double im) { this->re = re; this->im = im; } + + complex_t operator + (complex_t y) { complex_t r; r.re = re + y.re; r.im = im + y.im; return r; } + complex_t operator - (complex_t y) { complex_t r; r.re = re - y.re; r.im = im - y.im; return r; } + complex_t operator - () { complex_t r; r.re = -re; r.im = -im; return r; } + complex_t operator * (complex_t y) { return complex_t(re * y.re - im * y.im, im * y.re + re * y.im); } + + complex_t operator / (complex_t y) + { + long double abs_y_re = y.re < 0 ? -y.re : y.re; + long double abs_y_im = y.im < 0 ? -y.im : y.im; + long double r, den; + + if (abs_y_re < abs_y_im) + { + r = y.re / y.im; + den = y.im + r * y.re; + return complex_t((re * r + im) / den, + (im * r - re) / den); + } + else + { + r = y.im / y.re; + den = y.re + r * y.im; + return complex_t((re + r * im) / den, + (im - r * re) / den); + } + } + + operator bool () { return re || im; } + + int operator == (complex_t y) { return re == y.re && im == y.im; } + int operator != (complex_t y) { return re != y.re || im != y.im; } +}; + +inline complex_t operator * (long double x, complex_t y) { return complex_t(x) * y; } +inline complex_t operator * (complex_t x, long double y) { return x * complex_t(y); } +inline complex_t operator / (complex_t x, long double y) { return x / complex_t(y); } + + +inline long double creall(complex_t x) +{ + return x.re; +} + +inline long double cimagl(complex_t x) +{ + return x.im; +} + +#endif diff --git a/cond.c b/cond.c new file mode 100644 index 00000000..3ee03889 --- /dev/null +++ b/cond.c @@ -0,0 +1,399 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "id.h" +#include "init.h" +#include "declaration.h" +#include "identifier.h" +#include "expression.h" +#include "cond.h" +#include "module.h" +#include "template.h" +#include "lexer.h" +#include "mtype.h" +#include "scope.h" + +int findCondition(Strings *ids, Identifier *ident) +{ + if (ids) + { + for (size_t i = 0; i < ids->dim; i++) + { + const char *id = ids->tdata()[i]; + + if (strcmp(id, ident->toChars()) == 0) + return TRUE; + } + } + + return FALSE; +} + +/* ============================================================ */ + +Condition::Condition(Loc loc) +{ + this->loc = loc; + inc = 0; +} + +/* ============================================================ */ + +DVCondition::DVCondition(Module *mod, unsigned level, Identifier *ident) + : Condition(0) +{ + this->mod = mod; + this->level = level; + this->ident = ident; +} + +Condition *DVCondition::syntaxCopy() +{ + return this; // don't need to copy +} + +/* ============================================================ */ + +void DebugCondition::setGlobalLevel(unsigned level) +{ + global.params.debuglevel = level; +} + +void DebugCondition::addGlobalIdent(const char *ident) +{ + if (!global.params.debugids) + global.params.debugids = new Strings(); + global.params.debugids->push((char *)ident); +} + + +DebugCondition::DebugCondition(Module *mod, unsigned level, Identifier *ident) + : DVCondition(mod, level, ident) +{ +} + +int DebugCondition::include(Scope *sc, ScopeDsymbol *s) +{ + //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); + if (inc == 0) + { + inc = 2; + if (ident) + { + if (findCondition(mod->debugids, ident)) + inc = 1; + else if (findCondition(global.params.debugids, ident)) + inc = 1; + else + { if (!mod->debugidsNot) + mod->debugidsNot = new Strings(); + mod->debugidsNot->push(ident->toChars()); + } + } + else if (level <= global.params.debuglevel || level <= mod->debuglevel) + inc = 1; + } + return (inc == 1); +} + +void DebugCondition::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (ident) + buf->printf("debug (%s)", ident->toChars()); + else + buf->printf("debug (%u)", level); +} + +/* ============================================================ */ + +void VersionCondition::setGlobalLevel(unsigned level) +{ + global.params.versionlevel = level; +} + +void VersionCondition::checkPredefined(Loc loc, const char *ident) +{ + static const char* reserved[] = + { + "DigitalMars", "X86", "X86_64", + "Windows", "Win32", "Win64", + "linux", +#if DMDV2 + /* Although Posix is predefined by D1, disallowing its + * redefinition breaks makefiles and older builds. + */ + "Posix", + "D_NET", +#endif + "OSX", "FreeBSD", + "OpenBSD", + "Solaris", + "LittleEndian", "BigEndian", + "all", + "none", + }; + + for (unsigned i = 0; i < sizeof(reserved) / sizeof(reserved[0]); i++) + { + if (strcmp(ident, reserved[i]) == 0) + goto Lerror; + } + + if (ident[0] == 'D' && ident[1] == '_') + goto Lerror; + + return; + + Lerror: + error(loc, "version identifier '%s' is reserved and cannot be set", ident); +} + +void VersionCondition::addGlobalIdent(const char *ident) +{ + checkPredefined(0, ident); + addPredefinedGlobalIdent(ident); +} + +void VersionCondition::addPredefinedGlobalIdent(const char *ident) +{ + if (!global.params.versionids) + global.params.versionids = new Strings(); + global.params.versionids->push((char *)ident); +} + + +VersionCondition::VersionCondition(Module *mod, unsigned level, Identifier *ident) + : DVCondition(mod, level, ident) +{ +} + +int VersionCondition::include(Scope *sc, ScopeDsymbol *s) +{ + //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel); + //if (ident) printf("\tident = '%s'\n", ident->toChars()); + if (inc == 0) + { + inc = 2; + if (ident) + { + if (findCondition(mod->versionids, ident)) + inc = 1; + else if (findCondition(global.params.versionids, ident)) + inc = 1; + else + { + if (!mod->versionidsNot) + mod->versionidsNot = new Strings(); + mod->versionidsNot->push(ident->toChars()); + } + } + else if (level <= global.params.versionlevel || level <= mod->versionlevel) + inc = 1; + } + return (inc == 1); +} + +void VersionCondition::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (ident) + buf->printf("version (%s)", ident->toChars()); + else + buf->printf("version (%u)", level); +} + + +/**************************** StaticIfCondition *******************************/ + +StaticIfCondition::StaticIfCondition(Loc loc, Expression *exp) + : Condition(loc) +{ + this->exp = exp; +} + +Condition *StaticIfCondition::syntaxCopy() +{ + return new StaticIfCondition(loc, exp->syntaxCopy()); +} + +int StaticIfCondition::include(Scope *sc, ScopeDsymbol *s) +{ +#if 0 + printf("StaticIfCondition::include(sc = %p, s = %p)\n", sc, s); + if (s) + { + printf("\ts = '%s', kind = %s\n", s->toChars(), s->kind()); + } +#endif + if (inc == 0) + { + if (!sc) + { + error(loc, "static if conditional cannot be at global scope"); + inc = 2; + return 0; + } + + sc = sc->push(sc->scopesym); + sc->sd = s; // s gets any addMember() + sc->flags |= SCOPEstaticif; + Expression *e = exp->semantic(sc); + sc->pop(); + e = e->optimize(WANTvalue | WANTinterpret); + if (e->isBool(TRUE)) + inc = 1; + else if (e->isBool(FALSE)) + inc = 2; + else + { + e->error("expression %s is not constant or does not evaluate to a bool", e->toChars()); + inc = 2; + } + } + return (inc == 1); +} + +void StaticIfCondition::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("static if("); + exp->toCBuffer(buf, hgs); + buf->writeByte(')'); +} + + +/**************************** IftypeCondition *******************************/ + +IftypeCondition::IftypeCondition(Loc loc, Type *targ, Identifier *id, enum TOK tok, Type *tspec) + : Condition(loc) +{ + this->targ = targ; + this->id = id; + this->tok = tok; + this->tspec = tspec; +} + +Condition *IftypeCondition::syntaxCopy() +{ + return new IftypeCondition(loc, + targ->syntaxCopy(), + id, + tok, + tspec ? tspec->syntaxCopy() : NULL); +} + +int IftypeCondition::include(Scope *sc, ScopeDsymbol *sd) +{ + //printf("IftypeCondition::include()\n"); + if (inc == 0) + { + if (!sc) + { + error(loc, "iftype conditional cannot be at global scope"); + inc = 2; + return 0; + } + Type *t = targ->trySemantic(loc, sc); + if (t) + targ = t; + else + inc = 2; // condition is false + + if (!t) + { + } + else if (id && tspec) + { + /* Evaluate to TRUE if targ matches tspec. + * If TRUE, declare id as an alias for the specialized type. + */ + + MATCH m; + TemplateTypeParameter tp(loc, id, NULL, NULL); + + TemplateParameters parameters; + parameters.setDim(1); + parameters.tdata()[0] = &tp; + + Objects dedtypes; + dedtypes.setDim(1); + + m = targ->deduceType(sc, tspec, ¶meters, &dedtypes); + if (m == MATCHnomatch || + (m != MATCHexact && tok == TOKequal)) + inc = 2; + else + { + inc = 1; + Type *tded = (Type *)dedtypes.tdata()[0]; + if (!tded) + tded = targ; + Dsymbol *s = new AliasDeclaration(loc, id, tded); + s->semantic(sc); + sc->insert(s); + if (sd) + s->addMember(sc, sd, 1); + } + } + else if (id) + { + /* Declare id as an alias for type targ. Evaluate to TRUE + */ + Dsymbol *s = new AliasDeclaration(loc, id, targ); + s->semantic(sc); + sc->insert(s); + if (sd) + s->addMember(sc, sd, 1); + inc = 1; + } + else if (tspec) + { + /* Evaluate to TRUE if targ matches tspec + */ + tspec = tspec->semantic(loc, sc); + //printf("targ = %s\n", targ->toChars()); + //printf("tspec = %s\n", tspec->toChars()); + if (tok == TOKcolon) + { if (targ->implicitConvTo(tspec)) + inc = 1; + else + inc = 2; + } + else /* == */ + { if (targ->equals(tspec)) + inc = 1; + else + inc = 2; + } + } + else + inc = 1; + //printf("inc = %d\n", inc); + } + return (inc == 1); +} + +void IftypeCondition::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("iftype("); + targ->toCBuffer(buf, id, hgs); + if (tspec) + { + if (tok == TOKcolon) + buf->writestring(" : "); + else + buf->writestring(" == "); + tspec->toCBuffer(buf, NULL, hgs); + } + buf->writeByte(')'); +} + + diff --git a/cond.h b/cond.h new file mode 100644 index 00000000..789503a4 --- /dev/null +++ b/cond.h @@ -0,0 +1,105 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_DEBCOND_H +#define DMD_DEBCOND_H + +struct Expression; +struct Identifier; +struct OutBuffer; +struct Module; +struct Scope; +struct ScopeDsymbol; +struct DebugCondition; +#include "lexer.h" // dmdhg +enum TOK; +struct HdrGenState; + +int findCondition(Strings *ids, Identifier *ident); + +struct Condition +{ + Loc loc; + int inc; // 0: not computed yet + // 1: include + // 2: do not include + + Condition(Loc loc); + + virtual Condition *syntaxCopy() = 0; + virtual int include(Scope *sc, ScopeDsymbol *s) = 0; + virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs) = 0; + virtual DebugCondition *isDebugCondition() { return NULL; } +}; + +struct DVCondition : Condition +{ + unsigned level; + Identifier *ident; + Module *mod; + + DVCondition(Module *mod, unsigned level, Identifier *ident); + + Condition *syntaxCopy(); +}; + +struct DebugCondition : DVCondition +{ + static void setGlobalLevel(unsigned level); + static void addGlobalIdent(const char *ident); + static void addPredefinedGlobalIdent(const char *ident); + + DebugCondition(Module *mod, unsigned level, Identifier *ident); + + int include(Scope *sc, ScopeDsymbol *s); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + DebugCondition *isDebugCondition() { return this; } +}; + +struct VersionCondition : DVCondition +{ + static void setGlobalLevel(unsigned level); + static void checkPredefined(Loc loc, const char *ident); + static void addGlobalIdent(const char *ident); + static void addPredefinedGlobalIdent(const char *ident); + + VersionCondition(Module *mod, unsigned level, Identifier *ident); + + int include(Scope *sc, ScopeDsymbol *s); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct StaticIfCondition : Condition +{ + Expression *exp; + + StaticIfCondition(Loc loc, Expression *exp); + Condition *syntaxCopy(); + int include(Scope *sc, ScopeDsymbol *s); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct IftypeCondition : Condition +{ + /* iftype (targ id tok tspec) + */ + Type *targ; + Identifier *id; // can be NULL + enum TOK tok; // ':' or '==' + Type *tspec; // can be NULL + + IftypeCondition(Loc loc, Type *targ, Identifier *id, enum TOK tok, Type *tspec); + Condition *syntaxCopy(); + int include(Scope *sc, ScopeDsymbol *s); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + + +#endif diff --git a/constfold.c b/constfold.c new file mode 100644 index 00000000..acb65b75 --- /dev/null +++ b/constfold.c @@ -0,0 +1,1868 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include + +#if __DMC__ +#include +#endif + +#include "rmem.h" +#include "root.h" +#include "port.h" + +#include "mtype.h" +#include "expression.h" +#include "aggregate.h" +#include "declaration.h" +#include "utf.h" + +#define LOG 0 + +int RealEquals(real_t x1, real_t x2); + +Expression *expType(Type *type, Expression *e) +{ + if (type != e->type) + { + e = e->copy(); + e->type = type; + } + return e; +} + +/* ================================== isConst() ============================== */ + +int Expression::isConst() +{ + //printf("Expression::isConst(): %s\n", toChars()); + return 0; +} + +int IntegerExp::isConst() +{ + return 1; +} + +int RealExp::isConst() +{ + return 1; +} + +int ComplexExp::isConst() +{ + return 1; +} + +int NullExp::isConst() +{ + return 0; +} + +int SymOffExp::isConst() +{ + return 2; +} + +/* =============================== constFold() ============================== */ + +/* The constFold() functions were redundant with the optimize() ones, + * and so have been folded in with them. + */ + +/* ========================================================================== */ + +Expression *Neg(Type *type, Expression *e1) +{ Expression *e; + Loc loc = e1->loc; + + if (e1->type->isreal()) + { + e = new RealExp(loc, -e1->toReal(), type); + } + else if (e1->type->isimaginary()) + { + e = new RealExp(loc, -e1->toImaginary(), type); + } + else if (e1->type->iscomplex()) + { + e = new ComplexExp(loc, -e1->toComplex(), type); + } + else + e = new IntegerExp(loc, -e1->toInteger(), type); + return e; +} + +Expression *Com(Type *type, Expression *e1) +{ Expression *e; + Loc loc = e1->loc; + + e = new IntegerExp(loc, ~e1->toInteger(), type); + return e; +} + +Expression *Not(Type *type, Expression *e1) +{ Expression *e; + Loc loc = e1->loc; + + e = new IntegerExp(loc, e1->isBool(0), type); + return e; +} + +Expression *Bool(Type *type, Expression *e1) +{ Expression *e; + Loc loc = e1->loc; + + e = new IntegerExp(loc, e1->isBool(1), type); + return e; +} + +Expression *Add(Type *type, Expression *e1, Expression *e2) +{ Expression *e; + Loc loc = e1->loc; + +#if LOG + printf("Add(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); +#endif + if (type->isreal()) + { + e = new RealExp(loc, e1->toReal() + e2->toReal(), type); + } + else if (type->isimaginary()) + { + e = new RealExp(loc, e1->toImaginary() + e2->toImaginary(), type); + } + else if (type->iscomplex()) + { + // This rigamarole is necessary so that -0.0 doesn't get + // converted to +0.0 by doing an extraneous add with +0.0 + complex_t c1; + real_t r1; + real_t i1; + + complex_t c2; + real_t r2; + real_t i2; + + complex_t v; + int x; + + if (e1->type->isreal()) + { r1 = e1->toReal(); + x = 0; + } + else if (e1->type->isimaginary()) + { i1 = e1->toImaginary(); + x = 3; + } + else + { c1 = e1->toComplex(); + x = 6; + } + + if (e2->type->isreal()) + { r2 = e2->toReal(); + } + else if (e2->type->isimaginary()) + { i2 = e2->toImaginary(); + x += 1; + } + else + { c2 = e2->toComplex(); + x += 2; + } + + switch (x) + { +#if __DMC__ + case 0+0: v = (complex_t) (r1 + r2); break; + case 0+1: v = r1 + i2 * I; break; + case 0+2: v = r1 + c2; break; + case 3+0: v = i1 * I + r2; break; + case 3+1: v = (complex_t) ((i1 + i2) * I); break; + case 3+2: v = i1 * I + c2; break; + case 6+0: v = c1 + r2; break; + case 6+1: v = c1 + i2 * I; break; + case 6+2: v = c1 + c2; break; +#else + case 0+0: v = complex_t(r1 + r2, 0); break; + case 0+1: v = complex_t(r1, i2); break; + case 0+2: v = complex_t(r1 + creall(c2), cimagl(c2)); break; + case 3+0: v = complex_t(r2, i1); break; + case 3+1: v = complex_t(0, i1 + i2); break; + case 3+2: v = complex_t(creall(c2), i1 + cimagl(c2)); break; + case 6+0: v = complex_t(creall(c1) + r2, cimagl(c2)); break; + case 6+1: v = complex_t(creall(c1), cimagl(c1) + i2); break; + case 6+2: v = c1 + c2; break; +#endif + default: assert(0); + } + e = new ComplexExp(loc, v, type); + } + else if (e1->op == TOKsymoff) + { + SymOffExp *soe = (SymOffExp *)e1; + e = new SymOffExp(loc, soe->var, soe->offset + e2->toInteger()); + e->type = type; + } + else if (e2->op == TOKsymoff) + { + SymOffExp *soe = (SymOffExp *)e2; + e = new SymOffExp(loc, soe->var, soe->offset + e1->toInteger()); + e->type = type; + } + else + e = new IntegerExp(loc, e1->toInteger() + e2->toInteger(), type); + return e; +} + + +Expression *Min(Type *type, Expression *e1, Expression *e2) +{ Expression *e; + Loc loc = e1->loc; + + if (type->isreal()) + { + e = new RealExp(loc, e1->toReal() - e2->toReal(), type); + } + else if (type->isimaginary()) + { + e = new RealExp(loc, e1->toImaginary() - e2->toImaginary(), type); + } + else if (type->iscomplex()) + { + // This rigamarole is necessary so that -0.0 doesn't get + // converted to +0.0 by doing an extraneous add with +0.0 + complex_t c1; + real_t r1; + real_t i1; + + complex_t c2; + real_t r2; + real_t i2; + + complex_t v; + int x; + + if (e1->type->isreal()) + { r1 = e1->toReal(); + x = 0; + } + else if (e1->type->isimaginary()) + { i1 = e1->toImaginary(); + x = 3; + } + else + { c1 = e1->toComplex(); + x = 6; + } + + if (e2->type->isreal()) + { r2 = e2->toReal(); + } + else if (e2->type->isimaginary()) + { i2 = e2->toImaginary(); + x += 1; + } + else + { c2 = e2->toComplex(); + x += 2; + } + + switch (x) + { +#if __DMC__ + case 0+0: v = (complex_t) (r1 - r2); break; + case 0+1: v = r1 - i2 * I; break; + case 0+2: v = r1 - c2; break; + case 3+0: v = i1 * I - r2; break; + case 3+1: v = (complex_t) ((i1 - i2) * I); break; + case 3+2: v = i1 * I - c2; break; + case 6+0: v = c1 - r2; break; + case 6+1: v = c1 - i2 * I; break; + case 6+2: v = c1 - c2; break; +#else + case 0+0: v = complex_t(r1 - r2, 0); break; + case 0+1: v = complex_t(r1, -i2); break; + case 0+2: v = complex_t(r1 - creall(c2), -cimagl(c2)); break; + case 3+0: v = complex_t(-r2, i1); break; + case 3+1: v = complex_t(0, i1 - i2); break; + case 3+2: v = complex_t(-creall(c2), i1 - cimagl(c2)); break; + case 6+0: v = complex_t(creall(c1) - r2, cimagl(c1)); break; + case 6+1: v = complex_t(creall(c1), cimagl(c1) - i2); break; + case 6+2: v = c1 - c2; break; +#endif + default: assert(0); + } + e = new ComplexExp(loc, v, type); + } + else if (e1->op == TOKsymoff) + { + SymOffExp *soe = (SymOffExp *)e1; + e = new SymOffExp(loc, soe->var, soe->offset - e2->toInteger()); + e->type = type; + } + else + { + e = new IntegerExp(loc, e1->toInteger() - e2->toInteger(), type); + } + return e; +} + +Expression *Mul(Type *type, Expression *e1, Expression *e2) +{ Expression *e; + Loc loc = e1->loc; + + if (type->isfloating()) + { complex_t c; +#ifdef IN_GCC + real_t r; +#else + d_float80 r; +#endif + + if (e1->type->isreal()) + { +#if __DMC__ + c = e1->toReal() * e2->toComplex(); +#else + r = e1->toReal(); + c = e2->toComplex(); + c = complex_t(r * creall(c), r * cimagl(c)); +#endif + } + else if (e1->type->isimaginary()) + { +#if __DMC__ + c = e1->toImaginary() * I * e2->toComplex(); +#else + r = e1->toImaginary(); + c = e2->toComplex(); + c = complex_t(-r * cimagl(c), r * creall(c)); +#endif + } + else if (e2->type->isreal()) + { +#if __DMC__ + c = e2->toReal() * e1->toComplex(); +#else + r = e2->toReal(); + c = e1->toComplex(); + c = complex_t(r * creall(c), r * cimagl(c)); +#endif + } + else if (e2->type->isimaginary()) + { +#if __DMC__ + c = e1->toComplex() * e2->toImaginary() * I; +#else + r = e2->toImaginary(); + c = e1->toComplex(); + c = complex_t(-r * cimagl(c), r * creall(c)); +#endif + } + else + c = e1->toComplex() * e2->toComplex(); + + if (type->isreal()) + e = new RealExp(loc, creall(c), type); + else if (type->isimaginary()) + e = new RealExp(loc, cimagl(c), type); + else if (type->iscomplex()) + e = new ComplexExp(loc, c, type); + else + assert(0); + } + else + { + e = new IntegerExp(loc, e1->toInteger() * e2->toInteger(), type); + } + return e; +} + +Expression *Div(Type *type, Expression *e1, Expression *e2) +{ Expression *e; + Loc loc = e1->loc; + + if (type->isfloating()) + { complex_t c; +#ifdef IN_GCC + real_t r; +#else + d_float80 r; +#endif + + //e1->type->print(); + //e2->type->print(); + if (e2->type->isreal()) + { + if (e1->type->isreal()) + { + e = new RealExp(loc, e1->toReal() / e2->toReal(), type); + return e; + } +#if __DMC__ + //r = e2->toReal(); + //c = e1->toComplex(); + //printf("(%Lg + %Lgi) / %Lg\n", creall(c), cimagl(c), r); + + c = e1->toComplex() / e2->toReal(); +#else + r = e2->toReal(); + c = e1->toComplex(); + c = complex_t(creall(c) / r, cimagl(c) / r); +#endif + } + else if (e2->type->isimaginary()) + { +#if __DMC__ + //r = e2->toImaginary(); + //c = e1->toComplex(); + //printf("(%Lg + %Lgi) / %Lgi\n", creall(c), cimagl(c), r); + + c = e1->toComplex() / (e2->toImaginary() * I); +#else + r = e2->toImaginary(); + c = e1->toComplex(); + c = complex_t(cimagl(c) / r, -creall(c) / r); +#endif + } + else + { + c = e1->toComplex() / e2->toComplex(); + } + + if (type->isreal()) + e = new RealExp(loc, creall(c), type); + else if (type->isimaginary()) + e = new RealExp(loc, cimagl(c), type); + else if (type->iscomplex()) + e = new ComplexExp(loc, c, type); + else + assert(0); + } + else + { sinteger_t n1; + sinteger_t n2; + sinteger_t n; + + n1 = e1->toInteger(); + n2 = e2->toInteger(); + if (n2 == 0) + { e2->error("divide by 0"); + e2 = new IntegerExp(loc, 1, e2->type); + n2 = 1; + } + if (e1->type->isunsigned() || e2->type->isunsigned()) + n = ((d_uns64) n1) / ((d_uns64) n2); + else + n = n1 / n2; + e = new IntegerExp(loc, n, type); + } + return e; +} + +Expression *Mod(Type *type, Expression *e1, Expression *e2) +{ Expression *e; + Loc loc = e1->loc; + + if (type->isfloating()) + { + complex_t c; + + if (e2->type->isreal()) + { real_t r2 = e2->toReal(); + +#ifdef __DMC__ + c = Port::fmodl(e1->toReal(), r2) + Port::fmodl(e1->toImaginary(), r2) * I; +#elif defined(IN_GCC) + c = complex_t(e1->toReal() % r2, e1->toImaginary() % r2); +#else + c = complex_t(Port::fmodl(e1->toReal(), r2), Port::fmodl(e1->toImaginary(), r2)); +#endif + } + else if (e2->type->isimaginary()) + { real_t i2 = e2->toImaginary(); + +#ifdef __DMC__ + c = Port::fmodl(e1->toReal(), i2) + Port::fmodl(e1->toImaginary(), i2) * I; +#elif defined(IN_GCC) + c = complex_t(e1->toReal() % i2, e1->toImaginary() % i2); +#else + c = complex_t(Port::fmodl(e1->toReal(), i2), Port::fmodl(e1->toImaginary(), i2)); +#endif + } + else + assert(0); + + if (type->isreal()) + e = new RealExp(loc, creall(c), type); + else if (type->isimaginary()) + e = new RealExp(loc, cimagl(c), type); + else if (type->iscomplex()) + e = new ComplexExp(loc, c, type); + else + assert(0); + } + else + { sinteger_t n1; + sinteger_t n2; + sinteger_t n; + + n1 = e1->toInteger(); + n2 = e2->toInteger(); + if (n2 == 0) + { e2->error("divide by 0"); + e2 = new IntegerExp(loc, 1, e2->type); + n2 = 1; + } + if (n2 == -1 && !type->isunsigned()) + { // Check for int.min % -1 + if (n1 == 0xFFFFFFFF80000000ULL && type->toBasetype()->ty != Tint64) + { + e2->error("integer overflow: int.min % -1"); + e2 = new IntegerExp(loc, 1, e2->type); + n2 = 1; + } + else if (n1 == 0x8000000000000000LL) // long.min % -1 + { + e2->error("integer overflow: long.min % -1"); + e2 = new IntegerExp(loc, 1, e2->type); + n2 = 1; + } + } + if (e1->type->isunsigned() || e2->type->isunsigned()) + n = ((d_uns64) n1) % ((d_uns64) n2); + else + n = n1 % n2; + e = new IntegerExp(loc, n, type); + } + return e; +} + +Expression *Pow(Type *type, Expression *e1, Expression *e2) +{ Expression *e; + Loc loc = e1->loc; + + // Handle integer power operations. + if (e2->type->isintegral()) + { + Expression * r; + Expression * v; + dinteger_t n = e2->toInteger(); + bool neg; + + if (!e2->type->isunsigned() && (sinteger_t)n < 0) + { + if (e1->type->isintegral()) + return EXP_CANT_INTERPRET; + + // Don't worry about overflow, from now on n is unsigned. + neg = true; + n = -n; + } + else + neg = false; + + if (e1->type->isfloating()) + { + r = new RealExp(loc, e1->toReal(), e1->type); + v = new RealExp(loc, 1.0, e1->type); + } + else + { + r = new RealExp(loc, e1->toReal(), Type::tfloat64); + v = new RealExp(loc, 1.0, Type::tfloat64); + } + + while (n != 0) + { + if (n & 1) + v = Mul(v->type, v, r); + n >>= 1; + r = Mul(r->type, r, r); + } + + if (neg) + v = Div(v->type, new RealExp(loc, 1.0, v->type), v); + + if (type->isintegral()) + e = new IntegerExp(loc, v->toInteger(), type); + else + e = new RealExp(loc, v->toReal(), type); + } + else if (e2->type->isfloating()) + { + // x ^^ y for x < 0 and y not an integer is not defined + if (e1->toReal() < 0.0) + { + e = new RealExp(loc, Port::nan, type); + } + else if (e2->toReal() == 0.5) + { + // Special case: call sqrt directly. + Expressions args; + args.setDim(1); + args.tdata()[0] = e1; + e = eval_builtin(loc, BUILTINsqrt, &args); + if (!e) + e = EXP_CANT_INTERPRET; + } + else + e = EXP_CANT_INTERPRET; + } + else + e = EXP_CANT_INTERPRET; + + return e; +} + +Expression *Shl(Type *type, Expression *e1, Expression *e2) +{ Expression *e; + Loc loc = e1->loc; + + e = new IntegerExp(loc, e1->toInteger() << e2->toInteger(), type); + return e; +} + +Expression *Shr(Type *type, Expression *e1, Expression *e2) +{ + Loc loc = e1->loc; + + dinteger_t value = e1->toInteger(); + dinteger_t dcount = e2->toInteger(); + assert(dcount <= 0xFFFFFFFF); + unsigned count = (unsigned)dcount; + switch (e1->type->toBasetype()->ty) + { + case Tint8: + value = (d_int8)(value) >> count; + break; + + case Tuns8: + case Tchar: + value = (d_uns8)(value) >> count; + break; + + case Tint16: + value = (d_int16)(value) >> count; + break; + + case Tuns16: + case Twchar: + value = (d_uns16)(value) >> count; + break; + + case Tint32: + value = (d_int32)(value) >> count; + break; + + case Tuns32: + case Tdchar: + value = (d_uns32)(value) >> count; + break; + + case Tint64: + value = (d_int64)(value) >> count; + break; + + case Tuns64: + value = (d_uns64)(value) >> count; + break; + + case Terror: + return e1; + + default: + assert(0); + } + Expression *e = new IntegerExp(loc, value, type); + return e; +} + +Expression *Ushr(Type *type, Expression *e1, Expression *e2) +{ + Loc loc = e1->loc; + + dinteger_t value = e1->toInteger(); + dinteger_t dcount = e2->toInteger(); + assert(dcount <= 0xFFFFFFFF); + unsigned count = (unsigned)dcount; + switch (e1->type->toBasetype()->ty) + { + case Tint8: + case Tuns8: + case Tchar: + // Possible only with >>>=. >>> always gets promoted to int. + value = (value & 0xFF) >> count; + break; + + case Tint16: + case Tuns16: + case Twchar: + // Possible only with >>>=. >>> always gets promoted to int. + value = (value & 0xFFFF) >> count; + break; + + case Tint32: + case Tuns32: + case Tdchar: + value = (value & 0xFFFFFFFF) >> count; + break; + + case Tint64: + case Tuns64: + value = (d_uns64)(value) >> count; + break; + + case Terror: + return e1; + + default: + assert(0); + } + Expression *e = new IntegerExp(loc, value, type); + return e; +} + +Expression *And(Type *type, Expression *e1, Expression *e2) +{ + Expression *e; + e = new IntegerExp(e1->loc, e1->toInteger() & e2->toInteger(), type); + return e; +} + +Expression *Or(Type *type, Expression *e1, Expression *e2) +{ Expression *e; + e = new IntegerExp(e1->loc, e1->toInteger() | e2->toInteger(), type); + return e; +} + +Expression *Xor(Type *type, Expression *e1, Expression *e2) +{ Expression *e; + e = new IntegerExp(e1->loc, e1->toInteger() ^ e2->toInteger(), type); + return e; +} + +/* Also returns EXP_CANT_INTERPRET if cannot be computed. + */ +Expression *Equal(enum TOK op, Type *type, Expression *e1, Expression *e2) +{ Expression *e; + Loc loc = e1->loc; + int cmp; + real_t r1; + real_t r2; + + //printf("Equal(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); + + assert(op == TOKequal || op == TOKnotequal); + + if (e1->op == TOKnull) + { + if (e2->op == TOKnull) + cmp = 1; + else if (e2->op == TOKstring) + { StringExp *es2 = (StringExp *)e2; + cmp = (0 == es2->len); + } + else if (e2->op == TOKarrayliteral) + { ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; + cmp = !es2->elements || (0 == es2->elements->dim); + } + else + return EXP_CANT_INTERPRET; + } + else if (e2->op == TOKnull) + { + if (e1->op == TOKstring) + { StringExp *es1 = (StringExp *)e1; + cmp = (0 == es1->len); + } + else if (e1->op == TOKarrayliteral) + { ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; + cmp = !es1->elements || (0 == es1->elements->dim); + } + else + return EXP_CANT_INTERPRET; + } + else if (e1->op == TOKstring && e2->op == TOKstring) + { StringExp *es1 = (StringExp *)e1; + StringExp *es2 = (StringExp *)e2; + + if (es1->sz != es2->sz) + { + assert(global.errors); + return EXP_CANT_INTERPRET; + } + if (es1->len == es2->len && + memcmp(es1->string, es2->string, es1->sz * es1->len) == 0) + cmp = 1; + else + cmp = 0; + } + else if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral) + { ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; + ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; + + if ((!es1->elements || !es1->elements->dim) && + (!es2->elements || !es2->elements->dim)) + cmp = 1; // both arrays are empty + else if (!es1->elements || !es2->elements) + cmp = 0; + else if (es1->elements->dim != es2->elements->dim) + cmp = 0; + else + { + for (size_t i = 0; i < es1->elements->dim; i++) + { Expression *ee1 = (*es1->elements)[i]; + Expression *ee2 = (*es2->elements)[i]; + + Expression *v = Equal(TOKequal, Type::tint32, ee1, ee2); + if (v == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + cmp = v->toInteger(); + if (cmp == 0) + break; + } + } + } + else if (e1->op == TOKarrayliteral && e2->op == TOKstring) + { // Swap operands and use common code + Expression *etmp = e1; + e1 = e2; + e2 = etmp; + goto Lsa; + } + else if (e1->op == TOKstring && e2->op == TOKarrayliteral) + { + Lsa: + StringExp *es1 = (StringExp *)e1; + ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; + size_t dim1 = es1->len; + size_t dim2 = es2->elements ? es2->elements->dim : 0; + if (dim1 != dim2) + cmp = 0; + else + { + cmp = 1; // if dim1 winds up being 0 + for (size_t i = 0; i < dim1; i++) + { + uinteger_t c = es1->charAt(i); + Expression *ee2 = (*es2->elements)[i]; + if (ee2->isConst() != 1) + return EXP_CANT_INTERPRET; + cmp = (c == ee2->toInteger()); + if (cmp == 0) + break; + } + } + } + else if (e1->op == TOKstructliteral && e2->op == TOKstructliteral) + { StructLiteralExp *es1 = (StructLiteralExp *)e1; + StructLiteralExp *es2 = (StructLiteralExp *)e2; + + if (es1->sd != es2->sd) + cmp = 0; + else if ((!es1->elements || !es1->elements->dim) && + (!es2->elements || !es2->elements->dim)) + cmp = 1; // both arrays are empty + else if (!es1->elements || !es2->elements) + cmp = 0; + else if (es1->elements->dim != es2->elements->dim) + cmp = 0; + else + { + cmp = 1; + for (size_t i = 0; i < es1->elements->dim; i++) + { Expression *ee1 = (*es1->elements)[i]; + Expression *ee2 = (*es2->elements)[i]; + + if (ee1 == ee2) + continue; + if (!ee1 || !ee2) + { cmp = 0; + break; + } + Expression *v = Equal(TOKequal, Type::tint32, ee1, ee2); + if (v == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + cmp = v->toInteger(); + if (cmp == 0) + break; + } + } + } +#if 0 // Should handle this + else if (e1->op == TOKarrayliteral && e2->op == TOKstring) + { + } +#endif + else if (e1->isConst() != 1 || e2->isConst() != 1) + return EXP_CANT_INTERPRET; + else if (e1->type->isreal()) + { + r1 = e1->toReal(); + r2 = e2->toReal(); + goto L1; + } + else if (e1->type->isimaginary()) + { + r1 = e1->toImaginary(); + r2 = e2->toImaginary(); + L1: +#if __DMC__ + cmp = (r1 == r2); +#else + if (Port::isNan(r1) || Port::isNan(r2)) // if unordered + { + cmp = 0; + } + else + { + cmp = (r1 == r2); + } +#endif + } + else if (e1->type->iscomplex()) + { + cmp = e1->toComplex() == e2->toComplex(); + } + else if (e1->type->isintegral() || e1->type->toBasetype()->ty == Tpointer) + { + cmp = (e1->toInteger() == e2->toInteger()); + } + else + return EXP_CANT_INTERPRET; + if (op == TOKnotequal) + cmp ^= 1; + e = new IntegerExp(loc, cmp, type); + return e; +} + +Expression *Identity(enum TOK op, Type *type, Expression *e1, Expression *e2) +{ + Loc loc = e1->loc; + int cmp; + + if (e1->op == TOKnull) + { + cmp = (e2->op == TOKnull); + } + else if (e2->op == TOKnull) + { + cmp = 0; + } + else if (e1->op == TOKsymoff && e2->op == TOKsymoff) + { + SymOffExp *es1 = (SymOffExp *)e1; + SymOffExp *es2 = (SymOffExp *)e2; + + cmp = (es1->var == es2->var && es1->offset == es2->offset); + } + else + { + if (e1->type->isreal()) + { + cmp = RealEquals(e1->toReal(), e2->toReal()); + } + else if (e1->type->isimaginary()) + { + cmp = RealEquals(e1->toImaginary(), e2->toImaginary()); + } + else if (e1->type->iscomplex()) + { + complex_t v1 = e1->toComplex(); + complex_t v2 = e2->toComplex(); + cmp = RealEquals(creall(v1), creall(v2)) && + RealEquals(cimagl(v1), cimagl(v1)); + } + else + return Equal((op == TOKidentity) ? TOKequal : TOKnotequal, + type, e1, e2); + } + if (op == TOKnotidentity) + cmp ^= 1; + return new IntegerExp(loc, cmp, type); +} + + +Expression *Cmp(enum TOK op, Type *type, Expression *e1, Expression *e2) +{ Expression *e; + Loc loc = e1->loc; + dinteger_t n; + real_t r1; + real_t r2; + + //printf("Cmp(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); + + if (e1->op == TOKstring && e2->op == TOKstring) + { StringExp *es1 = (StringExp *)e1; + StringExp *es2 = (StringExp *)e2; + size_t sz = es1->sz; + assert(sz == es2->sz); + + size_t len = es1->len; + if (es2->len < len) + len = es2->len; + + int cmp = memcmp(es1->string, es2->string, sz * len); + if (cmp == 0) + cmp = es1->len - es2->len; + + switch (op) + { + case TOKlt: n = cmp < 0; break; + case TOKle: n = cmp <= 0; break; + case TOKgt: n = cmp > 0; break; + case TOKge: n = cmp >= 0; break; + + case TOKleg: n = 1; break; + case TOKlg: n = cmp != 0; break; + case TOKunord: n = 0; break; + case TOKue: n = cmp == 0; break; + case TOKug: n = cmp > 0; break; + case TOKuge: n = cmp >= 0; break; + case TOKul: n = cmp < 0; break; + case TOKule: n = cmp <= 0; break; + + default: + assert(0); + } + } + else if (e1->isConst() != 1 || e2->isConst() != 1) + return EXP_CANT_INTERPRET; + else if (e1->type->isreal()) + { + r1 = e1->toReal(); + r2 = e2->toReal(); + goto L1; + } + else if (e1->type->isimaginary()) + { + r1 = e1->toImaginary(); + r2 = e2->toImaginary(); + L1: +#if __DMC__ + // DMC is the only compiler I know of that handles NAN arguments + // correctly in comparisons. + switch (op) + { + case TOKlt: n = r1 < r2; break; + case TOKle: n = r1 <= r2; break; + case TOKgt: n = r1 > r2; break; + case TOKge: n = r1 >= r2; break; + + case TOKleg: n = r1 <>= r2; break; + case TOKlg: n = r1 <> r2; break; + case TOKunord: n = r1 !<>= r2; break; + case TOKue: n = r1 !<> r2; break; + case TOKug: n = r1 !<= r2; break; + case TOKuge: n = r1 !< r2; break; + case TOKul: n = r1 !>= r2; break; + case TOKule: n = r1 !> r2; break; + + default: + assert(0); + } +#else + // Don't rely on compiler, handle NAN arguments separately + if (Port::isNan(r1) || Port::isNan(r2)) // if unordered + { + switch (op) + { + case TOKlt: n = 0; break; + case TOKle: n = 0; break; + case TOKgt: n = 0; break; + case TOKge: n = 0; break; + + case TOKleg: n = 0; break; + case TOKlg: n = 0; break; + case TOKunord: n = 1; break; + case TOKue: n = 1; break; + case TOKug: n = 1; break; + case TOKuge: n = 1; break; + case TOKul: n = 1; break; + case TOKule: n = 1; break; + + default: + assert(0); + } + } + else + { + switch (op) + { + case TOKlt: n = r1 < r2; break; + case TOKle: n = r1 <= r2; break; + case TOKgt: n = r1 > r2; break; + case TOKge: n = r1 >= r2; break; + + case TOKleg: n = 1; break; + case TOKlg: n = r1 != r2; break; + case TOKunord: n = 0; break; + case TOKue: n = r1 == r2; break; + case TOKug: n = r1 > r2; break; + case TOKuge: n = r1 >= r2; break; + case TOKul: n = r1 < r2; break; + case TOKule: n = r1 <= r2; break; + + default: + assert(0); + } + } +#endif + } + else if (e1->type->iscomplex()) + { + assert(0); + } + else + { sinteger_t n1; + sinteger_t n2; + + n1 = e1->toInteger(); + n2 = e2->toInteger(); + if (e1->type->isunsigned() || e2->type->isunsigned()) + { + switch (op) + { + case TOKlt: n = ((d_uns64) n1) < ((d_uns64) n2); break; + case TOKle: n = ((d_uns64) n1) <= ((d_uns64) n2); break; + case TOKgt: n = ((d_uns64) n1) > ((d_uns64) n2); break; + case TOKge: n = ((d_uns64) n1) >= ((d_uns64) n2); break; + + case TOKleg: n = 1; break; + case TOKlg: n = ((d_uns64) n1) != ((d_uns64) n2); break; + case TOKunord: n = 0; break; + case TOKue: n = ((d_uns64) n1) == ((d_uns64) n2); break; + case TOKug: n = ((d_uns64) n1) > ((d_uns64) n2); break; + case TOKuge: n = ((d_uns64) n1) >= ((d_uns64) n2); break; + case TOKul: n = ((d_uns64) n1) < ((d_uns64) n2); break; + case TOKule: n = ((d_uns64) n1) <= ((d_uns64) n2); break; + + default: + assert(0); + } + } + else + { + switch (op) + { + case TOKlt: n = n1 < n2; break; + case TOKle: n = n1 <= n2; break; + case TOKgt: n = n1 > n2; break; + case TOKge: n = n1 >= n2; break; + + case TOKleg: n = 1; break; + case TOKlg: n = n1 != n2; break; + case TOKunord: n = 0; break; + case TOKue: n = n1 == n2; break; + case TOKug: n = n1 > n2; break; + case TOKuge: n = n1 >= n2; break; + case TOKul: n = n1 < n2; break; + case TOKule: n = n1 <= n2; break; + + default: + assert(0); + } + } + } + e = new IntegerExp(loc, n, type); + return e; +} + +/* Also returns EXP_CANT_INTERPRET if cannot be computed. + * to: type to cast to + * type: type to paint the result + */ + +Expression *Cast(Type *type, Type *to, Expression *e1) +{ Expression *e = EXP_CANT_INTERPRET; + Loc loc = e1->loc; + + //printf("Cast(type = %s, to = %s, e1 = %s)\n", type->toChars(), to->toChars(), e1->toChars()); + //printf("\te1->type = %s\n", e1->type->toChars()); + if (e1->type->equals(type) && type->equals(to)) + return e1; + if (e1->type->implicitConvTo(to) >= MATCHconst || + to->implicitConvTo(e1->type) >= MATCHconst) + return expType(to, e1); + + // Allow covariant converions of delegates + // (Perhaps implicit conversion from pure to impure should be a MATCHconst, + // then we wouldn't need this extra check.) + if (e1->type->toBasetype()->ty == Tdelegate && + e1->type->implicitConvTo(to) == MATCHconvert) + return expType(to, e1); + + Type *tb = to->toBasetype(); + Type *typeb = type->toBasetype(); + + /* Allow casting from one string type to another + */ + if (e1->op == TOKstring) + { + if (tb->ty == Tarray && typeb->ty == Tarray && + tb->nextOf()->size() == typeb->nextOf()->size()) + { + return expType(to, e1); + } + } + + if (e1->op == TOKarrayliteral && typeb == tb) + return e1; + + if (e1->isConst() != 1) + return EXP_CANT_INTERPRET; + + if (tb->ty == Tbool) + e = new IntegerExp(loc, e1->toInteger() != 0, type); + else if (type->isintegral()) + { + if (e1->type->isfloating()) + { dinteger_t result; + real_t r = e1->toReal(); + + switch (typeb->ty) + { + case Tint8: result = (d_int8)r; break; + case Tchar: + case Tuns8: result = (d_uns8)r; break; + case Tint16: result = (d_int16)r; break; + case Twchar: + case Tuns16: result = (d_uns16)r; break; + case Tint32: result = (d_int32)r; break; + case Tdchar: + case Tuns32: result = (d_uns32)r; break; + case Tint64: result = (d_int64)r; break; + case Tuns64: result = (d_uns64)r; break; + default: + assert(0); + } + + e = new IntegerExp(loc, result, type); + } + else if (type->isunsigned()) + e = new IntegerExp(loc, e1->toUInteger(), type); + else + e = new IntegerExp(loc, e1->toInteger(), type); + } + else if (tb->isreal()) + { real_t value = e1->toReal(); + + e = new RealExp(loc, value, type); + } + else if (tb->isimaginary()) + { real_t value = e1->toImaginary(); + + e = new RealExp(loc, value, type); + } + else if (tb->iscomplex()) + { complex_t value = e1->toComplex(); + + e = new ComplexExp(loc, value, type); + } + else if (tb->isscalar()) + e = new IntegerExp(loc, e1->toInteger(), type); + else if (tb->ty == Tvoid) + e = EXP_CANT_INTERPRET; + else if (tb->ty == Tstruct && e1->op == TOKint64) + { // Struct = 0; + StructDeclaration *sd = tb->toDsymbol(NULL)->isStructDeclaration(); + assert(sd); + Expressions *elements = new Expressions; + for (size_t i = 0; i < sd->fields.dim; i++) + { Dsymbol *s = sd->fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + + Expression *exp = new IntegerExp(0); + exp = Cast(v->type, v->type, exp); + if (exp == EXP_CANT_INTERPRET) + return exp; + elements->push(exp); + } + e = new StructLiteralExp(loc, sd, elements); + e->type = type; + } + else + { + if (type != Type::terror) + error(loc, "cannot cast %s to %s", e1->type->toChars(), type->toChars()); + e = new ErrorExp(); + } + return e; +} + + +Expression *ArrayLength(Type *type, Expression *e1) +{ Expression *e; + Loc loc = e1->loc; + + if (e1->op == TOKstring) + { StringExp *es1 = (StringExp *)e1; + + e = new IntegerExp(loc, es1->len, type); + } + else if (e1->op == TOKarrayliteral) + { ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; + size_t dim; + + dim = ale->elements ? ale->elements->dim : 0; + e = new IntegerExp(loc, dim, type); + } + else if (e1->op == TOKassocarrayliteral) + { AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e1; + size_t dim = ale->keys->dim; + + e = new IntegerExp(loc, dim, type); + } + else + e = EXP_CANT_INTERPRET; + return e; +} + +/* Also return EXP_CANT_INTERPRET if this fails + */ +Expression *Index(Type *type, Expression *e1, Expression *e2) +{ Expression *e = EXP_CANT_INTERPRET; + Loc loc = e1->loc; + + //printf("Index(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); + assert(e1->type); + if (e1->op == TOKstring && e2->op == TOKint64) + { StringExp *es1 = (StringExp *)e1; + uinteger_t i = e2->toInteger(); + + if (i >= es1->len) + { + e1->error("string index %ju is out of bounds [0 .. %zu]", i, es1->len); + e = new ErrorExp(); + } + else + { + e = new IntegerExp(loc, es1->charAt(i), type); + } + } + else if (e1->type->toBasetype()->ty == Tsarray && e2->op == TOKint64) + { TypeSArray *tsa = (TypeSArray *)e1->type->toBasetype(); + uinteger_t length = tsa->dim->toInteger(); + uinteger_t i = e2->toInteger(); + + if (i >= length) + { + e1->error("array index %ju is out of bounds %s[0 .. %ju]", i, e1->toChars(), length); + e = new ErrorExp(); + } + else if (e1->op == TOKarrayliteral) + { ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; + e = ale->elements->tdata()[i]; + e->type = type; + if (e->hasSideEffect()) + e = EXP_CANT_INTERPRET; + } + } + else if (e1->type->toBasetype()->ty == Tarray && e2->op == TOKint64) + { + uinteger_t i = e2->toInteger(); + + if (e1->op == TOKarrayliteral) + { ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; + if (i >= ale->elements->dim) + { + e1->error("array index %ju is out of bounds %s[0 .. %u]", i, e1->toChars(), ale->elements->dim); + e = new ErrorExp(); + } + else + { e = ale->elements->tdata()[i]; + e->type = type; + if (e->hasSideEffect()) + e = EXP_CANT_INTERPRET; + } + } + } + else if (e1->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e1; + /* Search the keys backwards, in case there are duplicate keys + */ + for (size_t i = ae->keys->dim; i;) + { + i--; + Expression *ekey = ae->keys->tdata()[i]; + Expression *ex = Equal(TOKequal, Type::tbool, ekey, e2); + if (ex == EXP_CANT_INTERPRET) + return ex; + if (ex->isBool(TRUE)) + { e = ae->values->tdata()[i]; + e->type = type; + if (e->hasSideEffect()) + e = EXP_CANT_INTERPRET; + break; + } + } + } + return e; +} + +/* Also return EXP_CANT_INTERPRET if this fails + */ +Expression *Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr) +{ Expression *e = EXP_CANT_INTERPRET; + Loc loc = e1->loc; + +#if LOG + printf("Slice()\n"); + if (lwr) + { printf("\te1 = %s\n", e1->toChars()); + printf("\tlwr = %s\n", lwr->toChars()); + printf("\tupr = %s\n", upr->toChars()); + } +#endif + if (e1->op == TOKstring && lwr->op == TOKint64 && upr->op == TOKint64) + { StringExp *es1 = (StringExp *)e1; + uinteger_t ilwr = lwr->toInteger(); + uinteger_t iupr = upr->toInteger(); + + if (iupr > es1->len || ilwr > iupr) + { + e1->error("string slice [%ju .. %ju] is out of bounds", ilwr, iupr); + e = new ErrorExp(); + } + else + { + void *s; + size_t len = iupr - ilwr; + int sz = es1->sz; + StringExp *es; + + s = mem.malloc((len + 1) * sz); + memcpy((unsigned char *)s, (unsigned char *)es1->string + ilwr * sz, len * sz); + memset((unsigned char *)s + len * sz, 0, sz); + + es = new StringExp(loc, s, len, es1->postfix); + es->sz = sz; + es->committed = 1; + es->type = type; + e = es; + } + } + else if (e1->op == TOKarrayliteral && + lwr->op == TOKint64 && upr->op == TOKint64 && + !e1->hasSideEffect()) + { ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; + uinteger_t ilwr = lwr->toInteger(); + uinteger_t iupr = upr->toInteger(); + + if (iupr > es1->elements->dim || ilwr > iupr) + { + e1->error("array slice [%ju .. %ju] is out of bounds", ilwr, iupr); + e = new ErrorExp(); + } + else + { + Expressions *elements = new Expressions(); + elements->setDim(iupr - ilwr); + memcpy(elements->tdata(), + es1->elements->tdata() + ilwr, + (iupr - ilwr) * sizeof(es1->elements->tdata()[0])); + e = new ArrayLiteralExp(e1->loc, elements); + e->type = type; + } + } + return e; +} + +/* Set a slice of char array literal 'existingAE' from a string 'newval'. + * existingAE[firstIndex..firstIndex+newval.length] = newval. + */ +void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, int firstIndex) +{ + size_t newlen = newval->len; + size_t sz = newval->sz; + unsigned char *s = (unsigned char *)newval->string; + Type *elemType = existingAE->type->nextOf(); + for (size_t j = 0; j < newlen; j++) + { + dinteger_t val; + switch (sz) + { + case 1: val = s[j]; break; + case 2: val = ((unsigned short *)s)[j]; break; + case 4: val = ((unsigned *)s)[j]; break; + default: + assert(0); + break; + } + existingAE->elements->tdata()[j+firstIndex] + = new IntegerExp(newval->loc, val, elemType); + } +} + +/* Set a slice of string 'existingSE' from a char array literal 'newae'. + * existingSE[firstIndex..firstIndex+newae.length] = newae. + */ +void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, int firstIndex) +{ + unsigned char *s = (unsigned char *)existingSE->string; + for (size_t j = 0; j < newae->elements->dim; j++) + { + unsigned value = (unsigned)(newae->elements->tdata()[j]->toInteger()); + switch (existingSE->sz) + { + case 1: s[j+firstIndex] = value; break; + case 2: ((unsigned short *)s)[j+firstIndex] = value; break; + case 4: ((unsigned *)s)[j+firstIndex] = value; break; + default: + assert(0); + break; + } + } +} + +/* Set a slice of string 'existingSE' from a string 'newstr'. + * existingSE[firstIndex..firstIndex+newstr.length] = newstr. + */ +void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, int firstIndex) +{ + unsigned char *s = (unsigned char *)existingSE->string; + size_t sz = existingSE->sz; + assert(sz == newstr->sz); + memcpy(s + firstIndex * sz, newstr->string, sz * newstr->len); +} + +/* Also return EXP_CANT_INTERPRET if this fails + */ +Expression *Cat(Type *type, Expression *e1, Expression *e2) +{ Expression *e = EXP_CANT_INTERPRET; + Loc loc = e1->loc; + Type *t; + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + + //printf("Cat(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); + //printf("\tt1 = %s, t2 = %s, type = %s\n", t1->toChars(), t2->toChars(), type->toChars()); + + if (e1->op == TOKnull && (e2->op == TOKint64 || e2->op == TOKstructliteral)) + { e = e2; + t = t1; + goto L2; + } + else if ((e1->op == TOKint64 || e1->op == TOKstructliteral) && e2->op == TOKnull) + { e = e1; + t = t2; + L2: + Type *tn = e->type->toBasetype(); + if (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar) + { + // Create a StringExp + void *s; + StringExp *es; + if (t->nextOf()) + t = t->nextOf()->toBasetype(); + int sz = t->size(); + + dinteger_t v = e->toInteger(); + + size_t len = (t->ty == tn->ty) ? 1 : utf_codeLength(sz, v); + s = mem.malloc((len + 1) * sz); + if (t->ty == tn->ty) + memcpy((unsigned char *)s, &v, sz); + else + utf_encode(sz, s, v); + + // Add terminating 0 + memset((unsigned char *)s + len * sz, 0, sz); + + es = new StringExp(loc, s, len); + es->sz = sz; + es->committed = 1; + e = es; + } + else + { // Create an ArrayLiteralExp + Expressions *elements = new Expressions(); + elements->push(e); + e = new ArrayLiteralExp(e->loc, elements); + } + e->type = type; + return e; + } + else if (e1->op == TOKnull && e2->op == TOKnull) + { + if (type == e1->type) + { + // Handle null ~= null + if (t1->ty == Tarray && t2 == t1->nextOf()) + { + e = new ArrayLiteralExp(e1->loc, e2); + e->type = type; + return e; + } + else + return e1; + } + if (type == e2->type) + return e2; + return new NullExp(e1->loc, type); + } + else if (e1->op == TOKstring && e2->op == TOKstring) + { + // Concatenate the strings + void *s; + StringExp *es1 = (StringExp *)e1; + StringExp *es2 = (StringExp *)e2; + StringExp *es; + size_t len = es1->len + es2->len; + int sz = es1->sz; + + if (sz != es2->sz) + { + /* Can happen with: + * auto s = "foo"d ~ "bar"c; + */ + assert(global.errors); + return e; + } + s = mem.malloc((len + 1) * sz); + memcpy(s, es1->string, es1->len * sz); + memcpy((unsigned char *)s + es1->len * sz, es2->string, es2->len * sz); + + // Add terminating 0 + memset((unsigned char *)s + len * sz, 0, sz); + + es = new StringExp(loc, s, len); + es->sz = sz; + es->committed = es1->committed | es2->committed; + es->type = type; + e = es; + } + else if (e2->op == TOKstring && e1->op == TOKarrayliteral && + t1->nextOf()->isintegral()) + { + // [chars] ~ string --> [chars] + StringExp *es = (StringExp *)e2; + ArrayLiteralExp *ea = (ArrayLiteralExp *)e1; + size_t len = es->len + ea->elements->dim; + Expressions * elems = new Expressions; + elems->setDim(len); + for (size_t i= 0; i < ea->elements->dim; ++i) + { + elems->tdata()[i] = ea->elements->tdata()[i]; + } + ArrayLiteralExp *dest = new ArrayLiteralExp(e1->loc, elems); + dest->type = type; + sliceAssignArrayLiteralFromString(dest, es, ea->elements->dim); + return dest; + } + else if (e1->op == TOKstring && e2->op == TOKarrayliteral && + t2->nextOf()->isintegral()) + { + // string ~ [chars] --> [chars] + StringExp *es = (StringExp *)e1; + ArrayLiteralExp *ea = (ArrayLiteralExp *)e2; + size_t len = es->len + ea->elements->dim; + Expressions * elems = new Expressions; + elems->setDim(len); + for (size_t i= 0; i < ea->elements->dim; ++i) + { + elems->tdata()[es->len + i] = ea->elements->tdata()[i]; + } + ArrayLiteralExp *dest = new ArrayLiteralExp(e1->loc, elems); + dest->type = type; + sliceAssignArrayLiteralFromString(dest, es, 0); + return dest; + } + else if (e1->op == TOKstring && e2->op == TOKint64) + { + // string ~ char --> string + void *s; + StringExp *es1 = (StringExp *)e1; + StringExp *es; + int sz = es1->sz; + dinteger_t v = e2->toInteger(); + + // Is it a concatentation of homogenous types? + // (char[] ~ char, wchar[]~wchar, or dchar[]~dchar) + bool homoConcat = (sz == t2->size()); + size_t len = es1->len; + len += homoConcat ? 1 : utf_codeLength(sz, v); + + s = mem.malloc((len + 1) * sz); + memcpy(s, es1->string, es1->len * sz); + if (homoConcat) + memcpy((unsigned char *)s + (sz * es1->len), &v, sz); + else + utf_encode(sz, (unsigned char *)s + (sz * es1->len), v); + + // Add terminating 0 + memset((unsigned char *)s + len * sz, 0, sz); + + es = new StringExp(loc, s, len); + es->sz = sz; + es->committed = es1->committed; + es->type = type; + e = es; + } + else if (e1->op == TOKint64 && e2->op == TOKstring) + { + // Concatenate the strings + void *s; + StringExp *es2 = (StringExp *)e2; + StringExp *es; + size_t len = 1 + es2->len; + int sz = es2->sz; + dinteger_t v = e1->toInteger(); + + s = mem.malloc((len + 1) * sz); + memcpy((unsigned char *)s, &v, sz); + memcpy((unsigned char *)s + sz, es2->string, es2->len * sz); + + // Add terminating 0 + memset((unsigned char *)s + len * sz, 0, sz); + + es = new StringExp(loc, s, len); + es->sz = sz; + es->committed = es2->committed; + es->type = type; + e = es; + } + else if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral && + t1->nextOf()->equals(t2->nextOf())) + { + // Concatenate the arrays + ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; + ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; + + es1 = new ArrayLiteralExp(es1->loc, (Expressions *)es1->elements->copy()); + es1->elements->insert(es1->elements->dim, es2->elements); + e = es1; + + if (type->toBasetype()->ty == Tsarray) + { + e->type = new TypeSArray(t1->nextOf(), new IntegerExp(loc, es1->elements->dim, Type::tindex)); + e->type = e->type->semantic(loc, NULL); + } + else + e->type = type; + } + else if (e1->op == TOKarrayliteral && e2->op == TOKnull && + t1->nextOf()->equals(t2->nextOf())) + { + e = e1; + goto L3; + } + else if (e1->op == TOKnull && e2->op == TOKarrayliteral && + t1->nextOf()->equals(t2->nextOf())) + { + e = e2; + L3: + // Concatenate the array with null + ArrayLiteralExp *es = (ArrayLiteralExp *)e; + + es = new ArrayLiteralExp(es->loc, (Expressions *)es->elements->copy()); + e = es; + + if (type->toBasetype()->ty == Tsarray) + { + e->type = new TypeSArray(t1->nextOf(), new IntegerExp(loc, es->elements->dim, Type::tindex)); + e->type = e->type->semantic(loc, NULL); + } + else + e->type = type; + } + else if ((e1->op == TOKarrayliteral || e1->op == TOKnull) && + e1->type->toBasetype()->nextOf()->equals(e2->type)) + { + ArrayLiteralExp *es1; + if (e1->op == TOKarrayliteral) + { es1 = (ArrayLiteralExp *)e1; + es1 = new ArrayLiteralExp(es1->loc, (Expressions *)es1->elements->copy()); + es1->elements->push(e2); + } + else + { + es1 = new ArrayLiteralExp(e1->loc, e2); + } + e = es1; + + if (type->toBasetype()->ty == Tsarray) + { + e->type = new TypeSArray(e2->type, new IntegerExp(loc, es1->elements->dim, Type::tindex)); + e->type = e->type->semantic(loc, NULL); + } + else + e->type = type; + } + else if (e2->op == TOKarrayliteral && + e2->type->toBasetype()->nextOf()->equals(e1->type)) + { + ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; + + es2 = new ArrayLiteralExp(es2->loc, (Expressions *)es2->elements->copy()); + es2->elements->shift(e1); + e = es2; + + if (type->toBasetype()->ty == Tsarray) + { + e->type = new TypeSArray(e1->type, new IntegerExp(loc, es2->elements->dim, Type::tindex)); + e->type = e->type->semantic(loc, NULL); + } + else + e->type = type; + } + else if (e1->op == TOKnull && e2->op == TOKstring) + { + t = e1->type; + e = e2; + goto L1; + } + else if (e1->op == TOKstring && e2->op == TOKnull) + { e = e1; + t = e2->type; + L1: + Type *tb = t->toBasetype(); + if (tb->ty == Tarray && tb->nextOf()->equals(e->type)) + { Expressions *expressions = new Expressions(); + expressions->push(e); + e = new ArrayLiteralExp(loc, expressions); + e->type = t; + } + if (!e->type->equals(type)) + { StringExp *se = (StringExp *)e->copy(); + e = se->castTo(NULL, type); + } + } + return e; +} + +Expression *Ptr(Type *type, Expression *e1) +{ + //printf("Ptr(e1 = %s)\n", e1->toChars()); + if (e1->op == TOKadd) + { AddExp *ae = (AddExp *)e1; + if (ae->e1->op == TOKaddress && ae->e2->op == TOKint64) + { AddrExp *ade = (AddrExp *)ae->e1; + if (ade->e1->op == TOKstructliteral) + { StructLiteralExp *se = (StructLiteralExp *)ade->e1; + unsigned offset = ae->e2->toInteger(); + Expression *e = se->getField(type, offset); + if (!e) + e = EXP_CANT_INTERPRET; + return e; + } + } + } + return EXP_CANT_INTERPRET; +} + diff --git a/cppmangle.c b/cppmangle.c new file mode 100644 index 00000000..630567ef --- /dev/null +++ b/cppmangle.c @@ -0,0 +1,454 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2010 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "mars.h" +#include "dsymbol.h" +#include "mtype.h" +#include "scope.h" +#include "init.h" +#include "expression.h" +#include "attrib.h" +#include "declaration.h" +#include "template.h" +#include "id.h" +#include "enum.h" +#include "import.h" +#include "aggregate.h" + +#if CPP_MANGLE + +/* Do mangling for C++ linkage. + * Follows Itanium C++ ABI 1.86 + * No attempt is made to support mangling of templates, operator + * overloading, or special functions. + * + * So why don't we use the C++ ABI for D name mangling? + * Because D supports a lot of things (like modules) that the C++ + * ABI has no concept of. These affect every D mangled name, + * so nothing would be compatible anyway. + */ + +struct CppMangleState +{ + static Voids components; + + int substitute(OutBuffer *buf, void *p); + int exist(void *p); + void store(void *p); +}; + +Voids CppMangleState::components; + + +void writeBase36(OutBuffer *buf, unsigned i) +{ + if (i >= 36) + { + writeBase36(buf, i / 36); + i %= 36; + } + if (i < 10) + buf->writeByte(i + '0'); + else if (i < 36) + buf->writeByte(i - 10 + 'A'); + else + assert(0); +} + +int CppMangleState::substitute(OutBuffer *buf, void *p) +{ + for (size_t i = 0; i < components.dim; i++) + { + if (p == components.tdata()[i]) + { + /* Sequence is S_, S0_, .., S9_, SA_, ..., SZ_, S10_, ... + */ + buf->writeByte('S'); + if (i) + writeBase36(buf, i - 1); + buf->writeByte('_'); + return 1; + } + } + components.push(p); + return 0; +} + +int CppMangleState::exist(void *p) +{ + for (size_t i = 0; i < components.dim; i++) + { + if (p == components.tdata()[i]) + { + return 1; + } + } + return 0; +} + +void CppMangleState::store(void *p) +{ + components.push(p); +} + +void source_name(OutBuffer *buf, Dsymbol *s) +{ + char *name = s->ident->toChars(); + buf->printf("%d%s", strlen(name), name); +} + +void prefix_name(OutBuffer *buf, CppMangleState *cms, Dsymbol *s) +{ + if (!cms->substitute(buf, s)) + { + Dsymbol *p = s->toParent(); + if (p && !p->isModule()) + { + prefix_name(buf, cms, p); + } + source_name(buf, s); + } +} + +void cpp_mangle_name(OutBuffer *buf, CppMangleState *cms, Dsymbol *s) +{ + Dsymbol *p = s->toParent(); + if (p && !p->isModule()) + { + buf->writeByte('N'); + + FuncDeclaration *fd = s->isFuncDeclaration(); + if (!fd) + { + s->error("C++ static variables not supported"); + } + else + if (fd->isConst()) + buf->writeByte('K'); + + prefix_name(buf, cms, p); + source_name(buf, s); + + buf->writeByte('E'); + } + else + source_name(buf, s); +} + + +char *cpp_mangle(Dsymbol *s) +{ + /* + * ::= _Z + * ::= + * ::= + * ::= + */ + + CppMangleState cms; + memset(&cms, 0, sizeof(cms)); + cms.components.setDim(0); + + OutBuffer buf; +#if MACHOBJ + buf.writestring("__Z"); +#else + buf.writestring("_Z"); +#endif + + cpp_mangle_name(&buf, &cms, s); + + FuncDeclaration *fd = s->isFuncDeclaration(); + if (fd) + { // add + TypeFunction *tf = (TypeFunction *)fd->type; + assert(tf->ty == Tfunction); + Parameter::argsCppMangle(&buf, &cms, tf->parameters, tf->varargs); + } + buf.writeByte(0); + return (char *)buf.extractData(); +} + +/* ============= Type Encodings ============================================= */ + +void Type::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + /* Make this the 'vendor extended type' when there is no + * C++ analog. + * u + */ + if (!cms->substitute(buf, this)) + { assert(deco); + buf->printf("u%d%s", strlen(deco), deco); + } +} + +void TypeBasic::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ char c; + char p = 0; + + /* ABI spec says: + * v void + * w wchar_t + * b bool + * c char + * a signed char + * h unsigned char + * s short + * t unsigned short + * i int + * j unsigned int + * l long + * m unsigned long + * x long long, __int64 + * y unsigned long long, __int64 + * n __int128 + * o unsigned __int128 + * f float + * d double + * e long double, __float80 + * g __float128 + * z ellipsis + * u # vendor extended type + */ + + switch (ty) + { + case Tvoid: c = 'v'; break; + case Tint8: c = 'a'; break; + case Tuns8: c = 'h'; break; + case Tint16: c = 's'; break; + case Tuns16: c = 't'; break; + case Tint32: c = 'i'; break; + case Tuns32: c = 'j'; break; + case Tfloat32: c = 'f'; break; + case Tint64: c = 'x'; break; + case Tuns64: c = 'y'; break; + case Tfloat64: c = 'd'; break; + case Tfloat80: c = 'e'; break; + case Tbool: c = 'b'; break; + case Tchar: c = 'c'; break; + case Twchar: c = 't'; break; + case Tdchar: c = 'w'; break; + + case Timaginary32: p = 'G'; c = 'f'; break; + case Timaginary64: p = 'G'; c = 'd'; break; + case Timaginary80: p = 'G'; c = 'e'; break; + case Tcomplex32: p = 'C'; c = 'f'; break; + case Tcomplex64: p = 'C'; c = 'd'; break; + case Tcomplex80: p = 'C'; c = 'e'; break; + + default: assert(0); + } + if (p || isConst()) + { + if (cms->substitute(buf, this)) + return; + } + + if (isConst()) + buf->writeByte('K'); + + if (p) + buf->writeByte(p); + + buf->writeByte(c); +} + + +void TypeVector::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + if (!cms->substitute(buf, this)) + { buf->writestring("U8__vector"); + basetype->toCppMangle(buf, cms); + } +} + +void TypeSArray::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + if (!cms->substitute(buf, this)) + { buf->printf("A%ju_", dim ? dim->toInteger() : 0); + next->toCppMangle(buf, cms); + } +} + +void TypeDArray::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + Type::toCppMangle(buf, cms); +} + + +void TypeAArray::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + Type::toCppMangle(buf, cms); +} + + +void TypePointer::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + if (!cms->exist(this)) + { buf->writeByte('P'); + next->toCppMangle(buf, cms); + cms->store(this); + } + else + cms->substitute(buf, this); +} + + +void TypeReference::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + if (!cms->exist(this)) + { buf->writeByte('R'); + next->toCppMangle(buf, cms); + cms->store(this); + } + else + cms->substitute(buf, this); +} + + +void TypeFunction::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ /* + * ::= F [Y] E + * ::= + + * # types are possible return type, then parameter types + */ + + /* ABI says: + "The type of a non-static member function is considered to be different, + for the purposes of substitution, from the type of a namespace-scope or + static member function whose type appears similar. The types of two + non-static member functions are considered to be different, for the + purposes of substitution, if the functions are members of different + classes. In other words, for the purposes of substitution, the class of + which the function is a member is considered part of the type of + function." + + BUG: Right now, types of functions are never merged, so our simplistic + component matcher always finds them to be different. + We should use Type::equals on these, and use different + TypeFunctions for non-static member functions, and non-static + member functions of different classes. + */ + if (!cms->substitute(buf, this)) + { + buf->writeByte('F'); + if (linkage == LINKc) + buf->writeByte('Y'); + next->toCppMangle(buf, cms); + Parameter::argsCppMangle(buf, cms, parameters, varargs); + buf->writeByte('E'); + } +} + + +void TypeDelegate::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + Type::toCppMangle(buf, cms); +} + + +void TypeStruct::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + if (!cms->exist(this)) + { + if (isConst()) + buf->writeByte('K'); + + if (!cms->substitute(buf, sym)) + cpp_mangle_name(buf, cms, sym); + + if (isConst()) + cms->store(this); + } + else + cms->substitute(buf, this); +} + + +void TypeEnum::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + if (!cms->exist(this)) + { + if (isConst()) + buf->writeByte('K'); + + if (!cms->substitute(buf, sym)) + cpp_mangle_name(buf, cms, sym); + + if (isConst()) + cms->store(this); + } + else + cms->substitute(buf, this); +} + + +void TypeTypedef::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + Type::toCppMangle(buf, cms); +} + + +void TypeClass::toCppMangle(OutBuffer *buf, CppMangleState *cms) +{ + if (!cms->substitute(buf, this)) + { buf->writeByte('P'); + if (!cms->substitute(buf, sym)) + cpp_mangle_name(buf, cms, sym); + } +} + + + +void Parameter::argsCppMangle(OutBuffer *buf, CppMangleState *cms, Parameters *arguments, int varargs) +{ int n = 0; + if (arguments) + { + for (size_t i = 0; i < arguments->dim; i++) + { Parameter *arg = arguments->tdata()[i]; + Type *t = arg->type; + if (arg->storageClass & (STCout | STCref)) + t = t->referenceTo(); + else if (arg->storageClass & STClazy) + { // Mangle as delegate + Type *td = new TypeFunction(NULL, t, 0, LINKd); + td = new TypeDelegate(td); + t = t->merge(); + } + if (t->ty == Tsarray) + { // Mangle static arrays as pointers + t = t->pointerTo(); + } + + /* If it is a basic, enum or struct type, + * then don't mark it const + */ + if ((t->ty == Tenum || t->ty == Tstruct || t->isTypeBasic()) && t->isConst()) + t->mutableOf()->toCppMangle(buf, cms); + else + t->toCppMangle(buf, cms); + + n++; + } + } + if (varargs) + buf->writestring("z"); + else if (!n) + buf->writeByte('v'); // encode ( ) arguments +} + + +#endif + diff --git a/declaration.c b/declaration.c new file mode 100644 index 00000000..3fc3df01 --- /dev/null +++ b/declaration.c @@ -0,0 +1,2258 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "init.h" +#include "declaration.h" +#include "attrib.h" +#include "mtype.h" +#include "template.h" +#include "scope.h" +#include "aggregate.h" +#include "module.h" +#include "id.h" +#include "expression.h" +#include "statement.h" +#include "hdrgen.h" + +/********************************* Declaration ****************************/ + +Declaration::Declaration(Identifier *id) + : Dsymbol(id) +{ + type = NULL; + originalType = NULL; + storage_class = STCundefined; + protection = PROTundefined; + linkage = LINKdefault; + inuse = 0; + sem = SemanticStart; +} + +void Declaration::semantic(Scope *sc) +{ +} + +const char *Declaration::kind() +{ + return "declaration"; +} + +unsigned Declaration::size(Loc loc) +{ + assert(type); + return type->size(); +} + +int Declaration::isDelete() +{ + return FALSE; +} + +int Declaration::isDataseg() +{ + return FALSE; +} + +int Declaration::isThreadlocal() +{ + return FALSE; +} + +int Declaration::isCodeseg() +{ + return FALSE; +} + +enum PROT Declaration::prot() +{ + return protection; +} + +/************************************* + * Check to see if declaration can be modified in this context (sc). + * Issue error if not. + */ + +#if DMDV2 + +void Declaration::checkModify(Loc loc, Scope *sc, Type *t) +{ + if (sc->incontract && isParameter()) + error(loc, "cannot modify parameter '%s' in contract", toChars()); + + if (sc->incontract && isResult()) + error(loc, "cannot modify result '%s' in contract", toChars()); + + if (isCtorinit() && !t->isMutable() || + (storage_class & STCnodefaultctor)) + { // It's only modifiable if inside the right constructor + modifyFieldVar(loc, sc, isVarDeclaration(), NULL); + } + else + { + VarDeclaration *v = isVarDeclaration(); + if (v && v->canassign == 0) + { + const char *p = NULL; + if (isConst()) + p = "const"; + else if (isImmutable()) + p = "immutable"; + else if (isWild()) + p = "inout"; + else if (storage_class & STCmanifest) + p = "enum"; + else if (!t->isAssignable()) + p = "struct with immutable members"; + if (p) + { error(loc, "cannot modify %s", p); + } + } + } +} +#endif + + +/********************************* TupleDeclaration ****************************/ + +TupleDeclaration::TupleDeclaration(Loc loc, Identifier *id, Objects *objects) + : Declaration(id) +{ + this->loc = loc; + this->type = NULL; + this->objects = objects; + this->isexp = 0; + this->tupletype = NULL; +} + +Dsymbol *TupleDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(0); + return NULL; +} + +const char *TupleDeclaration::kind() +{ + return "tuple"; +} + +Type *TupleDeclaration::getType() +{ + /* If this tuple represents a type, return that type + */ + + //printf("TupleDeclaration::getType() %s\n", toChars()); + if (isexp) + return NULL; + if (!tupletype) + { + /* It's only a type tuple if all the Object's are types + */ + for (size_t i = 0; i < objects->dim; i++) + { Object *o = objects->tdata()[i]; + + if (o->dyncast() != DYNCAST_TYPE) + { + //printf("\tnot[%d], %p, %d\n", i, o, o->dyncast()); + return NULL; + } + } + + /* We know it's a type tuple, so build the TypeTuple + */ + Types *types = (Types *)objects; + Parameters *args = new Parameters(); + args->setDim(objects->dim); + OutBuffer buf; + int hasdeco = 1; + for (size_t i = 0; i < types->dim; i++) + { Type *t = types->tdata()[i]; + + //printf("type = %s\n", t->toChars()); +#if 0 + buf.printf("_%s_%d", ident->toChars(), i); + char *name = (char *)buf.extractData(); + Identifier *id = new Identifier(name, TOKidentifier); + Parameter *arg = new Parameter(STCin, t, id, NULL); +#else + Parameter *arg = new Parameter(0, t, NULL, NULL); +#endif + args->tdata()[i] = arg; + if (!t->deco) + hasdeco = 0; + } + + tupletype = new TypeTuple(args); + if (hasdeco) + return tupletype->semantic(0, NULL); + } + + return tupletype; +} + +int TupleDeclaration::needThis() +{ + //printf("TupleDeclaration::needThis(%s)\n", toChars()); + for (size_t i = 0; i < objects->dim; i++) + { Object *o = objects->tdata()[i]; + if (o->dyncast() == DYNCAST_EXPRESSION) + { Expression *e = (Expression *)o; + if (e->op == TOKdsymbol) + { DsymbolExp *ve = (DsymbolExp *)e; + Declaration *d = ve->s->isDeclaration(); + if (d && d->needThis()) + { + return 1; + } + } + } + } + return 0; +} + + +/********************************* TypedefDeclaration ****************************/ + +TypedefDeclaration::TypedefDeclaration(Loc loc, Identifier *id, Type *basetype, Initializer *init) + : Declaration(id) +{ + this->type = new TypeTypedef(this); + this->basetype = basetype->toBasetype(); + this->init = init; + this->htype = NULL; + this->hbasetype = NULL; + this->loc = loc; + this->sinit = NULL; +} + +Dsymbol *TypedefDeclaration::syntaxCopy(Dsymbol *s) +{ + Type *basetype = this->basetype->syntaxCopy(); + + Initializer *init = NULL; + if (this->init) + init = this->init->syntaxCopy(); + + assert(!s); + TypedefDeclaration *st; + st = new TypedefDeclaration(loc, ident, basetype, init); + + // Syntax copy for header file + if (!htype) // Don't overwrite original + { if (type) // Make copy for both old and new instances + { htype = type->syntaxCopy(); + st->htype = type->syntaxCopy(); + } + } + else // Make copy of original for new instance + st->htype = htype->syntaxCopy(); + if (!hbasetype) + { if (basetype) + { hbasetype = basetype->syntaxCopy(); + st->hbasetype = basetype->syntaxCopy(); + } + } + else + st->hbasetype = hbasetype->syntaxCopy(); + + return st; +} + +void TypedefDeclaration::semantic(Scope *sc) +{ + //printf("TypedefDeclaration::semantic(%s) sem = %d\n", toChars(), sem); + if (sem == SemanticStart) + { sem = SemanticIn; + parent = sc->parent; + int errors = global.errors; + Type *savedbasetype = basetype; + basetype = basetype->semantic(loc, sc); + if (errors != global.errors) + { + basetype = savedbasetype; + sem = SemanticStart; + return; + } + sem = SemanticDone; +#if DMDV2 + type = type->addStorageClass(storage_class); +#endif + Type *savedtype = type; + type = type->semantic(loc, sc); + if (sc->parent->isFuncDeclaration() && init) + semantic2(sc); + if (errors != global.errors) + { + basetype = savedbasetype; + type = savedtype; + sem = SemanticStart; + return; + } + storage_class |= sc->stc & STCdeprecated; + } + else if (sem == SemanticIn) + { + error("circular definition"); + } +} + +void TypedefDeclaration::semantic2(Scope *sc) +{ + //printf("TypedefDeclaration::semantic2(%s) sem = %d\n", toChars(), sem); + if (sem == SemanticDone) + { sem = Semantic2Done; + if (init) + { + Initializer *savedinit = init; + int errors = global.errors; + init = init->semantic(sc, basetype, WANTinterpret); + if (errors != global.errors) + { + init = savedinit; + return; + } + + ExpInitializer *ie = init->isExpInitializer(); + if (ie) + { + if (ie->exp->type == basetype) + ie->exp->type = type; + } + } + } +} + +const char *TypedefDeclaration::kind() +{ + return "typedef"; +} + +Type *TypedefDeclaration::getType() +{ + return type; +} + +void TypedefDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("typedef "); + basetype->toCBuffer(buf, ident, hgs); + if (init) + { + buf->writestring(" = "); + init->toCBuffer(buf, hgs); + } + buf->writeByte(';'); + buf->writenl(); +} + +/********************************* AliasDeclaration ****************************/ + +AliasDeclaration::AliasDeclaration(Loc loc, Identifier *id, Type *type) + : Declaration(id) +{ + //printf("AliasDeclaration(id = '%s', type = %p)\n", id->toChars(), type); + //printf("type = '%s'\n", type->toChars()); + this->loc = loc; + this->type = type; + this->aliassym = NULL; + this->htype = NULL; + this->haliassym = NULL; + this->overnext = NULL; + this->inSemantic = 0; + assert(type); +} + +AliasDeclaration::AliasDeclaration(Loc loc, Identifier *id, Dsymbol *s) + : Declaration(id) +{ + //printf("AliasDeclaration(id = '%s', s = %p)\n", id->toChars(), s); + assert(s != this); + this->loc = loc; + this->type = NULL; + this->aliassym = s; + this->htype = NULL; + this->haliassym = NULL; + this->overnext = NULL; + this->inSemantic = 0; + assert(s); +} + +Dsymbol *AliasDeclaration::syntaxCopy(Dsymbol *s) +{ + //printf("AliasDeclaration::syntaxCopy()\n"); + assert(!s); + AliasDeclaration *sa; + if (type) + sa = new AliasDeclaration(loc, ident, type->syntaxCopy()); + else + sa = new AliasDeclaration(loc, ident, aliassym->syntaxCopy(NULL)); + + // Syntax copy for header file + if (!htype) // Don't overwrite original + { if (type) // Make copy for both old and new instances + { htype = type->syntaxCopy(); + sa->htype = type->syntaxCopy(); + } + } + else // Make copy of original for new instance + sa->htype = htype->syntaxCopy(); + if (!haliassym) + { if (aliassym) + { haliassym = aliassym->syntaxCopy(s); + sa->haliassym = aliassym->syntaxCopy(s); + } + } + else + sa->haliassym = haliassym->syntaxCopy(s); + + return sa; +} + +void AliasDeclaration::semantic(Scope *sc) +{ + //printf("AliasDeclaration::semantic() %s\n", toChars()); + if (aliassym) + { + if (aliassym->isTemplateInstance()) + aliassym->semantic(sc); + return; + } + this->inSemantic = 1; + +#if DMDV1 // don't really know why this is here + if (storage_class & STCconst) + error("cannot be const"); +#endif + + storage_class |= sc->stc & STCdeprecated; + protection = sc->protection; + + // Given: + // alias foo.bar.abc def; + // it is not knowable from the syntax whether this is an alias + // for a type or an alias for a symbol. It is up to the semantic() + // pass to distinguish. + // If it is a type, then type is set and getType() will return that + // type. If it is a symbol, then aliassym is set and type is NULL - + // toAlias() will return aliasssym. + + int errors = global.errors; + Type *savedtype = type; + + Dsymbol *s; + Type *t; + Expression *e; + + /* This section is needed because resolve() will: + * const x = 3; + * alias x y; + * try to alias y to 3. + */ + s = type->toDsymbol(sc); + if (s +#if DMDV2 + && ((s->getType() && type->equals(s->getType())) || s->isEnumMember()) +#endif + ) + goto L2; // it's a symbolic alias + +#if DMDV2 + type = type->addStorageClass(storage_class); + if (storage_class & (STCref | STCnothrow | STCpure | STCdisable)) + { // For 'ref' to be attached to function types, and picked + // up by Type::resolve(), it has to go into sc. + sc = sc->push(); + sc->stc |= storage_class & (STCref | STCnothrow | STCpure | STCshared | STCdisable); + type->resolve(loc, sc, &e, &t, &s); + sc = sc->pop(); + } + else +#endif + type->resolve(loc, sc, &e, &t, &s); + if (s) + { + goto L2; + } + else if (e) + { + // Try to convert Expression to Dsymbol + s = getDsymbol(e); + if (s) + goto L2; + + error("cannot alias an expression %s", e->toChars()); + t = e->type; + } + else if (t) + { + type = t->semantic(loc, sc); + //printf("\talias resolved to type %s\n", type->toChars()); + } + if (overnext) + ScopeDsymbol::multiplyDefined(0, this, overnext); + this->inSemantic = 0; + + if (errors != global.errors) + type = savedtype; + return; + + L2: + //printf("alias is a symbol %s %s\n", s->kind(), s->toChars()); + type = NULL; + VarDeclaration *v = s->isVarDeclaration(); + if (0 && v && v->linkage == LINKdefault) + { + error("forward reference of %s", v->toChars()); + s = NULL; + } + else + { + Dsymbol *savedovernext = overnext; + FuncDeclaration *f = s->toAlias()->isFuncDeclaration(); + if (f) + { + if (overnext) + { + FuncAliasDeclaration *fa = new FuncAliasDeclaration(f); + if (!fa->overloadInsert(overnext)) + ScopeDsymbol::multiplyDefined(0, f, overnext); + overnext = NULL; + s = fa; + s->parent = sc->parent; + } + } + if (overnext) + ScopeDsymbol::multiplyDefined(0, this, overnext); + if (s == this) + { + assert(global.errors); + s = NULL; + } + if (errors != global.errors) + { + type = savedtype; + overnext = savedovernext; + aliassym = NULL; + inSemantic = 0; + return; + } + } + //printf("setting aliassym %s to %s %s\n", toChars(), s->kind(), s->toChars()); + aliassym = s; + this->inSemantic = 0; +} + +int AliasDeclaration::overloadInsert(Dsymbol *s) +{ + /* Don't know yet what the aliased symbol is, so assume it can + * be overloaded and check later for correctness. + */ + + //printf("AliasDeclaration::overloadInsert('%s')\n", s->toChars()); + if (aliassym) // see test/test56.d + { + Dsymbol *a = aliassym->toAlias(); + FuncDeclaration *f = a->isFuncDeclaration(); + if (f) // BUG: what if it's a template? + { + FuncAliasDeclaration *fa = new FuncAliasDeclaration(f); + aliassym = fa; + return fa->overloadInsert(s); + } + } + + if (overnext == NULL) + { + if (s == this) + { + return TRUE; + } + overnext = s; + return TRUE; + } + else + { + return overnext->overloadInsert(s); + } +} + +const char *AliasDeclaration::kind() +{ + return "alias"; +} + +Type *AliasDeclaration::getType() +{ + return type; +} + +Dsymbol *AliasDeclaration::toAlias() +{ + //printf("AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s')\n", toChars(), this, aliassym, aliassym ? aliassym->kind() : ""); + assert(this != aliassym); + //static int count; if (++count == 10) *(char*)0=0; + if (inSemantic) + { error("recursive alias declaration"); + aliassym = new AliasDeclaration(loc, ident, Type::terror); + type = Type::terror; + } + else if (!aliassym && scope) + semantic(scope); + Dsymbol *s = aliassym ? aliassym->toAlias() : this; + return s; +} + +void AliasDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("alias "); +#if 0 + if (hgs->hdrgen) + { + if (haliassym) + { + haliassym->toCBuffer(buf, hgs); + buf->writeByte(' '); + buf->writestring(ident->toChars()); + } + else + htype->toCBuffer(buf, ident, hgs); + } + else +#endif + { + if (aliassym) + { + aliassym->toCBuffer(buf, hgs); + buf->writeByte(' '); + buf->writestring(ident->toChars()); + } + else + type->toCBuffer(buf, ident, hgs); + } + buf->writeByte(';'); + buf->writenl(); +} + +/********************************* VarDeclaration ****************************/ + +VarDeclaration::VarDeclaration(Loc loc, Type *type, Identifier *id, Initializer *init) + : Declaration(id) +{ + //printf("VarDeclaration('%s')\n", id->toChars()); +#ifdef DEBUG + if (!type && !init) + { printf("VarDeclaration('%s')\n", id->toChars()); + //*(char*)0=0; + } +#endif + assert(type || init); + this->type = type; + this->init = init; + this->htype = NULL; + this->hinit = NULL; + this->loc = loc; + offset = 0; + noscope = 0; +#if DMDV2 + isargptr = FALSE; +#endif +#if DMDV1 + nestedref = 0; +#endif + ctorinit = 0; + aliassym = NULL; + onstack = 0; + canassign = 0; + ctfeAdrOnStack = (size_t)(-1); +#if DMDV2 + rundtor = NULL; + edtor = NULL; +#endif +} + +Dsymbol *VarDeclaration::syntaxCopy(Dsymbol *s) +{ + //printf("VarDeclaration::syntaxCopy(%s)\n", toChars()); + + VarDeclaration *sv; + if (s) + { sv = (VarDeclaration *)s; + } + else + { + Initializer *init = NULL; + if (this->init) + { init = this->init->syntaxCopy(); + //init->isExpInitializer()->exp->print(); + //init->isExpInitializer()->exp->dump(0); + } + + sv = new VarDeclaration(loc, type ? type->syntaxCopy() : NULL, ident, init); + sv->storage_class = storage_class; + } + + // Syntax copy for header file + if (!htype) // Don't overwrite original + { if (type) // Make copy for both old and new instances + { htype = type->syntaxCopy(); + sv->htype = type->syntaxCopy(); + } + } + else // Make copy of original for new instance + sv->htype = htype->syntaxCopy(); + if (!hinit) + { if (init) + { hinit = init->syntaxCopy(); + sv->hinit = init->syntaxCopy(); + } + } + else + sv->hinit = hinit->syntaxCopy(); + + return sv; +} + +void VarDeclaration::semantic(Scope *sc) +{ +#if 0 + printf("VarDeclaration::semantic('%s', parent = '%s')\n", toChars(), sc->parent->toChars()); + printf(" type = %s\n", type ? type->toChars() : "null"); + printf(" stc = x%x\n", sc->stc); + printf(" storage_class = x%llx\n", storage_class); + printf("linkage = %d\n", sc->linkage); + //if (strcmp(toChars(), "mul") == 0) halt(); +#endif + +// if (sem > SemanticStart) +// return; +// sem = SemanticIn; + + if (scope) + { sc = scope; + scope = NULL; + } + + /* Pick up storage classes from context, but skip synchronized + */ + storage_class |= (sc->stc & ~STCsynchronized); + if (storage_class & STCextern && init) + error("extern symbols cannot have initializers"); + + AggregateDeclaration *ad = isThis(); + if (ad) + storage_class |= ad->storage_class & STC_TYPECTOR; + + /* If auto type inference, do the inference + */ + int inferred = 0; + if (!type) + { inuse++; + + ArrayInitializer *ai = init->isArrayInitializer(); + if (ai) + { Expression *e; + if (ai->isAssociativeArray()) + e = ai->toAssocArrayLiteral(); + else + e = init->toExpression(); + if (!e) + { + error("cannot infer type from initializer"); + e = new ErrorExp(); + } + init = new ExpInitializer(e->loc, e); + type = init->inferType(sc); + if (type->ty == Tsarray) + type = type->nextOf()->arrayOf(); + } + else + type = init->inferType(sc); + +//printf("test2: %s, %s, %s\n", toChars(), type->toChars(), type->deco); +// type = type->semantic(loc, sc); + + inuse--; + inferred = 1; + + if (init->isArrayInitializer() && type->toBasetype()->ty == Tsarray) + { // Prefer array literals to give a T[] type rather than a T[dim] + type = type->toBasetype()->nextOf()->arrayOf(); + } + + /* This is a kludge to support the existing syntax for RAII + * declarations. + */ + storage_class &= ~STCauto; + originalType = type; + } + else + { if (!originalType) + originalType = type; + type = type->semantic(loc, sc); + } + //printf(" semantic type = %s\n", type ? type->toChars() : "null"); + + type->checkDeprecated(loc, sc); + linkage = sc->linkage; + this->parent = sc->parent; + //printf("this = %p, parent = %p, '%s'\n", this, parent, parent->toChars()); + protection = sc->protection; + //printf("sc->stc = %x\n", sc->stc); + //printf("storage_class = x%x\n", storage_class); + +#if DMDV2 + // Safety checks + if (sc->func && !sc->intypeof) + { + if (storage_class & STCgshared) + { + if (sc->func->setUnsafe()) + error("__gshared not allowed in safe functions; use shared"); + } + if (init && init->isVoidInitializer() && type->hasPointers()) + { + if (sc->func->setUnsafe()) + error("void initializers for pointers not allowed in safe functions"); + } + if (type->hasPointers() && type->toDsymbol(sc)) + { + Dsymbol *s = type->toDsymbol(sc); + if (s) + { + AggregateDeclaration *ad2 = s->isAggregateDeclaration(); + if (ad2 && ad2->hasUnions) + { + if (sc->func->setUnsafe()) + error("unions containing pointers are not allowed in @safe functions"); + } + } + } + } +#endif + + Dsymbol *parent = toParent(); + FuncDeclaration *fd = parent->isFuncDeclaration(); + + Type *tb = type->toBasetype(); + if (tb->ty == Tvoid && !(storage_class & STClazy)) + { error("voids have no value"); + type = Type::terror; + tb = type; + } + if (tb->ty == Tfunction) + { error("cannot be declared to be a function"); + type = Type::terror; + tb = type; + } + if (tb->ty == Tstruct) + { TypeStruct *ts = (TypeStruct *)tb; + + if (!ts->sym->members) + { + error("no definition of struct %s", ts->toChars()); + } + } + if ((storage_class & STCauto) && !inferred) + error("storage class 'auto' has no effect if type is not inferred, did you mean 'scope'?"); + + if (tb->ty == Ttuple) + { /* Instead, declare variables for each of the tuple elements + * and add those. + */ + TypeTuple *tt = (TypeTuple *)tb; + size_t nelems = Parameter::dim(tt->arguments); + Objects *exps = new Objects(); + exps->setDim(nelems); + Expression *ie = init ? init->toExpression() : NULL; + if (ie) ie = ie->semantic(sc); + + if (nelems > 0 && ie) + { + Expressions *iexps = new Expressions(); + iexps->push(ie); + + Expressions *exps = new Expressions(); + + for (size_t pos = 0; pos < iexps->dim; pos++) + { + Lexpand1: + Expression *e = iexps->tdata()[pos]; + Parameter *arg = Parameter::getNth(tt->arguments, pos); + arg->type = arg->type->semantic(loc, sc); + //printf("[%d] iexps->dim = %d, ", pos, iexps->dim); + //printf("e = (%s %s, %s), ", Token::tochars[e->op], e->toChars(), e->type->toChars()); + //printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars()); + + if (e != ie) + { + if (iexps->dim > nelems) + goto Lnomatch; + if (e->type->implicitConvTo(arg->type)) + continue; + } + + if (e->op == TOKtuple) + { + TupleExp *te = (TupleExp *)e; + if (iexps->dim - 1 + te->exps->dim > nelems) + goto Lnomatch; + + iexps->remove(pos); + iexps->insert(pos, te->exps); + goto Lexpand1; + } + else if (isAliasThisTuple(e)) + { + Identifier *id = Lexer::uniqueId("__tup"); + ExpInitializer *ei = new ExpInitializer(e->loc, e); + VarDeclaration *v = new VarDeclaration(loc, NULL, id, ei); + v->storage_class = STCctfe | STCref | STCforeach; + VarExp *ve = new VarExp(loc, v); + ve->type = e->type; + + exps->setDim(1); + (*exps)[0] = ve; + expandAliasThisTuples(exps, 0); + + for (size_t u = 0; u < exps->dim ; u++) + { + Lexpand2: + Expression *ee = (*exps)[u]; + Parameter *arg = Parameter::getNth(tt->arguments, pos + u); + arg->type = arg->type->semantic(loc, sc); + //printf("[%d+%d] exps->dim = %d, ", pos, u, exps->dim); + //printf("ee = (%s %s, %s), ", Token::tochars[ee->op], ee->toChars(), ee->type->toChars()); + //printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars()); + + size_t iexps_dim = iexps->dim - 1 + exps->dim; + if (iexps_dim > nelems) + goto Lnomatch; + if (ee->type->implicitConvTo(arg->type)) + continue; + + if (expandAliasThisTuples(exps, u) != -1) + goto Lexpand2; + } + + if ((*exps)[0] != ve) + { + Expression *e0 = (*exps)[0]; + (*exps)[0] = new CommaExp(loc, new DeclarationExp(loc, v), e0); + (*exps)[0]->type = e0->type; + + iexps->remove(pos); + iexps->insert(pos, exps); + goto Lexpand1; + } + } + } + if (iexps->dim < nelems) + goto Lnomatch; + + ie = new TupleExp(init->loc, iexps); + } +Lnomatch: + + if (ie && ie->op == TOKtuple) + { size_t tedim = ((TupleExp *)ie)->exps->dim; + if (tedim != nelems) + { ::error(loc, "tuple of %d elements cannot be assigned to tuple of %d elements", (int)tedim, (int)nelems); + for (size_t u = tedim; u < nelems; u++) // fill dummy expression + ((TupleExp *)ie)->exps->push(new ErrorExp()); + } + } + + for (size_t i = 0; i < nelems; i++) + { Parameter *arg = Parameter::getNth(tt->arguments, i); + + OutBuffer buf; + buf.printf("_%s_field_%zu", ident->toChars(), i); + buf.writeByte(0); + const char *name = (const char *)buf.extractData(); + Identifier *id = Lexer::idPool(name); + + Expression *einit = ie; + if (ie && ie->op == TOKtuple) + { einit = ((TupleExp *)ie)->exps->tdata()[i]; + } + Initializer *ti = init; + if (einit) + { ti = new ExpInitializer(einit->loc, einit); + } + + VarDeclaration *v = new VarDeclaration(loc, arg->type, id, ti); + if (arg->storageClass & STCparameter) + v->storage_class |= arg->storageClass; + //printf("declaring field %s of type %s\n", v->toChars(), v->type->toChars()); + v->semantic(sc); + + if (sc->scopesym) + { //printf("adding %s to %s\n", v->toChars(), sc->scopesym->toChars()); + if (sc->scopesym->members) + sc->scopesym->members->push(v); + } + + Expression *e = new DsymbolExp(loc, v); + exps->tdata()[i] = e; + } + TupleDeclaration *v2 = new TupleDeclaration(loc, ident, exps); + v2->isexp = 1; + aliassym = v2; + return; + } + + /* Storage class can modify the type + */ + type = type->addStorageClass(storage_class); + + /* Adjust storage class to reflect type + */ + if (type->isConst()) + { storage_class |= STCconst; + if (type->isShared()) + storage_class |= STCshared; + } + else if (type->isImmutable()) + storage_class |= STCimmutable; + else if (type->isShared()) + storage_class |= STCshared; + else if (type->isWild()) + storage_class |= STCwild; + + if (isSynchronized()) + { + error("variable %s cannot be synchronized", toChars()); + } + else if (isOverride()) + { + error("override cannot be applied to variable"); + } + else if (isAbstract()) + { + error("abstract cannot be applied to variable"); + } + else if (storage_class & STCfinal) + { + error("final cannot be applied to variable"); + } + + if (storage_class & (STCstatic | STCextern | STCmanifest | STCtemplateparameter | STCtls | STCgshared | STCctfe)) + { + } + else + { + AggregateDeclaration *aad = sc->anonAgg; + if (!aad) + aad = parent->isAggregateDeclaration(); + if (aad) + { +#if DMDV2 + assert(!(storage_class & (STCextern | STCstatic | STCtls | STCgshared))); + + if (storage_class & (STCconst | STCimmutable) && init) + { + if (!tb->isTypeBasic()) + storage_class |= STCstatic; + } + else + { + aad->addField(sc, this); + if (tb->ty == Tstruct && ((TypeStruct *)tb)->sym->noDefaultCtor || + tb->ty == Tclass && ((TypeClass *)tb)->sym->noDefaultCtor) + aad->noDefaultCtor = TRUE; + } +#else + aad->addField(sc, this); +#endif + } + + InterfaceDeclaration *id = parent->isInterfaceDeclaration(); + if (id) + { + error("field not allowed in interface"); + } + + /* Templates cannot add fields to aggregates + */ + TemplateInstance *ti = parent->isTemplateInstance(); + if (ti) + { + // Take care of nested templates + while (1) + { + TemplateInstance *ti2 = ti->tempdecl->parent->isTemplateInstance(); + if (!ti2) + break; + ti = ti2; + } + + // If it's a member template + AggregateDeclaration *ad2 = ti->tempdecl->isMember(); + if (ad2 && storage_class != STCundefined) + { + error("cannot use template to add field to aggregate '%s'", ad2->toChars()); + } + } + } + +#if DMDV2 + if ((storage_class & (STCref | STCparameter | STCforeach)) == STCref && + ident != Id::This) + { + error("only parameters or foreach declarations can be ref"); + } + + if (type->hasWild() && + !(type->ty == Tpointer && type->nextOf()->ty == Tfunction || type->ty == Tdelegate)) + { + if (storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCfield) || + isDataseg() + ) + { + error("only parameters or stack based variables can be inout"); + } + FuncDeclaration *func = sc->func; + if (func) + { + if (func->fes) + func = func->fes->func; + if (!func->type->hasWild()) + { + error("inout variables can only be declared inside inout functions"); + } + } + } + + if (!(storage_class & (STCctfe | STCref)) && tb->ty == Tstruct && + ((TypeStruct *)tb)->sym->noDefaultCtor) + { + if (!init) + { if (storage_class & STCfield) + /* For fields, we'll check the constructor later to make sure it is initialized + */ + storage_class |= STCnodefaultctor; + else if (storage_class & STCparameter) + ; + else + error("initializer required for type %s", type->toChars()); + } + } +#endif + + if (type->isscope() && !noscope) + { + if (storage_class & (STCfield | STCout | STCref | STCstatic | STCmanifest | STCtls | STCgshared) || !fd) + { + error("globals, statics, fields, manifest constants, ref and out parameters cannot be scope"); + } + + if (!(storage_class & STCscope)) + { + if (!(storage_class & STCparameter) && ident != Id::withSym) + error("reference to scope class must be scope"); + } + } + + if (!init && !fd) + { // If not mutable, initializable by constructor only + storage_class |= STCctorinit; + } + + if (init) + storage_class |= STCinit; // remember we had an explicit initializer + else if (storage_class & STCmanifest) + error("manifest constants must have initializers"); + + enum TOK op = TOKconstruct; + if (!init && !sc->inunion && !isStatic() && fd && + (!(storage_class & (STCfield | STCin | STCforeach | STCparameter | STCresult)) + || (storage_class & STCout)) && + type->size() != 0) + { + // Provide a default initializer + //printf("Providing default initializer for '%s'\n", toChars()); + if (type->ty == Tstruct && + ((TypeStruct *)type)->sym->zeroInit == 1) + { /* If a struct is all zeros, as a special case + * set it's initializer to the integer 0. + * In AssignExp::toElem(), we check for this and issue + * a memset() to initialize the struct. + * Must do same check in interpreter. + */ + Expression *e = new IntegerExp(loc, 0, Type::tint32); + Expression *e1; + e1 = new VarExp(loc, this); + e = new ConstructExp(loc, e1, e); + e->type = e1->type; // don't type check this, it would fail + init = new ExpInitializer(loc, e); + goto Ldtor; + } + else if (type->ty == Ttypedef) + { TypeTypedef *td = (TypeTypedef *)type; + if (td->sym->init) + { init = td->sym->init; + ExpInitializer *ie = init->isExpInitializer(); + if (ie) + // Make copy so we can modify it + init = new ExpInitializer(ie->loc, ie->exp); + } + else + init = getExpInitializer(); + } + else + { + init = getExpInitializer(); + } + // Default initializer is always a blit + op = TOKblit; + } + + if (init) + { + sc = sc->push(); + sc->stc &= ~(STC_TYPECTOR | STCpure | STCnothrow | STCref | STCdisable); + + ArrayInitializer *ai = init->isArrayInitializer(); + if (ai && tb->ty == Taarray) + { + Expression *e = ai->toAssocArrayLiteral(); + init = new ExpInitializer(e->loc, e); + } + + StructInitializer *si = init->isStructInitializer(); + ExpInitializer *ei = init->isExpInitializer(); + + if (ei && ei->exp->op == TOKfunction && !inferred) + ((FuncExp *)ei->exp)->setType(type); + + if (ei && isScope()) + { + // See if initializer is a NewExp that can be allocated on the stack + if (ei->exp->op == TOKnew) + { NewExp *ne = (NewExp *)ei->exp; + if (!(ne->newargs && ne->newargs->dim)) + { ne->onstack = 1; + onstack = 1; + if (type->isBaseOf(ne->newtype->semantic(loc, sc), NULL)) + onstack = 2; + } + } + // or a delegate that doesn't escape a reference to the function + else if (ei->exp->op == TOKfunction) + { FuncDeclaration *f = ((FuncExp *)ei->exp)->fd; + f->tookAddressOf--; + } + } + + // If inside function, there is no semantic3() call + if (sc->func) + { + // If local variable, use AssignExp to handle all the various + // possibilities. + if (fd && + !(storage_class & (STCmanifest | STCstatic | STCtls | STCgshared | STCextern)) && + !init->isVoidInitializer()) + { + //printf("fd = '%s', var = '%s'\n", fd->toChars(), toChars()); + if (!ei) + { + Expression *e = init->toExpression(); + if (!e) + { + init = init->semantic(sc, type, 0); // Don't need to interpret + e = init->toExpression(); + if (!e) + { error("is not a static and cannot have static initializer"); + return; + } + } + ei = new ExpInitializer(init->loc, e); + init = ei; + } + + Expression *e1 = new VarExp(loc, this); + + Type *t = type->toBasetype(); + + Linit2: + if (t->ty == Tsarray && !(storage_class & (STCref | STCout))) + { + ei->exp = ei->exp->semantic(sc); + if (!ei->exp->implicitConvTo(type)) + { + dinteger_t dim = ((TypeSArray *)t)->dim->toInteger(); + // If multidimensional static array, treat as one large array + while (1) + { + t = t->nextOf()->toBasetype(); + if (t->ty != Tsarray) + break; + dim *= ((TypeSArray *)t)->dim->toInteger(); + e1->type = new TypeSArray(t->nextOf(), new IntegerExp(0, dim, Type::tindex)); + } + } + e1 = new SliceExp(loc, e1, NULL, NULL); + } + else if (t->ty == Tstruct) + { + ei->exp = ei->exp->semantic(sc); + ei->exp = resolveProperties(sc, ei->exp); + StructDeclaration *sd = ((TypeStruct *)t)->sym; +#if DMDV2 + Expression** pinit = &ei->exp; + while ((*pinit)->op == TOKcomma) + { + pinit = &((CommaExp *)*pinit)->e2; + } + + /* Look to see if initializer is a call to the constructor + */ + if (sd->ctor && // there are constructors + (*pinit)->type->ty == Tstruct && // rvalue is the same struct + ((TypeStruct *)(*pinit)->type)->sym == sd && + (*pinit)->op == TOKcall) + { + /* Look for form of constructor call which is: + * *__ctmp.ctor(arguments...) + */ + if (1) + { CallExp *ce = (CallExp *)(*pinit); + if (ce->e1->op == TOKdotvar) + { DotVarExp *dve = (DotVarExp *)ce->e1; + if (dve->var->isCtorDeclaration()) + { /* It's a constructor call, currently constructing + * a temporary __ctmp. + */ + /* Before calling the constructor, initialize + * variable with a bit copy of the default + * initializer + */ + + /* Remove ref if this declaration is ref binding. + * ref Type __self = (__ctmp = 0, __ctmp).this(...); + * -> Type __self = (__self = 0, __self.this(...)); + */ + storage_class &= ~(STCref | STCforeach | STCparameter); + + Expression *e; + if (sd->zeroInit == 1) + { + e = new ConstructExp(loc, new VarExp(loc, this), new IntegerExp(loc, 0, Type::tint32)); + } + else + { e = new AssignExp(loc, new VarExp(loc, this), t->defaultInit(loc)); + e->op = TOKblit; + } + e->type = t; + (*pinit) = new CommaExp(loc, e, (*pinit)); + + /* Replace __ctmp being constructed with e1 + */ + dve->e1 = e1; + (*pinit) = (*pinit)->semantic(sc); + goto Ldtor; + } + } + } + } + + /* Look for ((S tmp = S()),tmp) and replace it with just S() + */ + Expression *e2 = ei->exp->isTemp(); + if (e2) + { + ei->exp = e2; + goto Linit2; + } +#endif + if (!ei->exp->implicitConvTo(type)) + { + Type *ti = ei->exp->type->toBasetype(); + // Look for constructor first + if (sd->ctor && + /* Initializing with the same type is done differently + */ + !(ti->ty == Tstruct && t->toDsymbol(sc) == ti->toDsymbol(sc))) + { + // Rewrite as e1.ctor(arguments) + Expression *ector = new DotIdExp(loc, e1, Id::ctor); + ei->exp = new CallExp(loc, ector, ei->exp); + /* Before calling the constructor, initialize + * variable with a bit copy of the default + * initializer + */ + Expression *e = new AssignExp(loc, e1, t->defaultInit(loc)); + e->op = TOKblit; + e->type = t; + ei->exp = new CommaExp(loc, e, ei->exp); + } + else + /* Look for opCall + * See bugzilla 2702 for more discussion + */ + // Don't cast away invariant or mutability in initializer + if (search_function(sd, Id::call) && + /* Initializing with the same type is done differently + */ + !(ti->ty == Tstruct && t->toDsymbol(sc) == ti->toDsymbol(sc))) + { // Rewrite as e1.call(arguments) + Expression * eCall = new DotIdExp(loc, e1, Id::call); + ei->exp = new CallExp(loc, eCall, ei->exp); + } + } + } + ei->exp = new AssignExp(loc, e1, ei->exp); + ei->exp->op = op; + canassign++; + ei->exp = ei->exp->semantic(sc); + canassign--; + ei->exp->optimize(WANTvalue); + } + else + { + init = init->semantic(sc, type, WANTinterpret); + } + } + else if (storage_class & (STCconst | STCimmutable | STCmanifest) || + type->isConst() || type->isImmutable() || + parent->isAggregateDeclaration()) + { + /* Because we may need the results of a const declaration in a + * subsequent type, such as an array dimension, before semantic2() + * gets ordinarily run, try to run semantic2() now. + * Ignore failure. + */ + + if (!global.errors && !inferred) + { + unsigned errors = global.startGagging(); + Expression *e; + Initializer *i2 = init; + inuse++; + if (ei) + { + e = ei->exp->syntaxCopy(); + e = e->semantic(sc); + e = resolveProperties(sc, e); +#if DMDV2 + /* The problem is the following code: + * struct CopyTest { + * double x; + * this(double a) { x = a * 10.0;} + * this(this) { x += 2.0; } + * } + * const CopyTest z = CopyTest(5.3); // ok + * const CopyTest w = z; // not ok, postblit not run + * static assert(w.x == 55.0); + * because the postblit doesn't get run on the initialization of w. + */ + + Type *tb2 = e->type->toBasetype(); + if (tb2->ty == Tstruct) + { StructDeclaration *sd = ((TypeStruct *)tb2)->sym; + Type *typeb = type->toBasetype(); + /* Look to see if initializer involves a copy constructor + * (which implies a postblit) + */ + if (sd->cpctor && // there is a copy constructor + typeb->equals(tb2)) // rvalue is the same struct + { + // The only allowable initializer is a (non-copy) constructor + if (e->op == TOKcall) + { + CallExp *ce = (CallExp *)e; + if (ce->e1->op == TOKdotvar) + { + DotVarExp *dve = (DotVarExp *)ce->e1; + if (dve->var->isCtorDeclaration()) + goto LNoCopyConstruction; + } + } + global.gag--; + error("of type struct %s uses this(this), which is not allowed in static initialization", typeb->toChars()); + global.gag++; + + LNoCopyConstruction: + ; + } + } +#endif + e = e->implicitCastTo(sc, type); + } + else if (si || ai) + { i2 = init->syntaxCopy(); + i2 = i2->semantic(sc, type, WANTinterpret); + } + inuse--; + if (global.endGagging(errors)) // if errors happened + { +#if DMDV2 + /* Save scope for later use, to try again + */ + scope = new Scope(*sc); + scope->setNoFree(); +#endif + } + else if (ei) + { + if (isDataseg() || (storage_class & STCmanifest)) + e = e->optimize(WANTvalue | WANTinterpret); + else + e = e->optimize(WANTvalue); + switch (e->op) + { + case TOKint64: + case TOKfloat64: + case TOKstring: + case TOKarrayliteral: + case TOKassocarrayliteral: + case TOKstructliteral: + case TOKnull: + ei->exp = e; // no errors, keep result + break; + + default: +#if DMDV2 + /* Save scope for later use, to try again + */ + scope = new Scope(*sc); + scope->setNoFree(); +#endif + break; + } + } + else + init = i2; // no errors, keep result + } + } + sc = sc->pop(); + } + +Ldtor: + /* Build code to execute destruction, if necessary + */ + edtor = callScopeDtor(sc); + if (edtor) + { + edtor = edtor->semantic(sc); + +#if 0 // currently disabled because of std.stdio.stdin, stdout and stderr + if (isDataseg() && !(storage_class & STCextern)) + error("static storage variables cannot have destructors"); +#endif + } + + sem = SemanticDone; +} + +void VarDeclaration::semantic2(Scope *sc) +{ + //printf("VarDeclaration::semantic2('%s')\n", toChars()); + if (init && !toParent()->isFuncDeclaration()) + { inuse++; +#if 0 + ExpInitializer *ei = init->isExpInitializer(); + if (ei) + { + ei->exp->dump(0); + printf("type = %p\n", ei->exp->type); + } +#endif + init = init->semantic(sc, type, WANTinterpret); + inuse--; + } + sem = Semantic2Done; +} + +const char *VarDeclaration::kind() +{ + return "variable"; +} + +Dsymbol *VarDeclaration::toAlias() +{ + //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym); + assert(this != aliassym); + Dsymbol *s = aliassym ? aliassym->toAlias() : this; + return s; +} + +void VarDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + StorageClassDeclaration::stcToCBuffer(buf, storage_class); + + /* If changing, be sure and fix CompoundDeclarationStatement::toCBuffer() + * too. + */ + if (type) + type->toCBuffer(buf, ident, hgs); + else + buf->writestring(ident->toChars()); + if (init) + { buf->writestring(" = "); +#if DMDV2 + ExpInitializer *ie = init->isExpInitializer(); + if (ie && (ie->exp->op == TOKconstruct || ie->exp->op == TOKblit)) + ((AssignExp *)ie->exp)->e2->toCBuffer(buf, hgs); + else +#endif + init->toCBuffer(buf, hgs); + } + buf->writeByte(';'); + buf->writenl(); +} + +AggregateDeclaration *VarDeclaration::isThis() +{ + AggregateDeclaration *ad = NULL; + + if (!(storage_class & (STCstatic | STCextern | STCmanifest | STCtemplateparameter | + STCtls | STCgshared | STCctfe))) + { + if ((storage_class & (STCconst | STCimmutable | STCwild)) && init) + return NULL; + + for (Dsymbol *s = this; s; s = s->parent) + { + ad = s->isMember(); + if (ad) + break; + if (!s->parent || !s->parent->isTemplateMixin()) break; + } + } + return ad; +} + +int VarDeclaration::needThis() +{ + //printf("VarDeclaration::needThis(%s, x%x)\n", toChars(), storage_class); + return storage_class & STCfield; +} + +int VarDeclaration::isImportedSymbol() +{ + if (protection == PROTexport && !init && + (storage_class & STCstatic || parent->isModule())) + return TRUE; + return FALSE; +} + +void VarDeclaration::checkCtorConstInit() +{ +#if 0 /* doesn't work if more than one static ctor */ + if (ctorinit == 0 && isCtorinit() && !(storage_class & STCfield)) + error("missing initializer in static constructor for const variable"); +#endif +} + +/************************************ + * Check to see if this variable is actually in an enclosing function + * rather than the current one. + */ + +void VarDeclaration::checkNestedReference(Scope *sc, Loc loc) +{ + //printf("VarDeclaration::checkNestedReference() %s\n", toChars()); + if (parent && !isDataseg() && parent != sc->parent && + !(storage_class & STCmanifest)) + { + // The function that this variable is in + FuncDeclaration *fdv = toParent()->isFuncDeclaration(); + // The current function + FuncDeclaration *fdthis = sc->parent->isFuncDeclaration(); + + if (fdv && fdthis && fdv != fdthis && fdthis->ident != Id::ensure) + { + /* __ensure is always called directly, + * so it never becomes closure. + */ + + //printf("\tfdv = %s\n", fdv->toChars()); + //printf("\tfdthis = %s\n", fdthis->toChars()); + + if (loc.filename) + fdthis->getLevel(loc, sc, fdv); + + // Function literals from fdthis to fdv must be delegates + for (Dsymbol *s = fdthis; s && s != fdv; s = s->toParent2()) + { + // function literal has reference to enclosing scope is delegate + if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration()) + { + fld->tok = TOKdelegate; + } + } + + // Add fdthis to nestedrefs[] if not already there + for (size_t i = 0; 1; i++) + { + if (i == nestedrefs.dim) + { + nestedrefs.push(fdthis); + break; + } + if (nestedrefs[i] == fdthis) + break; + } + + // Add this to fdv->closureVars[] if not already there + for (size_t i = 0; 1; i++) + { + if (i == fdv->closureVars.dim) + { + fdv->closureVars.push(this); + break; + } + if (fdv->closureVars[i] == this) + break; + } + + //printf("fdthis is %s\n", fdthis->toChars()); + //printf("var %s in function %s is nested ref\n", toChars(), fdv->toChars()); + // __dollar creates problems because it isn't a real variable Bugzilla 3326 + if (ident == Id::dollar) + ::error(loc, "cannnot use $ inside a function literal"); + } + } +} + +/**************************** + * Get ExpInitializer for a variable, if there is one. + */ + +ExpInitializer *VarDeclaration::getExpInitializer() +{ + ExpInitializer *ei; + + if (init) + ei = init->isExpInitializer(); + else + { + Expression *e = type->defaultInit(loc); + if (e) + ei = new ExpInitializer(loc, e); + else + ei = NULL; + } + return ei; +} + +/******************************************* + * If variable has a constant expression initializer, get it. + * Otherwise, return NULL. + */ + +Expression *VarDeclaration::getConstInitializer() +{ + if ((isConst() || isImmutable() || storage_class & STCmanifest) && + storage_class & STCinit) + { + ExpInitializer *ei = getExpInitializer(); + if (ei) + return ei->exp; + } + + return NULL; +} + +/************************************* + * Return !=0 if we can take the address of this variable. + */ + +int VarDeclaration::canTakeAddressOf() +{ +#if 0 + /* Global variables and struct/class fields of the form: + * const int x = 3; + * are not stored and hence cannot have their address taken. + */ + if ((isConst() || isImmutable()) && + storage_class & STCinit && + (!(storage_class & (STCstatic | STCextern)) || (storage_class & STCfield)) && + (!parent || toParent()->isModule() || toParent()->isTemplateInstance()) && + type->toBasetype()->isTypeBasic() + ) + { + return 0; + } +#else + if (storage_class & STCmanifest) + return 0; +#endif + return 1; +} + + +/******************************* + * Does symbol go into data segment? + * Includes extern variables. + */ + +int VarDeclaration::isDataseg() +{ +#if 0 + printf("VarDeclaration::isDataseg(%p, '%s')\n", this, toChars()); + printf("%llx, isModule: %p, isTemplateInstance: %p\n", storage_class & (STCstatic | STCconst), parent->isModule(), parent->isTemplateInstance()); + printf("parent = '%s'\n", parent->toChars()); +#endif + if (storage_class & STCmanifest) + return 0; + Dsymbol *parent = this->toParent(); + if (!parent && !(storage_class & STCstatic)) + { error("forward referenced"); + type = Type::terror; + return 0; + } + return canTakeAddressOf() && + (storage_class & (STCstatic | STCextern | STCtls | STCgshared) || + toParent()->isModule() || + toParent()->isTemplateInstance()); +} + +/************************************ + * Does symbol go into thread local storage? + */ + +int VarDeclaration::isThreadlocal() +{ + //printf("VarDeclaration::isThreadlocal(%p, '%s')\n", this, toChars()); +#if 0 //|| TARGET_OSX + /* To be thread-local, must use the __thread storage class. + * BUG: OSX doesn't support thread local yet. + */ + return isDataseg() && + (storage_class & (STCtls | STCconst | STCimmutable | STCshared | STCgshared)) == STCtls; +#else + /* Data defaults to being thread-local. It is not thread-local + * if it is immutable, const or shared. + */ + int i = isDataseg() && + !(storage_class & (STCimmutable | STCconst | STCshared | STCgshared)); + //printf("\treturn %d\n", i); + return i; +#endif +} + +/******************************************** + * Can variable be read and written by CTFE? + */ + +int VarDeclaration::isCTFE() +{ + return (storage_class & STCctfe) != 0; // || !isDataseg(); +} + +int VarDeclaration::hasPointers() +{ + //printf("VarDeclaration::hasPointers() %s, ty = %d\n", toChars(), type->ty); + return (!isDataseg() && type->hasPointers()); +} + +/****************************************** + * Return TRUE if variable needs to call the destructor. + */ + +int VarDeclaration::needsAutoDtor() +{ + //printf("VarDeclaration::needsAutoDtor() %s\n", toChars()); + + if (noscope || !edtor) + return FALSE; + + return TRUE; +} + + +/****************************************** + * If a variable has a scope destructor call, return call for it. + * Otherwise, return NULL. + */ + +Expression *VarDeclaration::callScopeDtor(Scope *sc) +{ Expression *e = NULL; + + //printf("VarDeclaration::callScopeDtor() %s\n", toChars()); + + // Destruction of STCfield's is handled by buildDtor() + if (noscope || storage_class & (STCnodtor | STCref | STCout | STCfield)) + { + return NULL; + } + + // Destructors for structs and arrays of structs + bool array = false; + Type *tv = type->toBasetype(); + while (tv->ty == Tsarray) + { TypeSArray *ta = (TypeSArray *)tv; + array = true; + tv = tv->nextOf()->toBasetype(); + } + if (tv->ty == Tstruct) + { TypeStruct *ts = (TypeStruct *)tv; + StructDeclaration *sd = ts->sym; + if (sd->dtor) + { + if (array) + { + // Typeinfo.destroy(cast(void*)&v); + Expression *ea = new SymOffExp(loc, this, 0, 0); + ea = new CastExp(loc, ea, Type::tvoid->pointerTo()); + Expressions *args = new Expressions(); + args->push(ea); + + Expression *et = type->getTypeInfo(sc); + et = new DotIdExp(loc, et, Id::destroy); + + e = new CallExp(loc, et, args); + } + else + { + e = new VarExp(loc, this); + /* This is a hack so we can call destructors on const/immutable objects. + * Need to add things like "const ~this()" and "immutable ~this()" to + * fix properly. + */ + e->type = e->type->mutableOf(); + e = new DotVarExp(loc, e, sd->dtor, 0); + e = new CallExp(loc, e); + } + return e; + } + } + + // Destructors for classes + if (storage_class & (STCauto | STCscope)) + { + for (ClassDeclaration *cd = type->isClassHandle(); + cd; + cd = cd->baseClass) + { + /* We can do better if there's a way with onstack + * classes to determine if there's no way the monitor + * could be set. + */ + //if (cd->isInterfaceDeclaration()) + //error("interface %s cannot be scope", cd->toChars()); + if (1 || onstack || cd->dtors.dim) // if any destructors + { + // delete this; + Expression *ec; + + ec = new VarExp(loc, this); + e = new DeleteExp(loc, ec); + e->type = Type::tvoid; + break; + } + } + } + return e; +} + +/****************************************** + */ + +void ObjectNotFound(Identifier *id) +{ + Type::error(0, "%s not found. object.d may be incorrectly installed or corrupt.", id->toChars()); + fatal(); +} + + +/********************************* ClassInfoDeclaration ****************************/ + +ClassInfoDeclaration::ClassInfoDeclaration(ClassDeclaration *cd) + : VarDeclaration(0, ClassDeclaration::classinfo->type, cd->ident, NULL) +{ + this->cd = cd; + storage_class = STCstatic | STCgshared; +} + +Dsymbol *ClassInfoDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(0); // should never be produced by syntax + return NULL; +} + +void ClassInfoDeclaration::semantic(Scope *sc) +{ +} + +/********************************* ModuleInfoDeclaration ****************************/ + +ModuleInfoDeclaration::ModuleInfoDeclaration(Module *mod) + : VarDeclaration(0, Module::moduleinfo->type, mod->ident, NULL) +{ + this->mod = mod; + storage_class = STCstatic | STCgshared; +} + +Dsymbol *ModuleInfoDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(0); // should never be produced by syntax + return NULL; +} + +void ModuleInfoDeclaration::semantic(Scope *sc) +{ +} + +/********************************* TypeInfoDeclaration ****************************/ + +TypeInfoDeclaration::TypeInfoDeclaration(Type *tinfo, int internal) + : VarDeclaration(0, Type::typeinfo->type, tinfo->getTypeInfoIdent(internal), NULL) +{ + this->tinfo = tinfo; + storage_class = STCstatic | STCgshared; + protection = PROTpublic; + linkage = LINKc; +} + +Dsymbol *TypeInfoDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(0); // should never be produced by syntax + return NULL; +} + +void TypeInfoDeclaration::semantic(Scope *sc) +{ + assert(linkage == LINKc); +} + +/***************************** TypeInfoConstDeclaration **********************/ + +#if DMDV2 +TypeInfoConstDeclaration::TypeInfoConstDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfoconst) + { + ObjectNotFound(Id::TypeInfo_Const); + } + type = Type::typeinfoconst->type; +} +#endif + +/***************************** TypeInfoInvariantDeclaration **********************/ + +#if DMDV2 +TypeInfoInvariantDeclaration::TypeInfoInvariantDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfoinvariant) + { + ObjectNotFound(Id::TypeInfo_Invariant); + } + type = Type::typeinfoinvariant->type; +} +#endif + +/***************************** TypeInfoSharedDeclaration **********************/ + +#if DMDV2 +TypeInfoSharedDeclaration::TypeInfoSharedDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfoshared) + { + ObjectNotFound(Id::TypeInfo_Shared); + } + type = Type::typeinfoshared->type; +} +#endif + +/***************************** TypeInfoWildDeclaration **********************/ + +#if DMDV2 +TypeInfoWildDeclaration::TypeInfoWildDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfowild) + { + ObjectNotFound(Id::TypeInfo_Wild); + } + type = Type::typeinfowild->type; +} +#endif + +/***************************** TypeInfoStructDeclaration **********************/ + +TypeInfoStructDeclaration::TypeInfoStructDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfostruct) + { + ObjectNotFound(Id::TypeInfo_Struct); + } + type = Type::typeinfostruct->type; +} + +/***************************** TypeInfoClassDeclaration ***********************/ + +TypeInfoClassDeclaration::TypeInfoClassDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfoclass) + { + ObjectNotFound(Id::TypeInfo_Class); + } + type = Type::typeinfoclass->type; +} + +/***************************** TypeInfoInterfaceDeclaration *******************/ + +TypeInfoInterfaceDeclaration::TypeInfoInterfaceDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfointerface) + { + ObjectNotFound(Id::TypeInfo_Interface); + } + type = Type::typeinfointerface->type; +} + +/***************************** TypeInfoTypedefDeclaration *********************/ + +TypeInfoTypedefDeclaration::TypeInfoTypedefDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfotypedef) + { + ObjectNotFound(Id::TypeInfo_Typedef); + } + type = Type::typeinfotypedef->type; +} + +/***************************** TypeInfoPointerDeclaration *********************/ + +TypeInfoPointerDeclaration::TypeInfoPointerDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfopointer) + { + ObjectNotFound(Id::TypeInfo_Pointer); + } + type = Type::typeinfopointer->type; +} + +/***************************** TypeInfoArrayDeclaration ***********************/ + +TypeInfoArrayDeclaration::TypeInfoArrayDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfoarray) + { + ObjectNotFound(Id::TypeInfo_Array); + } + type = Type::typeinfoarray->type; +} + +/***************************** TypeInfoStaticArrayDeclaration *****************/ + +TypeInfoStaticArrayDeclaration::TypeInfoStaticArrayDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfostaticarray) + { + ObjectNotFound(Id::TypeInfo_StaticArray); + } + type = Type::typeinfostaticarray->type; +} + +/***************************** TypeInfoAssociativeArrayDeclaration ************/ + +TypeInfoAssociativeArrayDeclaration::TypeInfoAssociativeArrayDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfoassociativearray) + { + ObjectNotFound(Id::TypeInfo_AssociativeArray); + } + type = Type::typeinfoassociativearray->type; +} + +/***************************** TypeInfoVectorDeclaration ***********************/ + +TypeInfoVectorDeclaration::TypeInfoVectorDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfoarray) + { + ObjectNotFound(Id::TypeInfo_Vector); + } + type = Type::typeinfovector->type; +} + +/***************************** TypeInfoEnumDeclaration ***********************/ + +TypeInfoEnumDeclaration::TypeInfoEnumDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfoenum) + { + ObjectNotFound(Id::TypeInfo_Enum); + } + type = Type::typeinfoenum->type; +} + +/***************************** TypeInfoFunctionDeclaration ********************/ + +TypeInfoFunctionDeclaration::TypeInfoFunctionDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfofunction) + { + ObjectNotFound(Id::TypeInfo_Function); + } + type = Type::typeinfofunction->type; +} + +/***************************** TypeInfoDelegateDeclaration ********************/ + +TypeInfoDelegateDeclaration::TypeInfoDelegateDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfodelegate) + { + ObjectNotFound(Id::TypeInfo_Delegate); + } + type = Type::typeinfodelegate->type; +} + +/***************************** TypeInfoTupleDeclaration **********************/ + +TypeInfoTupleDeclaration::TypeInfoTupleDeclaration(Type *tinfo) + : TypeInfoDeclaration(tinfo, 0) +{ + if (!Type::typeinfotypelist) + { + ObjectNotFound(Id::TypeInfo_Tuple); + } + type = Type::typeinfotypelist->type; +} + +/********************************* ThisDeclaration ****************************/ + +// For the "this" parameter to member functions + +ThisDeclaration::ThisDeclaration(Loc loc, Type *t) + : VarDeclaration(loc, t, Id::This, NULL) +{ + noscope = 1; +} + +Dsymbol *ThisDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(0); // should never be produced by syntax + return NULL; +} + diff --git a/declaration.h b/declaration.h new file mode 100644 index 00000000..deab930e --- /dev/null +++ b/declaration.h @@ -0,0 +1,889 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_DECLARATION_H +#define DMD_DECLARATION_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "dsymbol.h" +#include "lexer.h" +#include "mtype.h" + +struct Expression; +struct Statement; +struct LabelDsymbol; +struct Initializer; +struct Module; +struct InlineScanState; +struct ForeachStatement; +struct FuncDeclaration; +struct ExpInitializer; +struct StructDeclaration; +struct TupleType; +struct InterState; +struct IRState; + +enum PROT; +enum LINK; +enum TOK; +enum MATCH; +enum PURE; + +#define STCundefined 0LL +#define STCstatic 1LL +#define STCextern 2LL +#define STCconst 4LL +#define STCfinal 8LL +#define STCabstract 0x10LL +#define STCparameter 0x20LL +#define STCfield 0x40LL +#define STCoverride 0x80LL +#define STCauto 0x100LL +#define STCsynchronized 0x200LL +#define STCdeprecated 0x400LL +#define STCin 0x800LL // in parameter +#define STCout 0x1000LL // out parameter +#define STClazy 0x2000LL // lazy parameter +#define STCforeach 0x4000LL // variable for foreach loop +#define STCcomdat 0x8000LL // should go into COMDAT record +#define STCvariadic 0x10000LL // variadic function argument +#define STCctorinit 0x20000LL // can only be set inside constructor +#define STCtemplateparameter 0x40000LL // template parameter +#define STCscope 0x80000LL // template parameter +#define STCimmutable 0x100000LL +#define STCref 0x200000LL +#define STCinit 0x400000LL // has explicit initializer +#define STCmanifest 0x800000LL // manifest constant +#define STCnodtor 0x1000000LL // don't run destructor +#define STCnothrow 0x2000000LL // never throws exceptions +#define STCpure 0x4000000LL // pure function +#define STCtls 0x8000000LL // thread local +#define STCalias 0x10000000LL // alias parameter +#define STCshared 0x20000000LL // accessible from multiple threads +#define STCgshared 0x40000000LL // accessible from multiple threads + // but not typed as "shared" +#define STCwild 0x80000000LL // for "wild" type constructor +#define STC_TYPECTOR (STCconst | STCimmutable | STCshared | STCwild) +#define STC_FUNCATTR (STCref | STCnothrow | STCpure | STCproperty | STCsafe | STCtrusted | STCsystem) + +#define STCproperty 0x100000000LL +#define STCsafe 0x200000000LL +#define STCtrusted 0x400000000LL +#define STCsystem 0x800000000LL +#define STCctfe 0x1000000000LL // can be used in CTFE, even if it is static +#define STCdisable 0x2000000000LL // for functions that are not callable +#define STCresult 0x4000000000LL // for result variables passed to out contracts +#define STCnodefaultctor 0x8000000000LL // must be set inside constructor + +struct Match +{ + int count; // number of matches found + MATCH last; // match level of lastf + FuncDeclaration *lastf; // last matching function we found + FuncDeclaration *nextf; // current matching function + FuncDeclaration *anyf; // pick a func, any func, to use for error recovery +}; + +void overloadResolveX(Match *m, FuncDeclaration *f, + Expression *ethis, Expressions *arguments); +int overloadApply(FuncDeclaration *fstart, + int (*fp)(void *, FuncDeclaration *), + void *param); + +enum Semantic +{ + SemanticStart, // semantic has not been run + SemanticIn, // semantic() is in progress + SemanticDone, // semantic() has been run + Semantic2Done, // semantic2() has been run +}; + +/**************************************************************/ + +struct Declaration : Dsymbol +{ + Type *type; + Type *originalType; // before semantic analysis + StorageClass storage_class; + enum PROT protection; + enum LINK linkage; + int inuse; // used to detect cycles + + enum Semantic sem; + + Declaration(Identifier *id); + void semantic(Scope *sc); + const char *kind(); + unsigned size(Loc loc); + void checkModify(Loc loc, Scope *sc, Type *t); + + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf); + + char *mangle(); + int isStatic() { return storage_class & STCstatic; } + virtual int isDelete(); + virtual int isDataseg(); + virtual int isThreadlocal(); + virtual int isCodeseg(); + int isCtorinit() { return storage_class & STCctorinit; } + int isFinal() { return storage_class & STCfinal; } + int isAbstract() { return storage_class & STCabstract; } + int isConst() { return storage_class & STCconst; } + int isImmutable() { return storage_class & STCimmutable; } + int isWild() { return storage_class & STCwild; } + int isAuto() { return storage_class & STCauto; } + int isScope() { return storage_class & STCscope; } + int isSynchronized() { return storage_class & STCsynchronized; } + int isParameter() { return storage_class & STCparameter; } + int isDeprecated() { return storage_class & STCdeprecated; } + int isOverride() { return storage_class & STCoverride; } + StorageClass isResult() { return storage_class & STCresult; } + + int isIn() { return storage_class & STCin; } + int isOut() { return storage_class & STCout; } + int isRef() { return storage_class & STCref; } + + enum PROT prot(); + + Declaration *isDeclaration() { return this; } +}; + +/**************************************************************/ + +struct TupleDeclaration : Declaration +{ + Objects *objects; + int isexp; // 1: expression tuple + + TypeTuple *tupletype; // !=NULL if this is a type tuple + + TupleDeclaration(Loc loc, Identifier *ident, Objects *objects); + Dsymbol *syntaxCopy(Dsymbol *); + const char *kind(); + Type *getType(); + int needThis(); + + TupleDeclaration *isTupleDeclaration() { return this; } +}; + +/**************************************************************/ + +struct TypedefDeclaration : Declaration +{ + Type *basetype; + Initializer *init; + + TypedefDeclaration(Loc loc, Identifier *ident, Type *basetype, Initializer *init); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + void semantic2(Scope *sc); + char *mangle(); + const char *kind(); + Type *getType(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Type *htype; + Type *hbasetype; + + void toDocBuffer(OutBuffer *buf); + + void toObjFile(int multiobj); // compile to .obj file + void toDebug(); + int cvMember(unsigned char *p); + + TypedefDeclaration *isTypedefDeclaration() { return this; } + + Symbol *sinit; + Symbol *toInitializer(); +}; + +/**************************************************************/ + +struct AliasDeclaration : Declaration +{ + Dsymbol *aliassym; + Dsymbol *overnext; // next in overload list + int inSemantic; + + AliasDeclaration(Loc loc, Identifier *ident, Type *type); + AliasDeclaration(Loc loc, Identifier *ident, Dsymbol *s); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + int overloadInsert(Dsymbol *s); + const char *kind(); + Type *getType(); + Dsymbol *toAlias(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Type *htype; + Dsymbol *haliassym; + + void toDocBuffer(OutBuffer *buf); + + AliasDeclaration *isAliasDeclaration() { return this; } +}; + +/**************************************************************/ + +struct VarDeclaration : Declaration +{ + Initializer *init; + unsigned offset; + int noscope; // no auto semantics +#if DMDV2 + FuncDeclarations nestedrefs; // referenced by these lexically nested functions + bool isargptr; // if parameter that _argptr points to +#else + int nestedref; // referenced by a lexically nested function +#endif + int ctorinit; // it has been initialized in a ctor + int onstack; // 1: it has been allocated on the stack + // 2: on stack, run destructor anyway + int canassign; // it can be assigned to + Dsymbol *aliassym; // if redone as alias to another symbol + + // When interpreting, these point to the value (NULL if value not determinable) + // The index of this variable on the CTFE stack, -1 if not allocated + size_t ctfeAdrOnStack; + // The various functions are used only to detect compiler CTFE bugs + Expression *getValue(); + bool hasValue(); + void setValueNull(); + void setValueWithoutChecking(Expression *newval); + void setValue(Expression *newval); + +#if DMDV2 + VarDeclaration *rundtor; // if !NULL, rundtor is tested at runtime to see + // if the destructor should be run. Used to prevent + // dtor calls on postblitted vars + Expression *edtor; // if !=NULL, does the destruction of the variable +#endif + + VarDeclaration(Loc loc, Type *t, Identifier *id, Initializer *init); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + void semantic2(Scope *sc); + const char *kind(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Type *htype; + Initializer *hinit; + AggregateDeclaration *isThis(); + int needThis(); + int isImportedSymbol(); + int isDataseg(); + int isThreadlocal(); + int isCTFE(); + int hasPointers(); +#if DMDV2 + int canTakeAddressOf(); + int needsAutoDtor(); +#endif + Expression *callScopeDtor(Scope *sc); + ExpInitializer *getExpInitializer(); + Expression *getConstInitializer(); + void checkCtorConstInit(); + void checkNestedReference(Scope *sc, Loc loc); + Dsymbol *toAlias(); + + Symbol *toSymbol(); + void toObjFile(int multiobj); // compile to .obj file + int cvMember(unsigned char *p); + + // Eliminate need for dynamic_cast + VarDeclaration *isVarDeclaration() { return (VarDeclaration *)this; } +}; + +/**************************************************************/ + +// This is a shell around a back end symbol + +struct SymbolDeclaration : Declaration +{ + Symbol *sym; + StructDeclaration *dsym; + + SymbolDeclaration(Loc loc, Symbol *s, StructDeclaration *dsym); + + Symbol *toSymbol(); + + // Eliminate need for dynamic_cast + SymbolDeclaration *isSymbolDeclaration() { return (SymbolDeclaration *)this; } +}; + +struct ClassInfoDeclaration : VarDeclaration +{ + ClassDeclaration *cd; + + ClassInfoDeclaration(ClassDeclaration *cd); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + + Symbol *toSymbol(); +}; + +struct ModuleInfoDeclaration : VarDeclaration +{ + Module *mod; + + ModuleInfoDeclaration(Module *mod); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + + Symbol *toSymbol(); +}; + +struct TypeInfoDeclaration : VarDeclaration +{ + Type *tinfo; + + TypeInfoDeclaration(Type *tinfo, int internal); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + + Symbol *toSymbol(); + void toObjFile(int multiobj); // compile to .obj file + virtual void toDt(dt_t **pdt); +}; + +struct TypeInfoStructDeclaration : TypeInfoDeclaration +{ + TypeInfoStructDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoClassDeclaration : TypeInfoDeclaration +{ + TypeInfoClassDeclaration(Type *tinfo); + Symbol *toSymbol(); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoInterfaceDeclaration : TypeInfoDeclaration +{ + TypeInfoInterfaceDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoTypedefDeclaration : TypeInfoDeclaration +{ + TypeInfoTypedefDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoPointerDeclaration : TypeInfoDeclaration +{ + TypeInfoPointerDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoArrayDeclaration : TypeInfoDeclaration +{ + TypeInfoArrayDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoStaticArrayDeclaration : TypeInfoDeclaration +{ + TypeInfoStaticArrayDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoAssociativeArrayDeclaration : TypeInfoDeclaration +{ + TypeInfoAssociativeArrayDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoEnumDeclaration : TypeInfoDeclaration +{ + TypeInfoEnumDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoFunctionDeclaration : TypeInfoDeclaration +{ + TypeInfoFunctionDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoDelegateDeclaration : TypeInfoDeclaration +{ + TypeInfoDelegateDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoTupleDeclaration : TypeInfoDeclaration +{ + TypeInfoTupleDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +#if DMDV2 +struct TypeInfoConstDeclaration : TypeInfoDeclaration +{ + TypeInfoConstDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoInvariantDeclaration : TypeInfoDeclaration +{ + TypeInfoInvariantDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoSharedDeclaration : TypeInfoDeclaration +{ + TypeInfoSharedDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoWildDeclaration : TypeInfoDeclaration +{ + TypeInfoWildDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; + +struct TypeInfoVectorDeclaration : TypeInfoDeclaration +{ + TypeInfoVectorDeclaration(Type *tinfo); + + void toDt(dt_t **pdt); +}; +#endif + +/**************************************************************/ + +struct ThisDeclaration : VarDeclaration +{ + ThisDeclaration(Loc loc, Type *t); + Dsymbol *syntaxCopy(Dsymbol *); + ThisDeclaration *isThisDeclaration() { return this; } +}; + +enum ILS +{ + ILSuninitialized, // not computed yet + ILSno, // cannot inline + ILSyes, // can inline +}; + +/**************************************************************/ +#if DMDV2 + +enum BUILTIN +{ + BUILTINunknown = -1, // not known if this is a builtin + BUILTINnot, // this is not a builtin + BUILTINsin, // std.math.sin + BUILTINcos, // std.math.cos + BUILTINtan, // std.math.tan + BUILTINsqrt, // std.math.sqrt + BUILTINfabs, // std.math.fabs + BUILTINatan2, // std.math.atan2 + BUILTINrndtol, // std.math.rndtol + BUILTINexpm1, // std.math.expm1 + BUILTINexp2, // std.math.exp2 + BUILTINyl2x, // std.math.yl2x + BUILTINyl2xp1, // std.math.yl2xp1 + BUILTINbsr, // core.bitop.bsr + BUILTINbsf, // core.bitop.bsf + BUILTINbswap, // core.bitop.bswap +}; + +Expression *eval_builtin(Loc loc, enum BUILTIN builtin, Expressions *arguments); + +#else +enum BUILTIN { }; +#endif + +struct FuncDeclaration : Declaration +{ + Types *fthrows; // Array of Type's of exceptions (not used) + Statement *frequire; + Statement *fensure; + Statement *fbody; + + FuncDeclarations foverrides; // functions this function overrides + FuncDeclaration *fdrequire; // function that does the in contract + FuncDeclaration *fdensure; // function that does the out contract + + Identifier *outId; // identifier for out statement + VarDeclaration *vresult; // variable corresponding to outId + LabelDsymbol *returnLabel; // where the return goes + + DsymbolTable *localsymtab; // used to prevent symbols in different + // scopes from having the same name + VarDeclaration *vthis; // 'this' parameter (member and nested) + VarDeclaration *v_arguments; // '_arguments' parameter +#if IN_GCC + VarDeclaration *v_argptr; // '_argptr' variable +#endif + VarDeclaration *v_argsave; // save area for args passed in registers for variadic functions + VarDeclarations *parameters; // Array of VarDeclaration's for parameters + DsymbolTable *labtab; // statement label symbol table + Declaration *overnext; // next in overload list + Loc endloc; // location of closing curly bracket + int vtblIndex; // for member functions, index into vtbl[] + int naked; // !=0 if naked + ILS inlineStatusStmt; + ILS inlineStatusExp; + int inlineNest; // !=0 if nested inline + int isArrayOp; // !=0 if array operation + enum PASS semanticRun; + int semantic3Errors; // !=0 if errors in semantic3 + // this function's frame ptr + ForeachStatement *fes; // if foreach body, this is the foreach + int introducing; // !=0 if 'introducing' function + Type *tintro; // if !=NULL, then this is the type + // of the 'introducing' function + // this one is overriding + int inferRetType; // !=0 if return type is to be inferred + StorageClass storage_class2; // storage class for template onemember's + + // Things that should really go into Scope + int hasReturnExp; // 1 if there's a return exp; statement + // 2 if there's a throw statement + // 4 if there's an assert(0) + // 8 if there's inline asm + + // Support for NRVO (named return value optimization) + int nrvo_can; // !=0 means we can do it + VarDeclaration *nrvo_var; // variable to replace with shidden + Symbol *shidden; // hidden pointer passed to function + +#if DMDV2 + enum BUILTIN builtin; // set if this is a known, builtin + // function we can evaluate at compile + // time + + int tookAddressOf; // set if someone took the address of + // this function + VarDeclarations closureVars; // local variables in this function + // which are referenced by nested + // functions + + unsigned flags; + #define FUNCFLAGpurityInprocess 1 // working on determining purity + #define FUNCFLAGsafetyInprocess 2 // working on determining safety + #define FUNCFLAGnothrowInprocess 4 // working on determining nothrow +#else + int nestedFrameRef; // !=0 if nested variables referenced +#endif + + FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + void semantic2(Scope *sc); + void semantic3(Scope *sc); + // called from semantic3 + void varArgs(Scope *sc, TypeFunction*, VarDeclaration *&, VarDeclaration *&); + VarDeclaration *declareThis(Scope *sc, AggregateDeclaration *ad); + int equals(Object *o); + + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void bodyToCBuffer(OutBuffer *buf, HdrGenState *hgs); + int overrides(FuncDeclaration *fd); + int findVtblIndex(Dsymbols *vtbl, int dim); + int overloadInsert(Dsymbol *s); + FuncDeclaration *overloadExactMatch(Type *t); + FuncDeclaration *overloadResolve(Loc loc, Expression *ethis, Expressions *arguments, int flags = 0); + MATCH leastAsSpecialized(FuncDeclaration *g); + LabelDsymbol *searchLabel(Identifier *ident); + AggregateDeclaration *isThis(); + AggregateDeclaration *isMember2(); + int getLevel(Loc loc, Scope *sc, FuncDeclaration *fd); // lexical nesting level difference + void appendExp(Expression *e); + void appendState(Statement *s); + char *mangle(); + const char *toPrettyChars(); + int isMain(); + int isWinMain(); + int isDllMain(); + enum BUILTIN isBuiltin(); + int isExport(); + int isImportedSymbol(); + int isAbstract(); + int isCodeseg(); + int isOverloadable(); + enum PURE isPure(); + enum PURE isPureBypassingInference(); + bool setImpure(); + int isSafe(); + int isTrusted(); + bool setUnsafe(); + virtual int isNested(); + int needThis(); + int isVirtualMethod(); + virtual int isVirtual(); + virtual int isFinal(); + virtual int addPreInvariant(); + virtual int addPostInvariant(); + Expression *interpret(InterState *istate, Expressions *arguments, Expression *thisexp = NULL); + void inlineScan(); + int canInline(int hasthis, int hdrscan, int statementsToo); + Expression *expandInline(InlineScanState *iss, Expression *ethis, Expressions *arguments, Statement **ps); + const char *kind(); + void toDocBuffer(OutBuffer *buf); + FuncDeclaration *isUnique(); + void checkNestedReference(Scope *sc, Loc loc); + int needsClosure(); + int hasNestedFrameRefs(); + Statement *mergeFrequire(Statement *); + Statement *mergeFensure(Statement *); + Parameters *getParameters(int *pvarargs); + + static FuncDeclaration *genCfunc(Type *treturn, const char *name); + static FuncDeclaration *genCfunc(Type *treturn, Identifier *id); + + Symbol *toSymbol(); + Symbol *toThunkSymbol(int offset); // thunk version + void toObjFile(int multiobj); // compile to .obj file + int cvMember(unsigned char *p); + void buildClosure(IRState *irs); + + FuncDeclaration *isFuncDeclaration() { return this; } +}; + +#if DMDV2 +FuncDeclaration *resolveFuncCall(Scope *sc, Loc loc, Dsymbol *s, + Objects *tiargs, + Expression *ethis, + Expressions *arguments, + int flags); +#endif + +struct FuncAliasDeclaration : FuncDeclaration +{ + FuncDeclaration *funcalias; + + FuncAliasDeclaration(FuncDeclaration *funcalias); + + FuncAliasDeclaration *isFuncAliasDeclaration() { return this; } + const char *kind(); + Symbol *toSymbol(); +}; + +struct FuncLiteralDeclaration : FuncDeclaration +{ + enum TOK tok; // TOKfunction or TOKdelegate + + FuncLiteralDeclaration(Loc loc, Loc endloc, Type *type, enum TOK tok, + ForeachStatement *fes); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Dsymbol *syntaxCopy(Dsymbol *); + int isNested(); + int isVirtual(); + + FuncLiteralDeclaration *isFuncLiteralDeclaration() { return this; } + const char *kind(); +}; + +struct CtorDeclaration : FuncDeclaration +{ + CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *type); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + const char *kind(); + char *toChars(); + int isVirtual(); + int addPreInvariant(); + int addPostInvariant(); + + CtorDeclaration *isCtorDeclaration() { return this; } +}; + +#if DMDV2 +struct PostBlitDeclaration : FuncDeclaration +{ + PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc = STCundefined); + PostBlitDeclaration(Loc loc, Loc endloc, Identifier *id); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + int isVirtual(); + int addPreInvariant(); + int addPostInvariant(); + int overloadInsert(Dsymbol *s); + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + + PostBlitDeclaration *isPostBlitDeclaration() { return this; } +}; +#endif + +struct DtorDeclaration : FuncDeclaration +{ + DtorDeclaration(Loc loc, Loc endloc); + DtorDeclaration(Loc loc, Loc endloc, Identifier *id); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + const char *kind(); + char *toChars(); + int isVirtual(); + int addPreInvariant(); + int addPostInvariant(); + int overloadInsert(Dsymbol *s); + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + + DtorDeclaration *isDtorDeclaration() { return this; } +}; + +struct StaticCtorDeclaration : FuncDeclaration +{ + StaticCtorDeclaration(Loc loc, Loc endloc); + StaticCtorDeclaration(Loc loc, Loc endloc, const char *name); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + AggregateDeclaration *isThis(); + int isVirtual(); + int addPreInvariant(); + int addPostInvariant(); + bool hasStaticCtorOrDtor(); + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + StaticCtorDeclaration *isStaticCtorDeclaration() { return this; } +}; + +#if DMDV2 +struct SharedStaticCtorDeclaration : StaticCtorDeclaration +{ + SharedStaticCtorDeclaration(Loc loc, Loc endloc); + Dsymbol *syntaxCopy(Dsymbol *); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration() { return this; } +}; +#endif + +struct StaticDtorDeclaration : FuncDeclaration +{ VarDeclaration *vgate; // 'gate' variable + + StaticDtorDeclaration(Loc loc, Loc endloc); + StaticDtorDeclaration(Loc loc, Loc endloc, const char *name); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + AggregateDeclaration *isThis(); + int isVirtual(); + bool hasStaticCtorOrDtor(); + int addPreInvariant(); + int addPostInvariant(); + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + StaticDtorDeclaration *isStaticDtorDeclaration() { return this; } +}; + +#if DMDV2 +struct SharedStaticDtorDeclaration : StaticDtorDeclaration +{ + SharedStaticDtorDeclaration(Loc loc, Loc endloc); + Dsymbol *syntaxCopy(Dsymbol *); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration() { return this; } +}; +#endif + +struct InvariantDeclaration : FuncDeclaration +{ + InvariantDeclaration(Loc loc, Loc endloc); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + int isVirtual(); + int addPreInvariant(); + int addPostInvariant(); + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + InvariantDeclaration *isInvariantDeclaration() { return this; } +}; + +struct UnitTestDeclaration : FuncDeclaration +{ + UnitTestDeclaration(Loc loc, Loc endloc); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + AggregateDeclaration *isThis(); + int isVirtual(); + int addPreInvariant(); + int addPostInvariant(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toJsonBuffer(OutBuffer *buf); + + UnitTestDeclaration *isUnitTestDeclaration() { return this; } +}; + +struct NewDeclaration : FuncDeclaration +{ Parameters *arguments; + int varargs; + + NewDeclaration(Loc loc, Loc endloc, Parameters *arguments, int varargs); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + const char *kind(); + int isVirtual(); + int addPreInvariant(); + int addPostInvariant(); + + NewDeclaration *isNewDeclaration() { return this; } +}; + + +struct DeleteDeclaration : FuncDeclaration +{ Parameters *arguments; + + DeleteDeclaration(Loc loc, Loc endloc, Parameters *arguments); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + const char *kind(); + int isDelete(); + int isVirtual(); + int addPreInvariant(); + int addPostInvariant(); + DeleteDeclaration *isDeleteDeclaration() { return this; } +}; + +#endif /* DMD_DECLARATION_H */ diff --git a/delegatize.c b/delegatize.c new file mode 100644 index 00000000..a4d37152 --- /dev/null +++ b/delegatize.c @@ -0,0 +1,140 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "mars.h" +#include "expression.h" +#include "statement.h" +#include "mtype.h" +#include "utf.h" +#include "declaration.h" +#include "aggregate.h" +#include "scope.h" + +/******************************************** + * Convert from expression to delegate that returns the expression, + * i.e. convert: + * expr + * to: + * t delegate() { return expr; } + */ + +int lambdaSetParent(Expression *e, void *param); +int lambdaCheckForNestedRef(Expression *e, void *param); + +Expression *Expression::toDelegate(Scope *sc, Type *t) +{ + //printf("Expression::toDelegate(t = %s) %s\n", t->toChars(), toChars()); + TypeFunction *tf = new TypeFunction(NULL, t, 0, LINKd); + FuncLiteralDeclaration *fld = + new FuncLiteralDeclaration(loc, loc, tf, TOKdelegate, NULL); + Expression *e; + sc = sc->push(); + sc->parent = fld; // set current function to be the delegate + e = this; + e->apply(&lambdaSetParent, sc); + e->apply(&lambdaCheckForNestedRef, sc); + sc = sc->pop(); + Statement *s; + if (t->ty == Tvoid) + s = new ExpStatement(loc, e); + else + s = new ReturnStatement(loc, e); + fld->fbody = s; + e = new FuncExp(loc, fld); + e = e->semantic(sc); + return e; +} + +/****************************************** + * Patch the parent of declarations to be the new function literal. + */ +int lambdaSetParent(Expression *e, void *param) +{ + Scope *sc = (Scope *)param; + /* We could use virtual functions instead of a switch, + * but it doesn't seem worth the bother. + */ + switch (e->op) + { + case TOKdeclaration: + { DeclarationExp *de = (DeclarationExp *)e; + de->declaration->parent = sc->parent; + break; + } + + case TOKindex: + { IndexExp *de = (IndexExp *)e; + if (de->lengthVar) + { //printf("lengthVar\n"); + de->lengthVar->parent = sc->parent; + } + break; + } + + case TOKslice: + { SliceExp *se = (SliceExp *)e; + if (se->lengthVar) + { //printf("lengthVar\n"); + se->lengthVar->parent = sc->parent; + } + break; + } + + default: + break; + } + return 0; +} + +/******************************************* + * Look for references to variables in a scope enclosing the new function literal. + */ +int lambdaCheckForNestedRef(Expression *e, void *param) +{ + Scope *sc = (Scope *)param; + /* We could use virtual functions instead of a switch, + * but it doesn't seem worth the bother. + */ + switch (e->op) + { + case TOKsymoff: + { SymOffExp *se = (SymOffExp *)e; + VarDeclaration *v = se->var->isVarDeclaration(); + if (v) + v->checkNestedReference(sc, 0); + break; + } + + case TOKvar: + { VarExp *ve = (VarExp *)e; + VarDeclaration *v = ve->var->isVarDeclaration(); + if (v) + v->checkNestedReference(sc, 0); + break; + } + + case TOKthis: + case TOKsuper: + { ThisExp *te = (ThisExp *)e; + VarDeclaration *v = te->var->isVarDeclaration(); + if (v) + v->checkNestedReference(sc, 0); + break; + } + + default: + break; + } + return 0; +} + diff --git a/doc.c b/doc.c new file mode 100644 index 00000000..51c9d9c0 --- /dev/null +++ b/doc.c @@ -0,0 +1,2226 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2012 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +// This implements the Ddoc capability. + +#include +#include +#include +#include +#include + +#include "rmem.h" +#include "root.h" + +#include "mars.h" +#include "dsymbol.h" +#include "macro.h" +#include "template.h" +#include "lexer.h" +#include "aggregate.h" +#include "declaration.h" +#include "enum.h" +#include "id.h" +#include "module.h" +#include "scope.h" +#include "hdrgen.h" +#include "doc.h" +#include "mtype.h" +#include "utf.h" + +struct Escape +{ + const char *strings[256]; + + static const char *escapeChar(unsigned c); +}; + +struct Section +{ + unsigned char *name; + unsigned namelen; + + unsigned char *body; + unsigned bodylen; + + int nooutput; + + virtual void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf); +}; + +struct ParamSection : Section +{ + void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf); +}; + +struct MacroSection : Section +{ + void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf); +}; + +typedef ArrayBase
Sections; + +struct DocComment +{ + Sections sections; // Section*[] + + Section *summary; + Section *copyright; + Section *macros; + Macro **pmacrotable; + Escape **pescapetable; + + DocComment(); + + static DocComment *parse(Scope *sc, Dsymbol *s, unsigned char *comment); + static void parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen); + static void parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen); + + void parseSections(unsigned char *comment); + void writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf); +}; + + +int cmp(const char *stringz, void *s, size_t slen); +int icmp(const char *stringz, void *s, size_t slen); +int isDitto(unsigned char *comment); +unsigned char *skipwhitespace(unsigned char *p); +unsigned skiptoident(OutBuffer *buf, size_t i); +unsigned skippastident(OutBuffer *buf, size_t i); +unsigned skippastURL(OutBuffer *buf, size_t i); +void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); +void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); +void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); +Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len); + +int isIdStart(unsigned char *p); +int isIdTail(unsigned char *p); +int utfStride(unsigned char *p); + +static unsigned char ddoc_default[] = "\ +DDOC = \n\ + \n\ + $(TITLE)\n\ + \n\ +

$(TITLE)

\n\ + $(BODY)\n\ +
$(SMALL Page generated by $(LINK2 http://www.digitalmars.com/d/2.0/ddoc.html, Ddoc). $(COPYRIGHT))\n\ + \n\ +\n\ +B = $0\n\ +I = $0\n\ +U = $0\n\ +P =

$0

\n\ +DL =
$0
\n\ +DT =
$0
\n\ +DD =
$0
\n\ +TABLE = $0
\n\ +TR = $0\n\ +TH = $0\n\ +TD = $0\n\ +OL =
    $0
\n\ +UL =
    $0
\n\ +LI =
  • $0
  • \n\ +BIG = $0\n\ +SMALL = $0\n\ +BR =
    \n\ +LINK = $0\n\ +LINK2 = $+\n\ +LPAREN= (\n\ +RPAREN= )\n\ +DOLLAR= $\n\ +\n\ +RED = $0\n\ +BLUE = $0\n\ +GREEN = $0\n\ +YELLOW =$0\n\ +BLACK = $0\n\ +WHITE = $0\n\ +\n\ +D_CODE =
    $0
    \n\ +D_COMMENT = $(GREEN $0)\n\ +D_STRING = $(RED $0)\n\ +D_KEYWORD = $(BLUE $0)\n\ +D_PSYMBOL = $(U $0)\n\ +D_PARAM = $(I $0)\n\ +\n\ +DDOC_COMMENT = \n\ +DDOC_DECL = $(DT $(BIG $0))\n\ +DDOC_DECL_DD = $(DD $0)\n\ +DDOC_DITTO = $(BR)$0\n\ +DDOC_SECTIONS = $0\n\ +DDOC_SUMMARY = $0$(BR)$(BR)\n\ +DDOC_DESCRIPTION = $0$(BR)$(BR)\n\ +DDOC_AUTHORS = $(B Authors:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_BUGS = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_DATE = $(B Date:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_EXAMPLES = $(B Examples:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_HISTORY = $(B History:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_LICENSE = $(B License:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_RETURNS = $(B Returns:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_SEE_ALSO = $(B See Also:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_THROWS = $(B Throws:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_VERSION = $(B Version:)$(BR)\n$0$(BR)$(BR)\n\ +DDOC_SECTION_H = $(B $0)$(BR)\n\ +DDOC_SECTION = $0$(BR)$(BR)\n\ +DDOC_MEMBERS = $(DL $0)\n\ +DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)\n\ +DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0)\n\ +DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)\n\ +DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0)\n\ +DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)\n\ +DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR)\n\ +DDOC_PARAM_ROW = $(TR $0)\n\ +DDOC_PARAM_ID = $(TD $0)\n\ +DDOC_PARAM_DESC = $(TD $0)\n\ +DDOC_BLANKLINE = $(BR)$(BR)\n\ +\n\ +DDOC_PSYMBOL = $(U $0)\n\ +DDOC_KEYWORD = $(B $0)\n\ +DDOC_PARAM = $(I $0)\n\ +\n\ +ESCAPES = //>/\n\ + /&/&/\n\ +"; + +static char ddoc_decl_s[] = "$(DDOC_DECL "; +static char ddoc_decl_e[] = ")\n"; + +static char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD "; +static char ddoc_decl_dd_e[] = ")\n"; + + +/**************************************************** + */ + +void Module::gendocfile() +{ + static OutBuffer mbuf; + static int mbuf_done; + + OutBuffer buf; + + //printf("Module::gendocfile()\n"); + + if (!mbuf_done) // if not already read the ddoc files + { mbuf_done = 1; + + // Use our internal default + mbuf.write(ddoc_default, sizeof(ddoc_default) - 1); + + // Override with DDOCFILE specified in the sc.ini file + char *p = getenv("DDOCFILE"); + if (p) + global.params.ddocfiles->shift(p); + + // Override with the ddoc macro files from the command line + for (size_t i = 0; i < global.params.ddocfiles->dim; i++) + { + FileName f(global.params.ddocfiles->tdata()[i], 0); + File file(&f); + file.readv(); + // BUG: convert file contents to UTF-8 before use + + //printf("file: '%.*s'\n", file.len, file.buffer); + mbuf.write(file.buffer, file.len); + } + } + DocComment::parseMacros(&escapetable, ¯otable, mbuf.data, mbuf.offset); + + Scope *sc = Scope::createGlobal(this); // create root scope + sc->docbuf = &buf; + + DocComment *dc = DocComment::parse(sc, this, comment); + dc->pmacrotable = ¯otable; + dc->pescapetable = &escapetable; + + // Generate predefined macros + + // Set the title to be the name of the module + { const char *p = toPrettyChars(); + Macro::define(¯otable, (unsigned char *)"TITLE", 5, (unsigned char *)p, strlen(p)); + } + + // Set time macros + { time_t t; + time(&t); + char *p = ctime(&t); + p = mem.strdup(p); + Macro::define(¯otable, (unsigned char *)"DATETIME", 8, (unsigned char *)p, strlen(p)); + Macro::define(¯otable, (unsigned char *)"YEAR", 4, (unsigned char *)p + 20, 4); + } + + char *docfilename = docfile->toChars(); + Macro::define(¯otable, (unsigned char *)"DOCFILENAME", 11, (unsigned char *)docfilename, strlen(docfilename)); + + if (dc->copyright) + { + dc->copyright->nooutput = 1; + Macro::define(¯otable, (unsigned char *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen); + } + + buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile->toChars()); + if (isDocFile) + { + size_t commentlen = strlen((char *)comment); + if (dc->macros) + { + commentlen = dc->macros->name - comment; + dc->macros->write(dc, sc, this, sc->docbuf); + } + sc->docbuf->write(comment, commentlen); + highlightText(NULL, this, sc->docbuf, 0); + } + else + { + dc->writeSections(sc, this, sc->docbuf); + emitMemberComments(sc); + } + + //printf("BODY= '%.*s'\n", buf.offset, buf.data); + Macro::define(¯otable, (unsigned char *)"BODY", 4, buf.data, buf.offset); + + OutBuffer buf2; + buf2.writestring("$(DDOC)\n"); + unsigned end = buf2.offset; + macrotable->expand(&buf2, 0, &end, NULL, 0); + +#if 1 + /* Remove all the escape sequences from buf2, + * and make CR-LF the newline. + */ + { + buf.setsize(0); + buf.reserve(buf2.offset); + unsigned char *p = buf2.data; + for (unsigned j = 0; j < buf2.offset; j++) + { + unsigned char c = p[j]; + if (c == 0xFF && j + 1 < buf2.offset) + { + j++; + continue; + } + if (c == '\n') + buf.writeByte('\r'); + else if (c == '\r') + { + buf.writestring("\r\n"); + if (j + 1 < buf2.offset && p[j + 1] == '\n') + { + j++; + } + continue; + } + buf.writeByte(c); + } + } + + // Transfer image to file + assert(docfile); + docfile->setbuffer(buf.data, buf.offset); + docfile->ref = 1; + char *pt = FileName::path(docfile->toChars()); + if (*pt) + FileName::ensurePathExists(pt); + mem.free(pt); + docfile->writev(); +#else + /* Remove all the escape sequences from buf2 + */ + { unsigned i = 0; + unsigned char *p = buf2.data; + for (unsigned j = 0; j < buf2.offset; j++) + { + if (p[j] == 0xFF && j + 1 < buf2.offset) + { + j++; + continue; + } + p[i] = p[j]; + i++; + } + buf2.setsize(i); + } + + // Transfer image to file + docfile->setbuffer(buf2.data, buf2.offset); + docfile->ref = 1; + char *pt = FileName::path(docfile->toChars()); + if (*pt) + FileName::ensurePathExists(pt); + mem.free(pt); + docfile->writev(); +#endif +} + +/**************************************************** + * Having unmatched parentheses can hose the output of Ddoc, + * as the macros depend on properly nested parentheses. + * This function replaces all ( with $(LPAREN) and ) with $(RPAREN) + * to preserve text literally. This also means macros in the + * text won't be expanded. + */ +void escapeDdocString(OutBuffer *buf, unsigned start) +{ + for (unsigned u = start; u < buf->offset; u++) + { + unsigned char c = buf->data[u]; + switch(c) + { + case '$': + buf->remove(u, 1); + buf->insert(u, "$(DOLLAR)", 9); + u += 8; + break; + + case '(': + buf->remove(u, 1); //remove the ( + buf->insert(u, "$(LPAREN)", 9); //insert this instead + u += 8; //skip over newly inserted macro + break; + + case ')': + buf->remove(u, 1); //remove the ) + buf->insert(u, "$(RPAREN)", 9); //insert this instead + u += 8; //skip over newly inserted macro + break; + } + } +} + +/**************************************************** + * Having unmatched parentheses can hose the output of Ddoc, + * as the macros depend on properly nested parentheses. + + * Fix by replacing unmatched ( with $(LPAREN) and unmatched ) with $(RPAREN). + */ +void escapeStrayParenthesis(OutBuffer *buf, unsigned start, Loc loc) +{ + unsigned par_open = 0; + + for (unsigned u = start; u < buf->offset; u++) + { + unsigned char c = buf->data[u]; + switch(c) + { + case '(': + par_open++; + break; + + case ')': + if (par_open == 0) + { + //stray ')' + if (global.params.warnings) + warning(loc, "Ddoc: Stray ')'. This may cause incorrect Ddoc output." + " Use $(RPAREN) instead for unpaired right parentheses."); + buf->remove(u, 1); //remove the ) + buf->insert(u, "$(RPAREN)", 9); //insert this instead + u += 8; //skip over newly inserted macro + } + else + par_open--; + break; +#if 0 + // For this to work, loc must be set to the beginning of the passed + // text which is currently not possible + // (loc is set to the Loc of the Dsymbol) + case '\n': + loc.linnum++; + break; +#endif + } + } + + if (par_open) // if any unmatched lparens + { par_open = 0; + for (unsigned u = buf->offset; u > start;) + { u--; + unsigned char c = buf->data[u]; + switch(c) + { + case ')': + par_open++; + break; + + case '(': + if (par_open == 0) + { + //stray '(' + if (global.params.warnings) + warning(loc, "Ddoc: Stray '('. This may cause incorrect Ddoc output." + " Use $(LPAREN) instead for unpaired left parentheses."); + buf->remove(u, 1); //remove the ( + buf->insert(u, "$(LPAREN)", 9); //insert this instead + } + else + par_open--; + break; + } + } + } +} + +/******************************* emitComment **********************************/ + +/* + * Emit doc comment to documentation file + */ + +void Dsymbol::emitDitto(Scope *sc) +{ + //printf("Dsymbol::emitDitto() %s %s\n", kind(), toChars()); + OutBuffer *buf = sc->docbuf; + unsigned o; + OutBuffer b; + + b.writestring("$(DDOC_DITTO "); + o = b.offset; + toDocBuffer(&b); + //printf("b: '%.*s'\n", b.offset, b.data); + /* If 'this' is a function template, then highlightCode() was + * already run by FuncDeclaration::toDocbuffer(). + */ + TemplateDeclaration *td; + if (parent && + (td = parent->isTemplateDeclaration()) != NULL && + td->onemember == this) + { + } + else + highlightCode(sc, this, &b, o); + b.writeByte(')'); + buf->spread(sc->lastoffset, b.offset); + memcpy(buf->data + sc->lastoffset, b.data, b.offset); + sc->lastoffset += b.offset; +} + +void ScopeDsymbol::emitMemberComments(Scope *sc) +{ + //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars()); + OutBuffer *buf = sc->docbuf; + + if (members) + { const char *m = "$(DDOC_MEMBERS \n"; + + if (isModule()) + m = "$(DDOC_MODULE_MEMBERS \n"; + else if (isClassDeclaration()) + m = "$(DDOC_CLASS_MEMBERS \n"; + else if (isStructDeclaration()) + m = "$(DDOC_STRUCT_MEMBERS \n"; + else if (isEnumDeclaration()) + m = "$(DDOC_ENUM_MEMBERS \n"; + else if (isTemplateDeclaration()) + m = "$(DDOC_TEMPLATE_MEMBERS \n"; + + unsigned offset1 = buf->offset; // save starting offset + buf->writestring(m); + unsigned offset2 = buf->offset; // to see if we write anything + sc = sc->push(this); + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + //printf("\ts = '%s'\n", s->toChars()); + s->emitComment(sc); + } + sc->pop(); + if (buf->offset == offset2) + { + /* Didn't write out any members, so back out last write + */ + buf->offset = offset1; + } + else + buf->writestring(")\n"); + } +} + +void emitProtection(OutBuffer *buf, PROT prot) +{ + const char *p; + + switch (prot) + { + case PROTpackage: p = "package"; break; + case PROTprotected: p = "protected"; break; + case PROTexport: p = "export"; break; + default: p = NULL; break; + } + if (p) + buf->printf("%s ", p); +} + +void Dsymbol::emitComment(Scope *sc) { } +void InvariantDeclaration::emitComment(Scope *sc) { } +#if DMDV2 +void PostBlitDeclaration::emitComment(Scope *sc) { } +#endif +void DtorDeclaration::emitComment(Scope *sc) { } +void StaticCtorDeclaration::emitComment(Scope *sc) { } +void StaticDtorDeclaration::emitComment(Scope *sc) { } +void ClassInfoDeclaration::emitComment(Scope *sc) { } +void ModuleInfoDeclaration::emitComment(Scope *sc) { } +void TypeInfoDeclaration::emitComment(Scope *sc) { } + + +void Declaration::emitComment(Scope *sc) +{ + //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment); + //printf("type = %p\n", type); + + if (protection == PROTprivate || !ident || + (!type && !isCtorDeclaration() && !isAliasDeclaration())) + return; + if (!comment) + return; + + OutBuffer *buf = sc->docbuf; + DocComment *dc = DocComment::parse(sc, this, comment); + unsigned o; + + if (!dc) + { + emitDitto(sc); + return; + } + dc->pmacrotable = &sc->module->macrotable; + + buf->writestring(ddoc_decl_s); + o = buf->offset; + toDocBuffer(buf); + highlightCode(sc, this, buf, o); + sc->lastoffset = buf->offset; + buf->writestring(ddoc_decl_e); + + buf->writestring(ddoc_decl_dd_s); + dc->writeSections(sc, this, buf); + buf->writestring(ddoc_decl_dd_e); +} + +void AggregateDeclaration::emitComment(Scope *sc) +{ + //printf("AggregateDeclaration::emitComment() '%s'\n", toChars()); + if (prot() == PROTprivate) + return; + if (!comment) + return; + + OutBuffer *buf = sc->docbuf; + DocComment *dc = DocComment::parse(sc, this, comment); + + if (!dc) + { + emitDitto(sc); + return; + } + dc->pmacrotable = &sc->module->macrotable; + + buf->writestring(ddoc_decl_s); + toDocBuffer(buf); + sc->lastoffset = buf->offset; + buf->writestring(ddoc_decl_e); + + buf->writestring(ddoc_decl_dd_s); + dc->writeSections(sc, this, buf); + emitMemberComments(sc); + buf->writestring(ddoc_decl_dd_e); +} + +void TemplateDeclaration::emitComment(Scope *sc) +{ + //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", toChars(), kind()); + if (prot() == PROTprivate) + return; + + unsigned char *com = comment; + int hasmembers = 1; + + Dsymbol *ss = this; + + if (onemember) + { + ss = onemember->isAggregateDeclaration(); + if (!ss) + { + ss = onemember->isFuncDeclaration(); + if (ss) + { hasmembers = 0; + if (com != ss->comment) + com = Lexer::combineComments(com, ss->comment); + } + else + ss = this; + } + } + + if (!com) + return; + + OutBuffer *buf = sc->docbuf; + DocComment *dc = DocComment::parse(sc, this, com); + unsigned o; + + if (!dc) + { + ss->emitDitto(sc); + return; + } + dc->pmacrotable = &sc->module->macrotable; + + buf->writestring(ddoc_decl_s); + o = buf->offset; + ss->toDocBuffer(buf); + if (ss == this) + highlightCode(sc, this, buf, o); + sc->lastoffset = buf->offset; + buf->writestring(ddoc_decl_e); + + buf->writestring(ddoc_decl_dd_s); + dc->writeSections(sc, this, buf); + if (hasmembers) + ((ScopeDsymbol *)ss)->emitMemberComments(sc); + buf->writestring(ddoc_decl_dd_e); +} + +void EnumDeclaration::emitComment(Scope *sc) +{ + if (prot() == PROTprivate) + return; +// if (!comment) + { if (isAnonymous() && members) + { + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + s->emitComment(sc); + } + return; + } + } + if (!comment) + return; + if (isAnonymous()) + return; + + OutBuffer *buf = sc->docbuf; + DocComment *dc = DocComment::parse(sc, this, comment); + + if (!dc) + { + emitDitto(sc); + return; + } + dc->pmacrotable = &sc->module->macrotable; + + buf->writestring(ddoc_decl_s); + toDocBuffer(buf); + sc->lastoffset = buf->offset; + buf->writestring(ddoc_decl_e); + + buf->writestring(ddoc_decl_dd_s); + dc->writeSections(sc, this, buf); + emitMemberComments(sc); + buf->writestring(ddoc_decl_dd_e); +} + +void EnumMember::emitComment(Scope *sc) +{ + //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment); + if (prot() == PROTprivate) + return; + if (!comment) + return; + + OutBuffer *buf = sc->docbuf; + DocComment *dc = DocComment::parse(sc, this, comment); + unsigned o; + + if (!dc) + { + emitDitto(sc); + return; + } + dc->pmacrotable = &sc->module->macrotable; + + buf->writestring(ddoc_decl_s); + o = buf->offset; + toDocBuffer(buf); + highlightCode(sc, this, buf, o); + sc->lastoffset = buf->offset; + buf->writestring(ddoc_decl_e); + + buf->writestring(ddoc_decl_dd_s); + dc->writeSections(sc, this, buf); + buf->writestring(ddoc_decl_dd_e); +} + +/******************************* toDocBuffer **********************************/ + +void Dsymbol::toDocBuffer(OutBuffer *buf) +{ + //printf("Dsymbol::toDocbuffer() %s\n", toChars()); + HdrGenState hgs; + + hgs.ddoc = 1; + toCBuffer(buf, &hgs); +} + +void prefix(OutBuffer *buf, Dsymbol *s) +{ + if (s->isDeprecated()) + buf->writestring("deprecated "); + Declaration *d = s->isDeclaration(); + if (d) + { + emitProtection(buf, d->protection); + if (d->isAbstract()) + buf->writestring("abstract "); + if (d->isStatic()) + buf->writestring("static "); + if (d->isConst()) + buf->writestring("const "); +#if DMDV2 + if (d->isImmutable()) + buf->writestring("immutable "); +#endif + if (d->isFinal()) + buf->writestring("final "); + if (d->isSynchronized()) + buf->writestring("synchronized "); + } +} + +void declarationToDocBuffer(Declaration *decl, OutBuffer *buf, TemplateDeclaration *td) +{ + //printf("declarationToDocBuffer() %s, originalType = %s, td = %s\n", decl->toChars(), decl->originalType ? decl->originalType->toChars() : "--", td ? td->toChars() : "--"); + if (decl->ident) + { + prefix(buf, decl); + + if (decl->type) + { HdrGenState hgs; + hgs.ddoc = 1; + Type *origType = decl->originalType ? decl->originalType : decl->type; + if (origType->ty == Tfunction) + { + TypeFunction *attrType = (TypeFunction*)(decl->ident == Id::ctor ? origType : decl->type); + ((TypeFunction*)origType)->toCBufferWithAttributes(buf, decl->ident, &hgs, attrType, td); + } + else + origType->toCBuffer(buf, decl->ident, &hgs); + } + else + buf->writestring(decl->ident->toChars()); + buf->writestring(";\n"); + } +} + +void Declaration::toDocBuffer(OutBuffer *buf) +{ + declarationToDocBuffer(this, buf, NULL); +} + +void AliasDeclaration::toDocBuffer(OutBuffer *buf) +{ + //printf("AliasDeclaration::toDocbuffer() %s\n", toChars()); + if (ident) + { + if (isDeprecated()) + buf->writestring("deprecated "); + + emitProtection(buf, protection); + buf->writestring("alias "); + buf->writestring(toChars()); + buf->writestring(";\n"); + } +} + + +void TypedefDeclaration::toDocBuffer(OutBuffer *buf) +{ + if (ident) + { + if (isDeprecated()) + buf->writestring("deprecated "); + + emitProtection(buf, protection); + buf->writestring("typedef "); + buf->writestring(toChars()); + buf->writestring(";\n"); + } +} + + +void FuncDeclaration::toDocBuffer(OutBuffer *buf) +{ + //printf("FuncDeclaration::toDocbuffer() %s\n", toChars()); + if (ident) + { + TemplateDeclaration *td; + + if (parent && + (td = parent->isTemplateDeclaration()) != NULL && + td->onemember == this) + { /* It's a function template + */ + unsigned o = buf->offset; + + declarationToDocBuffer(this, buf, td); + + highlightCode(NULL, this, buf, o); + } + else + { + Declaration::toDocBuffer(buf); + } + } +} + +#if DMDV1 +void CtorDeclaration::toDocBuffer(OutBuffer *buf) +{ + HdrGenState hgs; + + buf->writestring("this"); + Parameter::argsToCBuffer(buf, &hgs, arguments, varargs); + buf->writestring(";\n"); +} +#endif + +void AggregateDeclaration::toDocBuffer(OutBuffer *buf) +{ + if (ident) + { +#if 0 + emitProtection(buf, protection); +#endif + buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); + buf->writestring(";\n"); + } +} + +void StructDeclaration::toDocBuffer(OutBuffer *buf) +{ + //printf("StructDeclaration::toDocbuffer() %s\n", toChars()); + if (ident) + { +#if 0 + emitProtection(buf, protection); +#endif + TemplateDeclaration *td; + + if (parent && + (td = parent->isTemplateDeclaration()) != NULL && + td->onemember == this) + { unsigned o = buf->offset; + td->toDocBuffer(buf); + highlightCode(NULL, this, buf, o); + } + else + { + buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); + } + buf->writestring(";\n"); + } +} + +void ClassDeclaration::toDocBuffer(OutBuffer *buf) +{ + //printf("ClassDeclaration::toDocbuffer() %s\n", toChars()); + if (ident) + { +#if 0 + emitProtection(buf, protection); +#endif + TemplateDeclaration *td; + + if (parent && + (td = parent->isTemplateDeclaration()) != NULL && + td->onemember == this) + { unsigned o = buf->offset; + td->toDocBuffer(buf); + highlightCode(NULL, this, buf, o); + } + else + { + if (isAbstract()) + buf->writestring("abstract "); + buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); + } + int any = 0; + for (size_t i = 0; i < baseclasses->dim; i++) + { BaseClass *bc = (*baseclasses)[i]; + + if (bc->protection == PROTprivate) + continue; + if (bc->base && bc->base->ident == Id::Object) + continue; + + if (any) + buf->writestring(", "); + else + { buf->writestring(": "); + any = 1; + } + emitProtection(buf, bc->protection); + if (bc->base) + { + buf->writestring(bc->base->toPrettyChars()); + } + else + { + HdrGenState hgs; + bc->type->toCBuffer(buf, NULL, &hgs); + } + } + buf->writestring(";\n"); + } +} + + +void EnumDeclaration::toDocBuffer(OutBuffer *buf) +{ + if (ident) + { + buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); + buf->writestring(";\n"); + } +} + +void EnumMember::toDocBuffer(OutBuffer *buf) +{ + if (ident) + { + buf->writestring(toChars()); + } +} + + +/********************************* DocComment *********************************/ + +DocComment::DocComment() +{ + memset(this, 0, sizeof(DocComment)); +} + +DocComment *DocComment::parse(Scope *sc, Dsymbol *s, unsigned char *comment) +{ + //printf("parse(%s): '%s'\n", s->toChars(), comment); + if (sc->lastdc && isDitto(comment)) + return NULL; + + DocComment *dc = new DocComment(); + if (!comment) + return dc; + + dc->parseSections(comment); + + for (size_t i = 0; i < dc->sections.dim; i++) + { Section *sec = dc->sections[i]; + + if (icmp("copyright", sec->name, sec->namelen) == 0) + { + dc->copyright = sec; + } + if (icmp("macros", sec->name, sec->namelen) == 0) + { + dc->macros = sec; + } + } + + sc->lastdc = dc; + return dc; +} + +/***************************************** + * Parse next paragraph out of *pcomment. + * Update *pcomment to point past paragraph. + * Returns NULL if no more paragraphs. + * If paragraph ends in 'identifier:', + * then (*pcomment)[0 .. idlen] is the identifier. + */ + +void DocComment::parseSections(unsigned char *comment) +{ unsigned char *p; + unsigned char *pstart; + unsigned char *pend; + unsigned char *idstart; + unsigned idlen; + + unsigned char *name = NULL; + unsigned namelen = 0; + + //printf("parseSections('%s')\n", comment); + p = comment; + while (*p) + { + p = skipwhitespace(p); + pstart = p; + pend = p; + + /* Find end of section, which is ended by one of: + * 'identifier:' (but not inside a code section) + * '\0' + */ + idlen = 0; + int inCode = 0; + while (1) + { + // Check for start/end of a code section + if (*p == '-') + { + int numdash = 0; + while (*p == '-') + { + ++numdash; + p++; + } + // BUG: handle UTF PS and LS too + if (!*p || *p == '\r' || *p == '\n' && numdash >= 3) + inCode ^= 1; + pend = p; + } + + if (!inCode && isIdStart(p)) + { + unsigned char *q = p + utfStride(p); + while (isIdTail(q)) + q += utfStride(q); + if (*q == ':') // identifier: ends it + { idlen = q - p; + idstart = p; + for (pend = p; pend > pstart; pend--) + { if (pend[-1] == '\n') + break; + } + p = q + 1; + break; + } + } + while (1) + { + if (!*p) + goto L1; + if (*p == '\n') + { p++; + if (*p == '\n' && !summary && !namelen && !inCode) + { + pend = p; + p++; + goto L1; + } + break; + } + p++; + pend = p; + } + p = skipwhitespace(p); + } + L1: + + if (namelen || pstart < pend) + { + Section *s; + if (icmp("Params", name, namelen) == 0) + s = new ParamSection(); + else if (icmp("Macros", name, namelen) == 0) + s = new MacroSection(); + else + s = new Section(); + s->name = name; + s->namelen = namelen; + s->body = pstart; + s->bodylen = pend - pstart; + s->nooutput = 0; + + //printf("Section: '%.*s' = '%.*s'\n", s->namelen, s->name, s->bodylen, s->body); + + sections.push(s); + + if (!summary && !namelen) + summary = s; + } + + if (idlen) + { name = idstart; + namelen = idlen; + } + else + { name = NULL; + namelen = 0; + if (!*p) + break; + } + } +} + +void DocComment::writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf) +{ + //printf("DocComment::writeSections()\n"); + if (sections.dim) + { + buf->writestring("$(DDOC_SECTIONS \n"); + for (size_t i = 0; i < sections.dim; i++) + { Section *sec = sections[i]; + + if (sec->nooutput) + continue; + //printf("Section: '%.*s' = '%.*s'\n", sec->namelen, sec->name, sec->bodylen, sec->body); + if (sec->namelen || i) + sec->write(this, sc, s, buf); + else + { + buf->writestring("$(DDOC_SUMMARY "); + unsigned o = buf->offset; + buf->write(sec->body, sec->bodylen); + escapeStrayParenthesis(buf, o, s->loc); + highlightText(sc, s, buf, o); + buf->writestring(")\n"); + } + } + buf->writestring(")\n"); + } + else + { + buf->writestring("$(DDOC_BLANKLINE)\n"); + } +} + +/*************************************************** + */ + +void Section::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) +{ + if (namelen) + { + static const char *table[] = + { "AUTHORS", "BUGS", "COPYRIGHT", "DATE", + "DEPRECATED", "EXAMPLES", "HISTORY", "LICENSE", + "RETURNS", "SEE_ALSO", "STANDARDS", "THROWS", + "VERSION" }; + + for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++) + { + if (icmp(table[i], name, namelen) == 0) + { + buf->printf("$(DDOC_%s ", table[i]); + goto L1; + } + } + + buf->writestring("$(DDOC_SECTION "); + // Replace _ characters with spaces + buf->writestring("$(DDOC_SECTION_H "); + unsigned o = buf->offset; + for (unsigned u = 0; u < namelen; u++) + { unsigned char c = name[u]; + buf->writeByte((c == '_') ? ' ' : c); + } + escapeStrayParenthesis(buf, o, s->loc); + buf->writestring(":)\n"); + } + else + { + buf->writestring("$(DDOC_DESCRIPTION "); + } + L1: + unsigned o = buf->offset; + buf->write(body, bodylen); + escapeStrayParenthesis(buf, o, s->loc); + highlightText(sc, s, buf, o); + buf->writestring(")\n"); +} + +/*************************************************** + */ + +void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) +{ + unsigned char *p = body; + unsigned len = bodylen; + unsigned char *pend = p + len; + + unsigned char *tempstart; + unsigned templen; + + unsigned char *namestart; + unsigned namelen = 0; // !=0 if line continuation + + unsigned char *textstart; + unsigned textlen; + + unsigned o; + Parameter *arg; + + buf->writestring("$(DDOC_PARAMS \n"); + while (p < pend) + { + // Skip to start of macro + while (1) + { + switch (*p) + { + case ' ': + case '\t': + p++; + continue; + + case '\n': + p++; + goto Lcont; + + default: + if (isIdStart(p)) + break; + if (namelen) + goto Ltext; // continuation of prev macro + goto Lskipline; + } + break; + } + tempstart = p; + + while (isIdTail(p)) + p += utfStride(p); + templen = p - tempstart; + + while (*p == ' ' || *p == '\t') + p++; + + if (*p != '=') + { if (namelen) + goto Ltext; // continuation of prev macro + goto Lskipline; + } + p++; + + if (namelen) + { // Output existing param + + L1: + //printf("param '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart); + HdrGenState hgs; + buf->writestring("$(DDOC_PARAM_ROW "); + buf->writestring("$(DDOC_PARAM_ID "); + o = buf->offset; + arg = isFunctionParameter(s, namestart, namelen); + if (arg && arg->type && arg->ident) + arg->type->toCBuffer(buf, arg->ident, &hgs); + else + buf->write(namestart, namelen); + escapeStrayParenthesis(buf, o, s->loc); + highlightCode(sc, s, buf, o); + buf->writestring(")\n"); + + buf->writestring("$(DDOC_PARAM_DESC "); + o = buf->offset; + buf->write(textstart, textlen); + escapeStrayParenthesis(buf, o, s->loc); + highlightText(sc, s, buf, o); + buf->writestring(")"); + buf->writestring(")\n"); + namelen = 0; + if (p >= pend) + break; + } + + namestart = tempstart; + namelen = templen; + + while (*p == ' ' || *p == '\t') + p++; + textstart = p; + + Ltext: + while (*p != '\n') + p++; + textlen = p - textstart; + p++; + + Lcont: + continue; + + Lskipline: + // Ignore this line + while (*p++ != '\n') + ; + } + if (namelen) + goto L1; // write out last one + buf->writestring(")\n"); +} + +/*************************************************** + */ + +void MacroSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) +{ + //printf("MacroSection::write()\n"); + DocComment::parseMacros(dc->pescapetable, dc->pmacrotable, body, bodylen); +} + +/************************************************ + * Parse macros out of Macros: section. + * Macros are of the form: + * name1 = value1 + * + * name2 = value2 + */ + +void DocComment::parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen) +{ + unsigned char *p = m; + unsigned len = mlen; + unsigned char *pend = p + len; + + unsigned char *tempstart; + unsigned templen; + + unsigned char *namestart; + unsigned namelen = 0; // !=0 if line continuation + + unsigned char *textstart; + unsigned textlen; + + while (p < pend) + { + // Skip to start of macro + while (1) + { + if (p >= pend) + goto Ldone; + switch (*p) + { + case ' ': + case '\t': + p++; + continue; + + case '\n': + p++; + goto Lcont; + + default: + if (isIdStart(p)) + break; + if (namelen) + goto Ltext; // continuation of prev macro + goto Lskipline; + } + break; + } + tempstart = p; + + while (1) + { + if (p >= pend) + goto Ldone; + if (!isIdTail(p)) + break; + p += utfStride(p); + } + templen = p - tempstart; + + while (1) + { + if (p >= pend) + goto Ldone; + if (!(*p == ' ' || *p == '\t')) + break; + p++; + } + + if (*p != '=') + { if (namelen) + goto Ltext; // continuation of prev macro + goto Lskipline; + } + p++; + if (p >= pend) + goto Ldone; + + if (namelen) + { // Output existing macro + L1: + //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart); + if (icmp("ESCAPES", namestart, namelen) == 0) + parseEscapes(pescapetable, textstart, textlen); + else + Macro::define(pmacrotable, namestart, namelen, textstart, textlen); + namelen = 0; + if (p >= pend) + break; + } + + namestart = tempstart; + namelen = templen; + + while (p < pend && (*p == ' ' || *p == '\t')) + p++; + textstart = p; + + Ltext: + while (p < pend && *p != '\n') + p++; + textlen = p - textstart; + + // Remove trailing \r if there is one + if (p > m && p[-1] == '\r') + textlen--; + + p++; + //printf("p = %p, pend = %p\n", p, pend); + + Lcont: + continue; + + Lskipline: + // Ignore this line + while (p < pend && *p++ != '\n') + ; + } +Ldone: + if (namelen) + goto L1; // write out last one +} + +/************************************** + * Parse escapes of the form: + * /c/string/ + * where c is a single character. + * Multiple escapes can be separated + * by whitespace and/or commas. + */ + +void DocComment::parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen) +{ Escape *escapetable = *pescapetable; + + if (!escapetable) + { escapetable = new Escape; + *pescapetable = escapetable; + } + unsigned char *p = textstart; + unsigned char *pend = p + textlen; + + while (1) + { + while (1) + { + if (p + 4 >= pend) + return; + if (!(*p == ' ' || *p == '\t' || *p == '\n' || *p == ',')) + break; + p++; + } + if (p[0] != '/' || p[2] != '/') + return; + unsigned char c = p[1]; + p += 3; + unsigned char *start = p; + while (1) + { + if (p >= pend) + return; + if (*p == '/') + break; + p++; + } + size_t len = p - start; + char *s = (char *)memcpy(mem.malloc(len + 1), start, len); + s[len] = 0; + escapetable->strings[c] = s; + //printf("%c = '%s'\n", c, s); + p++; + } +} + + +/****************************************** + * Compare 0-terminated string with length terminated string. + * Return < 0, ==0, > 0 + */ + +int cmp(const char *stringz, void *s, size_t slen) +{ + size_t len1 = strlen(stringz); + + if (len1 != slen) + return len1 - slen; + return memcmp(stringz, s, slen); +} + +int icmp(const char *stringz, void *s, size_t slen) +{ + size_t len1 = strlen(stringz); + + if (len1 != slen) + return len1 - slen; + return memicmp(stringz, (char *)s, slen); +} + +/***************************************** + * Return !=0 if comment consists entirely of "ditto". + */ + +int isDitto(unsigned char *comment) +{ + if (comment) + { + unsigned char *p = skipwhitespace(comment); + + if (memicmp((char *)p, "ditto", 5) == 0 && *skipwhitespace(p + 5) == 0) + return 1; + } + return 0; +} + +/********************************************** + * Skip white space. + */ + +unsigned char *skipwhitespace(unsigned char *p) +{ + for (; 1; p++) + { switch (*p) + { + case ' ': + case '\t': + case '\n': + continue; + } + break; + } + return p; +} + + +/************************************************ + * Scan forward to one of: + * start of identifier + * beginning of next line + * end of buf + */ + +unsigned skiptoident(OutBuffer *buf, size_t i) +{ + while (i < buf->offset) + { dchar_t c; + + size_t oi = i; + if (utf_decodeChar((unsigned char *)buf->data, buf->offset, &i, &c)) + /* Ignore UTF errors, but still consume input + */ + break; + if (c >= 0x80) + { + if (!isUniAlpha(c)) + continue; + } + else if (!(isalpha(c) || c == '_' || c == '\n')) + continue; + i = oi; + break; + } + return i; +} + +/************************************************ + * Scan forward past end of identifier. + */ + +unsigned skippastident(OutBuffer *buf, size_t i) +{ + while (i < buf->offset) + { dchar_t c; + + size_t oi = i; + if (utf_decodeChar((unsigned char *)buf->data, buf->offset, &i, &c)) + /* Ignore UTF errors, but still consume input + */ + break; + if (c >= 0x80) + { + if (isUniAlpha(c)) + continue; + } + else if (isalnum(c) || c == '_') + continue; + i = oi; + break; + } + return i; +} + + +/************************************************ + * Scan forward past URL starting at i. + * We don't want to highlight parts of a URL. + * Returns: + * i if not a URL + * index just past it if it is a URL + */ + +unsigned skippastURL(OutBuffer *buf, size_t i) +{ unsigned length = buf->offset - i; + unsigned char *p = &buf->data[i]; + unsigned j; + unsigned sawdot = 0; + + if (length > 7 && memicmp((char *)p, "http://", 7) == 0) + { + j = 7; + } + else if (length > 8 && memicmp((char *)p, "https://", 8) == 0) + { + j = 8; + } + else + goto Lno; + + for (; j < length; j++) + { unsigned char c = p[j]; + if (isalnum(c)) + continue; + if (c == '-' || c == '_' || c == '?' || + c == '=' || c == '%' || c == '&' || + c == '/' || c == '+' || c == '#' || + c == '~') + continue; + if (c == '.') + { + sawdot = 1; + continue; + } + break; + } + if (sawdot) + return i + j; + +Lno: + return i; +} + + +/**************************************************** + */ + +int isKeyword(unsigned char *p, unsigned len) +{ + static const char *table[] = { "true", "false", "null" }; + + for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++) + { + if (cmp(table[i], p, len) == 0) + return 1; + } + return 0; +} + +/**************************************************** + */ + +Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len) +{ + FuncDeclaration *f = s->isFuncDeclaration(); + + /* f->type may be NULL for template members. + */ + if (f && f->type) + { + TypeFunction *tf; + if (f->originalType) + { + tf = (TypeFunction *)f->originalType; + } + else + tf = (TypeFunction *)f->type; + + if (tf->parameters) + { + for (size_t k = 0; k < tf->parameters->dim; k++) + { Parameter *arg = (*tf->parameters)[k]; + + if (arg->ident && cmp(arg->ident->toChars(), p, len) == 0) + { + return arg; + } + } + } + } + return NULL; +} + +/************************************************** + * Highlight text section. + */ + +void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) +{ + //printf("highlightText()\n"); + const char *sid = s->ident->toChars(); + FuncDeclaration *f = s->isFuncDeclaration(); + unsigned char *p; + const char *se; + + int leadingBlank = 1; + int inCode = 0; + int inComment = 0; // in comment + unsigned iCodeStart; // start of code section + + unsigned iLineStart = offset; + + for (unsigned i = offset; i < buf->offset; i++) + { unsigned char c = buf->data[i]; + + Lcont: + switch (c) + { + case ' ': + case '\t': + break; + + case '\n': + if (sc && !inCode && i == iLineStart && i + 1 < buf->offset) // if "\n\n" + { + static char blankline[] = "$(DDOC_BLANKLINE)\n"; + + i = buf->insert(i, blankline, sizeof(blankline) - 1); + } + leadingBlank = 1; + iLineStart = i + 1; + break; + + case '<': + leadingBlank = 0; + if (inCode) + break; + p = &buf->data[i]; + + // Skip over comments + if (p[1] == '!' && p[2] == '-' && p[3] == '-') + { unsigned j = i + 4; + p += 4; + while (1) + { + if (j == buf->offset) + goto L1; + if (p[0] == '-' && p[1] == '-' && p[2] == '>') + { + i = j + 2; // place on closing '>' + break; + } + j++; + p++; + } + break; + } + + // Skip over HTML tag + if (isalpha(p[1]) || (p[1] == '/' && isalpha(p[2]))) + { unsigned j = i + 2; + p += 2; + while (1) + { + if (j == buf->offset) + goto L1; + if (p[0] == '>') + { + i = j; // place on closing '>' + break; + } + j++; + p++; + } + break; + } + + L1: + // Replace '<' with '<' character entity + se = Escape::escapeChar('<'); + if (se) + { size_t len = strlen(se); + buf->remove(i, 1); + i = buf->insert(i, se, len); + i--; // point to ';' + } + break; + + case '>': + leadingBlank = 0; + if (inCode) + break; + // Replace '>' with '>' character entity + se = Escape::escapeChar('>'); + if (se) + { size_t len = strlen(se); + buf->remove(i, 1); + i = buf->insert(i, se, len); + i--; // point to ';' + } + break; + + case '&': + leadingBlank = 0; + if (inCode) + break; + p = &buf->data[i]; + if (p[1] == '#' || isalpha(p[1])) + break; // already a character entity + // Replace '&' with '&' character entity + se = Escape::escapeChar('&'); + if (se) + { size_t len = strlen(se); + buf->remove(i, 1); + i = buf->insert(i, se, len); + i--; // point to ';' + } + break; + + case '-': + /* A line beginning with --- delimits a code section. + * inCode tells us if it is start or end of a code section. + */ + if (leadingBlank) + { int istart = i; + int eollen = 0; + + leadingBlank = 0; + while (1) + { + ++i; + if (i >= buf->offset) + break; + c = buf->data[i]; + if (c == '\n') + { eollen = 1; + break; + } + if (c == '\r') + { + eollen = 1; + if (i + 1 >= buf->offset) + break; + if (buf->data[i + 1] == '\n') + { eollen = 2; + break; + } + } + // BUG: handle UTF PS and LS too + if (c != '-') + goto Lcont; + } + if (i - istart < 3) + goto Lcont; + + // We have the start/end of a code section + + // Remove the entire --- line, including blanks and \n + buf->remove(iLineStart, i - iLineStart + eollen); + i = iLineStart; + + if (inCode && (i <= iCodeStart)) + { // Empty code section, just remove it completely. + inCode = 0; + break; + } + + if (inCode) + { + inCode = 0; + // The code section is from iCodeStart to i + OutBuffer codebuf; + + codebuf.write(buf->data + iCodeStart, i - iCodeStart); + codebuf.writeByte(0); + highlightCode2(sc, s, &codebuf, 0); + buf->remove(iCodeStart, i - iCodeStart); + i = buf->insert(iCodeStart, codebuf.data, codebuf.offset); + i = buf->insert(i, ")\n", 2); + i--; + } + else + { static char pre[] = "$(D_CODE \n"; + + inCode = 1; + i = buf->insert(i, pre, sizeof(pre) - 1); + iCodeStart = i; + i--; // place i on > + leadingBlank = true; + } + } + break; + + default: + leadingBlank = 0; + if (sc && !inCode && isIdStart(&buf->data[i])) + { unsigned j; + + j = skippastident(buf, i); + if (j > i) + { + unsigned k = skippastURL(buf, i); + if (k > i) + { i = k - 1; + break; + } + + if (buf->data[i] == '_') // leading '_' means no highlight + { + buf->remove(i, 1); + i = j - 1; + } + else + { + if (cmp(sid, buf->data + i, j - i) == 0) + { + i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1; + break; + } + else if (isKeyword(buf->data + i, j - i)) + { + i = buf->bracket(i, "$(DDOC_KEYWORD ", j, ")") - 1; + break; + } + else + { + if (f && isFunctionParameter(f, buf->data + i, j - i)) + { + //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); + i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1; + break; + } + } + i = j - 1; + } + } + } + break; + } + } + if (inCode) + s->error("unmatched --- in DDoc comment"); + ; +} + +/************************************************** + * Highlight code for DDOC section. + */ + +void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) +{ + char *sid = s->ident->toChars(); + FuncDeclaration *f = s->isFuncDeclaration(); + + //printf("highlightCode(s = '%s', kind = %s)\n", sid, s->kind()); + for (unsigned i = offset; i < buf->offset; i++) + { unsigned char c = buf->data[i]; + const char *se; + + se = Escape::escapeChar(c); + if (se) + { + size_t len = strlen(se); + buf->remove(i, 1); + i = buf->insert(i, se, len); + i--; // point to ';' + } + else if (isIdStart(&buf->data[i])) + { unsigned j; + + j = skippastident(buf, i); + if (j > i) + { + if (cmp(sid, buf->data + i, j - i) == 0) + { + i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1; + continue; + } + else if (f) + { + if (isFunctionParameter(f, buf->data + i, j - i)) + { + //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); + i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1; + continue; + } + } + i = j - 1; + } + } + } +} + +/**************************************** + */ + +void highlightCode3(OutBuffer *buf, unsigned char *p, unsigned char *pend) +{ + for (; p < pend; p++) + { const char *s = Escape::escapeChar(*p); + if (s) + buf->writestring(s); + else + buf->writeByte(*p); + } +} + +/************************************************** + * Highlight code for CODE section. + */ + + +void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) +{ + char *sid = s->ident->toChars(); + FuncDeclaration *f = s->isFuncDeclaration(); + unsigned errorsave = global.errors; + Lexer lex(NULL, buf->data, 0, buf->offset - 1, 0, 1); + Token tok; + OutBuffer res; + unsigned char *lastp = buf->data; + const char *highlight; + + //printf("highlightCode2('%.*s')\n", buf->offset - 1, buf->data); + res.reserve(buf->offset); + while (1) + { + lex.scan(&tok); + highlightCode3(&res, lastp, tok.ptr); + highlight = NULL; + switch (tok.value) + { + case TOKidentifier: + if (!sc) + break; + if (cmp(sid, tok.ptr, lex.p - tok.ptr) == 0) + { + highlight = "$(D_PSYMBOL "; + break; + } + else if (f) + { + if (isFunctionParameter(f, tok.ptr, lex.p - tok.ptr)) + { + //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); + highlight = "$(D_PARAM "; + break; + } + } + break; + + case TOKcomment: + highlight = "$(D_COMMENT "; + break; + + case TOKstring: + highlight = "$(D_STRING "; + break; + + default: + if (tok.isKeyword()) + highlight = "$(D_KEYWORD "; + break; + } + if (highlight) + res.writestring(highlight); + highlightCode3(&res, tok.ptr, lex.p); + if (highlight) + res.writeByte(')'); + if (tok.value == TOKeof) + break; + lastp = lex.p; + } + buf->setsize(offset); + buf->write(&res); + global.errors = errorsave; +} + +/*************************************** + * Find character string to replace c with. + */ + +const char *Escape::escapeChar(unsigned c) +{ const char *s; + + switch (c) + { + case '<': + s = "<"; + break; + case '>': + s = ">"; + break; + case '&': + s = "&"; + break; + default: + s = NULL; + break; + } + return s; +} + +/**************************************** + * Determine if p points to the start of an identifier. + */ + +int isIdStart(unsigned char *p) +{ + unsigned c = *p; + if (isalpha(c) || c == '_') + return 1; + if (c >= 0x80) + { size_t i = 0; + if (utf_decodeChar(p, 4, &i, &c)) + return 0; // ignore errors + if (isUniAlpha(c)) + return 1; + } + return 0; +} + +/**************************************** + * Determine if p points to the rest of an identifier. + */ + +int isIdTail(unsigned char *p) +{ + unsigned c = *p; + if (isalnum(c) || c == '_') + return 1; + if (c >= 0x80) + { size_t i = 0; + if (utf_decodeChar(p, 4, &i, &c)) + return 0; // ignore errors + if (isUniAlpha(c)) + return 1; + } + return 0; +} + +/***************************************** + * Return number of bytes in UTF character. + */ + +int utfStride(unsigned char *p) +{ + unsigned c = *p; + if (c < 0x80) + return 1; + size_t i = 0; + utf_decodeChar(p, 4, &i, &c); // ignore errors, but still consume input + return i; +} diff --git a/doc.h b/doc.h new file mode 100644 index 00000000..ffa97fb9 --- /dev/null +++ b/doc.h @@ -0,0 +1,20 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_DOC_H +#define DMD_DOC_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +void escapeDdocString(OutBuffer *buf, unsigned start); + +#endif diff --git a/dsymbol.c b/dsymbol.c new file mode 100644 index 00000000..be460198 --- /dev/null +++ b/dsymbol.c @@ -0,0 +1,1451 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include + +#include "rmem.h" +#include "speller.h" +#include "aav.h" + +#include "mars.h" +#include "dsymbol.h" +#include "aggregate.h" +#include "identifier.h" +#include "module.h" +#include "mtype.h" +#include "expression.h" +#include "statement.h" +#include "declaration.h" +#include "id.h" +#include "scope.h" +#include "init.h" +#include "import.h" +#include "template.h" +#include "attrib.h" + +/****************************** Dsymbol ******************************/ + +Dsymbol::Dsymbol() +{ + //printf("Dsymbol::Dsymbol(%p)\n", this); + this->ident = NULL; + this->c_ident = NULL; + this->parent = NULL; + this->csym = NULL; + this->isym = NULL; + this->loc = 0; + this->comment = NULL; + this->scope = NULL; +} + +Dsymbol::Dsymbol(Identifier *ident) +{ + //printf("Dsymbol::Dsymbol(%p, ident)\n", this); + this->ident = ident; + this->c_ident = NULL; + this->parent = NULL; + this->csym = NULL; + this->isym = NULL; + this->loc = 0; + this->comment = NULL; + this->scope = NULL; +} + +int Dsymbol::equals(Object *o) +{ Dsymbol *s; + + if (this == o) + return TRUE; + s = (Dsymbol *)(o); + // Overload sets don't have an ident + if (s && ident && s->ident && ident->equals(s->ident)) + return TRUE; + return FALSE; +} + +/************************************** + * Copy the syntax. + * Used for template instantiations. + * If s is NULL, allocate the new object, otherwise fill it in. + */ + +Dsymbol *Dsymbol::syntaxCopy(Dsymbol *s) +{ + print(); + printf("%s %s\n", kind(), toChars()); + assert(0); + return NULL; +} + +/************************************** + * Determine if this symbol is only one. + * Returns: + * FALSE, *ps = NULL: There are 2 or more symbols + * TRUE, *ps = NULL: There are zero symbols + * TRUE, *ps = symbol: The one and only one symbol + */ + +int Dsymbol::oneMember(Dsymbol **ps, Identifier *ident) +{ + //printf("Dsymbol::oneMember()\n"); + *ps = this; + return TRUE; +} + +/***************************************** + * Same as Dsymbol::oneMember(), but look at an array of Dsymbols. + */ + +int Dsymbol::oneMembers(Dsymbols *members, Dsymbol **ps, Identifier *ident) +{ + //printf("Dsymbol::oneMembers() %d\n", members ? members->dim : 0); + Dsymbol *s = NULL; + + if (members) + { + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *sx = (*members)[i]; + + int x = sx->oneMember(ps, ident); + //printf("\t[%d] kind %s = %d, s = %p\n", i, sx->kind(), x, *ps); + if (!x) + { + //printf("\tfalse 1\n"); + assert(*ps == NULL); + return FALSE; + } + if (*ps) + { + if (ident) + { + if (!(*ps)->ident || !(*ps)->ident->equals(ident)) + continue; + } + if (s) // more than one symbol + { *ps = NULL; + //printf("\tfalse 2\n"); + return FALSE; + } + s = *ps; + } + } + } + *ps = s; // s is the one symbol, NULL if none + //printf("\ttrue\n"); + return TRUE; +} + +/***************************************** + * Is Dsymbol a variable that contains pointers? + */ + +int Dsymbol::hasPointers() +{ + //printf("Dsymbol::hasPointers() %s\n", toChars()); + return 0; +} + +bool Dsymbol::hasStaticCtorOrDtor() +{ + //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars()); + return FALSE; +} + +char *Dsymbol::toChars() +{ + return ident ? ident->toChars() : (char *)"__anonymous"; +} + +const char *Dsymbol::toPrettyChars() +{ Dsymbol *p; + char *s; + char *q; + size_t len; + + //printf("Dsymbol::toPrettyChars() '%s'\n", toChars()); + if (!parent) + return toChars(); + + len = 0; + for (p = this; p; p = p->parent) + len += strlen(p->toChars()) + 1; + + s = (char *)mem.malloc(len); + q = s + len - 1; + *q = 0; + for (p = this; p; p = p->parent) + { + char *t = p->toChars(); + len = strlen(t); + q -= len; + memcpy(q, t, len); + if (q == s) + break; + q--; +#if TARGET_NET + if (AggregateDeclaration* ad = p->isAggregateDeclaration()) + { + if (ad->isNested() && p->parent && p->parent->isAggregateDeclaration()) + { + *q = '/'; + continue; + } + } +#endif + *q = '.'; + } + return s; +} + +char *Dsymbol::locToChars() +{ + OutBuffer buf; + + if (!loc.filename) // avoid bug 5861. + { + Module *m = getModule(); + + if (m && m->srcfile) + loc.filename = m->srcfile->toChars(); + } + return loc.toChars(); +} + +const char *Dsymbol::kind() +{ + return "symbol"; +} + +/********************************* + * If this symbol is really an alias for another, + * return that other. + */ + +Dsymbol *Dsymbol::toAlias() +{ + return this; +} + +Dsymbol *Dsymbol::toParent() +{ + return parent ? parent->pastMixin() : NULL; +} + +Dsymbol *Dsymbol::pastMixin() +{ + Dsymbol *s = this; + + //printf("Dsymbol::pastMixin() %s\n", toChars()); + while (s && s->isTemplateMixin()) + s = s->parent; + return s; +} + +/********************************** + * Use this instead of toParent() when looking for the + * 'this' pointer of the enclosing function/class. + */ + +Dsymbol *Dsymbol::toParent2() +{ + Dsymbol *s = parent; + while (s && s->isTemplateInstance()) + s = s->parent; + return s; +} + +TemplateInstance *Dsymbol::inTemplateInstance() +{ + for (Dsymbol *parent = this->parent; parent; parent = parent->parent) + { + TemplateInstance *ti = parent->isTemplateInstance(); + if (ti) + return ti; + } + return NULL; +} + +int Dsymbol::isAnonymous() +{ + return ident ? 0 : 1; +} + +/************************************* + * Set scope for future semantic analysis so we can + * deal better with forward references. + */ + +void Dsymbol::setScope(Scope *sc) +{ + //printf("Dsymbol::setScope() %p %s\n", this, toChars()); + if (!sc->nofree) + sc->setNoFree(); // may need it even after semantic() finishes + scope = sc; +} + +void Dsymbol::importAll(Scope *sc) +{ +} + +/************************************* + * Does semantic analysis on the public face of declarations. + */ + +void Dsymbol::semantic0(Scope *sc) +{ +} + +void Dsymbol::semantic(Scope *sc) +{ + error("%p has no semantic routine", this); +} + +/************************************* + * Does semantic analysis on initializers and members of aggregates. + */ + +void Dsymbol::semantic2(Scope *sc) +{ + // Most Dsymbols have no further semantic analysis needed +} + +/************************************* + * Does semantic analysis on function bodies. + */ + +void Dsymbol::semantic3(Scope *sc) +{ + // Most Dsymbols have no further semantic analysis needed +} + +/************************************* + * Look for function inlining possibilities. + */ + +void Dsymbol::inlineScan() +{ + // Most Dsymbols aren't functions +} + +/********************************************* + * Search for ident as member of s. + * Input: + * flags: 1 don't find private members + * 2 don't give error messages + * 4 return NULL if ambiguous + * Returns: + * NULL if not found + */ + +Dsymbol *Dsymbol::search(Loc loc, Identifier *ident, int flags) +{ + //printf("Dsymbol::search(this=%p,%s, ident='%s')\n", this, toChars(), ident->toChars()); + return NULL; +} + +/*************************************************** + * Search for symbol with correct spelling. + */ + +void *symbol_search_fp(void *arg, const char *seed) +{ + Dsymbol *s = (Dsymbol *)arg; + Identifier id(seed, 0); + Module::clearCache(); + s = s->search(0, &id, 4|2); + return s; +} + +Dsymbol *Dsymbol::search_correct(Identifier *ident) +{ + if (global.gag) + return NULL; // don't do it for speculative compiles; too time consuming + + return (Dsymbol *)speller(ident->toChars(), &symbol_search_fp, this, idchars); +} + +/*************************************** + * Search for identifier id as a member of 'this'. + * id may be a template instance. + * Returns: + * symbol found, NULL if not + */ + +Dsymbol *Dsymbol::searchX(Loc loc, Scope *sc, Identifier *id) +{ + //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident->toChars()); + Dsymbol *s = toAlias(); + Dsymbol *sm; + + switch (id->dyncast()) + { + case DYNCAST_IDENTIFIER: + sm = s->search(loc, id, 0); + break; + + case DYNCAST_DSYMBOL: + { // It's a template instance + //printf("\ttemplate instance id\n"); + Dsymbol *st = (Dsymbol *)id; + TemplateInstance *ti = st->isTemplateInstance(); + id = ti->name; + sm = s->search(loc, id, 0); + if (!sm) + { error("template identifier %s is not a member of %s %s", + id->toChars(), s->kind(), s->toChars()); + return NULL; + } + sm = sm->toAlias(); + TemplateDeclaration *td = sm->isTemplateDeclaration(); + if (!td) + { + error("%s is not a template, it is a %s", id->toChars(), sm->kind()); + return NULL; + } + ti->tempdecl = td; + if (!ti->semanticRun) + ti->semantic(sc); + sm = ti->toAlias(); + break; + } + + default: + assert(0); + } + return sm; +} + +int Dsymbol::overloadInsert(Dsymbol *s) +{ + //printf("Dsymbol::overloadInsert('%s')\n", s->toChars()); + return FALSE; +} + +void Dsymbol::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(toChars()); +} + +unsigned Dsymbol::size(Loc loc) +{ + error("Dsymbol '%s' has no size\n", toChars()); + return 0; +} + +int Dsymbol::isforwardRef() +{ + return FALSE; +} + +AggregateDeclaration *Dsymbol::isThis() +{ + return NULL; +} + +AggregateDeclaration *Dsymbol::isAggregateMember() // are we a member of an aggregate? +{ + Dsymbol *parent = toParent(); + if (parent && parent->isAggregateDeclaration()) + return (AggregateDeclaration *)parent; + return NULL; +} + +ClassDeclaration *Dsymbol::isClassMember() // are we a member of a class? +{ + AggregateDeclaration *ad = isAggregateMember(); + return ad ? ad->isClassDeclaration() : NULL; +} + +void Dsymbol::defineRef(Dsymbol *s) +{ + assert(0); +} + +int Dsymbol::isExport() +{ + return FALSE; +} + +int Dsymbol::isImportedSymbol() +{ + return FALSE; +} + +int Dsymbol::isDeprecated() +{ + return FALSE; +} + +#if DMDV2 +int Dsymbol::isOverloadable() +{ + return 0; +} +#endif + +LabelDsymbol *Dsymbol::isLabel() // is this a LabelDsymbol()? +{ + return NULL; +} + +AggregateDeclaration *Dsymbol::isMember() // is this a member of an AggregateDeclaration? +{ + //printf("Dsymbol::isMember() %s\n", toChars()); + Dsymbol *parent = toParent(); + //printf("parent is %s %s\n", parent->kind(), parent->toChars()); + return parent ? parent->isAggregateDeclaration() : NULL; +} + +Type *Dsymbol::getType() +{ + return NULL; +} + +int Dsymbol::needThis() +{ + return FALSE; +} + +int Dsymbol::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +{ + //printf("Dsymbol::addMember('%s')\n", toChars()); + //printf("Dsymbol::addMember(this = %p, '%s' scopesym = '%s')\n", this, toChars(), sd->toChars()); + //printf("Dsymbol::addMember(this = %p, '%s' sd = %p, sd->symtab = %p)\n", this, toChars(), sd, sd->symtab); + parent = sd; + if (!isAnonymous()) // no name, so can't add it to symbol table + { + if (!sd->symtabInsert(this)) // if name is already defined + { + Dsymbol *s2; + + s2 = sd->symtab->lookup(ident); + if (!s2->overloadInsert(this)) + { + sd->multiplyDefined(0, this, s2); + } + } + if (sd->isAggregateDeclaration() || sd->isEnumDeclaration()) + { + if (ident == Id::__sizeof || ident == Id::__xalignof || ident == Id::mangleof) + error(".%s property cannot be redefined", ident->toChars()); + } + return 1; + } + return 0; +} + +void Dsymbol::error(const char *format, ...) +{ + //printf("Dsymbol::error()\n"); + if (!loc.filename) // avoid bug 5861. + { + Module *m = getModule(); + + if (m && m->srcfile) + loc.filename = m->srcfile->toChars(); + } + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); +} + +void Dsymbol::error(Loc loc, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); +} + +void Dsymbol::verror(Loc loc, const char *format, va_list ap) +{ + if (!global.gag) + { + char *p = loc.toChars(); + if (!*p) + p = locToChars(); + + if (*p) + fprintf(stdmsg, "%s: ", p); + mem.free(p); + + fprintf(stdmsg, "Error: "); + fprintf(stdmsg, "%s %s ", kind(), toPrettyChars()); + + vfprintf(stdmsg, format, ap); + + fprintf(stdmsg, "\n"); + fflush(stdmsg); +//halt(); + } + else + { + global.gaggedErrors++; + } + + global.errors++; + + //fatal(); +} + +void Dsymbol::checkDeprecated(Loc loc, Scope *sc) +{ + if (!global.params.useDeprecated && isDeprecated()) + { + // Don't complain if we're inside a deprecated symbol's scope + for (Dsymbol *sp = sc->parent; sp; sp = sp->parent) + { if (sp->isDeprecated()) + goto L1; + } + + for (Scope *sc2 = sc; sc2; sc2 = sc2->enclosing) + { + if (sc2->scopesym && sc2->scopesym->isDeprecated()) + goto L1; + + // If inside a StorageClassDeclaration that is deprecated + if (sc2->stc & STCdeprecated) + goto L1; + } + + error(loc, "is deprecated"); + } + + L1: + Declaration *d = isDeclaration(); + if (d && d->storage_class & STCdisable) + { + if (!(sc->func && sc->func->storage_class & STCdisable)) + { + if (d->ident == Id::cpctor && d->toParent()) + d->toParent()->error(loc, "is not copyable because it is annotated with @disable"); + else + error(loc, "is not callable because it is annotated with @disable"); + } + } +} + +/********************************** + * Determine which Module a Dsymbol is in. + */ + +Module *Dsymbol::getModule() +{ + //printf("Dsymbol::getModule()\n"); + TemplateDeclaration *td = getFuncTemplateDecl(this); + if (td) + return td->getModule(); + + Dsymbol *s = this; + while (s) + { + //printf("\ts = %s '%s'\n", s->kind(), s->toPrettyChars()); + Module *m = s->isModule(); + if (m) + return m; + s = s->parent; + } + return NULL; +} + +/********************************** + * Determine which Module a Dsymbol is in, as far as access rights go. + */ + +Module *Dsymbol::getAccessModule() +{ + //printf("Dsymbol::getAccessModule()\n"); + TemplateDeclaration *td = getFuncTemplateDecl(this); + if (td) + return td->getAccessModule(); + + Dsymbol *s = this; + while (s) + { + //printf("\ts = %s '%s'\n", s->kind(), s->toPrettyChars()); + Module *m = s->isModule(); + if (m) + return m; + TemplateInstance *ti = s->isTemplateInstance(); + if (ti && ti->isnested) + /* Because of local template instantiation, the parent isn't where the access + * rights come from - it's the template declaration + */ + s = ti->tempdecl; + else + s = s->parent; + } + return NULL; +} + +/************************************* + */ + +enum PROT Dsymbol::prot() +{ + return PROTpublic; +} + +/************************************* + * Do syntax copy of an array of Dsymbol's. + */ + + +Dsymbols *Dsymbol::arraySyntaxCopy(Dsymbols *a) +{ + + Dsymbols *b = NULL; + if (a) + { + b = a->copy(); + for (size_t i = 0; i < b->dim; i++) + { + Dsymbol *s = (*b)[i]; + + s = s->syntaxCopy(NULL); + (*b)[i] = s; + } + } + return b; +} + + +/**************************************** + * Add documentation comment to Dsymbol. + * Ignore NULL comments. + */ + +void Dsymbol::addComment(unsigned char *comment) +{ + //if (comment) + //printf("adding comment '%s' to symbol %p '%s'\n", comment, this, toChars()); + + if (!this->comment) + this->comment = comment; +#if 1 + else if (comment && strcmp((char *)comment, (char *)this->comment)) + { // Concatenate the two + this->comment = Lexer::combineComments(this->comment, comment); + } +#endif +} + +/********************************* OverloadSet ****************************/ + +#if DMDV2 +OverloadSet::OverloadSet() + : Dsymbol() +{ +} + +void OverloadSet::push(Dsymbol *s) +{ + a.push(s); +} + +const char *OverloadSet::kind() +{ + return "overloadset"; +} +#endif + + +/********************************* ScopeDsymbol ****************************/ + +ScopeDsymbol::ScopeDsymbol() + : Dsymbol() +{ + members = NULL; + symtab = NULL; + imports = NULL; + prots = NULL; +} + +ScopeDsymbol::ScopeDsymbol(Identifier *id) + : Dsymbol(id) +{ + members = NULL; + symtab = NULL; + imports = NULL; + prots = NULL; +} + +Dsymbol *ScopeDsymbol::syntaxCopy(Dsymbol *s) +{ + //printf("ScopeDsymbol::syntaxCopy('%s')\n", toChars()); + + ScopeDsymbol *sd; + if (s) + sd = (ScopeDsymbol *)s; + else + sd = new ScopeDsymbol(ident); + sd->members = arraySyntaxCopy(members); + return sd; +} + +Dsymbol *ScopeDsymbol::search(Loc loc, Identifier *ident, int flags) +{ + //printf("%s->ScopeDsymbol::search(ident='%s', flags=x%x)\n", toChars(), ident->toChars(), flags); + //if (strcmp(ident->toChars(),"c") == 0) *(char*)0=0; + + // Look in symbols declared in this module + Dsymbol *s = symtab ? symtab->lookup(ident) : NULL; + //printf("\ts = %p, imports = %p, %d\n", s, imports, imports ? imports->dim : 0); + if (s) + { + //printf("\ts = '%s.%s'\n",toChars(),s->toChars()); + } + else if (imports) + { + OverloadSet *a = NULL; + + // Look in imported modules + for (size_t i = 0; i < imports->dim; i++) + { Dsymbol *ss = (*imports)[i]; + Dsymbol *s2; + + // If private import, don't search it + if (flags & 1 && prots[i] == PROTprivate) + continue; + + //printf("\tscanning import '%s', prots = %d, isModule = %p, isImport = %p\n", ss->toChars(), prots[i], ss->isModule(), ss->isImport()); + /* Don't find private members if ss is a module + */ + s2 = ss->search(loc, ident, ss->isModule() ? 1 : 0); + if (!s) + s = s2; + else if (s2 && s != s2) + { + if (s->toAlias() == s2->toAlias()) + { + /* After following aliases, we found the same + * symbol, so it's not an ambiguity. But if one + * alias is deprecated or less accessible, prefer + * the other. + */ + if (s->isDeprecated() || + s2->prot() > s->prot() && s2->prot() != PROTnone) + s = s2; + } + else + { + /* Two imports of the same module should be regarded as + * the same. + */ + Import *i1 = s->isImport(); + Import *i2 = s2->isImport(); + if (!(i1 && i2 && + (i1->mod == i2->mod || + (!i1->parent->isImport() && !i2->parent->isImport() && + i1->ident->equals(i2->ident)) + ) + ) + ) + { + /* If both s2 and s are overloadable (though we only + * need to check s once) + */ + if (s2->isOverloadable() && (a || s->isOverloadable())) + { if (!a) + a = new OverloadSet(); + /* Don't add to a[] if s2 is alias of previous sym + */ + for (size_t j = 0; j < a->a.dim; j++) + { Dsymbol *s3 = a->a[j]; + if (s2->toAlias() == s3->toAlias()) + { + if (s3->isDeprecated() || + s2->prot() > s3->prot() && s2->prot() != PROTnone) + a->a[j] = s2; + goto Lcontinue; + } + } + a->push(s2); + Lcontinue: + continue; + } + if (flags & 4) // if return NULL on ambiguity + return NULL; + if (!(flags & 2)) + ScopeDsymbol::multiplyDefined(loc, s, s2); + break; + } + } + } + } + + /* Build special symbol if we had multiple finds + */ + if (a) + { assert(s); + a->push(s); + s = a; + } + + if (s) + { + Declaration *d = s->isDeclaration(); + if (d && d->protection == PROTprivate && + !d->parent->isTemplateMixin() && + !(flags & 2)) + error(loc, "%s is private", d->toPrettyChars()); + } + } + return s; +} + +void ScopeDsymbol::importScope(Dsymbol *s, enum PROT protection) +{ + //printf("%s->ScopeDsymbol::importScope(%s, %d)\n", toChars(), s->toChars(), protection); + + // No circular or redundant import's + if (s != this) + { + if (!imports) + imports = new Dsymbols(); + else + { + for (size_t i = 0; i < imports->dim; i++) + { Dsymbol *ss = (*imports)[i]; + if (ss == s) // if already imported + { + if (protection > prots[i]) + prots[i] = protection; // upgrade access + return; + } + } + } + imports->push(s); + prots = (unsigned char *)mem.realloc(prots, imports->dim * sizeof(prots[0])); + prots[imports->dim - 1] = protection; + } +} + +int ScopeDsymbol::isforwardRef() +{ + return (members == NULL); +} + +void ScopeDsymbol::defineRef(Dsymbol *s) +{ + ScopeDsymbol *ss; + + ss = s->isScopeDsymbol(); + members = ss->members; + ss->members = NULL; +} + +void ScopeDsymbol::multiplyDefined(Loc loc, Dsymbol *s1, Dsymbol *s2) +{ +#if 0 + printf("ScopeDsymbol::multiplyDefined()\n"); + printf("s1 = %p, '%s' kind = '%s', parent = %s\n", s1, s1->toChars(), s1->kind(), s1->parent ? s1->parent->toChars() : ""); + printf("s2 = %p, '%s' kind = '%s', parent = %s\n", s2, s2->toChars(), s2->kind(), s2->parent ? s2->parent->toChars() : ""); +#endif + if (loc.filename) + { ::error(loc, "%s at %s conflicts with %s at %s", + s1->toPrettyChars(), + s1->locToChars(), + s2->toPrettyChars(), + s2->locToChars()); + } + else + { + s1->error(loc, "conflicts with %s %s at %s", + s2->kind(), + s2->toPrettyChars(), + s2->locToChars()); + } +} + +Dsymbol *ScopeDsymbol::nameCollision(Dsymbol *s) +{ + Dsymbol *sprev; + + // Look to see if we are defining a forward referenced symbol + + sprev = symtab->lookup(s->ident); + assert(sprev); + if (s->equals(sprev)) // if the same symbol + { + if (s->isforwardRef()) // if second declaration is a forward reference + return sprev; + if (sprev->isforwardRef()) + { + sprev->defineRef(s); // copy data from s into sprev + return sprev; + } + } + multiplyDefined(0, s, sprev); + return sprev; +} + +const char *ScopeDsymbol::kind() +{ + return "ScopeDsymbol"; +} + +Dsymbol *ScopeDsymbol::symtabInsert(Dsymbol *s) +{ + return symtab->insert(s); +} + +/**************************************** + * Return true if any of the members are static ctors or static dtors, or if + * any members have members that are. + */ + +bool ScopeDsymbol::hasStaticCtorOrDtor() +{ + if (members) + { + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *member = (*members)[i]; + + if (member->hasStaticCtorOrDtor()) + return TRUE; + } + } + return FALSE; +} + +/*************************************** + * Determine number of Dsymbols, folding in AttribDeclaration members. + */ + +#if DMDV2 +static int dimDg(void *ctx, size_t n, Dsymbol *) +{ + ++*(size_t *)ctx; + return 0; +} + +size_t ScopeDsymbol::dim(Dsymbols *members) +{ + size_t n = 0; + foreach(members, &dimDg, &n); + return n; +} +#endif + +/*************************************** + * Get nth Dsymbol, folding in AttribDeclaration members. + * Returns: + * Dsymbol* nth Dsymbol + * NULL not found, *pn gets incremented by the number + * of Dsymbols + */ + +#if DMDV2 +struct GetNthSymbolCtx +{ + size_t nth; + Dsymbol *sym; +}; + +static int getNthSymbolDg(void *ctx, size_t n, Dsymbol *sym) +{ + GetNthSymbolCtx *p = (GetNthSymbolCtx *)ctx; + if (n == p->nth) + { p->sym = sym; + return 1; + } + return 0; +} + +Dsymbol *ScopeDsymbol::getNth(Dsymbols *members, size_t nth, size_t *pn) +{ + GetNthSymbolCtx ctx = { nth, NULL }; + int res = foreach(members, &getNthSymbolDg, &ctx); + return res ? ctx.sym : NULL; +} +#endif + +/*************************************** + * Expands attribute declarations in members in depth first + * order. Calls dg(void *ctx, size_t symidx, Dsymbol *sym) for each + * member. + * If dg returns !=0, stops and returns that value else returns 0. + * Use this function to avoid the O(N + N^2/2) complexity of + * calculating dim and calling N times getNth. + */ + +#if DMDV2 +int ScopeDsymbol::foreach(Dsymbols *members, ScopeDsymbol::ForeachDg dg, void *ctx, size_t *pn) +{ + assert(dg); + if (!members) + return 0; + + size_t n = pn ? *pn : 0; // take over index + int result = 0; + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = (*members)[i]; + + if (AttribDeclaration *a = s->isAttribDeclaration()) + result = foreach(a->decl, dg, ctx, &n); + else if (TemplateMixin *tm = s->isTemplateMixin()) + result = foreach(tm->members, dg, ctx, &n); + else if (s->isTemplateInstance()) + ; + else + result = dg(ctx, n++, s); + + if (result) + break; + } + + if (pn) + *pn = n; // update index + return result; +} +#endif + +/******************************************* + * Look for member of the form: + * const(MemberInfo)[] getMembers(string); + * Returns NULL if not found + */ + +#if DMDV2 +FuncDeclaration *ScopeDsymbol::findGetMembers() +{ + Dsymbol *s = search_function(this, Id::getmembers); + FuncDeclaration *fdx = s ? s->isFuncDeclaration() : NULL; + +#if 0 // Finish + static TypeFunction *tfgetmembers; + + if (!tfgetmembers) + { + Scope sc; + Parameters *arguments = new Parameters; + Parameters *arg = new Parameter(STCin, Type::tchar->constOf()->arrayOf(), NULL, NULL); + arguments->push(arg); + + Type *tret = NULL; + tfgetmembers = new TypeFunction(arguments, tret, 0, LINKd); + tfgetmembers = (TypeFunction *)tfgetmembers->semantic(0, &sc); + } + if (fdx) + fdx = fdx->overloadExactMatch(tfgetmembers); +#endif + if (fdx && fdx->isVirtual()) + fdx = NULL; + + return fdx; +} +#endif + + +/****************************** WithScopeSymbol ******************************/ + +WithScopeSymbol::WithScopeSymbol(WithStatement *withstate) + : ScopeDsymbol() +{ + this->withstate = withstate; +} + +Dsymbol *WithScopeSymbol::search(Loc loc, Identifier *ident, int flags) +{ + // Acts as proxy to the with class declaration + return withstate->exp->type->toDsymbol(NULL)->search(loc, ident, 0); +} + +/****************************** ArrayScopeSymbol ******************************/ + +ArrayScopeSymbol::ArrayScopeSymbol(Scope *sc, Expression *e) + : ScopeDsymbol() +{ + assert(e->op == TOKindex || e->op == TOKslice || e->op == TOKarray); + exp = e; + type = NULL; + td = NULL; + this->sc = sc; +} + +ArrayScopeSymbol::ArrayScopeSymbol(Scope *sc, TypeTuple *t) + : ScopeDsymbol() +{ + exp = NULL; + type = t; + td = NULL; + this->sc = sc; +} + +ArrayScopeSymbol::ArrayScopeSymbol(Scope *sc, TupleDeclaration *s) + : ScopeDsymbol() +{ + exp = NULL; + type = NULL; + td = s; + this->sc = sc; +} + +Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) +{ + //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident->toChars(), flags); + if (ident == Id::length || ident == Id::dollar) + { VarDeclaration **pvar; + Expression *ce; + + if (ident == Id::length && !global.params.useDeprecated) + error("using 'length' inside [ ] is deprecated, use '$' instead"); + + L1: + + if (td) + { /* $ gives the number of elements in the tuple + */ + VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); + Expression *e = new IntegerExp(0, td->objects->dim, Type::tsize_t); + v->init = new ExpInitializer(0, e); + v->storage_class |= STCstatic | STCconst; + v->semantic(sc); + return v; + } + + if (type) + { /* $ gives the number of type entries in the type tuple + */ + VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); + Expression *e = new IntegerExp(0, type->arguments->dim, Type::tsize_t); + v->init = new ExpInitializer(0, e); + v->storage_class |= STCstatic | STCconst; + v->semantic(sc); + return v; + } + + if (exp->op == TOKindex) + { /* array[index] where index is some function of $ + */ + IndexExp *ie = (IndexExp *)exp; + + pvar = &ie->lengthVar; + ce = ie->e1; + } + else if (exp->op == TOKslice) + { /* array[lwr .. upr] where lwr or upr is some function of $ + */ + SliceExp *se = (SliceExp *)exp; + + pvar = &se->lengthVar; + ce = se->e1; + } + else if (exp->op == TOKarray) + { /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $ + * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...) + */ + ArrayExp *ae = (ArrayExp *)exp; + AggregateDeclaration *ad = NULL; + + Type *t = ae->e1->type->toBasetype(); + if (t->ty == Tclass) + { + ad = ((TypeClass *)t)->sym; + } + else if (t->ty == Tstruct) + { + ad = ((TypeStruct *)t)->sym; + } + assert(ad); + + Dsymbol *dsym = search_function(ad, Id::opDollar); + if (!dsym) // no dollar exists -- search in higher scope + return NULL; + VarDeclaration *v = ae->lengthVar; + if (!v) + { // $ is lazily initialized. Create it now. + TemplateDeclaration *td = dsym->isTemplateDeclaration(); + if (td) + { // Instantiate opDollar!(dim) with the index as a template argument + Objects *tdargs = new Objects(); + tdargs->setDim(1); + + Expression *x = new IntegerExp(0, ae->currentDimension, Type::tsize_t); + x = x->semantic(sc); + tdargs->data[0] = x; + + //TemplateInstance *ti = new TemplateInstance(loc, td, tdargs); + //ti->semantic(sc); + + DotTemplateInstanceExp *dte = new DotTemplateInstanceExp(loc, ae->e1, td->ident, tdargs); + + v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(0, dte)); + } + else + { /* opDollar exists, but it's a function, not a template. + * This is acceptable ONLY for single-dimension indexing. + * Note that it's impossible to have both template & function opDollar, + * because both take no arguments. + */ + if (ae->arguments->dim != 1) { + ae->error("%s only defines opDollar for one dimension", ad->toChars()); + return NULL; + } + FuncDeclaration *fd = dsym->isFuncDeclaration(); + assert(fd); + Expression * x = new DotVarExp(loc, ae->e1, fd); + + v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(0, x)); + } + v->semantic(sc); + ae->lengthVar = v; + } + return v; + } + else + /* Didn't find $, look in enclosing scope(s). + */ + return NULL; + + /* If we are indexing into an array that is really a type + * tuple, rewrite this as an index into a type tuple and + * try again. + */ + if (ce->op == TOKtype) + { + Type *t = ((TypeExp *)ce)->type; + if (t->ty == Ttuple) + { type = (TypeTuple *)t; + goto L1; + } + } + + /* *pvar is lazily initialized, so if we refer to $ + * multiple times, it gets set only once. + */ + if (!*pvar) // if not already initialized + { /* Create variable v and set it to the value of $ + */ + VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); + if (ce->op == TOKtuple) + { /* It is for an expression tuple, so the + * length will be a const. + */ + Expression *e = new IntegerExp(0, ((TupleExp *)ce)->exps->dim, Type::tsize_t); + v->init = new ExpInitializer(0, e); + v->storage_class |= STCstatic | STCconst; + } + else + { /* For arrays, $ will either be a compile-time constant + * (in which case its value in set during constant-folding), + * or a variable (in which case an expression is created in + * toir.c). + */ + VoidInitializer *e = new VoidInitializer(0); + e->type = Type::tsize_t; + v->init = e; + } + *pvar = v; + } + (*pvar)->semantic(sc); + return (*pvar); + } + return NULL; +} + + +/****************************** DsymbolTable ******************************/ + +DsymbolTable::DsymbolTable() +{ +#if STRINGTABLE + tab = new StringTable; + tab->init(); +#else + tab = NULL; +#endif +} + +DsymbolTable::~DsymbolTable() +{ +#if STRINGTABLE + delete tab; +#endif +} + +Dsymbol *DsymbolTable::lookup(Identifier *ident) +{ +#if STRINGTABLE +#ifdef DEBUG + assert(ident); + assert(tab); +#endif + //printf("DsymbolTable::lookup(%s)\n", (char*)ident->string); + StringValue *sv = tab->lookup((char*)ident->string, ident->len); + return (Dsymbol *)(sv ? sv->ptrvalue : NULL); +#else + //printf("DsymbolTable::lookup(%s)\n", (char*)ident->string); + return (Dsymbol *)_aaGetRvalue(tab, ident); +#endif +} + +Dsymbol *DsymbolTable::insert(Dsymbol *s) +{ + //printf("DsymbolTable::insert(this = %p, '%s')\n", this, s->ident->toChars()); + Identifier *ident = s->ident; +#if STRINGTABLE +#ifdef DEBUG + assert(ident); + assert(tab); +#endif + StringValue *sv = tab->insert(ident->toChars(), ident->len); + if (!sv) + return NULL; // already in table + sv->ptrvalue = s; + return s; +#else + Dsymbol **ps = (Dsymbol **)_aaGet(&tab, ident); + if (*ps) + return NULL; // already in table + *ps = s; + return s; +#endif +} + +Dsymbol *DsymbolTable::insert(Identifier *ident, Dsymbol *s) +{ + //printf("DsymbolTable::insert()\n"); +#if STRINGTABLE + StringValue *sv = tab->insert(ident->toChars(), ident->len); + if (!sv) + return NULL; // already in table + sv->ptrvalue = s; + return s; +#else + Dsymbol **ps = (Dsymbol **)_aaGet(&tab, ident); + if (*ps) + return NULL; // already in table + *ps = s; + return s; +#endif +} + +Dsymbol *DsymbolTable::update(Dsymbol *s) +{ + Identifier *ident = s->ident; +#if STRINGTABLE + StringValue *sv = tab->update(ident->toChars(), ident->len); + sv->ptrvalue = s; + return s; +#else + Dsymbol **ps = (Dsymbol **)_aaGet(&tab, ident); + *ps = s; + return s; +#endif +} + + + + diff --git a/dsymbol.h b/dsymbol.h new file mode 100644 index 00000000..97406f0b --- /dev/null +++ b/dsymbol.h @@ -0,0 +1,348 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_DSYMBOL_H +#define DMD_DSYMBOL_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "root.h" +#include "stringtable.h" + +#include "mars.h" +#include "arraytypes.h" + +struct Identifier; +struct Scope; +struct DsymbolTable; +struct Declaration; +struct ThisDeclaration; +struct TupleDeclaration; +struct TypedefDeclaration; +struct AliasDeclaration; +struct AggregateDeclaration; +struct EnumDeclaration; +struct ClassDeclaration; +struct InterfaceDeclaration; +struct StructDeclaration; +struct UnionDeclaration; +struct FuncDeclaration; +struct FuncAliasDeclaration; +struct FuncLiteralDeclaration; +struct CtorDeclaration; +struct PostBlitDeclaration; +struct DtorDeclaration; +struct StaticCtorDeclaration; +struct StaticDtorDeclaration; +struct SharedStaticCtorDeclaration; +struct SharedStaticDtorDeclaration; +struct InvariantDeclaration; +struct UnitTestDeclaration; +struct NewDeclaration; +struct VarDeclaration; +struct AttribDeclaration; +struct Symbol; +struct Package; +struct Module; +struct Import; +struct Type; +struct TypeTuple; +struct WithStatement; +struct LabelDsymbol; +struct ScopeDsymbol; +struct TemplateDeclaration; +struct TemplateInstance; +struct TemplateMixin; +struct EnumMember; +struct ScopeDsymbol; +struct WithScopeSymbol; +struct ArrayScopeSymbol; +struct SymbolDeclaration; +struct Expression; +struct DeleteDeclaration; +struct HdrGenState; +struct OverloadSet; +struct AA; +#if TARGET_NET +struct PragmaScope; +#endif +#if IN_GCC +union tree_node; +typedef union tree_node TYPE; +#else +struct TYPE; +#endif + +// Back end +struct Classsym; + +enum PROT +{ + PROTundefined, + PROTnone, // no access + PROTprivate, + PROTpackage, + PROTprotected, + PROTpublic, + PROTexport, +}; + +/* State of symbol in winding its way through the passes of the compiler + */ +enum PASS +{ + PASSinit, // initial state + PASSsemantic, // semantic() started + PASSsemanticdone, // semantic() done + PASSsemantic2, // semantic2() run + PASSsemantic3, // semantic3() started + PASSsemantic3done, // semantic3() done + PASSobj, // toObjFile() run +}; + +struct Dsymbol : Object +{ + Identifier *ident; + Identifier *c_ident; + Dsymbol *parent; + Symbol *csym; // symbol for code generator + Symbol *isym; // import version of csym + unsigned char *comment; // documentation comment for this Dsymbol + Loc loc; // where defined + Scope *scope; // !=NULL means context to use for semantic() + + Dsymbol(); + Dsymbol(Identifier *); + char *toChars(); + char *locToChars(); + int equals(Object *o); + int isAnonymous(); + void error(Loc loc, const char *format, ...); + void error(const char *format, ...); + void verror(Loc loc, const char *format, va_list ap); + void checkDeprecated(Loc loc, Scope *sc); + Module *getModule(); + Module *getAccessModule(); + Dsymbol *pastMixin(); + Dsymbol *toParent(); + Dsymbol *toParent2(); + TemplateInstance *inTemplateInstance(); + + int dyncast() { return DYNCAST_DSYMBOL; } // kludge for template.isSymbol() + + static Dsymbols *arraySyntaxCopy(Dsymbols *a); + + virtual const char *toPrettyChars(); + virtual const char *kind(); + virtual Dsymbol *toAlias(); // resolve real symbol + virtual int addMember(Scope *sc, ScopeDsymbol *s, int memnum); + virtual void setScope(Scope *sc); + virtual void importAll(Scope *sc); + virtual void semantic0(Scope *sc); + virtual void semantic(Scope *sc); + virtual void semantic2(Scope *sc); + virtual void semantic3(Scope *sc); + virtual void inlineScan(); + virtual Dsymbol *search(Loc loc, Identifier *ident, int flags); + Dsymbol *search_correct(Identifier *id); + Dsymbol *searchX(Loc loc, Scope *sc, Identifier *id); + virtual int overloadInsert(Dsymbol *s); + char *toHChars(); + virtual void toHBuffer(OutBuffer *buf, HdrGenState *hgs); + virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + virtual void toDocBuffer(OutBuffer *buf); + virtual void toJsonBuffer(OutBuffer *buf); + virtual unsigned size(Loc loc); + virtual int isforwardRef(); + virtual void defineRef(Dsymbol *s); + virtual AggregateDeclaration *isThis(); // is a 'this' required to access the member + AggregateDeclaration *isAggregateMember(); // are we a member of an aggregate? + ClassDeclaration *isClassMember(); // are we a member of a class? + virtual int isExport(); // is Dsymbol exported? + virtual int isImportedSymbol(); // is Dsymbol imported? + virtual int isDeprecated(); // is Dsymbol deprecated? +#if DMDV2 + virtual int isOverloadable(); +#endif + virtual LabelDsymbol *isLabel(); // is this a LabelDsymbol? + virtual AggregateDeclaration *isMember(); // is this symbol a member of an AggregateDeclaration? + virtual Type *getType(); // is this a type? + virtual char *mangle(); + virtual int needThis(); // need a 'this' pointer? + virtual enum PROT prot(); + virtual Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees + virtual int oneMember(Dsymbol **ps, Identifier *ident); + static int oneMembers(Dsymbols *members, Dsymbol **ps, Identifier *ident = NULL); + virtual int hasPointers(); + virtual bool hasStaticCtorOrDtor(); + virtual void addLocalClass(ClassDeclarations *) { } + virtual void checkCtorConstInit() { } + + virtual void addComment(unsigned char *comment); + virtual void emitComment(Scope *sc); + void emitDitto(Scope *sc); + + // Backend + + virtual Symbol *toSymbol(); // to backend symbol + virtual void toObjFile(int multiobj); // compile to .obj file + virtual int cvMember(unsigned char *p); // emit cv debug info for member + + Symbol *toImport(); // to backend import symbol + static Symbol *toImport(Symbol *s); // to backend import symbol + + Symbol *toSymbolX(const char *prefix, int sclass, TYPE *t, const char *suffix); // helper + + // Eliminate need for dynamic_cast + virtual Package *isPackage() { return NULL; } + virtual Module *isModule() { return NULL; } + virtual EnumMember *isEnumMember() { return NULL; } + virtual TemplateDeclaration *isTemplateDeclaration() { return NULL; } + virtual TemplateInstance *isTemplateInstance() { return NULL; } + virtual TemplateMixin *isTemplateMixin() { return NULL; } + virtual Declaration *isDeclaration() { return NULL; } + virtual ThisDeclaration *isThisDeclaration() { return NULL; } + virtual TupleDeclaration *isTupleDeclaration() { return NULL; } + virtual TypedefDeclaration *isTypedefDeclaration() { return NULL; } + virtual AliasDeclaration *isAliasDeclaration() { return NULL; } + virtual AggregateDeclaration *isAggregateDeclaration() { return NULL; } + virtual FuncDeclaration *isFuncDeclaration() { return NULL; } + virtual FuncAliasDeclaration *isFuncAliasDeclaration() { return NULL; } + virtual FuncLiteralDeclaration *isFuncLiteralDeclaration() { return NULL; } + virtual CtorDeclaration *isCtorDeclaration() { return NULL; } + virtual PostBlitDeclaration *isPostBlitDeclaration() { return NULL; } + virtual DtorDeclaration *isDtorDeclaration() { return NULL; } + virtual StaticCtorDeclaration *isStaticCtorDeclaration() { return NULL; } + virtual StaticDtorDeclaration *isStaticDtorDeclaration() { return NULL; } + virtual SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration() { return NULL; } + virtual SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration() { return NULL; } + virtual InvariantDeclaration *isInvariantDeclaration() { return NULL; } + virtual UnitTestDeclaration *isUnitTestDeclaration() { return NULL; } + virtual NewDeclaration *isNewDeclaration() { return NULL; } + virtual VarDeclaration *isVarDeclaration() { return NULL; } + virtual ClassDeclaration *isClassDeclaration() { return NULL; } + virtual StructDeclaration *isStructDeclaration() { return NULL; } + virtual UnionDeclaration *isUnionDeclaration() { return NULL; } + virtual InterfaceDeclaration *isInterfaceDeclaration() { return NULL; } + virtual ScopeDsymbol *isScopeDsymbol() { return NULL; } + virtual WithScopeSymbol *isWithScopeSymbol() { return NULL; } + virtual ArrayScopeSymbol *isArrayScopeSymbol() { return NULL; } + virtual Import *isImport() { return NULL; } + virtual EnumDeclaration *isEnumDeclaration() { return NULL; } + virtual DeleteDeclaration *isDeleteDeclaration() { return NULL; } + virtual SymbolDeclaration *isSymbolDeclaration() { return NULL; } + virtual AttribDeclaration *isAttribDeclaration() { return NULL; } + virtual OverloadSet *isOverloadSet() { return NULL; } +#if TARGET_NET + virtual PragmaScope* isPragmaScope() { return NULL; } +#endif +}; + +// Dsymbol that generates a scope + +struct ScopeDsymbol : Dsymbol +{ + Dsymbols *members; // all Dsymbol's in this scope + DsymbolTable *symtab; // members[] sorted into table + + Dsymbols *imports; // imported Dsymbol's + unsigned char *prots; // array of PROT, one for each import + + ScopeDsymbol(); + ScopeDsymbol(Identifier *id); + Dsymbol *syntaxCopy(Dsymbol *s); + Dsymbol *search(Loc loc, Identifier *ident, int flags); + void importScope(Dsymbol *s, enum PROT protection); + int isforwardRef(); + void defineRef(Dsymbol *s); + static void multiplyDefined(Loc loc, Dsymbol *s1, Dsymbol *s2); + Dsymbol *nameCollision(Dsymbol *s); + const char *kind(); + FuncDeclaration *findGetMembers(); + virtual Dsymbol *symtabInsert(Dsymbol *s); + bool hasStaticCtorOrDtor(); + + void emitMemberComments(Scope *sc); + + static size_t dim(Dsymbols *members); + static Dsymbol *getNth(Dsymbols *members, size_t nth, size_t *pn = NULL); + + typedef int (*ForeachDg)(void *ctx, size_t idx, Dsymbol *s); + static int foreach(Dsymbols *members, ForeachDg dg, void *ctx, size_t *pn=NULL); + + ScopeDsymbol *isScopeDsymbol() { return this; } +}; + +// With statement scope + +struct WithScopeSymbol : ScopeDsymbol +{ + WithStatement *withstate; + + WithScopeSymbol(WithStatement *withstate); + Dsymbol *search(Loc loc, Identifier *ident, int flags); + + WithScopeSymbol *isWithScopeSymbol() { return this; } +}; + +// Array Index/Slice scope + +struct ArrayScopeSymbol : ScopeDsymbol +{ + Expression *exp; // IndexExp or SliceExp + TypeTuple *type; // for tuple[length] + TupleDeclaration *td; // for tuples of objects + Scope *sc; + + ArrayScopeSymbol(Scope *sc, Expression *e); + ArrayScopeSymbol(Scope *sc, TypeTuple *t); + ArrayScopeSymbol(Scope *sc, TupleDeclaration *td); + Dsymbol *search(Loc loc, Identifier *ident, int flags); + + ArrayScopeSymbol *isArrayScopeSymbol() { return this; } +}; + +// Overload Sets + +#if DMDV2 +struct OverloadSet : Dsymbol +{ + Dsymbols a; // array of Dsymbols + + OverloadSet(); + void push(Dsymbol *s); + OverloadSet *isOverloadSet() { return this; } + const char *kind(); +}; +#endif + +// Table of Dsymbol's + +struct DsymbolTable : Object +{ + AA *tab; + + DsymbolTable(); + ~DsymbolTable(); + + // Look up Identifier. Return Dsymbol if found, NULL if not. + Dsymbol *lookup(Identifier *ident); + + // Insert Dsymbol in table. Return NULL if already there. + Dsymbol *insert(Dsymbol *s); + + // Look for Dsymbol in table. If there, return it. If not, insert s and return that. + Dsymbol *update(Dsymbol *s); + Dsymbol *insert(Identifier *ident, Dsymbol *s); // when ident and s are not the same +}; + +#endif /* DMD_DSYMBOL_H */ diff --git a/dump.c b/dump.c new file mode 100644 index 00000000..4fd0d04e --- /dev/null +++ b/dump.c @@ -0,0 +1,152 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include + +#include "mars.h" +#include "mtype.h" +#include "declaration.h" +#include "expression.h" +#include "template.h" + +static void indent(int indent) +{ + int i; + + for (i = 0; i < indent; i++) + printf(" "); +} + +static char *type_print(Type *type) +{ + return type ? type->toChars() : (char *) "null"; +} + +void dumpExpressions(int i, Expressions *exps) +{ + if (exps) + { + for (size_t j = 0; j < exps->dim; j++) + { Expression *e = exps->tdata()[j]; + indent(i); + printf("(\n"); + e->dump(i + 2); + indent(i); + printf(")\n"); + } + } +} + +void Expression::dump(int i) +{ + indent(i); + printf("%p %s type=%s\n", this, Token::toChars(op), type_print(type)); +} + +void IntegerExp::dump(int i) +{ + indent(i); + printf("%p %jd type=%s\n", this, (intmax_t)value, type_print(type)); +} + +void IdentifierExp::dump(int i) +{ + indent(i); + printf("%p ident '%s' type=%s\n", this, ident->toChars(), type_print(type)); +} + +void DsymbolExp::dump(int i) +{ + indent(i); + printf("%p %s type=%s\n", this, s->toChars(), type_print(type)); +} + +void VarExp::dump(int i) +{ + indent(i); + printf("%p %s var=%s type=%s\n", this, Token::toChars(op), var->toChars(), type_print(type)); +} + +void UnaExp::dump(int i) +{ + indent(i); + printf("%p %s type=%s e1=%p\n", this, Token::toChars(op), type_print(type), e1); + if (e1) + e1->dump(i + 2); +} + +void CallExp::dump(int i) +{ + UnaExp::dump(i); + dumpExpressions(i, arguments); +} + +void SliceExp::dump(int i) +{ + indent(i); + printf("%p %s type=%s e1=%p\n", this, Token::toChars(op), type_print(type), e1); + if (e1) + e1->dump(i + 2); + if (lwr) + lwr->dump(i + 2); + if (upr) + upr->dump(i + 2); +} + +void DotIdExp::dump(int i) +{ + indent(i); + printf("%p %s type=%s ident=%s e1=%p\n", this, Token::toChars(op), type_print(type), ident->toChars(), e1); + if (e1) + e1->dump(i + 2); +} + +void DotVarExp::dump(int i) +{ + indent(i); + printf("%p %s type=%s var='%s' e1=%p\n", this, Token::toChars(op), type_print(type), var->toChars(), e1); + if (e1) + e1->dump(i + 2); +} + +void DotTemplateInstanceExp::dump(int i) +{ + indent(i); + printf("%p %s type=%s ti='%s' e1=%p\n", this, Token::toChars(op), type_print(type), ti->toChars(), e1); + if (e1) + e1->dump(i + 2); +} + +void DelegateExp::dump(int i) +{ + indent(i); + printf("%p %s func=%s type=%s e1=%p\n", this, Token::toChars(op), func->toChars(), type_print(type), e1); + if (e1) + e1->dump(i + 2); +} + +void BinExp::dump(int i) +{ + indent(i); + const char *sop = Token::toChars(op); + if (op == TOKblit) + sop = "blit"; + else if (op == TOKconstruct) + sop = "construct"; + printf("%p %s type=%s e1=%p e2=%p\n", this, sop, type_print(type), e1, e2); + if (e1) + e1->dump(i + 2); + if (e2) + e2->dump(i + 2); +} + + diff --git a/e2ir.c b/e2ir.c new file mode 100644 index 00000000..35060b81 --- /dev/null +++ b/e2ir.c @@ -0,0 +1,5146 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include + +#include "port.h" + +#include "lexer.h" +#include "expression.h" +#include "mtype.h" +#include "dsymbol.h" +#include "declaration.h" +#include "enum.h" +#include "aggregate.h" +#include "attrib.h" +#include "module.h" +#include "init.h" +#include "template.h" + +#include "mem.h" // for tk/mem_malloc + +#include "cc.h" +#include "el.h" +#include "oper.h" +#include "global.h" +#include "code.h" +#include "type.h" +#include "dt.h" +#include "irstate.h" +#include "id.h" +#include "type.h" +#include "toir.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +typedef ArrayBase Elems; + +elem *addressElem(elem *e, Type *t); +elem *array_toPtr(Type *t, elem *e); +elem *appendDtors(IRState *irs, elem *er, size_t starti, size_t endi); + +#define el_setLoc(e,loc) ((e)->Esrcpos.Sfilename = (char *)(loc).filename, \ + (e)->Esrcpos.Slinnum = (loc).linnum) + +/* If variable var of type typ is a reference + */ +#if SARRAYVALUE +#define ISREF(var, tb) (var->isOut() || var->isRef()) +#else +#define ISREF(var, tb) ((var->isParameter() && tb->ty == Tsarray) || var->isOut() || var->isRef()) +#endif + +/************************************ + * Call a function. + */ + +elem *callfunc(Loc loc, + IRState *irs, + int directcall, // 1: don't do virtual call + Type *tret, // return type + elem *ec, // evaluates to function address + Type *ectype, // original type of ec + FuncDeclaration *fd, // if !=NULL, this is the function being called + Type *t, // TypeDelegate or TypeFunction for this function + elem *ehidden, // if !=NULL, this is the 'hidden' argument + Expressions *arguments) +{ + elem *ep; + elem *e; + elem *ethis = NULL; + elem *eside = NULL; + tym_t ty; + tym_t tyret; + enum RET retmethod; + int reverse; + TypeFunction *tf; + int op; + +#if 0 + printf("callfunc(directcall = %d, tret = '%s', ec = %p, fd = %p)\n", + directcall, tret->toChars(), ec, fd); + printf("ec: "); elem_print(ec); + if (fd) + printf("fd = '%s', vtblIndex = %d, isVirtual() = %d\n", fd->toChars(), fd->vtblIndex, fd->isVirtual()); + if (ehidden) + { printf("ehidden: "); elem_print(ehidden); } +#endif + + t = t->toBasetype(); + if (t->ty == Tdelegate) + { + // A delegate consists of: + // { Object *this; Function *funcptr; } + assert(!fd); + assert(t->nextOf()->ty == Tfunction); + tf = (TypeFunction *)(t->nextOf()); + ethis = ec; + ec = el_same(ðis); + ethis = el_una(I64 ? OP128_64 : OP64_32, TYnptr, ethis); // get this + ec = array_toPtr(t, ec); // get funcptr + ec = el_una(OPind, tf->totym(), ec); + } + else + { assert(t->ty == Tfunction); + tf = (TypeFunction *)(t); + } + retmethod = tf->retStyle(); + ty = ec->Ety; + if (fd) + ty = fd->toSymbol()->Stype->Tty; + reverse = tyrevfunc(ty); + ep = NULL; + if (arguments) + { + // j=1 if _arguments[] is first argument + int j = (tf->linkage == LINKd && tf->varargs == 1); + + for (size_t i = 0; i < arguments->dim ; i++) + { Expression *arg = arguments->tdata()[i]; + elem *ea; + + //printf("\targ[%d]: %s\n", i, arg->toChars()); + + size_t nparams = Parameter::dim(tf->parameters); + if (i - j < nparams && i >= j) + { + Parameter *p = Parameter::getNth(tf->parameters, i - j); + + if (p->storageClass & (STCout | STCref)) + { + // Convert argument to a pointer, + // use AddrExp::toElem() + Expression *ae = arg->addressOf(NULL); + ea = ae->toElem(irs); + goto L1; + } + } + ea = arg->toElem(irs); + L1: + if (tybasic(ea->Ety) == TYstruct || tybasic(ea->Ety) == TYarray) + { + ea = el_una(OPstrpar, TYstruct, ea); + ea->ET = ea->E1->ET; + } + if (reverse) + ep = el_param(ep,ea); + else + ep = el_param(ea,ep); + } + } + + if (retmethod == RETstack) + { + if (!ehidden) + { // Don't have one, so create one + type *tc; + + Type *tret = tf->next; + if (tret->toBasetype()->ty == Tstruct || + tret->toBasetype()->ty == Tsarray) + tc = tret->toCtype(); + else + tc = type_fake(tret->totym()); + Symbol *stmp = symbol_genauto(tc); + ehidden = el_ptr(stmp); + } + if ((global.params.isLinux || + global.params.isOSX || + global.params.isFreeBSD || + global.params.isSolaris) && tf->linkage != LINKd) + ; // ehidden goes last on Linux/OSX C++ + else + { + if (ep) + { +#if 0 // BUG: implement + if (reverse && type_mangle(tfunc) == mTYman_cpp) + ep = el_param(ehidden,ep); + else +#endif + ep = el_param(ep,ehidden); + } + else + ep = ehidden; + ehidden = NULL; + } + } + + if (fd && fd->isMember2()) + { + Symbol *sfunc; + AggregateDeclaration *ad; + + ad = fd->isThis(); + if (ad) + { + ethis = ec; + if (ad->isStructDeclaration() && tybasic(ec->Ety) != TYnptr) + { + ethis = addressElem(ec, ectype); + } + } + else + { + // Evaluate ec for side effects + eside = ec; + } + sfunc = fd->toSymbol(); + + if (!fd->isVirtual() || + directcall || // BUG: fix + fd->isFinal() + /* Future optimization: || (whole program analysis && not overridden) + */ + ) + { + // make static call + ec = el_var(sfunc); + } + else + { + // make virtual call + elem *ev; + unsigned vindex; + + assert(ethis); + ev = el_same(ðis); + ev = el_una(OPind, TYnptr, ev); + vindex = fd->vtblIndex; + assert((int)vindex >= 0); + + // Build *(ev + vindex * 4) +if (I32) assert(tysize[TYnptr] == 4); + ec = el_bin(OPadd,TYnptr,ev,el_long(TYsize_t, vindex * tysize[TYnptr])); + ec = el_una(OPind,TYnptr,ec); + ec = el_una(OPind,tybasic(sfunc->Stype->Tty),ec); + } + } + else if (fd && fd->isNested()) + { + assert(!ethis); + ethis = getEthis(0, irs, fd); + } + + ep = el_param(ep, ethis); + if (ehidden) + ep = el_param(ep, ehidden); // if ehidden goes last + + tyret = tret->totym(); + + // Look for intrinsic functions + if (ec->Eoper == OPvar && (op = intrinsic_op(ec->EV.sp.Vsym->Sident)) != -1) + { + el_free(ec); + if (OTbinary(op)) + { + ep->Eoper = op; + ep->Ety = tyret; + e = ep; + if (op == OPscale) + { + elem *et = e->E1; + e->E1 = el_una(OPs32_d, TYdouble, e->E2); + e->E1 = el_una(OPd_ld, TYldouble, e->E1); + e->E2 = et; + } + else if (op == OPyl2x || op == OPyl2xp1) + { + elem *et = e->E1; + e->E1 = e->E2; + e->E2 = et; + } + } + else + e = el_una(op,tyret,ep); + } + else + { /* Do not do "no side effect" calls if a hidden parameter is passed, + * as the return value is stored through the hidden parameter, which + * is a side effect. + */ + //printf("1: fd = %p prity = %d, nothrow = %d, retmethod = %d, use-assert = %d\n", + // fd, (fd ? fd->isPure() : tf->purity), tf->isnothrow, retmethod, global.params.useAssert); + //printf("\tfd = %s, tf = %s\n", fd->toChars(), tf->toChars()); + /* assert() has 'implicit side effect' so disable this optimization. + */ + int ns = ((fd ? fd->isPure() : tf->purity) == PUREstrong && + tf->isnothrow && (retmethod != RETstack) && + !global.params.useAssert && global.params.optimize); + if (ep) + e = el_bin(ns ? OPcallns : OPcall, tyret, ec, ep); + else + e = el_una(ns ? OPucallns : OPucall, tyret, ec); + + if (tf->varargs) + e->Eflags |= EFLAGS_variadic; + } + + if (retmethod == RETstack) + { + e->Ety = TYnptr; + e = el_una(OPind, tyret, e); + } + +#if DMDV2 + if (tf->isref) + { + e->Ety = TYnptr; + e = el_una(OPind, tyret, e); + } +#endif + + if (tybasic(tyret) == TYstruct) + { + e->ET = tret->toCtype(); + } + e = el_combine(eside, e); + return e; +} + +/******************************************* + * Take address of an elem. + */ + +elem *addressElem(elem *e, Type *t) +{ + elem **pe; + + //printf("addressElem()\n"); + + for (pe = &e; (*pe)->Eoper == OPcomma; pe = &(*pe)->E2) + ; + if ((*pe)->Eoper != OPvar && (*pe)->Eoper != OPind) + { Symbol *stmp; + elem *eeq; + elem *e2 = *pe; + type *tx; + + // Convert to ((tmp=e2),tmp) + TY ty; + if (t && ((ty = t->toBasetype()->ty) == Tstruct || ty == Tsarray)) + tx = t->toCtype(); + else + tx = type_fake(e2->Ety); + stmp = symbol_genauto(tx); + eeq = el_bin(OPeq,e2->Ety,el_var(stmp),e2); + if (tybasic(e2->Ety) == TYstruct) + { + eeq->Eoper = OPstreq; + eeq->ET = e2->ET; + } + else if (tybasic(e2->Ety) == TYarray) + { + eeq->Eoper = OPstreq; + eeq->Ejty = eeq->Ety = TYstruct; + eeq->ET = t ? t->toCtype() : tx; + } + *pe = el_bin(OPcomma,e2->Ety,eeq,el_var(stmp)); + } + e = el_una(OPaddr,TYnptr,e); + return e; +} + +/***************************************** + * Convert array to a pointer to the data. + */ + +elem *array_toPtr(Type *t, elem *e) +{ + //printf("array_toPtr()\n"); + //elem_print(e); + t = t->toBasetype(); + switch (t->ty) + { + case Tpointer: + break; + + case Tarray: + case Tdelegate: + if (e->Eoper == OPcomma) + { + e->Ety = TYnptr; + e->E2 = array_toPtr(t, e->E2); + } + else if (e->Eoper == OPpair) + { + e->Eoper = OPcomma; + e->Ety = TYnptr; + } + else + { +#if 1 + e = el_una(OPmsw, TYnptr, e); +#else + e = el_una(OPaddr, TYnptr, e); + e = el_bin(OPadd, TYnptr, e, el_long(TYsize_t, 4)); + e = el_una(OPind, TYnptr, e); +#endif + } + break; + + case Tsarray: + e = el_una(OPaddr, TYnptr, e); + break; + + default: + t->print(); + assert(0); + } + return e; +} + +/***************************************** + * Convert array to a dynamic array. + */ + +elem *array_toDarray(Type *t, elem *e) +{ + unsigned dim; + elem *ef = NULL; + elem *ex; + + //printf("array_toDarray(t = %s)\n", t->toChars()); + //elem_print(e); + t = t->toBasetype(); + switch (t->ty) + { + case Tarray: + break; + + case Tsarray: + e = addressElem(e, t); + dim = ((TypeSArray *)t)->dim->toInteger(); + e = el_pair(TYdarray, el_long(TYsize_t, dim), e); + break; + + default: + L1: + switch (e->Eoper) + { + case OPconst: + { + size_t len = tysize[tybasic(e->Ety)]; + elem *es = el_calloc(); + es->Eoper = OPstring; + + // freed in el_free + es->EV.ss.Vstring = (char *)mem_malloc(len); + memcpy(es->EV.ss.Vstring, &e->EV, len); + + es->EV.ss.Vstrlen = len; + es->Ety = TYnptr; + e = es; + break; + } + + case OPvar: + e = el_una(OPaddr, TYnptr, e); + break; + + case OPcomma: + ef = el_combine(ef, e->E1); + ex = e; + e = e->E2; + ex->E1 = NULL; + ex->E2 = NULL; + el_free(ex); + goto L1; + + case OPind: + ex = e; + e = e->E1; + ex->E1 = NULL; + ex->E2 = NULL; + el_free(ex); + break; + + default: + { + // Copy expression to a variable and take the + // address of that variable. + Symbol *stmp; + tym_t ty = tybasic(e->Ety); + + if (ty == TYstruct) + { unsigned sz = type_size(e->ET); + if (sz <= 4) + ty = TYint; + else if (sz <= 8) + ty = TYllong; + } + e->Ety = ty; + stmp = symbol_genauto(type_fake(ty)); + e = el_bin(OPeq, e->Ety, el_var(stmp), e); + e = el_bin(OPcomma, TYnptr, e, el_una(OPaddr, TYnptr, el_var(stmp))); + break; + } + } + dim = 1; + e = el_pair(TYdarray, el_long(TYsize_t, dim), e); + break; + } + return el_combine(ef, e); +} + +/***************************************** + * Evaluate elem and convert to dynamic array. + */ + +elem *eval_Darray(IRState *irs, Expression *e) +{ + elem *ex; + + ex = e->toElem(irs); + return array_toDarray(e->type, ex); +} + +/************************************ + */ + +elem *sarray_toDarray(Loc loc, Type *tfrom, Type *tto, elem *e) +{ + //printf("sarray_toDarray()\n"); + //elem_print(e); + + dinteger_t dim = ((TypeSArray *)tfrom)->dim->toInteger(); + + if (tto) + { + unsigned fsize = tfrom->nextOf()->size(); + unsigned tsize = tto->nextOf()->size(); + + if ((dim * fsize) % tsize != 0) + { + error(loc, "cannot cast %s to %s since sizes don't line up", tfrom->toChars(), tto->toChars()); + } + dim = (dim * fsize) / tsize; + } + elem *elen = el_long(TYsize_t, dim); + e = addressElem(e, tfrom); + e = el_pair(TYdarray, elen, e); + return e; +} + +/******************************************** + * Determine if t is an array of structs that need a postblit. + */ + +StructDeclaration *needsPostblit(Type *t) +{ + t = t->toBasetype(); + while (t->ty == Tsarray) + t = t->nextOf()->toBasetype(); + if (t->ty == Tstruct) + { StructDeclaration *sd = ((TypeStruct *)t)->sym; + if (sd->postblit) + return sd; + } + return NULL; +} + +/******************************************* + * Set an array pointed to by eptr to evalue: + * eptr[0..edim] = evalue; + * Input: + * eptr where to write the data to + * evalue value to write + * edim number of times to write evalue to eptr[] + * tb type of evalue + */ + +elem *setArray(elem *eptr, elem *edim, Type *tb, elem *evalue, IRState *irs, int op) +{ int r; + elem *e; + unsigned sz = tb->size(); + + switch (tb->ty) + { + case Tfloat80: + case Timaginary80: + r = RTLSYM_MEMSET80; + break; + case Tcomplex80: + r = RTLSYM_MEMSET160; + break; + case Tcomplex64: + r = RTLSYM_MEMSET128; + break; + case Tfloat32: + case Timaginary32: + if (I32) + goto Ldefault; // legacy binary compatibility + r = RTLSYM_MEMSETFLOAT; + break; + case Tfloat64: + case Timaginary64: + if (I32) + goto Ldefault; // legacy binary compatibility + r = RTLSYM_MEMSETDOUBLE; + break; + + default: + Ldefault: + switch (sz) + { + case 1: r = RTLSYM_MEMSET8; break; + case 2: r = RTLSYM_MEMSET16; break; + case 4: r = RTLSYM_MEMSET32; break; + case 8: r = RTLSYM_MEMSET64; break; + case 16: r = RTLSYM_MEMSET128; break; + default: r = RTLSYM_MEMSETN; break; + } + + /* Determine if we need to do postblit + */ + if (op != TOKblit) + { + StructDeclaration *sd = needsPostblit(tb); + if (sd) + { /* Need to do postblit. + * void *_d_arraysetassign(void *p, void *value, int dim, TypeInfo ti); + */ + r = (op == TOKconstruct) ? RTLSYM_ARRAYSETCTOR : RTLSYM_ARRAYSETASSIGN; + evalue = el_una(OPaddr, TYnptr, evalue); + Expression *ti = tb->getTypeInfo(NULL); + elem *eti = ti->toElem(irs); + e = el_params(eti, edim, evalue, eptr, NULL); + e = el_bin(OPcall,TYnptr,el_var(rtlsym[r]),e); + return e; + } + } + + if (r == RTLSYM_MEMSETN) + { + // void *_memsetn(void *p, void *value, int dim, int sizelem) + evalue = el_una(OPaddr, TYnptr, evalue); + elem *esz = el_long(TYsize_t, sz); + e = el_params(esz, edim, evalue, eptr, NULL); + e = el_bin(OPcall,TYnptr,el_var(rtlsym[r]),e); + return e; + } + break; + } + if (sz > 1 && sz <= 8 && + evalue->Eoper == OPconst && el_allbits(evalue, 0)) + { + r = RTLSYM_MEMSET8; + edim = el_bin(OPmul, TYsize_t, edim, el_long(TYsize_t, sz)); + } + + if (tybasic(evalue->Ety) == TYstruct || tybasic(evalue->Ety) == TYarray) + { + evalue = el_una(OPstrpar, TYstruct, evalue); + evalue->ET = evalue->E1->ET; + } + + // Be careful about parameter side effect ordering + if (r == RTLSYM_MEMSET8) + { + e = el_param(edim, evalue); + e = el_bin(OPmemset,TYnptr,eptr,e); + } + else + { + e = el_params(edim, evalue, eptr, NULL); + e = el_bin(OPcall,TYnptr,el_var(rtlsym[r]),e); + } + return e; +} + +/*************************************** + */ + +elem *Expression::toElem(IRState *irs) +{ + print(); + assert(0); + return NULL; +} + +/******************************************* + * Evaluate Expression, then call destructors on any temporaries in it. + */ + +elem *Expression::toElemDtor(IRState *irs) +{ + //printf("Expression::toElemDtor() %s\n", toChars()); + size_t starti = irs->varsInScope ? irs->varsInScope->dim : 0; + elem *er = toElem(irs); + size_t endi = irs->varsInScope ? irs->varsInScope->dim : 0; + + // Add destructors + er = appendDtors(irs, er, starti, endi); + return er; +} + +/************************************ + */ +#if DMDV2 +elem *SymbolExp::toElem(IRState *irs) +{ Symbol *s; + elem *e; + tym_t tym; + Type *tb = (op == TOKsymoff) ? var->type->toBasetype() : type->toBasetype(); + int offset = (op == TOKsymoff) ? ((SymOffExp*)this)->offset : 0; + FuncDeclaration *fd; + VarDeclaration *v = var->isVarDeclaration(); + + //printf("SymbolExp::toElem('%s') %p\n", toChars(), this); + //printf("\tparent = '%s'\n", var->parent ? var->parent->toChars() : "null"); + if (op == TOKvar && var->needThis()) + { + error("need 'this' to access member %s", toChars()); + return el_long(TYsize_t, 0); + } + + /* The magic variable __ctfe is always false at runtime + */ + if (op == TOKvar && v && v->ident == Id::ctfe) + return el_long(type->totym(), 0); + + s = var->toSymbol(); + fd = NULL; + if (var->toParent2()) + fd = var->toParent2()->isFuncDeclaration(); + + int nrvo = 0; + if (fd && fd->nrvo_can && fd->nrvo_var == var) + { + s = fd->shidden; + nrvo = 1; + } + + if (s->Sclass == SCauto || s->Sclass == SCparameter) + { + if (fd && fd != irs->getFunc()) + { // 'var' is a variable in an enclosing function. + elem *ethis; + int soffset; + + ethis = getEthis(loc, irs, fd); + ethis = el_una(OPaddr, TYnptr, ethis); + + if (v && v->offset) + soffset = v->offset; + else + { + soffset = s->Soffset; + /* If fd is a non-static member function of a class or struct, + * then ethis isn't the frame pointer. + * ethis is the 'this' pointer to the class/struct instance. + * We must offset it. + */ + if (fd->vthis) + { + soffset -= fd->vthis->toSymbol()->Soffset; + } + //printf("\tSoffset = x%x, sthis->Soffset = x%x\n", s->Soffset, irs->sthis->Soffset); + } + + if (!nrvo) + soffset += offset; + + e = el_bin(OPadd, TYnptr, ethis, el_long(TYnptr, soffset)); + if (op == TOKvar) + e = el_una(OPind, TYnptr, e); + if (ISREF(var, tb)) + e = el_una(OPind, s->ty(), e); + else if (op == TOKsymoff && nrvo) + { e = el_una(OPind, TYnptr, e); + e = el_bin(OPadd, e->Ety, e, el_long(TYsize_t, offset)); + } + goto L1; + } + } + + /* If var is a member of a closure + */ + if (v && v->offset) + { assert(irs->sclosure); + e = el_var(irs->sclosure); + e = el_bin(OPadd, TYnptr, e, el_long(TYsize_t, v->offset)); + if (op == TOKvar) + { e = el_una(OPind, type->totym(), e); + if (tybasic(e->Ety) == TYstruct) + e->ET = type->toCtype(); + el_setLoc(e, loc); + } + if (ISREF(var, tb)) + { e->Ety = TYnptr; + e = el_una(OPind, s->ty(), e); + } + else if (op == TOKsymoff && nrvo) + { e = el_una(OPind, TYnptr, e); + e = el_bin(OPadd, e->Ety, e, el_long(TYsize_t, offset)); + } + else if (op == TOKsymoff) + { + e = el_bin(OPadd, e->Ety, e, el_long(TYsize_t, offset)); + } + goto L1; + } + + if (s->Sclass == SCauto && s->Ssymnum == -1) + { + //printf("\tadding symbol %s\n", s->Sident); + symbol_add(s); + } + + if (var->isImportedSymbol()) + { + assert(op == TOKvar); + e = el_var(var->toImport()); + e = el_una(OPind,s->ty(),e); + } + else if (ISREF(var, tb)) + { // Static arrays are really passed as pointers to the array + // Out parameters are really references + e = el_var(s); + e->Ety = TYnptr; + if (op == TOKvar) + e = el_una(OPind, s->ty(), e); + else if (offset) + e = el_bin(OPadd, TYnptr, e, el_long(TYsize_t, offset)); + } + else if (op == TOKvar) + e = el_var(s); + else + { e = nrvo ? el_var(s) : el_ptr(s); + e = el_bin(OPadd, e->Ety, e, el_long(TYsize_t, offset)); + } +L1: + if (op == TOKvar) + { + if (nrvo) + { + e->Ety = TYnptr; + e = el_una(OPind, 0, e); + } + if (tb->ty == Tfunction) + { + tym = s->Stype->Tty; + } + else + tym = type->totym(); + e->Ejty = e->Ety = tym; + if (tybasic(tym) == TYstruct) + { + e->ET = type->toCtype(); + } + else if (tybasic(tym) == TYarray) + { + e->Ejty = e->Ety = TYstruct; + e->ET = type->toCtype(); + } + } + el_setLoc(e,loc); + return e; +} +#endif + +#if DMDV1 +elem *VarExp::toElem(IRState *irs) +{ Symbol *s; + elem *e; + tym_t tym; + Type *tb = type->toBasetype(); + FuncDeclaration *fd; + VarDeclaration *v = var->isVarDeclaration(); + + //printf("VarExp::toElem('%s') %p\n", toChars(), this); + //printf("\tparent = '%s'\n", var->parent ? var->parent->toChars() : "null"); + if (var->needThis()) + { + error("need 'this' to access member %s", toChars()); + return el_long(TYsize_t, 0); + } + s = var->toSymbol(); + fd = NULL; + if (var->toParent2()) + fd = var->toParent2()->isFuncDeclaration(); + + int nrvo = 0; + if (fd && fd->nrvo_can && fd->nrvo_var == var) + { + s = fd->shidden; + nrvo = 1; + } + + if (s->Sclass == SCauto || s->Sclass == SCparameter) + { + if (fd && fd != irs->getFunc()) + { // 'var' is a variable in an enclosing function. + elem *ethis; + int soffset; + + ethis = getEthis(loc, irs, fd); + ethis = el_una(OPaddr, TYnptr, ethis); + + if (v && v->offset) + soffset = v->offset; + else + { + soffset = s->Soffset; + /* If fd is a non-static member function of a class or struct, + * then ethis isn't the frame pointer. + * ethis is the 'this' pointer to the class/struct instance. + * We must offset it. + */ + if (fd->vthis) + { + soffset -= fd->vthis->toSymbol()->Soffset; + } + //printf("\tSoffset = x%x, sthis->Soffset = x%x\n", s->Soffset, irs->sthis->Soffset); + } + + ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYnptr, soffset)); + e = el_una(OPind, 0, ethis); + if (ISREF(var, tb)) + goto L2; + goto L1; + } + } + + /* If var is a member of a closure + */ + if (v && v->offset) + { assert(irs->sclosure); + e = el_var(irs->sclosure); + e = el_bin(OPadd, TYnptr, e, el_long(TYsize_t, v->offset)); + e = el_una(OPind, type->totym(), e); + if (tybasic(e->Ety) == TYstruct) + e->ET = type->toCtype(); + el_setLoc(e, loc); + + if (ISREF(var, tb)) + goto L2; + goto L1; + } + + if (s->Sclass == SCauto && s->Ssymnum == -1) + { + //printf("\tadding symbol\n"); + symbol_add(s); + } + + if (var->isImportedSymbol()) + { + e = el_var(var->toImport()); + e = el_una(OPind,s->ty(),e); + } + else if (ISREF(var, tb)) + { // Static arrays are really passed as pointers to the array + // Out parameters are really references + e = el_var(s); +L2: + e->Ety = TYnptr; + e = el_una(OPind, s->ty(), e); + } + else + e = el_var(s); +L1: + if (nrvo) + { + e->Ety = TYnptr; + e = el_una(OPind, 0, e); + } + if (tb->ty == Tfunction) + { + tym = s->Stype->Tty; + } + else + tym = type->totym(); + e->Ejty = e->Ety = tym; + if (tybasic(tym) == TYstruct) + { + e->ET = type->toCtype(); + } + else if (tybasic(tym) == TYarray) + { + e->Ejty = e->Ety = TYstruct; + e->ET = type->toCtype(); + } + el_setLoc(e,loc); + return e; +} +#endif + +#if 0 +elem *SymOffExp::toElem(IRState *irs) +{ Symbol *s; + elem *e; + Type *tb = var->type->toBasetype(); + VarDeclaration *v = var->isVarDeclaration(); + FuncDeclaration *fd = NULL; + if (var->toParent2()) + fd = var->toParent2()->isFuncDeclaration(); + + //printf("SymOffExp::toElem(): %s\n", toChars()); + s = var->toSymbol(); + + int nrvo = 0; + if (fd && fd->nrvo_can && fd->nrvo_var == var) + { s = fd->shidden; + nrvo = 1; + } + + if (s->Sclass == SCauto && s->Ssymnum == -1) + symbol_add(s); + assert(!var->isImportedSymbol()); + + // This code closely parallels that in VarExp::toElem() + if (s->Sclass == SCauto || s->Sclass == SCparameter) + { + if (fd && fd != irs->getFunc()) + { // 'var' is a variable in an enclosing function. + elem *ethis; + int soffset; + + ethis = getEthis(loc, irs, fd); + ethis = el_una(OPaddr, TYnptr, ethis); + + if (v && v->offset) + soffset = v->offset; + else + { + soffset = s->Soffset; + /* If fd is a non-static member function of a class or struct, + * then ethis isn't the frame pointer. + * ethis is the 'this' pointer to the class/struct instance. + * We must offset it. + */ + if (fd->vthis) + { + soffset -= fd->vthis->toSymbol()->Soffset; + } + //printf("\tSoffset = x%x, sthis->Soffset = x%x\n", s->Soffset, irs->sthis->Soffset); + } + + if (!nrvo) + soffset += offset; + e = el_bin(OPadd, TYnptr, ethis, el_long(TYnptr, soffset)); + if (ISREF(var, tb)) + e = el_una(OPind, s->ty(), e); + else if (nrvo) + { e = el_una(OPind, TYnptr, e); + e = el_bin(OPadd, e->Ety, e, el_long(TYsize_t, offset)); + } + goto L1; + } + } + + /* If var is a member of a closure + */ + if (v && v->offset) + { assert(irs->sclosure); + e = el_var(irs->sclosure); + e = el_bin(OPadd, TYnptr, e, el_long(TYsize_t, v->offset)); + if (ISREF(var, tb)) + e = el_una(OPind, s->ty(), e); + else if (nrvo) + { e = el_una(OPind, TYnptr, e); + e = el_bin(OPadd, e->Ety, e, el_long(TYsize_t, offset)); + } + goto L1; + } + + if (ISREF(var, tb)) + { // Static arrays are really passed as pointers to the array + // Out parameters are really references + e = el_var(s); + e->Ety = TYnptr; + if (offset) + e = el_bin(OPadd, TYnptr, e, el_long(TYsize_t, offset)); + } + else + { e = nrvo ? el_var(s) : el_ptr(s); + e = el_bin(OPadd, e->Ety, e, el_long(TYsize_t, offset)); + } + +L1: + el_setLoc(e,loc); + return e; +} +#endif + +/************************************** + */ + +elem *FuncExp::toElem(IRState *irs) +{ + elem *e; + Symbol *s; + + //printf("FuncExp::toElem() %s\n", toChars()); + if (fd->tok == TOKreserved && type->ty == Tpointer && fd->vthis) + { fd->tok = TOKfunction; + fd->vthis = NULL; + } + s = fd->toSymbol(); + e = el_ptr(s); + if (fd->isNested()) + { + elem *ethis = getEthis(loc, irs, fd); + e = el_pair(TYdelegate, ethis, e); + } + + irs->deferToObj->push(fd); + el_setLoc(e,loc); + return e; +} + +/************************************** + * Mirrors logic in Dsymbol_canThrow(). + */ + +elem *Dsymbol_toElem(Dsymbol *s, IRState *irs) +{ + elem *e = NULL; + Symbol *sp; + AttribDeclaration *ad; + VarDeclaration *vd; + ClassDeclaration *cd; + StructDeclaration *sd; + FuncDeclaration *fd; + TemplateMixin *tm; + TupleDeclaration *td; + TypedefDeclaration *tyd; + + //printf("Dsymbol_toElem() %s\n", s->toChars()); + ad = s->isAttribDeclaration(); + if (ad) + { + Dsymbols *decl = ad->include(NULL, NULL); + if (decl && decl->dim) + { + for (size_t i = 0; i < decl->dim; i++) + { + s = decl->tdata()[i]; + e = el_combine(e, Dsymbol_toElem(s, irs)); + } + } + } + else if ((vd = s->isVarDeclaration()) != NULL) + { + s = s->toAlias(); + if (s != vd) + return Dsymbol_toElem(s, irs); + if (vd->storage_class & STCmanifest) + return NULL; + else if (vd->isStatic() || vd->storage_class & (STCextern | STCtls | STCgshared)) + vd->toObjFile(0); + else + { + sp = s->toSymbol(); + symbol_add(sp); + //printf("\tadding symbol '%s'\n", sp->Sident); + if (vd->init) + { + ExpInitializer *ie; + + ie = vd->init->isExpInitializer(); + if (ie) + e = ie->exp->toElem(irs); + } + + /* Mark the point of construction of a variable that needs to be destructed. + */ + if (vd->edtor && !vd->noscope) + { + e = el_dctor(e, vd); + + // Put vd on list of things needing destruction + if (!irs->varsInScope) + irs->varsInScope = new VarDeclarations(); + irs->varsInScope->push(vd); + } + } + } + else if ((cd = s->isClassDeclaration()) != NULL) + { + irs->deferToObj->push(s); + } + else if ((sd = s->isStructDeclaration()) != NULL) + { + irs->deferToObj->push(sd); + } + else if ((fd = s->isFuncDeclaration()) != NULL) + { + //printf("function %s\n", fd->toChars()); + irs->deferToObj->push(fd); + } + else if ((tm = s->isTemplateMixin()) != NULL) + { + //printf("%s\n", tm->toChars()); + if (tm->members) + { + for (size_t i = 0; i < tm->members->dim; i++) + { + Dsymbol *sm = tm->members->tdata()[i]; + e = el_combine(e, Dsymbol_toElem(sm, irs)); + } + } + } + else if ((td = s->isTupleDeclaration()) != NULL) + { + for (size_t i = 0; i < td->objects->dim; i++) + { Object *o = td->objects->tdata()[i]; + if (o->dyncast() == DYNCAST_EXPRESSION) + { Expression *eo = (Expression *)o; + if (eo->op == TOKdsymbol) + { DsymbolExp *se = (DsymbolExp *)eo; + e = el_combine(e, Dsymbol_toElem(se->s, irs)); + } + } + } + } + else if ((tyd = s->isTypedefDeclaration()) != NULL) + { + irs->deferToObj->push(tyd); + } + return e; +} + +elem *DeclarationExp::toElem(IRState *irs) +{ + //printf("DeclarationExp::toElem() %s\n", toChars()); + elem *e = Dsymbol_toElem(declaration, irs); + return e; +} + +/*************************************** + */ + +elem *ThisExp::toElem(IRState *irs) +{ elem *ethis; + FuncDeclaration *fd; + + //printf("ThisExp::toElem()\n"); + assert(irs->sthis); + + if (var) + { + assert(var->parent); + fd = var->toParent2()->isFuncDeclaration(); + assert(fd); + ethis = getEthis(loc, irs, fd); + } + else + ethis = el_var(irs->sthis); + +#if STRUCTTHISREF + if (type->ty == Tstruct) + { ethis = el_una(OPind, TYstruct, ethis); + ethis->ET = type->toCtype(); + } +#endif + el_setLoc(ethis,loc); + return ethis; +} + +/*************************************** + */ + +elem *IntegerExp::toElem(IRState *irs) +{ + elem *e = el_long(type->totym(), value); + el_setLoc(e,loc); + return e; +} + +/*************************************** + */ + +elem *RealExp::toElem(IRState *irs) +{ union eve c; + tym_t ty; + + //printf("RealExp::toElem(%p) %s\n", this, toChars()); + memset(&c, 0, sizeof(c)); + ty = type->toBasetype()->totym(); + switch (tybasic(ty)) + { + case TYfloat: + case TYifloat: + /* This assignment involves a conversion, which + * unfortunately also converts SNAN to QNAN. + */ + c.Vfloat = value; + if (Port::isSignallingNan(value)) + // Put SNAN back + c.Vuns &= 0xFFBFFFFFL; + break; + + case TYdouble: + case TYidouble: + /* This assignment involves a conversion, which + * unfortunately also converts SNAN to QNAN. + */ + c.Vdouble = value; + if (Port::isSignallingNan(value)) + // Put SNAN back + c.Vullong &= 0xFFF7FFFFFFFFFFFFULL; + break; + + case TYldouble: + case TYildouble: + c.Vldouble = value; + break; + + default: + print(); + type->print(); + type->toBasetype()->print(); + printf("ty = %d, tym = %x\n", type->ty, ty); + assert(0); + } + return el_const(ty, &c); +} + + +/*************************************** + */ + +elem *ComplexExp::toElem(IRState *irs) +{ union eve c; + tym_t ty; + real_t re; + real_t im; + + //printf("ComplexExp::toElem(%p) %s\n", this, toChars()); + + memset(&c, 0, sizeof(c)); + re = creall(value); + im = cimagl(value); + + ty = type->totym(); + switch (tybasic(ty)) + { + case TYcfloat: + c.Vcfloat.re = (float) re; + if (Port::isSignallingNan(re)) + { union { float f; unsigned i; } u; + u.f = c.Vcfloat.re; + u.i &= 0xFFBFFFFFL; + c.Vcfloat.re = u.f; + } + c.Vcfloat.im = (float) im; + if (Port::isSignallingNan(im)) + { union { float f; unsigned i; } u; + u.f = c.Vcfloat.im; + u.i &= 0xFFBFFFFFL; + c.Vcfloat.im = u.f; + } + break; + + case TYcdouble: + c.Vcdouble.re = (double) re; + if (Port::isSignallingNan(re)) + { union { double d; unsigned long long i; } u; + u.d = c.Vcdouble.re; + u.i &= 0xFFF7FFFFFFFFFFFFULL; + c.Vcdouble.re = u.d; + } + c.Vcdouble.im = (double) im; + if (Port::isSignallingNan(re)) + { union { double d; unsigned long long i; } u; + u.d = c.Vcdouble.im; + u.i &= 0xFFF7FFFFFFFFFFFFULL; + c.Vcdouble.im = u.d; + } + break; + + case TYcldouble: +#if 1 + c.Vcldouble.re = re; + c.Vcldouble.im = im; +#else +{unsigned short *p = (unsigned short *)&c.Vcldouble; +for (int i = 0; i < (LNGDBLSIZE*2)/2; i++) printf("%04x ", p[i]); +printf("\n");} + c.Vcldouble.im = im; +{unsigned short *p = (unsigned short *)&c.Vcldouble; +for (int i = 0; i < (LNGDBLSIZE*2)/2; i++) printf("%04x ", p[i]); +printf("\n");} + c.Vcldouble.re = re; +{unsigned short *p = (unsigned short *)&c.Vcldouble; +for (int i = 0; i < (LNGDBLSIZE*2)/2; i++) printf("%04x ", p[i]); +printf("\n");} +#endif + break; + + default: + assert(0); + } + return el_const(ty, &c); +} + +/*************************************** + */ + +elem *NullExp::toElem(IRState *irs) +{ + return el_long(type->totym(), 0); +} + +/*************************************** + */ + +struct StringTab +{ + Module *m; // module we're generating code for + Symbol *si; + void *string; + size_t sz; + size_t len; +}; + +#define STSIZE 16 +StringTab stringTab[STSIZE]; +size_t stidx; + +static Symbol *assertexp_sfilename = NULL; +static const char *assertexp_name = NULL; +static Module *assertexp_mn = NULL; + +void clearStringTab() +{ + //printf("clearStringTab()\n"); + memset(stringTab, 0, sizeof(stringTab)); + stidx = 0; + + assertexp_sfilename = NULL; + assertexp_name = NULL; + assertexp_mn = NULL; +} + +elem *StringExp::toElem(IRState *irs) +{ + elem *e; + Type *tb= type->toBasetype(); + + +#if 0 + printf("StringExp::toElem() %s, type = %s\n", toChars(), type->toChars()); +#endif + + if (tb->ty == Tarray) + { + Symbol *si; + dt_t *dt; + StringTab *st; + +#if 0 + printf("irs->m = %p\n", irs->m); + printf(" m = %s\n", irs->m->toChars()); + printf(" len = %d\n", len); + printf(" sz = %d\n", sz); +#endif + for (size_t i = 0; i < STSIZE; i++) + { + st = &stringTab[(stidx + i) % STSIZE]; + //if (!st->m) continue; + //printf(" st.m = %s\n", st->m->toChars()); + //printf(" st.len = %d\n", st->len); + //printf(" st.sz = %d\n", st->sz); + if (st->m == irs->m && + st->si && + st->len == len && + st->sz == sz && + memcmp(st->string, string, sz * len) == 0) + { + //printf("use cached value\n"); + si = st->si; // use cached value + goto L1; + } + } + + stidx = (stidx + 1) % STSIZE; + st = &stringTab[stidx]; + + dt = NULL; + toDt(&dt); + + si = symbol_generate(SCstatic,type_fake(TYdarray)); + si->Sdt = dt; + si->Sfl = FLdata; +#if ELFOBJ // Burton + si->Sseg = CDATA; +#endif +#if MACHOBJ + si->Sseg = DATA; +#endif + outdata(si); + + st->m = irs->m; + st->si = si; + st->string = string; + st->len = len; + st->sz = sz; + L1: + e = el_var(si); + } + else if (tb->ty == Tsarray) + { + dt_t *dt = NULL; + + toDt(&dt); + dtnzeros(&dt, sz); // leave terminating 0 + + ::type *t = type_allocn(TYarray, tschar); + t->Tdim = sz * len; + Symbol *si = symbol_generate(SCstatic, t); + si->Sdt = dt; + si->Sfl = FLdata; + +#if ELFOBJ || MACHOBJ // Burton + si->Sseg = CDATA; +#endif + outdata(si); + + e = el_var(si); + e->ET = t; + t->Tcount++; + } + else if (tb->ty == Tpointer) + { + e = el_calloc(); + e->Eoper = OPstring; + // freed in el_free + e->EV.ss.Vstring = (char *)mem_malloc((len + 1) * sz); + memcpy(e->EV.ss.Vstring, string, (len + 1) * sz); + e->EV.ss.Vstrlen = (len + 1) * sz; + e->Ety = TYnptr; + } + else + { + printf("type is %s\n", type->toChars()); + assert(0); + } + el_setLoc(e,loc); + return e; +} + +elem *NewExp::toElem(IRState *irs) +{ elem *e; + Type *t; + Type *ectype; + + //printf("NewExp::toElem() %s\n", toChars()); + t = type->toBasetype(); + //printf("\ttype = %s\n", t->toChars()); + //if (member) + //printf("\tmember = %s\n", member->toChars()); + if (t->ty == Tclass) + { + Symbol *csym; + + t = newtype->toBasetype(); + assert(t->ty == Tclass); + TypeClass *tclass = (TypeClass *)(t); + ClassDeclaration *cd = tclass->sym; + + /* Things to do: + * 1) ex: call allocator + * 2) ey: set vthis for nested classes + * 3) ez: call constructor + */ + + elem *ex = NULL; + elem *ey = NULL; + elem *ez = NULL; + + if (allocator || onstack) + { elem *ei; + Symbol *si; + + if (onstack) + { + /* Create an instance of the class on the stack, + * and call it stmp. + * Set ex to be the &stmp. + */ + Symbol *s = symbol_calloc(tclass->sym->toChars()); + s->Sclass = SCstruct; + s->Sstruct = struct_calloc(); + s->Sstruct->Sflags |= 0; + s->Sstruct->Salignsize = tclass->sym->alignsize; + s->Sstruct->Sstructalign = tclass->sym->structalign; + s->Sstruct->Sstructsize = tclass->sym->structsize; + + ::type *tc = type_alloc(TYstruct); + tc->Ttag = (Classsym *)s; // structure tag name + tc->Tcount++; + s->Stype = tc; + + Symbol *stmp = symbol_genauto(tc); + ex = el_ptr(stmp); + } + else + { + ex = el_var(allocator->toSymbol()); + ex = callfunc(loc, irs, 1, type, ex, allocator->type, + allocator, allocator->type, NULL, newargs); + } + + si = tclass->sym->toInitializer(); + ei = el_var(si); + + if (cd->isNested()) + { + ey = el_same(&ex); + ez = el_copytree(ey); + } + else if (member) + ez = el_same(&ex); + + ex = el_una(OPind, TYstruct, ex); + ex = el_bin(OPstreq, TYnptr, ex, ei); + ex->ET = tclass->toCtype()->Tnext; + ex = el_una(OPaddr, TYnptr, ex); + ectype = tclass; + } + else + { + csym = cd->toSymbol(); + ex = el_bin(OPcall,TYnptr,el_var(rtlsym[RTLSYM_NEWCLASS]),el_ptr(csym)); + ectype = NULL; + + if (cd->isNested()) + { + ey = el_same(&ex); + ez = el_copytree(ey); + } + else if (member) + ez = el_same(&ex); +//elem_print(ex); +//elem_print(ey); +//elem_print(ez); + } + + if (thisexp) + { ClassDeclaration *cdthis = thisexp->type->isClassHandle(); + assert(cdthis); + //printf("cd = %s\n", cd->toChars()); + //printf("cdthis = %s\n", cdthis->toChars()); + assert(cd->isNested()); + int offset = 0; + Dsymbol *cdp = cd->toParent2(); // class we're nested in + elem *ethis; + +//printf("member = %p\n", member); +//printf("cdp = %s\n", cdp->toChars()); +//printf("cdthis = %s\n", cdthis->toChars()); + if (cdp != cdthis) + { int i = cdp->isClassDeclaration()->isBaseOf(cdthis, &offset); + assert(i); + } + ethis = thisexp->toElem(irs); + if (offset) + ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYsize_t, offset)); + + if (!cd->vthis) + { + error("forward reference to %s", cd->toChars()); + } + else + { + ey = el_bin(OPadd, TYnptr, ey, el_long(TYsize_t, cd->vthis->offset)); + ey = el_una(OPind, TYnptr, ey); + ey = el_bin(OPeq, TYnptr, ey, ethis); + } +//printf("ex: "); elem_print(ex); +//printf("ey: "); elem_print(ey); +//printf("ez: "); elem_print(ez); + } + else if (cd->isNested()) + { /* Initialize cd->vthis: + * *(ey + cd.vthis.offset) = this; + */ + ey = setEthis(loc, irs, ey, cd); + } + + if (member) + // Call constructor + ez = callfunc(loc, irs, 1, type, ez, ectype, member, member->type, NULL, arguments); + + e = el_combine(ex, ey); + e = el_combine(e, ez); + } +#if DMDV2 + else if (t->ty == Tpointer && t->nextOf()->toBasetype()->ty == Tstruct) + { + t = newtype->toBasetype(); + assert(t->ty == Tstruct); + TypeStruct *tclass = (TypeStruct *)(t); + StructDeclaration *cd = tclass->sym; + + /* Things to do: + * 1) ex: call allocator + * 2) ey: set vthis for nested classes + * 3) ez: call constructor + */ + + elem *ex = NULL; + elem *ey = NULL; + elem *ez = NULL; + + if (allocator) + { elem *ei; + Symbol *si; + + ex = el_var(allocator->toSymbol()); + ex = callfunc(loc, irs, 1, type, ex, allocator->type, + allocator, allocator->type, NULL, newargs); + + si = tclass->sym->toInitializer(); + ei = el_var(si); + + if (cd->isNested()) + { + ey = el_same(&ex); + ez = el_copytree(ey); + } + else if (member) + ez = el_same(&ex); + + if (!member) + { /* Statically intialize with default initializer + */ + ex = el_una(OPind, TYstruct, ex); + ex = el_bin(OPstreq, TYnptr, ex, ei); + ex->ET = tclass->toCtype(); + ex = el_una(OPaddr, TYnptr, ex); + } + ectype = tclass; + } + else + { + d_uns64 elemsize = cd->size(loc); + + // call _d_newarrayT(ti, 1) + e = el_long(TYsize_t, 1); + e = el_param(e, type->getTypeInfo(NULL)->toElem(irs)); + + int rtl = t->isZeroInit() ? RTLSYM_NEWARRAYT : RTLSYM_NEWARRAYIT; + e = el_bin(OPcall,TYdarray,el_var(rtlsym[rtl]),e); + + // The new functions return an array, so convert to a pointer + // ex -> (unsigned)(e >> 32) + e = el_bin(OPshr, TYdarray, e, el_long(TYint, PTRSIZE * 8)); + ex = el_una(OP64_32, TYnptr, e); + + ectype = NULL; + + if (cd->isNested()) + { + ey = el_same(&ex); + ez = el_copytree(ey); + } + else if (member) + ez = el_same(&ex); +//elem_print(ex); +//elem_print(ey); +//elem_print(ez); + } + + if (cd->isNested()) + { /* Initialize cd->vthis: + * *(ey + cd.vthis.offset) = this; + */ + ey = setEthis(loc, irs, ey, cd); + } + + if (member) + { // Call constructor + ez = callfunc(loc, irs, 1, type, ez, ectype, member, member->type, NULL, arguments); +#if STRUCTTHISREF + /* Structs return a ref, which gets automatically dereferenced. + * But we want a pointer to the instance. + */ + ez = el_una(OPaddr, TYnptr, ez); +#endif + } + + e = el_combine(ex, ey); + e = el_combine(e, ez); + } +#endif + else if (t->ty == Tarray) + { + TypeDArray *tda = (TypeDArray *)(t); + + assert(arguments && arguments->dim >= 1); + if (arguments->dim == 1) + { // Single dimension array allocations + Expression *arg = arguments->tdata()[0]; // gives array length + e = arg->toElem(irs); + d_uns64 elemsize = tda->next->size(); + + // call _d_newT(ti, arg) + e = el_param(e, type->getTypeInfo(NULL)->toElem(irs)); + int rtl = tda->next->isZeroInit() ? RTLSYM_NEWARRAYT : RTLSYM_NEWARRAYIT; + e = el_bin(OPcall,TYdarray,el_var(rtlsym[rtl]),e); + } + else + { // Multidimensional array allocations + e = el_long(TYsize_t, arguments->dim); + for (size_t i = 0; i < arguments->dim; i++) + { + Expression *arg = arguments->tdata()[i]; // gives array length + e = el_param(arg->toElem(irs), e); + assert(t->ty == Tarray); + t = t->nextOf(); + assert(t); + } + + e = el_param(e, type->getTypeInfo(NULL)->toElem(irs)); + + int rtl = t->isZeroInit() ? RTLSYM_NEWARRAYMT : RTLSYM_NEWARRAYMIT; + e = el_bin(OPcall,TYdarray,el_var(rtlsym[rtl]),e); + e->Eflags |= EFLAGS_variadic; + } + } + else if (t->ty == Tpointer) + { + TypePointer *tp = (TypePointer *)t; + d_uns64 elemsize = tp->next->size(); + Expression *di = tp->next->defaultInit(); + d_uns64 disize = di->type->size(); + + // call _d_newarrayT(ti, 1) + e = el_long(TYsize_t, 1); + e = el_param(e, type->getTypeInfo(NULL)->toElem(irs)); + + int rtl = tp->next->isZeroInit() ? RTLSYM_NEWARRAYT : RTLSYM_NEWARRAYIT; + e = el_bin(OPcall,TYdarray,el_var(rtlsym[rtl]),e); + + // The new functions return an array, so convert to a pointer + // e -> (unsigned)(e >> 32) + e = el_bin(OPshr, TYdarray, e, el_long(TYsize_t, PTRSIZE * 8)); + e = el_una(I64 ? OP128_64 : OP64_32, t->totym(), e); + } + else + { + assert(0); + } + + el_setLoc(e,loc); + return e; +} + +//////////////////////////// Unary /////////////////////////////// + +/*************************************** + */ + +elem *NegExp::toElem(IRState *irs) +{ + elem *e = e1->toElem(irs); + Type *tb1 = e1->type->toBasetype(); + switch (tb1->ty) + { + case Tarray: + case Tsarray: + error("Array operation %s not implemented", toChars()); + e = el_long(type->totym(), 0); // error recovery + break; + + case Tvector: + { // rewrite (-e) as (0-e) + elem *ez = el_calloc(); + ez->Eoper = OPconst; + ez->Ety = e->Ety; + ez->EV.Vcent.lsw = 0; + ez->EV.Vcent.msw = 0; + e = el_bin(OPmin, type->totym(), ez, e); + break; + } + + default: + e = el_una(OPneg, type->totym(), e); + break; + } + + el_setLoc(e,loc); + return e; +} + +/*************************************** + */ + +elem *ComExp::toElem(IRState *irs) +{ + elem *e1 = this->e1->toElem(irs); + Type *tb1 = this->e1->type->toBasetype(); + tym_t ty = type->totym(); + + elem *e; + switch (tb1->ty) + { + case Tbool: + e = el_bin(OPxor, ty, e1, el_long(ty, 1)); + break; + + case Tvector: + { // rewrite (~e) as (e^~0) + elem *ec = el_calloc(); + ec->Eoper = OPconst; + ec->Ety = e1->Ety; + ec->EV.Vcent.lsw = ~0LL; + ec->EV.Vcent.msw = ~0LL; + e = el_bin(OPxor, ty, e1, ec); + break; + } + + default: + e = el_una(OPcom,ty,e1); + break; + } + + el_setLoc(e,loc); + return e; +} + +/*************************************** + */ + +elem *NotExp::toElem(IRState *irs) +{ + elem *e = el_una(OPnot, type->totym(), e1->toElem(irs)); + el_setLoc(e,loc); + return e; +} + + +/*************************************** + */ + +elem *HaltExp::toElem(IRState *irs) +{ elem *e; + + e = el_calloc(); + e->Ety = TYvoid; + e->Eoper = OPhalt; + el_setLoc(e,loc); + return e; +} + +/******************************************** + */ + +elem *AssertExp::toElem(IRState *irs) +{ elem *e; + elem *ea; + Type *t1 = e1->type->toBasetype(); + + //printf("AssertExp::toElem() %s\n", toChars()); + if (global.params.useAssert) + { + e = e1->toElem(irs); + symbol *ts = NULL; + elem *einv = NULL; + + InvariantDeclaration *inv = (InvariantDeclaration *)(void *)1; + + // If e1 is a class object, call the class invariant on it + if (global.params.useInvariants && t1->ty == Tclass && + !((TypeClass *)t1)->sym->isInterfaceDeclaration()) + { + ts = symbol_genauto(t1->toCtype()); +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_SOLARIS + einv = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM__DINVARIANT]), el_var(ts)); +#else + einv = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_DINVARIANT]), el_var(ts)); +#endif + } + // If e1 is a struct object, call the struct invariant on it + else if (global.params.useInvariants && + t1->ty == Tpointer && + t1->nextOf()->ty == Tstruct && + (inv = ((TypeStruct *)t1->nextOf())->sym->inv) != NULL) + { + ts = symbol_genauto(t1->toCtype()); + einv = callfunc(loc, irs, 1, inv->type->nextOf(), el_var(ts), e1->type, inv, inv->type, NULL, NULL); + } + + // Construct: (e1 || ModuleAssert(line)) + Module *m = irs->blx->module; + char *mname = m->srcfile->toChars(); + + //printf("filename = '%s'\n", loc.filename); + //printf("module = '%s'\n", m->srcfile->toChars()); + + /* Determine if we are in a unittest + */ + FuncDeclaration *fd = irs->getFunc(); + UnitTestDeclaration *ud = fd ? fd->isUnitTestDeclaration() : NULL; + + /* If the source file name has changed, probably due + * to a #line directive. + */ + if (loc.filename && (msg || strcmp(loc.filename, mname) != 0)) + { elem *efilename; + + /* Cache values. + */ + //static Symbol *assertexp_sfilename = NULL; + //static char *assertexp_name = NULL; + //static Module *assertexp_mn = NULL; + + if (!assertexp_sfilename || strcmp(loc.filename, assertexp_name) != 0 || assertexp_mn != m) + { + dt_t *dt = NULL; + const char *id; + int len; + + id = loc.filename; + len = strlen(id); + dtsize_t(&dt, len); + dtabytes(&dt,TYnptr, 0, len + 1, id); + + assertexp_sfilename = symbol_generate(SCstatic,type_fake(TYdarray)); + assertexp_sfilename->Sdt = dt; + assertexp_sfilename->Sfl = FLdata; +#if ELFOBJ + assertexp_sfilename->Sseg = CDATA; +#endif +#if MACHOBJ + assertexp_sfilename->Sseg = DATA; +#endif + outdata(assertexp_sfilename); + + assertexp_mn = m; + assertexp_name = id; + } + + efilename = el_var(assertexp_sfilename); + + if (msg) + { elem *emsg = msg->toElem(irs); + ea = el_var(rtlsym[ud ? RTLSYM_DUNITTEST_MSG : RTLSYM_DASSERT_MSG]); + ea = el_bin(OPcall, TYvoid, ea, el_params(el_long(TYint, loc.linnum), efilename, emsg, NULL)); + } + else + { + ea = el_var(rtlsym[ud ? RTLSYM_DUNITTEST : RTLSYM_DASSERT]); + ea = el_bin(OPcall, TYvoid, ea, el_param(el_long(TYint, loc.linnum), efilename)); + } + } + else + { + Symbol *sassert = ud ? m->toModuleUnittest() : m->toModuleAssert(); + ea = el_bin(OPcall,TYvoid,el_var(sassert), + el_long(TYint, loc.linnum)); + } + if (einv) + { // tmp = e, e || assert, e->inv + elem *eassign = el_bin(OPeq, e->Ety, el_var(ts), e); + e = el_combine(eassign, el_bin(OPoror, TYvoid, el_var(ts), ea)); + e = el_combine(e, einv); + } + else + e = el_bin(OPoror,TYvoid,e,ea); + } + else + { // BUG: should replace assert(0); with a HLT instruction + e = el_long(TYint, 0); + } + el_setLoc(e,loc); + return e; +} + +elem *PostExp::toElem(IRState *irs) +{ + elem *e = e1->toElem(irs); + elem *einc = e2->toElem(irs); + e = el_bin((op == TOKplusplus) ? OPpostinc : OPpostdec, + e->Ety,e,einc); + el_setLoc(e,loc); + return e; +} + +//////////////////////////// Binary /////////////////////////////// + +/******************************************** + */ + +elem *BinExp::toElemBin(IRState *irs,int op) +{ + //printf("toElemBin() '%s'\n", toChars()); + + Type *tb1 = e1->type->toBasetype(); + Type *tb2 = e2->type->toBasetype(); + + if ((tb1->ty == Tarray || tb1->ty == Tsarray || + tb2->ty == Tarray || tb2->ty == Tsarray) && + tb2->ty != Tvoid && + op != OPeq && op != OPandand && op != OPoror + ) + { + error("Array operation %s not implemented", toChars()); + return el_long(type->totym(), 0); // error recovery + } + + tym_t tym = type->totym(); + + elem *el = e1->toElem(irs); + elem *er = e2->toElem(irs); + elem *e = el_bin(op,tym,el,er); + + el_setLoc(e,loc); + return e; +} + +/*************************************** + */ + +elem *AddExp::toElem(IRState *irs) +{ + elem *e = toElemBin(irs,OPadd); + return e; +} + +/*************************************** + */ + +elem *MinExp::toElem(IRState *irs) +{ + elem *e = toElemBin(irs,OPmin); + return e; +} + +/*************************************** + */ + +elem *CatExp::toElem(IRState *irs) +{ elem *e; + +#if 0 + printf("CatExp::toElem()\n"); + print(); +#endif + + Type *tb1 = e1->type->toBasetype(); + Type *tb2 = e2->type->toBasetype(); + + Type *ta = (tb1->ty == Tarray || tb1->ty == Tsarray) ? tb1 : tb2; + Type *tn = ta->nextOf(); + + if (e1->op == TOKcat) + { + elem *ep; + CatExp *ce = this; + int n = 2; + + ep = eval_Darray(irs, ce->e2); + do + { + n++; + ce = (CatExp *)ce->e1; + ep = el_param(ep, eval_Darray(irs, ce->e2)); + } while (ce->e1->op == TOKcat); + ep = el_param(ep, eval_Darray(irs, ce->e1)); + ep = el_params( + ep, + el_long(TYsize_t, n), + ta->getTypeInfo(NULL)->toElem(irs), + NULL); + e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYCATNT]), ep); + e->Eflags |= EFLAGS_variadic; + } + else + { + elem *e1; + elem *e2; + elem *ep; + + e1 = eval_Darray(irs, this->e1); + e2 = eval_Darray(irs, this->e2); + ep = el_params(e2, e1, ta->getTypeInfo(NULL)->toElem(irs), NULL); + e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYCATT]), ep); + } + el_setLoc(e,loc); + return e; +} + +/*************************************** + */ + +elem *MulExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPmul); +} + +/************************************ + */ + +elem *DivExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPdiv); +} + +/*************************************** + */ + +elem *ModExp::toElem(IRState *irs) +{ + elem *e; + elem *e1; + elem *e2; + tym_t tym; + + tym = type->totym(); + + e1 = this->e1->toElem(irs); + e2 = this->e2->toElem(irs); + +#if 0 // Now inlined + if (this->e1->type->isfloating()) + { elem *ep; + + switch (this->e1->type->ty) + { + case Tfloat32: + case Timaginary32: + e1 = el_una(OPf_d, TYdouble, e1); + e2 = el_una(OPf_d, TYdouble, e2); + case Tfloat64: + case Timaginary64: + e1 = el_una(OPd_ld, TYldouble, e1); + e2 = el_una(OPd_ld, TYldouble, e2); + break; + case Tfloat80: + case Timaginary80: + break; + default: + assert(0); + break; + } + ep = el_param(e2,e1); + e = el_bin(OPcall,tym,el_var(rtlsym[RTLSYM_MODULO]),ep); + } + else +#endif + e = el_bin(OPmod,tym,e1,e2); + el_setLoc(e,loc); + return e; +} + +/*************************************** + */ + +elem *CmpExp::toElem(IRState *irs) +{ + elem *e; + enum OPER eop; + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + + switch (op) + { + case TOKlt: eop = OPlt; break; + case TOKgt: eop = OPgt; break; + case TOKle: eop = OPle; break; + case TOKge: eop = OPge; break; + case TOKequal: eop = OPeqeq; break; + case TOKnotequal: eop = OPne; break; + + // NCEG floating point compares + case TOKunord: eop = OPunord; break; + case TOKlg: eop = OPlg; break; + case TOKleg: eop = OPleg; break; + case TOKule: eop = OPule; break; + case TOKul: eop = OPul; break; + case TOKuge: eop = OPuge; break; + case TOKug: eop = OPug; break; + case TOKue: eop = OPue; break; + default: + dump(0); + assert(0); + } + if (!t1->isfloating()) + { + // Convert from floating point compare to equivalent + // integral compare + eop = (enum OPER)rel_integral(eop); + } + if ((int)eop > 1 && t1->ty == Tclass && t2->ty == Tclass) + { +#if 1 + assert(0); +#else + elem *ec1; + elem *ec2; + + ec1 = e1->toElem(irs); + ec2 = e2->toElem(irs); + e = el_bin(OPcall,TYint,el_var(rtlsym[RTLSYM_OBJ_CMP]),el_param(ec1, ec2)); + e = el_bin(eop, TYint, e, el_long(TYint, 0)); +#endif + } + else if ((int)eop > 1 && + (t1->ty == Tarray || t1->ty == Tsarray) && + (t2->ty == Tarray || t2->ty == Tsarray)) + { + elem *ea1; + elem *ea2; + elem *ep; + Type *telement = t1->nextOf()->toBasetype(); + int rtlfunc; + + ea1 = e1->toElem(irs); + ea1 = array_toDarray(t1, ea1); + ea2 = e2->toElem(irs); + ea2 = array_toDarray(t2, ea2); + +#if DMDV2 + ep = el_params(telement->arrayOf()->getInternalTypeInfo(NULL)->toElem(irs), + ea2, ea1, NULL); + rtlfunc = RTLSYM_ARRAYCMP2; +#else + ep = el_params(telement->getInternalTypeInfo(NULL)->toElem(irs), ea2, ea1, NULL); + rtlfunc = RTLSYM_ARRAYCMP; +#endif + e = el_bin(OPcall, TYint, el_var(rtlsym[rtlfunc]), ep); + e = el_bin(eop, TYint, e, el_long(TYint, 0)); + el_setLoc(e,loc); + } + else + { + if ((int)eop <= 1) + { + /* The result is determinate, create: + * (e1 , e2) , eop + */ + e = toElemBin(irs,OPcomma); + e = el_bin(OPcomma,e->Ety,e,el_long(e->Ety,(int)eop)); + } + else + e = toElemBin(irs,eop); + } + return e; +} + +elem *EqualExp::toElem(IRState *irs) +{ + //printf("EqualExp::toElem() %s\n", toChars()); + + elem *e; + enum OPER eop; + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + + switch (op) + { + case TOKequal: eop = OPeqeq; break; + case TOKnotequal: eop = OPne; break; + default: + dump(0); + assert(0); + } + + //printf("EqualExp::toElem()\n"); + if (t1->ty == Tstruct) + { // Do bit compare of struct's + + elem *es1 = e1->toElem(irs); + elem *es2 = e2->toElem(irs); + es1 = addressElem(es1, t1); + es2 = addressElem(es2, t2); + e = el_param(es1, es2); + elem *ecount = el_long(TYsize_t, t1->size()); + e = el_bin(OPmemcmp, TYint, e, ecount); + e = el_bin(eop, TYint, e, el_long(TYint, 0)); + el_setLoc(e,loc); + } +#if 0 + else if (t1->ty == Tclass && t2->ty == Tclass) + { + elem *ec1 = e1->toElem(irs); + elem *ec2 = e2->toElem(irs); + e = el_bin(OPcall,TYint,el_var(rtlsym[RTLSYM_OBJ_EQ]),el_param(ec1, ec2)); + } +#endif + else if ((t1->ty == Tarray || t1->ty == Tsarray) && + (t2->ty == Tarray || t2->ty == Tsarray)) + { + Type *telement = t1->nextOf()->toBasetype(); + + elem *ea1 = e1->toElem(irs); + ea1 = array_toDarray(t1, ea1); + elem *ea2 = e2->toElem(irs); + ea2 = array_toDarray(t2, ea2); + +#if DMDV2 + elem *ep = el_params(telement->arrayOf()->getInternalTypeInfo(NULL)->toElem(irs), + ea2, ea1, NULL); + int rtlfunc = RTLSYM_ARRAYEQ2; +#else + elem *ep = el_params(telement->getInternalTypeInfo(NULL)->toElem(irs), ea2, ea1, NULL); + int rtlfunc = RTLSYM_ARRAYEQ; +#endif + e = el_bin(OPcall, TYint, el_var(rtlsym[rtlfunc]), ep); + if (op == TOKnotequal) + e = el_bin(OPxor, TYint, e, el_long(TYint, 1)); + el_setLoc(e,loc); + } + else if (t1->ty == Taarray && t2->ty == Taarray) + { TypeAArray *taa = (TypeAArray *)t1; + Symbol *s = taa->aaGetSymbol("Equal", 0); + elem *ti = taa->getTypeInfo(NULL)->toElem(irs); + elem *ea1 = e1->toElem(irs); + elem *ea2 = e2->toElem(irs); + // aaEqual(ti, e1, e2) + elem *ep = el_params(ea2, ea1, ti, NULL); + e = el_bin(OPcall, TYnptr, el_var(s), ep); + if (op == TOKnotequal) + e = el_bin(OPxor, TYint, e, el_long(TYint, 1)); + el_setLoc(e,loc); + return e; + } + else + e = toElemBin(irs, eop); + return e; +} + +elem *IdentityExp::toElem(IRState *irs) +{ + elem *e; + enum OPER eop; + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + + switch (op) + { + case TOKidentity: eop = OPeqeq; break; + case TOKnotidentity: eop = OPne; break; + default: + dump(0); + assert(0); + } + + //printf("IdentityExp::toElem() %s\n", toChars()); + + if (t1->ty == Tstruct || t1->isfloating()) + { // Do bit compare of struct's + elem *es1; + elem *es2; + elem *ecount; + + es1 = e1->toElem(irs); + es1 = addressElem(es1, e1->type); + //es1 = el_una(OPaddr, TYnptr, es1); + es2 = e2->toElem(irs); + es2 = addressElem(es2, e2->type); + //es2 = el_una(OPaddr, TYnptr, es2); + e = el_param(es1, es2); + ecount = el_long(TYsize_t, t1->size()); + e = el_bin(OPmemcmp, TYint, e, ecount); + e = el_bin(eop, TYint, e, el_long(TYint, 0)); + el_setLoc(e,loc); + } + else if ((t1->ty == Tarray || t1->ty == Tsarray) && + (t2->ty == Tarray || t2->ty == Tsarray)) + { + elem *ea1; + elem *ea2; + + ea1 = e1->toElem(irs); + ea1 = array_toDarray(t1, ea1); + ea2 = e2->toElem(irs); + ea2 = array_toDarray(t2, ea2); + + e = el_bin(eop, type->totym(), ea1, ea2); + el_setLoc(e,loc); + } + else + e = toElemBin(irs, eop); + + return e; +} + + +/*************************************** + */ + +elem *InExp::toElem(IRState *irs) +{ elem *e; + elem *key = e1->toElem(irs); + elem *aa = e2->toElem(irs); + elem *ep; + elem *keyti; + TypeAArray *taa = (TypeAArray *)e2->type->toBasetype(); + + // aaInX(aa, keyti, key); + key = addressElem(key, e1->type); + Symbol *s = taa->aaGetSymbol("InX", 0); + keyti = taa->index->getInternalTypeInfo(NULL)->toElem(irs); + ep = el_params(key, keyti, aa, NULL); + e = el_bin(OPcall, type->totym(), el_var(s), ep); + + el_setLoc(e,loc); + return e; +} + +/*************************************** + */ + +elem *RemoveExp::toElem(IRState *irs) +{ elem *e; + Type *tb = e1->type->toBasetype(); + assert(tb->ty == Taarray); + TypeAArray *taa = (TypeAArray *)tb; + elem *ea = e1->toElem(irs); + elem *ekey = e2->toElem(irs); + elem *ep; + elem *keyti; + + ekey = addressElem(ekey, e1->type); + Symbol *s = taa->aaGetSymbol("DelX", 0); + keyti = taa->index->getInternalTypeInfo(NULL)->toElem(irs); + ep = el_params(ekey, keyti, ea, NULL); + e = el_bin(OPcall, TYnptr, el_var(s), ep); + + el_setLoc(e,loc); + return e; +} + +/*************************************** + */ + +elem *AssignExp::toElem(IRState *irs) +{ + //printf("AssignExp::toElem('%s')\n", toChars()); + Type *t1b = e1->type->toBasetype(); + + // Look for array.length = n + if (e1->op == TOKarraylength) + { + // Generate: + // _d_arraysetlength(e2, sizeelem, &ale->e1); + + ArrayLengthExp *ale = (ArrayLengthExp *)e1; + + elem *p1 = e2->toElem(irs); + elem *p3 = ale->e1->toElem(irs); + p3 = addressElem(p3, NULL); + Type *t1 = ale->e1->type->toBasetype(); + + // call _d_arraysetlengthT(ti, e2, &ale->e1); + elem *p2 = t1->getTypeInfo(NULL)->toElem(irs); + elem *ep = el_params(p3, p1, p2, NULL); // c function + int r = t1->nextOf()->isZeroInit() ? RTLSYM_ARRAYSETLENGTHT : RTLSYM_ARRAYSETLENGTHIT; + + elem *e = el_bin(OPcall, type->totym(), el_var(rtlsym[r]), ep); + el_setLoc(e, loc); + return e; + } + + elem *e; + IndexExp *ae; + + // Look for array[]=n + if (e1->op == TOKslice) + { + SliceExp *are = (SliceExp *)(e1); + Type *t1 = t1b; + Type *t2 = e2->type->toBasetype(); + + // which we do if the 'next' types match + if (ismemset) + { // Do a memset for array[]=v + //printf("Lpair %s\n", toChars()); + elem *evalue; + elem *enbytes; + elem *elength; + elem *einit; + Type *ta = are->e1->type->toBasetype(); + Type *tb = ta->nextOf()->toBasetype(); + unsigned sz = tb->size(); + tym_t tym = type->totym(); + + elem *n1 = are->e1->toElem(irs); + elem *elwr = are->lwr ? are->lwr->toElem(irs) : NULL; + elem *eupr = are->upr ? are->upr->toElem(irs) : NULL; + + elem *n1x = n1; + + // Look for array[]=n + if (ta->ty == Tsarray) + { + TypeSArray *ts = (TypeSArray *) ta; + n1 = array_toPtr(ta, n1); + enbytes = ts->dim->toElem(irs); + n1x = n1; + n1 = el_same(&n1x); + einit = resolveLengthVar(are->lengthVar, &n1, ta); + } + else if (ta->ty == Tarray) + { + n1 = el_same(&n1x); + einit = resolveLengthVar(are->lengthVar, &n1, ta); + enbytes = el_copytree(n1); + n1 = array_toPtr(ta, n1); + enbytes = el_una(I64 ? OP128_64 : OP64_32, TYsize_t, enbytes); + } + else if (ta->ty == Tpointer) + { + n1 = el_same(&n1x); + enbytes = el_long(TYsize_t, -1); // largest possible index + einit = NULL; + } + + // Enforce order of evaluation of n1[elwr..eupr] as n1,elwr,eupr + elem *elwrx = elwr; + if (elwr) elwr = el_same(&elwrx); + elem *euprx = eupr; + if (eupr) eupr = el_same(&euprx); + +#if 0 + printf("sz = %d\n", sz); + printf("n1x\n"); + elem_print(n1x); + printf("einit\n"); + elem_print(einit); + printf("elwrx\n"); + elem_print(elwrx); + printf("euprx\n"); + elem_print(euprx); + printf("n1\n"); + elem_print(n1); + printf("elwr\n"); + elem_print(elwr); + printf("eupr\n"); + elem_print(eupr); + printf("enbytes\n"); + elem_print(enbytes); +#endif + einit = el_combine(n1x, einit); + einit = el_combine(einit, elwrx); + einit = el_combine(einit, euprx); + + evalue = this->e2->toElem(irs); + +#if 0 + printf("n1\n"); + elem_print(n1); + printf("enbytes\n"); + elem_print(enbytes); +#endif + + if (irs->arrayBoundsCheck() && eupr && ta->ty != Tpointer) + { + elem *c1; + elem *c2; + elem *ea; + elem *eb; + elem *enbytesx; + + assert(elwr); + enbytesx = enbytes; + enbytes = el_same(&enbytesx); + c1 = el_bin(OPle, TYint, el_copytree(eupr), enbytesx); + c2 = el_bin(OPle, TYint, el_copytree(elwr), el_copytree(eupr)); + c1 = el_bin(OPandand, TYint, c1, c2); + + // Construct: (c1 || ModuleArray(line)) + Symbol *sassert; + + sassert = irs->blx->module->toModuleArray(); + ea = el_bin(OPcall,TYvoid,el_var(sassert), el_long(TYint, loc.linnum)); + eb = el_bin(OPoror,TYvoid,c1,ea); + einit = el_combine(einit, eb); + } + + if (elwr) + { elem *elwr2; + + el_free(enbytes); + elwr2 = el_copytree(elwr); + elwr2 = el_bin(OPmul, TYsize_t, elwr2, el_long(TYsize_t, sz)); + n1 = el_bin(OPadd, TYnptr, n1, elwr2); + enbytes = el_bin(OPmin, TYsize_t, eupr, elwr); + elength = el_copytree(enbytes); + } + else + elength = el_copytree(enbytes); + e = setArray(n1, enbytes, tb, evalue, irs, op); + e = el_pair(TYdarray, elength, e); + e = el_combine(einit, e); + //elem_print(e); + goto Lret; + } +#if 0 + else if (e2->op == TOKadd || e2->op == TOKmin) + { + /* It's ea[] = eb[] +- ec[] + */ + BinExp *e2a = (BinExp *)e2; + Type *t = e2->type->toBasetype()->nextOf()->toBasetype(); + if (t->ty != Tfloat32 && t->ty != Tfloat64 && t->ty != Tfloat80) + { + e2->error("array add/min for %s not supported", t->toChars()); + return el_long(TYint, 0); + } + elem *ea = e1->toElem(irs); + ea = array_toDarray(e1->type, ea); + elem *eb = e2a->e1->toElem(irs); + eb = array_toDarray(e2a->e1->type, eb); + elem *ec = e2a->e2->toElem(irs); + ec = array_toDarray(e2a->e2->type, ec); + + int rtl = RTLSYM_ARRAYASSADDFLOAT; + if (t->ty == Tfloat64) + rtl = RTLSYM_ARRAYASSADDDOUBLE; + else if (t->ty == Tfloat80) + rtl = RTLSYM_ARRAYASSADDREAL; + if (e2->op == TOKmin) + { + rtl = RTLSYM_ARRAYASSMINFLOAT; + if (t->ty == Tfloat64) + rtl = RTLSYM_ARRAYASSMINDOUBLE; + else if (t->ty == Tfloat80) + rtl = RTLSYM_ARRAYASSMINREAL; + } + + /* Set parameters so the order of evaluation is eb, ec, ea + */ + elem *ep = el_params(eb, ec, ea, NULL); + e = el_bin(OPcall, type->totym(), el_var(rtlsym[rtl]), ep); + goto Lret; + } +#endif + else + { + /* It's array1[]=array2[] + * which is a memcpy + */ + elem *ep; + + elem *eto = e1->toElem(irs); + elem *efrom = e2->toElem(irs); + + unsigned size = t1->nextOf()->size(); + elem *esize = el_long(TYsize_t, size); + + /* Determine if we need to do postblit + */ + int postblit = 0; + if (needsPostblit(t1)) + postblit = 1; + + assert(e2->type->ty != Tpointer); + + if (!postblit && !irs->arrayBoundsCheck()) + { + elem *ex = el_same(&eto); + + // Determine if elen is a constant + elem *elen; + if (eto->Eoper == OPpair && + eto->E1->Eoper == OPconst) + { + elen = el_copytree(eto->E1); + } + else + { + // It's not a constant, so pull it from the dynamic array + elen = el_una(I64 ? OP128_64 : OP64_32, TYsize_t, el_copytree(ex)); + } + + esize = el_bin(OPmul, TYsize_t, elen, esize); + elem *epto = array_toPtr(e1->type, ex); + elem *epfr = array_toPtr(e2->type, efrom); +#if 1 + // memcpy() is faster, so if we can't beat 'em, join 'em + e = el_params(esize, epfr, epto, NULL); + e = el_bin(OPcall,TYnptr,el_var(rtlsym[RTLSYM_MEMCPY]),e); +#else + e = el_bin(OPmemcpy, TYnptr, epto, el_param(epfr, esize)); +#endif + e = el_pair(eto->Ety, el_copytree(elen), e); + e = el_combine(eto, e); + } +#if DMDV2 + else if (postblit && op != TOKblit) + { + /* Generate: + * _d_arrayassign(ti, efrom, eto) + * or: + * _d_arrayctor(ti, efrom, eto) + */ + el_free(esize); + Expression *ti = t1->nextOf()->toBasetype()->getTypeInfo(NULL); + ep = el_params(eto, efrom, ti->toElem(irs), NULL); + int rtl = (op == TOKconstruct) ? RTLSYM_ARRAYCTOR : RTLSYM_ARRAYASSIGN; + e = el_bin(OPcall, type->totym(), el_var(rtlsym[rtl]), ep); + } +#endif + else + { + // Generate: + // _d_arraycopy(eto, efrom, esize) + + ep = el_params(eto, efrom, esize, NULL); + e = el_bin(OPcall, type->totym(), el_var(rtlsym[RTLSYM_ARRAYCOPY]), ep); + } + el_setLoc(e, loc); + return e; + } + } + + if (e1->op == TOKindex) + { + ae = (IndexExp *)(e1); + } + +#if DMDV2 + /* Look for reference initializations + */ + if (op == TOKconstruct && e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e1; + Declaration *s = ve->var; + if (s->storage_class & (STCout | STCref)) + { +#if 0 + Expression *ae = e2->addressOf(NULL); + e = ae->toElem(irs); +#else + e = e2->toElem(irs); + e = addressElem(e, e2->type); +#endif + elem *es = el_var(s->toSymbol()); + es->Ety = TYnptr; + e = el_bin(OPeq, TYnptr, es, e); +// BUG: type is struct, and e2 is TOKint64 + goto Lret; + } + } +#endif + +#if 1 + /* This will work if we can distinguish an assignment from + * an initialization of the lvalue. It'll work if the latter. + * If the former, because of aliasing of the return value with + * function arguments, it'll fail. + */ + if (op == TOKconstruct && e2->op == TOKcall) + { CallExp *ce = (CallExp *)e2; + + TypeFunction *tf = (TypeFunction *)ce->e1->type->toBasetype(); + if (tf->ty == Tfunction && tf->retStyle() == RETstack) + { + elem *ehidden = e1->toElem(irs); + ehidden = el_una(OPaddr, TYnptr, ehidden); + assert(!irs->ehidden); + irs->ehidden = ehidden; + e = e2->toElem(irs); + goto Lret; + } + } +#endif +//if (op == TOKconstruct) printf("construct\n"); + if (t1b->ty == Tstruct || t1b->ty == Tsarray) + { elem *eleft = e1->toElem(irs); + + if (e2->op == TOKint64) + { /* Implement: + * (struct = 0) + * with: + * memset(&struct, 0, struct.sizeof) + */ + elem *ey = NULL; + unsigned sz = e1->type->size(); + StructDeclaration *sd = ((TypeStruct *)t1b)->sym; + if (sd->isnested && op == TOKconstruct) + { + ey = el_una(OPaddr, TYnptr, eleft); + eleft = el_same(&ey); + ey = setEthis(loc, irs, ey, sd); + sz = sd->vthis->offset; + } + + elem *el = eleft; + elem *enbytes = el_long(TYsize_t, sz); + elem *evalue = el_long(TYsize_t, 0); + + if (!(sd->isnested && op == TOKconstruct)) + el = el_una(OPaddr, TYnptr, el); + e = el_param(enbytes, evalue); + e = el_bin(OPmemset,TYnptr,el,e); + e = el_combine(ey, e); + el_setLoc(e, loc); + //e = el_una(OPind, TYstruct, e); + } + else + { + //printf("toElemBin() '%s'\n", toChars()); + + tym_t tym = type->totym(); + + elem *e1 = eleft; + elem *ex = e1; + if (e1->Eoper == OPind) + ex = e1->E1; + if (this->e2->op == TOKstructliteral && + ex->Eoper == OPvar && ex->EV.sp.Voffset == 0) + { StructLiteralExp *se = (StructLiteralExp *)this->e2; + + Symbol *symSave = se->sym; + size_t soffsetSave = se->soffset; + int fillHolesSave = se->fillHoles; + + se->sym = ex->EV.sp.Vsym; + se->soffset = 0; + se->fillHoles = (op == TOKconstruct || op == TOKblit) ? 1 : 0; + + el_free(e1); + e = this->e2->toElem(irs); + + se->sym = symSave; + se->soffset = soffsetSave; + se->fillHoles = fillHolesSave; + } + else + { + elem *e2 = this->e2->toElem(irs); + e = el_bin(OPstreq,tym,e1,e2); + e->ET = this->e1->type->toCtype(); + if (type_size(e->ET) == 0) + e->Eoper = OPcomma; + } + goto Lret; + } + } + else + e = toElemBin(irs,OPeq); + return e; + + Lret: + el_setLoc(e,loc); + return e; +} + +/*************************************** + */ + +elem *AddAssignExp::toElem(IRState *irs) +{ + //printf("AddAssignExp::toElem() %s\n", toChars()); + elem *e = toElemBin(irs,OPaddass); + return e; +} + + +/*************************************** + */ + +elem *MinAssignExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPminass); +} + +/*************************************** + */ + +elem *CatAssignExp::toElem(IRState *irs) +{ + //printf("CatAssignExp::toElem('%s')\n", toChars()); + elem *e; + Type *tb1 = e1->type->toBasetype(); + Type *tb2 = e2->type->toBasetype(); + + if (tb1->ty == Tarray && tb2->ty == Tdchar && + (tb1->nextOf()->ty == Tchar || tb1->nextOf()->ty == Twchar)) + { // Append dchar to char[] or wchar[] + + elem *e1 = this->e1->toElem(irs); + e1 = el_una(OPaddr, TYnptr, e1); + + elem *e2 = this->e2->toElem(irs); + + elem *ep = el_params(e2, e1, NULL); + int rtl = (tb1->nextOf()->ty == Tchar) + ? RTLSYM_ARRAYAPPENDCD + : RTLSYM_ARRAYAPPENDWD; + e = el_bin(OPcall, TYdarray, el_var(rtlsym[rtl]), ep); + el_setLoc(e,loc); + } + else if (tb1->ty == Tarray || tb2->ty == Tsarray) + { + elem *e1 = this->e1->toElem(irs); + elem *e2 = this->e2->toElem(irs); + + Type *tb1n = tb1->nextOf()->toBasetype(); + if ((tb2->ty == Tarray || tb2->ty == Tsarray) && + tb1n->equals(tb2->nextOf()->toBasetype())) + { // Append array + e1 = el_una(OPaddr, TYnptr, e1); + if (tybasic(e2->Ety) == TYstruct || tybasic(e2->Ety) == TYarray) + { + e2 = el_una(OPstrpar, TYstruct, e2); + e2->ET = e2->E1->ET; + } + elem *ep = el_params(e2, e1, this->e1->type->getTypeInfo(NULL)->toElem(irs), NULL); + e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYAPPENDT]), ep); + } + else if (I64) + { // Append element + + elem *e2x = NULL; + + if (e2->Eoper != OPvar && e2->Eoper != OPconst) + { + // Evaluate e2 and assign result to temporary s2. + // Do this because of: + // a ~= a[$-1] + // because $ changes its value + symbol *s2 = symbol_genauto(tb2->toCtype()); + e2x = el_bin(OPeq, e2->Ety, el_var(s2), e2); + if (tybasic(e2->Ety) == TYstruct) + { + e2x->Eoper = OPstreq; + e2x->ET = tb1n->toCtype(); + } + else if (tybasic(e2->Ety) == TYarray) + { + e2x->Eoper = OPstreq; + e2x->Ejty = e2x->Ety = TYstruct; + e2x->ET = tb1n->toCtype(); + } + e2 = el_var(s2); + } + + // Extend array with _d_arrayappendcTX(TypeInfo ti, e1, 1) + e1 = el_una(OPaddr, TYnptr, e1); + elem *ep = el_param(e1, this->e1->type->getTypeInfo(NULL)->toElem(irs)); + ep = el_param(el_long(TYsize_t, 1), ep); + e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYAPPENDCTX]), ep); + symbol *stmp = symbol_genauto(tb1->toCtype()); + e = el_bin(OPeq, TYdarray, el_var(stmp), e); + + // Assign e2 to last element in stmp[] + // *(stmp.ptr + (stmp.length - 1) * szelem) = e2 + + elem *eptr = array_toPtr(tb1, el_var(stmp)); + elem *elength = el_una(I64 ? OP128_64 : OP64_32, TYsize_t, el_var(stmp)); + elength = el_bin(OPmin, TYsize_t, elength, el_long(TYsize_t, 1)); + elength = el_bin(OPmul, TYsize_t, elength, el_long(TYsize_t, this->e2->type->size())); + eptr = el_bin(OPadd, TYnptr, eptr, elength); + StructDeclaration *sd = needsPostblit(tb2); + elem *epost = NULL; + if (sd) + epost = el_same(&eptr); + elem *ederef = el_una(OPind, e2->Ety, eptr); + elem *eeq = el_bin(OPeq, e2->Ety, ederef, e2); + + if (tybasic(e2->Ety) == TYstruct) + { + eeq->Eoper = OPstreq; + eeq->ET = tb1n->toCtype(); + } + else if (tybasic(e2->Ety) == TYarray) + { + eeq->Eoper = OPstreq; + eeq->Ejty = eeq->Ety = TYstruct; + eeq->ET = tb1n->toCtype(); + } + + /* Need to call postblit on eeq + */ + if (sd) + { FuncDeclaration *fd = sd->postblit; + epost = callfunc(loc, irs, 1, Type::tvoid, epost, sd->type->pointerTo(), fd, fd->type, NULL, NULL); + eeq = el_bin(OPcomma, epost->Ety, eeq, epost); + } + + e = el_combine(e2x, e); + e = el_combine(e, eeq); + e = el_combine(e, el_var(stmp)); + } + else + { // Append element + e1 = el_una(OPaddr, TYnptr, e1); + if (tybasic(e2->Ety) == TYstruct || tybasic(e2->Ety) == TYarray) + { + e2 = el_una(OPstrpar, TYstruct, e2); + e2->ET = e2->E1->ET; + } + elem *ep = el_params(e2, e1, this->e1->type->getTypeInfo(NULL)->toElem(irs), NULL); + e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYAPPENDCT]), ep); + e->Eflags |= EFLAGS_variadic; + } + + el_setLoc(e,loc); + } + else + assert(0); + return e; +} + + +/*************************************** + */ + +elem *DivAssignExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPdivass); +} + + +/*************************************** + */ + +elem *ModAssignExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPmodass); +} + + +/*************************************** + */ + +elem *MulAssignExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPmulass); +} + + +/*************************************** + */ + +elem *ShlAssignExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPshlass); +} + + +/*************************************** + */ + +elem *ShrAssignExp::toElem(IRState *irs) +{ + //printf("ShrAssignExp::toElem() %s, %s\n", e1->type->toChars(), e1->toChars()); + Type *t1 = e1->type; + if (e1->op == TOKcast) + { /* Use the type before it was integrally promoted to int + */ + CastExp *ce = (CastExp *)e1; + t1 = ce->e1->type; + } + return toElemBin(irs, t1->isunsigned() ? OPshrass : OPashrass); +} + + +/*************************************** + */ + +elem *UshrAssignExp::toElem(IRState *irs) +{ + return toElemBin(irs, OPshrass); +} + + +/*************************************** + */ + +elem *AndAssignExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPandass); +} + + +/*************************************** + */ + +elem *OrAssignExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPorass); +} + + +/*************************************** + */ + +elem *XorAssignExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPxorass); +} + + +/*************************************** + */ + +elem *PowAssignExp::toElem(IRState *irs) +{ + Type *tb1 = e1->type->toBasetype(); + if (tb1->ty == Tarray || tb1->ty == Tsarray) + { + error("Array operation %s not implemented", toChars()); + return el_long(type->totym(), 0); // error recovery + } + else + { assert(0); + return NULL; + } +} + + +/*************************************** + */ + +elem *AndAndExp::toElem(IRState *irs) +{ + tym_t tym = type->totym(); + + elem *el = e1->toElem(irs); + elem *er = e2->toElemDtor(irs); + elem *e = el_bin(OPandand,tym,el,er); + + el_setLoc(e,loc); + + if (global.params.cov && e2->loc.linnum) + e->E2 = el_combine(incUsageElem(irs, e2->loc), e->E2); + return e; +} + + +/*************************************** + */ + +elem *OrOrExp::toElem(IRState *irs) +{ + tym_t tym = type->totym(); + + elem *el = e1->toElem(irs); + elem *er = e2->toElemDtor(irs); + elem *e = el_bin(OPoror,tym,el,er); + + el_setLoc(e,loc); + + if (global.params.cov && e2->loc.linnum) + e->E2 = el_combine(incUsageElem(irs, e2->loc), e->E2); + return e; +} + + +/*************************************** + */ + +elem *XorExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPxor); +} + + +/*************************************** + */ + +elem *PowExp::toElem(IRState *irs) +{ + Type *tb1 = e1->type->toBasetype(); + if (tb1->ty == Tarray || tb1->ty == Tsarray) + { + error("Array operation %s not implemented", toChars()); + return el_long(type->totym(), 0); // error recovery + } + assert(0); + return NULL; +} + + +/*************************************** + */ + +elem *AndExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPand); +} + + +/*************************************** + */ + +elem *OrExp::toElem(IRState *irs) +{ + return toElemBin(irs,OPor); +} + + +/*************************************** + */ + +elem *ShlExp::toElem(IRState *irs) +{ + return toElemBin(irs, OPshl); +} + + +/*************************************** + */ + +elem *ShrExp::toElem(IRState *irs) +{ + return toElemBin(irs, e1->type->isunsigned() ? OPshr : OPashr); +} + + +/*************************************** + */ + +elem *UshrExp::toElem(IRState *irs) +{ + //return toElemBin(irs, OPshr); + elem *eleft = e1->toElem(irs); + eleft->Ety = touns(eleft->Ety); + elem *eright = e2->toElem(irs); + elem *e = el_bin(OPshr, type->totym(), eleft, eright); + el_setLoc(e, loc); + return e; +} + +/**************************************** + */ + +elem *CommaExp::toElem(IRState *irs) +{ + assert(e1 && e2); + elem *eleft = e1->toElem(irs); + elem *eright = e2->toElem(irs); + elem *e = el_combine(eleft, eright); + if (e) + el_setLoc(e, loc); + return e; +} + + +/*************************************** + */ + +elem *CondExp::toElem(IRState *irs) +{ + elem *ec = econd->toElem(irs); + + elem *eleft = e1->toElemDtor(irs); + tym_t ty = eleft->Ety; + if (global.params.cov && e1->loc.linnum) + eleft = el_combine(incUsageElem(irs, e1->loc), eleft); + + elem *eright = e2->toElemDtor(irs); + if (global.params.cov && e2->loc.linnum) + eright = el_combine(incUsageElem(irs, e2->loc), eright); + + elem *e = el_bin(OPcond, ty, ec, el_bin(OPcolon, ty, eleft, eright)); + if (tybasic(ty) == TYstruct) + e->ET = e1->type->toCtype(); + el_setLoc(e, loc); + return e; +} + + +/*************************************** + */ + +elem *TypeExp::toElem(IRState *irs) +{ +#ifdef DEBUG + printf("TypeExp::toElem()\n"); +#endif + error("type %s is not an expression", toChars()); + return el_long(TYint, 0); +} + +elem *ScopeExp::toElem(IRState *irs) +{ + error("%s is not an expression", sds->toChars()); + return el_long(TYint, 0); +} + +elem *DotVarExp::toElem(IRState *irs) +{ + // *(&e + offset) + + //printf("DotVarExp::toElem('%s')\n", toChars()); + + VarDeclaration *v = var->isVarDeclaration(); + if (!v) + { + error("%s is not a field, but a %s", var->toChars(), var->kind()); + } + + elem *e = e1->toElem(irs); + Type *tb1 = e1->type->toBasetype(); + if (tb1->ty != Tclass && tb1->ty != Tpointer) + //e = el_una(OPaddr, TYnptr, e); + e = addressElem(e, tb1); + e = el_bin(OPadd, TYnptr, e, el_long(TYsize_t, v ? v->offset : 0)); + e = el_una(OPind, type->totym(), e); + if (tybasic(e->Ety) == TYstruct) + { + e->ET = type->toCtype(); + } + el_setLoc(e,loc); + return e; +} + +elem *DelegateExp::toElem(IRState *irs) +{ + elem *e; + elem *ethis; + elem *ep; + Symbol *sfunc; + int directcall = 0; + + //printf("DelegateExp::toElem() '%s'\n", toChars()); + sfunc = func->toSymbol(); + if (func->isNested()) + { + ep = el_ptr(sfunc); + ethis = getEthis(loc, irs, func); + } + else + { + ethis = e1->toElem(irs); + if (e1->type->ty != Tclass && e1->type->ty != Tpointer) + ethis = addressElem(ethis, e1->type); + + if (e1->op == TOKsuper || e1->op == TOKdottype) + directcall = 1; + + if (!func->isThis()) + error("delegates are only for non-static functions"); + + if (!func->isVirtual() || + directcall || + func->isFinal()) + { + ep = el_ptr(sfunc); + } + else + { + // Get pointer to function out of virtual table + unsigned vindex; + + assert(ethis); + ep = el_same(ðis); + ep = el_una(OPind, TYnptr, ep); + vindex = func->vtblIndex; + + if ((int)vindex < 0) + error("Internal compiler error: malformed delegate. See Bugzilla 4860"); + + // Build *(ep + vindex * 4) + ep = el_bin(OPadd,TYnptr,ep,el_long(TYsize_t, vindex * PTRSIZE)); + ep = el_una(OPind,TYnptr,ep); + } + +// if (func->tintro) +// func->error(loc, "cannot form delegate due to covariant return type"); + } + if (ethis->Eoper == OPcomma) + { + ethis->E2 = el_pair(TYdelegate, ethis->E2, ep); + ethis->Ety = TYdelegate; + e = ethis; + } + else + e = el_pair(TYdelegate, ethis, ep); + el_setLoc(e,loc); + return e; +} + +elem *DotTypeExp::toElem(IRState *irs) +{ + // Just a pass-thru to e1 + elem *e; + + //printf("DotTypeExp::toElem() %s\n", toChars()); + e = e1->toElem(irs); + el_setLoc(e,loc); + return e; +} + +elem *CallExp::toElem(IRState *irs) +{ + //printf("CallExp::toElem('%s')\n", toChars()); + assert(e1->type); + elem *ec; + int directcall; + FuncDeclaration *fd; + Type *t1 = e1->type->toBasetype(); + Type *ectype = t1; + elem *eeq = NULL; + + elem *ehidden = irs->ehidden; + irs->ehidden = NULL; + + directcall = 0; + fd = NULL; + if (e1->op == TOKdotvar && t1->ty != Tdelegate) + { DotVarExp *dve = (DotVarExp *)e1; + + fd = dve->var->isFuncDeclaration(); + Expression *ex = dve->e1; + while (1) + { + switch (ex->op) + { + case TOKsuper: // super.member() calls directly + case TOKdottype: // type.member() calls directly + directcall = 1; + break; + + case TOKcast: + ex = ((CastExp *)ex)->e1; + continue; + + default: + //ex->dump(0); + break; + } + break; + } + ec = dve->e1->toElem(irs); + ectype = dve->e1->type->toBasetype(); + } + else if (e1->op == TOKvar) + { + fd = ((VarExp *)e1)->var->isFuncDeclaration(); + + if (fd && fd->ident == Id::alloca && + !fd->fbody && fd->linkage == LINKc && + arguments && arguments->dim == 1) + { Expression *arg = arguments->tdata()[0]; + arg = arg->optimize(WANTvalue); + if (arg->isConst() && arg->type->isintegral()) + { dinteger_t sz = arg->toInteger(); + if (sz > 0 && sz < 0x40000) + { + // It's an alloca(sz) of a fixed amount. + // Replace with an array allocated on the stack + // of the same size: char[sz] tmp; + + Symbol *stmp; + ::type *t; + + assert(!ehidden); + t = type_allocn(TYarray, tschar); + t->Tdim = sz; + stmp = symbol_genauto(t); + ec = el_ptr(stmp); + el_setLoc(ec,loc); + return ec; + } + } + } + + ec = e1->toElem(irs); + } + else + { + ec = e1->toElem(irs); + if (arguments && arguments->dim) + { + /* The idea is to enforce expressions being evaluated left to right, + * even though call trees are evaluated parameters first. + * We just do a quick hack to catch the more obvious cases, though + * we need to solve this generally. + */ + if (ec->Eoper == OPind && el_sideeffect(ec->E1)) + { /* Rewrite (*exp)(arguments) as: + * tmp=exp, (*tmp)(arguments) + */ + elem *ec1 = ec->E1; + Symbol *stmp = symbol_genauto(type_fake(ec1->Ety)); + eeq = el_bin(OPeq, ec->Ety, el_var(stmp), ec1); + ec->E1 = el_var(stmp); + } + else if (tybasic(ec->Ety) == TYdelegate && el_sideeffect(ec)) + { /* Rewrite (exp)(arguments) as: + * tmp=exp, (tmp)(arguments) + */ + Symbol *stmp = symbol_genauto(type_fake(ec->Ety)); + eeq = el_bin(OPeq, ec->Ety, el_var(stmp), ec); + ec = el_var(stmp); + } + } + } + ec = callfunc(loc, irs, directcall, type, ec, ectype, fd, t1, ehidden, arguments); + el_setLoc(ec,loc); + if (eeq) + ec = el_combine(eeq, ec); + return ec; +} + +elem *AddrExp::toElem(IRState *irs) +{ + //printf("AddrExp::toElem('%s')\n", toChars()); + elem *e = e1->toElem(irs); + e = addressElem(e, e1->type); + e->Ety = type->totym(); + el_setLoc(e,loc); + return e; +} + +elem *PtrExp::toElem(IRState *irs) +{ + //printf("PtrExp::toElem() %s\n", toChars()); + elem *e = e1->toElem(irs); + e = el_una(OPind,type->totym(),e); + if (tybasic(e->Ety) == TYstruct) + { + e->ET = type->toCtype(); + } + el_setLoc(e,loc); + return e; +} + +elem *BoolExp::toElem(IRState *irs) +{ + elem *e1 = this->e1->toElem(irs); + return el_una(OPbool,type->totym(),e1); +} + +elem *DeleteExp::toElem(IRState *irs) +{ elem *e; + int rtl; + Type *tb; + + //printf("DeleteExp::toElem()\n"); + if (e1->op == TOKindex) + { + IndexExp *ae = (IndexExp *)(e1); + tb = ae->e1->type->toBasetype(); + if (tb->ty == Taarray) + { + TypeAArray *taa = (TypeAArray *)tb; + elem *ea = ae->e1->toElem(irs); + elem *ekey = ae->e2->toElem(irs); + elem *ep; + elem *keyti; + + if (tybasic(ekey->Ety) == TYstruct || tybasic(ekey->Ety) == TYarray) + { + ekey = el_una(OPstrpar, TYstruct, ekey); + ekey->ET = ekey->E1->ET; + } + + Symbol *s = taa->aaGetSymbol("Del", 0); + keyti = taa->index->getInternalTypeInfo(NULL)->toElem(irs); + ep = el_params(ekey, keyti, ea, NULL); + e = el_bin(OPcall, TYnptr, el_var(s), ep); + goto Lret; + } + } + //e1->type->print(); + e = e1->toElem(irs); + tb = e1->type->toBasetype(); + switch (tb->ty) + { + case Tarray: + { e = addressElem(e, e1->type); + rtl = RTLSYM_DELARRAYT; + + /* See if we need to run destructors on the array contents + */ + elem *et = NULL; + Type *tv = tb->nextOf()->toBasetype(); + while (tv->ty == Tsarray) + { TypeSArray *ta = (TypeSArray *)tv; + tv = tv->nextOf()->toBasetype(); + } + if (tv->ty == Tstruct) + { TypeStruct *ts = (TypeStruct *)tv; + StructDeclaration *sd = ts->sym; + if (sd->dtor) + et = tb->nextOf()->getTypeInfo(NULL)->toElem(irs); + } + if (!et) // if no destructors needed + et = el_long(TYnptr, 0); // pass null for TypeInfo + e = el_params(et, e, NULL); + // call _d_delarray_t(e, et); + e = el_bin(OPcall, TYvoid, el_var(rtlsym[rtl]), e); + goto Lret; + } + case Tclass: + if (e1->op == TOKvar) + { VarExp *ve = (VarExp *)e1; + if (ve->var->isVarDeclaration() && + ve->var->isVarDeclaration()->onstack) + { + rtl = RTLSYM_CALLFINALIZER; + if (tb->isClassHandle()->isInterfaceDeclaration()) + rtl = RTLSYM_CALLINTERFACEFINALIZER; + break; + } + } + e = addressElem(e, e1->type); + rtl = RTLSYM_DELCLASS; + if (tb->isClassHandle()->isInterfaceDeclaration()) + rtl = RTLSYM_DELINTERFACE; + break; + + case Tpointer: + e = addressElem(e, e1->type); + rtl = RTLSYM_DELMEMORY; + break; + + default: + assert(0); + break; + } + e = el_bin(OPcall, TYvoid, el_var(rtlsym[rtl]), e); + + Lret: + el_setLoc(e,loc); + return e; +} + +elem *VectorExp::toElem(IRState *irs) +{ +#if 0 + printf("VectorExp::toElem()\n"); + print(); + printf("\tfrom: %s\n", e1->type->toChars()); + printf("\tto : %s\n", to->toChars()); +#endif + + dinteger_t d; + real_t r; + if (e1->type->isfloating()) + r = e1->toReal(); + else if (e1->type->isintegral()) + d = e1->toInteger(); + else + assert(0); + elem *e = el_calloc(); + e->Eoper = OPconst; + e->Ety = type->totym(); + switch (tybasic(e->Ety)) + { + case TYfloat4: + for (size_t i = 0; i < 4; i++) + ((targ_float *)&e->EV.Vcent)[i] = r; + break; + case TYdouble2: + ((targ_double *)&e->EV.Vcent.lsw)[0] = r; + ((targ_double *)&e->EV.Vcent.msw)[0] = r; + break; + case TYschar16: + case TYuchar16: + for (size_t i = 0; i < 16; i++) + ((targ_uchar *)&e->EV.Vcent)[i] = d; + break; + case TYshort8: + case TYushort8: + for (size_t i = 0; i < 8; i++) + ((targ_ushort *)&e->EV.Vcent)[i] = d; + break; + case TYlong4: + case TYulong4: + for (size_t i = 0; i < 4; i++) + ((targ_ulong *)&e->EV.Vcent)[i] = d; + break; + case TYllong2: + case TYullong2: e->EV.Vcent.lsw = d; + e->EV.Vcent.msw = d; + break; + default: + assert(0); + } + el_setLoc(e, loc); + return e; +} + +elem *CastExp::toElem(IRState *irs) +{ + TY fty; + TY tty; + tym_t ftym; + tym_t ttym; + enum OPER eop; + +#if 0 + printf("CastExp::toElem()\n"); + print(); + printf("\tfrom: %s\n", e1->type->toChars()); + printf("\tto : %s\n", to->toChars()); +#endif + + elem *e = e1->toElem(irs); + Type *tfrom = e1->type->toBasetype(); + Type *t = to->toBasetype(); // skip over typedef's + +#if DMDV2 + if (tfrom->ty == Taarray) + tfrom = ((TypeAArray*)tfrom)->getImpl()->type; + if (t->ty == Taarray) + t = ((TypeAArray*)t)->getImpl()->type; +#endif + + if (t->equals(tfrom)) + goto Lret; + + fty = tfrom->ty; + //printf("fty = %d\n", fty); + tty = t->ty; + + if (tty == Tpointer && fty == Tarray +#if 0 + && (t->next->ty == Tvoid || t->next->equals(e1->type->next)) +#endif + ) + { + if (e->Eoper == OPvar) + { + // e1 -> *(&e1 + 4) + e = el_una(OPaddr, TYnptr, e); + e = el_bin(OPadd, TYnptr, e, el_long(TYsize_t, tysize[TYnptr])); + e = el_una(OPind,t->totym(),e); + } + else + { + // e1 -> (unsigned)(e1 >> 32) + if (I64) + { + e = el_bin(OPshr, TYucent, e, el_long(TYint, 64)); + e = el_una(OP128_64, t->totym(), e); + } + else + { + e = el_bin(OPshr, TYullong, e, el_long(TYint, 32)); + e = el_una(OP64_32, t->totym(), e); + } + } + goto Lret; + } + + if (tty == Tpointer && fty == Tsarray +#if 0 + && (t->next->ty == Tvoid || t->next->equals(e1->type->next)) +#endif + ) + { + // e1 -> &e1 + e = el_una(OPaddr, TYnptr, e); + goto Lret; + } + + // Convert from static array to dynamic array + if (tty == Tarray && fty == Tsarray) + { + e = sarray_toDarray(loc, tfrom, t, e); + goto Lret; + } + + // Convert from dynamic array to dynamic array + if (tty == Tarray && fty == Tarray) + { + unsigned fsize = tfrom->nextOf()->size(); + unsigned tsize = t->nextOf()->size(); + + if (fsize != tsize) + { // Array element sizes do not match, so we must adjust the dimensions + elem *ep = el_params(e, el_long(TYsize_t, fsize), el_long(TYsize_t, tsize), NULL); + e = el_bin(OPcall, type->totym(), el_var(rtlsym[RTLSYM_ARRAYCAST]), ep); + } + goto Lret; + } + + // Casting from base class to derived class requires a runtime check + if (fty == Tclass && tty == Tclass) + { + // Casting from derived class to base class is a no-op + int offset; + int rtl = RTLSYM_DYNAMIC_CAST; + + ClassDeclaration *cdfrom = tfrom->isClassHandle(); + ClassDeclaration *cdto = t->isClassHandle(); + if (cdfrom->isInterfaceDeclaration()) + { + rtl = RTLSYM_INTERFACE_CAST; + if (cdfrom->isCPPinterface()) + { + if (cdto->isCPPinterface()) + { + /* Casting from a C++ interface to a C++ interface + * is always a 'paint' operation + */ + goto Lret; // no-op + } + + /* Casting from a C++ interface to a class + * always results in null because there is no runtime + * information available to do it. + * + * Casting from a C++ interface to a non-C++ interface + * always results in null because there's no way one + * can be derived from the other. + */ + e = el_bin(OPcomma, TYnptr, e, el_long(TYnptr, 0)); + goto Lret; + } + } + if (cdto->isBaseOf(cdfrom, &offset) && offset != OFFSET_RUNTIME) + { + /* The offset from cdfrom=>cdto is known at compile time. + */ + + //printf("offset = %d\n", offset); + if (offset) + { /* Rewrite cast as (e ? e + offset : null) + */ + if (e1->op == TOKthis) + { // Assume 'this' is never null, so skip null check + e = el_bin(OPadd, TYnptr, e, el_long(TYsize_t, offset)); + } + else + { + elem *etmp = el_same(&e); + elem *ex = el_bin(OPadd, TYnptr, etmp, el_long(TYsize_t, offset)); + ex = el_bin(OPcolon, TYnptr, ex, el_long(TYnptr, 0)); + e = el_bin(OPcond, TYnptr, e, ex); + } + } + goto Lret; // no-op + } + + /* The offset from cdfrom=>cdto can only be determined at runtime. + */ + elem *ep = el_param(el_ptr(cdto->toSymbol()), e); + e = el_bin(OPcall, TYnptr, el_var(rtlsym[rtl]), ep); + goto Lret; + } + + if (fty == Tvector && tty == Tsarray) + { + if (tfrom->size() == t->size()) + goto Lret; + } + + ftym = tybasic(e->Ety); + ttym = tybasic(t->totym()); + if (ftym == ttym) + goto Lret; + + /* Reduce combinatorial explosion by rewriting the 'to' and 'from' types to a + * generic equivalent (as far as casting goes) + */ + switch (tty) + { + case Tpointer: + if (fty == Tdelegate) + goto Lpaint; + tty = I64 ? Tuns64 : Tuns32; + break; + + case Tchar: tty = Tuns8; break; + case Twchar: tty = Tuns16; break; + case Tdchar: tty = Tuns32; break; + case Tvoid: goto Lpaint; + + case Tbool: + { + // Construct e?true:false + e = el_una(OPbool, ttym, e); + goto Lret; + } + } + + switch (fty) + { + case Tpointer: fty = I64 ? Tuns64 : Tuns32; break; + case Tchar: fty = Tuns8; break; + case Twchar: fty = Tuns16; break; + case Tdchar: fty = Tuns32; break; + } + + #define X(fty, tty) ((fty) * TMAX + (tty)) +Lagain: + switch (X(fty,tty)) + { + /* ============================= */ + + case X(Tbool,Tint8): + case X(Tbool,Tuns8): + goto Lpaint; + case X(Tbool,Tint16): + case X(Tbool,Tuns16): + case X(Tbool,Tint32): + case X(Tbool,Tuns32): eop = OPu8_16; goto Leop; + case X(Tbool,Tint64): + case X(Tbool,Tuns64): + case X(Tbool,Tfloat32): + case X(Tbool,Tfloat64): + case X(Tbool,Tfloat80): + case X(Tbool,Tcomplex32): + case X(Tbool,Tcomplex64): + case X(Tbool,Tcomplex80): + e = el_una(OPu8_16, TYuint, e); + fty = Tuns32; + goto Lagain; + case X(Tbool,Timaginary32): + case X(Tbool,Timaginary64): + case X(Tbool,Timaginary80): goto Lzero; + + /* ============================= */ + + case X(Tint8,Tuns8): goto Lpaint; + case X(Tint8,Tint16): + case X(Tint8,Tuns16): + case X(Tint8,Tint32): + case X(Tint8,Tuns32): eop = OPs8_16; goto Leop; + case X(Tint8,Tint64): + case X(Tint8,Tuns64): + case X(Tint8,Tfloat32): + case X(Tint8,Tfloat64): + case X(Tint8,Tfloat80): + case X(Tint8,Tcomplex32): + case X(Tint8,Tcomplex64): + case X(Tint8,Tcomplex80): + e = el_una(OPs8_16, TYint, e); + fty = Tint32; + goto Lagain; + case X(Tint8,Timaginary32): + case X(Tint8,Timaginary64): + case X(Tint8,Timaginary80): goto Lzero; + + /* ============================= */ + + case X(Tuns8,Tint8): goto Lpaint; + case X(Tuns8,Tint16): + case X(Tuns8,Tuns16): + case X(Tuns8,Tint32): + case X(Tuns8,Tuns32): eop = OPu8_16; goto Leop; + case X(Tuns8,Tint64): + case X(Tuns8,Tuns64): + case X(Tuns8,Tfloat32): + case X(Tuns8,Tfloat64): + case X(Tuns8,Tfloat80): + case X(Tuns8,Tcomplex32): + case X(Tuns8,Tcomplex64): + case X(Tuns8,Tcomplex80): + e = el_una(OPu8_16, TYuint, e); + fty = Tuns32; + goto Lagain; + case X(Tuns8,Timaginary32): + case X(Tuns8,Timaginary64): + case X(Tuns8,Timaginary80): goto Lzero; + + /* ============================= */ + + case X(Tint16,Tint8): + case X(Tint16,Tuns8): eop = OP16_8; goto Leop; + case X(Tint16,Tuns16): goto Lpaint; + case X(Tint16,Tint32): + case X(Tint16,Tuns32): eop = OPs16_32; goto Leop; + case X(Tint16,Tint64): + case X(Tint16,Tuns64): e = el_una(OPs16_32, TYint, e); + fty = Tint32; + goto Lagain; + case X(Tint16,Tfloat32): + case X(Tint16,Tfloat64): + case X(Tint16,Tfloat80): + case X(Tint16,Tcomplex32): + case X(Tint16,Tcomplex64): + case X(Tint16,Tcomplex80): + e = el_una(OPs16_d, TYdouble, e); + fty = Tfloat64; + goto Lagain; + case X(Tint16,Timaginary32): + case X(Tint16,Timaginary64): + case X(Tint16,Timaginary80): goto Lzero; + + /* ============================= */ + + case X(Tuns16,Tint8): + case X(Tuns16,Tuns8): eop = OP16_8; goto Leop; + case X(Tuns16,Tint16): goto Lpaint; + case X(Tuns16,Tint32): + case X(Tuns16,Tuns32): eop = OPu16_32; goto Leop; + case X(Tuns16,Tint64): + case X(Tuns16,Tuns64): + case X(Tuns16,Tfloat64): + case X(Tuns16,Tfloat32): + case X(Tuns16,Tfloat80): + case X(Tuns16,Tcomplex32): + case X(Tuns16,Tcomplex64): + case X(Tuns16,Tcomplex80): + e = el_una(OPu16_32, TYuint, e); + fty = Tuns32; + goto Lagain; + case X(Tuns16,Timaginary32): + case X(Tuns16,Timaginary64): + case X(Tuns16,Timaginary80): goto Lzero; + + /* ============================= */ + + case X(Tint32,Tint8): + case X(Tint32,Tuns8): e = el_una(OP32_16, TYshort, e); + fty = Tint16; + goto Lagain; + case X(Tint32,Tint16): + case X(Tint32,Tuns16): eop = OP32_16; goto Leop; + case X(Tint32,Tuns32): goto Lpaint; + case X(Tint32,Tint64): + case X(Tint32,Tuns64): eop = OPs32_64; goto Leop; + case X(Tint32,Tfloat32): + case X(Tint32,Tfloat64): + case X(Tint32,Tfloat80): + case X(Tint32,Tcomplex32): + case X(Tint32,Tcomplex64): + case X(Tint32,Tcomplex80): + e = el_una(OPs32_d, TYdouble, e); + fty = Tfloat64; + goto Lagain; + case X(Tint32,Timaginary32): + case X(Tint32,Timaginary64): + case X(Tint32,Timaginary80): goto Lzero; + + /* ============================= */ + + case X(Tuns32,Tint8): + case X(Tuns32,Tuns8): e = el_una(OP32_16, TYshort, e); + fty = Tuns16; + goto Lagain; + case X(Tuns32,Tint16): + case X(Tuns32,Tuns16): eop = OP32_16; goto Leop; + case X(Tuns32,Tint32): goto Lpaint; + case X(Tuns32,Tint64): + case X(Tuns32,Tuns64): eop = OPu32_64; goto Leop; + case X(Tuns32,Tfloat32): + case X(Tuns32,Tfloat64): + case X(Tuns32,Tfloat80): + case X(Tuns32,Tcomplex32): + case X(Tuns32,Tcomplex64): + case X(Tuns32,Tcomplex80): + e = el_una(OPu32_d, TYdouble, e); + fty = Tfloat64; + goto Lagain; + case X(Tuns32,Timaginary32): + case X(Tuns32,Timaginary64): + case X(Tuns32,Timaginary80): goto Lzero; + + /* ============================= */ + + case X(Tint64,Tint8): + case X(Tint64,Tuns8): + case X(Tint64,Tint16): + case X(Tint64,Tuns16): e = el_una(OP64_32, TYint, e); + fty = Tint32; + goto Lagain; + case X(Tint64,Tint32): + case X(Tint64,Tuns32): eop = OP64_32; goto Leop; + case X(Tint64,Tuns64): goto Lpaint; + case X(Tint64,Tfloat32): + case X(Tint64,Tfloat64): + case X(Tint64,Tfloat80): + case X(Tint64,Tcomplex32): + case X(Tint64,Tcomplex64): + case X(Tint64,Tcomplex80): + e = el_una(OPs64_d, TYdouble, e); + fty = Tfloat64; + goto Lagain; + case X(Tint64,Timaginary32): + case X(Tint64,Timaginary64): + case X(Tint64,Timaginary80): goto Lzero; + + /* ============================= */ + + case X(Tuns64,Tint8): + case X(Tuns64,Tuns8): + case X(Tuns64,Tint16): + case X(Tuns64,Tuns16): e = el_una(OP64_32, TYint, e); + fty = Tint32; + goto Lagain; + case X(Tuns64,Tint32): + case X(Tuns64,Tuns32): eop = OP64_32; goto Leop; + case X(Tuns64,Tint64): goto Lpaint; + case X(Tuns64,Tfloat32): + case X(Tuns64,Tfloat64): + case X(Tuns64,Tfloat80): + case X(Tuns64,Tcomplex32): + case X(Tuns64,Tcomplex64): + case X(Tuns64,Tcomplex80): + e = el_una(OPu64_d, TYdouble, e); + fty = Tfloat64; + goto Lagain; + case X(Tuns64,Timaginary32): + case X(Tuns64,Timaginary64): + case X(Tuns64,Timaginary80): goto Lzero; + + /* ============================= */ + + case X(Tfloat32,Tint8): + case X(Tfloat32,Tuns8): + case X(Tfloat32,Tint16): + case X(Tfloat32,Tuns16): + case X(Tfloat32,Tint32): + case X(Tfloat32,Tuns32): + case X(Tfloat32,Tint64): + case X(Tfloat32,Tuns64): + case X(Tfloat32,Tfloat80): e = el_una(OPf_d, TYdouble, e); + fty = Tfloat64; + goto Lagain; + case X(Tfloat32,Tfloat64): eop = OPf_d; goto Leop; + case X(Tfloat32,Timaginary32): goto Lzero; + case X(Tfloat32,Timaginary64): goto Lzero; + case X(Tfloat32,Timaginary80): goto Lzero; + case X(Tfloat32,Tcomplex32): + case X(Tfloat32,Tcomplex64): + case X(Tfloat32,Tcomplex80): + e = el_bin(OPadd,TYcfloat,el_long(TYifloat,0),e); + fty = Tcomplex32; + goto Lagain; + + /* ============================= */ + + case X(Tfloat64,Tint8): + case X(Tfloat64,Tuns8): e = el_una(OPd_s16, TYshort, e); + fty = Tint16; + goto Lagain; + case X(Tfloat64,Tint16): eop = OPd_s16; goto Leop; + case X(Tfloat64,Tuns16): eop = OPd_u16; goto Leop; + case X(Tfloat64,Tint32): eop = OPd_s32; goto Leop; + case X(Tfloat64,Tuns32): eop = OPd_u32; goto Leop; + case X(Tfloat64,Tint64): eop = OPd_s64; goto Leop; + case X(Tfloat64,Tuns64): eop = OPd_u64; goto Leop; + case X(Tfloat64,Tfloat32): eop = OPd_f; goto Leop; + case X(Tfloat64,Tfloat80): eop = OPd_ld; goto Leop; + case X(Tfloat64,Timaginary32): goto Lzero; + case X(Tfloat64,Timaginary64): goto Lzero; + case X(Tfloat64,Timaginary80): goto Lzero; + case X(Tfloat64,Tcomplex32): + case X(Tfloat64,Tcomplex64): + case X(Tfloat64,Tcomplex80): + e = el_bin(OPadd,TYcfloat,el_long(TYidouble,0),e); + fty = Tcomplex64; + goto Lagain; + + /* ============================= */ + + case X(Tfloat80,Tint8): + case X(Tfloat80,Tuns8): + case X(Tfloat80,Tint16): + case X(Tfloat80,Tuns16): + case X(Tfloat80,Tint32): + case X(Tfloat80,Tuns32): + case X(Tfloat80,Tint64): + case X(Tfloat80,Tfloat32): e = el_una(OPld_d, TYdouble, e); + fty = Tfloat64; + goto Lagain; + case X(Tfloat80,Tuns64): + eop = OPld_u64; goto Leop; + case X(Tfloat80,Tfloat64): eop = OPld_d; goto Leop; + case X(Tfloat80,Timaginary32): goto Lzero; + case X(Tfloat80,Timaginary64): goto Lzero; + case X(Tfloat80,Timaginary80): goto Lzero; + case X(Tfloat80,Tcomplex32): + case X(Tfloat80,Tcomplex64): + case X(Tfloat80,Tcomplex80): + e = el_bin(OPadd,TYcldouble,e,el_long(TYildouble,0)); + fty = Tcomplex80; + goto Lagain; + + /* ============================= */ + + case X(Timaginary32,Tint8): + case X(Timaginary32,Tuns8): + case X(Timaginary32,Tint16): + case X(Timaginary32,Tuns16): + case X(Timaginary32,Tint32): + case X(Timaginary32,Tuns32): + case X(Timaginary32,Tint64): + case X(Timaginary32,Tuns64): + case X(Timaginary32,Tfloat32): + case X(Timaginary32,Tfloat64): + case X(Timaginary32,Tfloat80): goto Lzero; + case X(Timaginary32,Timaginary64): eop = OPf_d; goto Leop; + case X(Timaginary32,Timaginary80): + e = el_una(OPf_d, TYidouble, e); + fty = Timaginary64; + goto Lagain; + case X(Timaginary32,Tcomplex32): + case X(Timaginary32,Tcomplex64): + case X(Timaginary32,Tcomplex80): + e = el_bin(OPadd,TYcfloat,el_long(TYfloat,0),e); + fty = Tcomplex32; + goto Lagain; + + /* ============================= */ + + case X(Timaginary64,Tint8): + case X(Timaginary64,Tuns8): + case X(Timaginary64,Tint16): + case X(Timaginary64,Tuns16): + case X(Timaginary64,Tint32): + case X(Timaginary64,Tuns32): + case X(Timaginary64,Tint64): + case X(Timaginary64,Tuns64): + case X(Timaginary64,Tfloat32): + case X(Timaginary64,Tfloat64): + case X(Timaginary64,Tfloat80): goto Lzero; + case X(Timaginary64,Timaginary32): eop = OPd_f; goto Leop; + case X(Timaginary64,Timaginary80): eop = OPd_ld; goto Leop; + case X(Timaginary64,Tcomplex32): + case X(Timaginary64,Tcomplex64): + case X(Timaginary64,Tcomplex80): + e = el_bin(OPadd,TYcdouble,el_long(TYdouble,0),e); + fty = Tcomplex64; + goto Lagain; + + /* ============================= */ + + case X(Timaginary80,Tint8): + case X(Timaginary80,Tuns8): + case X(Timaginary80,Tint16): + case X(Timaginary80,Tuns16): + case X(Timaginary80,Tint32): + case X(Timaginary80,Tuns32): + case X(Timaginary80,Tint64): + case X(Timaginary80,Tuns64): + case X(Timaginary80,Tfloat32): + case X(Timaginary80,Tfloat64): + case X(Timaginary80,Tfloat80): goto Lzero; + case X(Timaginary80,Timaginary32): e = el_una(OPf_d, TYidouble, e); + fty = Timaginary64; + goto Lagain; + case X(Timaginary80,Timaginary64): eop = OPld_d; goto Leop; + case X(Timaginary80,Tcomplex32): + case X(Timaginary80,Tcomplex64): + case X(Timaginary80,Tcomplex80): + e = el_bin(OPadd,TYcldouble,el_long(TYldouble,0),e); + fty = Tcomplex80; + goto Lagain; + + /* ============================= */ + + case X(Tcomplex32,Tint8): + case X(Tcomplex32,Tuns8): + case X(Tcomplex32,Tint16): + case X(Tcomplex32,Tuns16): + case X(Tcomplex32,Tint32): + case X(Tcomplex32,Tuns32): + case X(Tcomplex32,Tint64): + case X(Tcomplex32,Tuns64): + case X(Tcomplex32,Tfloat32): + case X(Tcomplex32,Tfloat64): + case X(Tcomplex32,Tfloat80): + e = el_una(OPc_r, TYfloat, e); + fty = Tfloat32; + goto Lagain; + case X(Tcomplex32,Timaginary32): + case X(Tcomplex32,Timaginary64): + case X(Tcomplex32,Timaginary80): + e = el_una(OPc_i, TYifloat, e); + fty = Timaginary32; + goto Lagain; + case X(Tcomplex32,Tcomplex64): + case X(Tcomplex32,Tcomplex80): + e = el_una(OPf_d, TYcdouble, e); + fty = Tcomplex64; + goto Lagain; + + /* ============================= */ + + case X(Tcomplex64,Tint8): + case X(Tcomplex64,Tuns8): + case X(Tcomplex64,Tint16): + case X(Tcomplex64,Tuns16): + case X(Tcomplex64,Tint32): + case X(Tcomplex64,Tuns32): + case X(Tcomplex64,Tint64): + case X(Tcomplex64,Tuns64): + case X(Tcomplex64,Tfloat32): + case X(Tcomplex64,Tfloat64): + case X(Tcomplex64,Tfloat80): + e = el_una(OPc_r, TYdouble, e); + fty = Tfloat64; + goto Lagain; + case X(Tcomplex64,Timaginary32): + case X(Tcomplex64,Timaginary64): + case X(Tcomplex64,Timaginary80): + e = el_una(OPc_i, TYidouble, e); + fty = Timaginary64; + goto Lagain; + case X(Tcomplex64,Tcomplex32): eop = OPd_f; goto Leop; + case X(Tcomplex64,Tcomplex80): eop = OPd_ld; goto Leop; + + /* ============================= */ + + case X(Tcomplex80,Tint8): + case X(Tcomplex80,Tuns8): + case X(Tcomplex80,Tint16): + case X(Tcomplex80,Tuns16): + case X(Tcomplex80,Tint32): + case X(Tcomplex80,Tuns32): + case X(Tcomplex80,Tint64): + case X(Tcomplex80,Tuns64): + case X(Tcomplex80,Tfloat32): + case X(Tcomplex80,Tfloat64): + case X(Tcomplex80,Tfloat80): + e = el_una(OPc_r, TYldouble, e); + fty = Tfloat80; + goto Lagain; + case X(Tcomplex80,Timaginary32): + case X(Tcomplex80,Timaginary64): + case X(Tcomplex80,Timaginary80): + e = el_una(OPc_i, TYildouble, e); + fty = Timaginary80; + goto Lagain; + case X(Tcomplex80,Tcomplex32): + case X(Tcomplex80,Tcomplex64): + e = el_una(OPld_d, TYcdouble, e); + fty = Tcomplex64; + goto Lagain; + + /* ============================= */ + + default: + if (fty == tty) + goto Lpaint; + //dump(0); + //printf("fty = %d, tty = %d, %d\n", fty, tty, t->ty); + error("e2ir: cannot cast %s of type %s to type %s", e1->toChars(), e1->type->toChars(), t->toChars()); + goto Lzero; + + Lzero: + e = el_long(ttym, 0); + break; + + Lpaint: + e->Ety = ttym; + break; + + Leop: + e = el_una(eop, ttym, e); + break; + } +Lret: + // Adjust for any type paints + t = type->toBasetype(); + e->Ety = t->totym(); + + el_setLoc(e,loc); + return e; +} + +elem *ArrayLengthExp::toElem(IRState *irs) +{ + elem *e = e1->toElem(irs); + e = el_una(I64 ? OP128_64 : OP64_32, type->totym(), e); + el_setLoc(e,loc); + return e; +} + +elem *SliceExp::toElem(IRState *irs) +{ + //printf("SliceExp::toElem()\n"); + Type *t1 = e1->type->toBasetype(); + elem *e = e1->toElem(irs); + if (lwr) + { + elem *einit = resolveLengthVar(lengthVar, &e, t1); + + unsigned sz = t1->nextOf()->size(); + + elem *elwr = lwr->toElem(irs); + elem *eupr = upr->toElem(irs); + + elem *elwr2 = el_same(&elwr); + + // Create an array reference where: + // length is (upr - lwr) + // pointer is (ptr + lwr*sz) + // Combine as (length pair ptr) + + if (irs->arrayBoundsCheck()) + { + // Checks (unsigned compares): + // upr <= array.length + // lwr <= upr + + elem *c1; + elem *c2; + elem *ea; + elem *eb; + elem *eupr2; + elem *elength; + + if (t1->ty == Tpointer) + { + // Just do lwr <= upr check + + eupr2 = el_same(&eupr); + eupr2->Ety = TYsize_t; // make sure unsigned comparison + c1 = el_bin(OPle, TYint, elwr2, eupr2); + c1 = el_combine(eupr, c1); + goto L2; + } + else if (t1->ty == Tsarray) + { TypeSArray *tsa = (TypeSArray *)t1; + dinteger_t length = tsa->dim->toInteger(); + + elength = el_long(TYsize_t, length); + goto L1; + } + else if (t1->ty == Tarray) + { + if (lengthVar && !(lengthVar->storage_class & STCconst)) + elength = el_var(lengthVar->toSymbol()); + else + { + elength = e; + e = el_same(&elength); + elength = el_una(I64 ? OP128_64 : OP64_32, TYsize_t, elength); + } + L1: + eupr2 = el_same(&eupr); + c1 = el_bin(OPle, TYint, eupr, elength); + eupr2->Ety = TYsize_t; // make sure unsigned comparison + c2 = el_bin(OPle, TYint, elwr2, eupr2); + c1 = el_bin(OPandand, TYint, c1, c2); // (c1 && c2) + + L2: + // Construct: (c1 || ModuleArray(line)) + Symbol *sassert; + + sassert = irs->blx->module->toModuleArray(); + ea = el_bin(OPcall,TYvoid,el_var(sassert), el_long(TYint, loc.linnum)); + eb = el_bin(OPoror,TYvoid,c1,ea); + elwr = el_combine(elwr, eb); + + elwr2 = el_copytree(elwr2); + eupr = el_copytree(eupr2); + } + } + + elem *eptr = array_toPtr(e1->type, e); + + elem *elength = el_bin(OPmin, TYsize_t, eupr, elwr2); + eptr = el_bin(OPadd, TYnptr, eptr, el_bin(OPmul, TYsize_t, el_copytree(elwr2), el_long(TYsize_t, sz))); + + e = el_pair(TYdarray, elength, eptr); + e = el_combine(elwr, e); + e = el_combine(einit, e); + } + else if (t1->ty == Tsarray) + { + e = sarray_toDarray(loc, t1, NULL, e); + } + el_setLoc(e,loc); + return e; +} + +elem *IndexExp::toElem(IRState *irs) +{ elem *e; + elem *n1 = e1->toElem(irs); + elem *eb = NULL; + + //printf("IndexExp::toElem() %s\n", toChars()); + Type *t1 = e1->type->toBasetype(); + if (t1->ty == Taarray) + { + // set to: + // *aaGetX(aa, keyti, valuesize, &key); + + TypeAArray *taa = (TypeAArray *)t1; + unsigned vsize = taa->next->size(); + Symbol *s; + + // n2 becomes the index, also known as the key + elem *n2 = e2->toElem(irs); + + /* Turn n2 into a pointer to the index. If it's an lvalue, + * take the address of it. If not, copy it to a temp and + * take the address of that. + */ + n2 = addressElem(n2, taa->index); + + elem *valuesize = el_long(TYsize_t, vsize); + //printf("valuesize: "); elem_print(valuesize); + if (modifiable) + { + n1 = el_una(OPaddr, TYnptr, n1); + s = taa->aaGetSymbol("GetX", 1); + } + else + { + s = taa->aaGetSymbol("GetRvalueX", 1); + } + //printf("taa->index = %s\n", taa->index->toChars()); + elem* keyti = taa->index->getInternalTypeInfo(NULL)->toElem(irs); + //keyti = taa->index->getTypeInfo(NULL)->toElem(irs); + //printf("keyti:\n"); + //elem_print(keyti); + elem* ep = el_params(n2, valuesize, keyti, n1, NULL); + e = el_bin(OPcall, TYnptr, el_var(s), ep); + if (irs->arrayBoundsCheck()) + { + elem *ea; + + elem *n = el_same(&e); + + // Construct: ((e || ModuleAssert(line)),n) + Symbol *sassert = irs->blx->module->toModuleArray(); + ea = el_bin(OPcall,TYvoid,el_var(sassert), + el_long(TYint, loc.linnum)); + e = el_bin(OPoror,TYvoid,e,ea); + e = el_bin(OPcomma, TYnptr, e, n); + } + e = el_una(OPind, type->totym(), e); + if (tybasic(e->Ety) == TYstruct) + e->ET = type->toCtype(); + } + else + { + elem *einit = resolveLengthVar(lengthVar, &n1, t1); + elem *n2 = e2->toElem(irs); + + if (irs->arrayBoundsCheck()) + { + elem *elength; + elem *n2x; + elem *ea; + + if (t1->ty == Tsarray) + { TypeSArray *tsa = (TypeSArray *)t1; + dinteger_t length = tsa->dim->toInteger(); + + elength = el_long(TYsize_t, length); + goto L1; + } + else if (t1->ty == Tarray) + { + elength = n1; + n1 = el_same(&elength); + elength = el_una(I64 ? OP128_64 : OP64_32, TYsize_t, elength); + L1: + n2x = n2; + n2 = el_same(&n2x); + n2x = el_bin(OPlt, TYint, n2x, elength); + + // Construct: (n2x || ModuleAssert(line)) + Symbol *sassert; + + sassert = irs->blx->module->toModuleArray(); + ea = el_bin(OPcall,TYvoid,el_var(sassert), + el_long(TYint, loc.linnum)); + eb = el_bin(OPoror,TYvoid,n2x,ea); + } + } + + n1 = array_toPtr(t1, n1); + + { + elem *escale = el_long(TYsize_t, t1->nextOf()->size()); + n2 = el_bin(OPmul, TYsize_t, n2, escale); + e = el_bin(OPadd, TYnptr, n1, n2); + e = el_una(OPind, type->totym(), e); + if (tybasic(e->Ety) == TYstruct || tybasic(e->Ety) == TYarray) + { e->Ety = TYstruct; + e->ET = type->toCtype(); + } + } + + eb = el_combine(einit, eb); + e = el_combine(eb, e); + } + el_setLoc(e,loc); + return e; +} + + +elem *TupleExp::toElem(IRState *irs) +{ elem *e = NULL; + + //printf("TupleExp::toElem() %s\n", toChars()); + for (size_t i = 0; i < exps->dim; i++) + { Expression *el = exps->tdata()[i]; + elem *ep = el->toElem(irs); + + e = el_combine(e, ep); + } + return e; +} + +#if DMDV2 +elem *tree_insert(Elems *args, int low, int high) +{ + assert(low < high); + if (low + 1 == high) + return args->tdata()[low]; + int mid = (low + high) >> 1; + return el_param(tree_insert(args, low, mid), + tree_insert(args, mid, high)); +} +#endif + +elem *ArrayLiteralExp::toElem(IRState *irs) +{ elem *e; + size_t dim; + elem *earg = NULL; + + //printf("ArrayLiteralExp::toElem() %s, type = %s\n", toChars(), type->toChars()); + Type *tb = type->toBasetype(); + if (elements) + { + /* Instead of passing the initializers on the stack, allocate the + * array and assign the members inline. + * Avoids the whole variadic arg mess. + */ + dim = elements->dim; + Elems args; + args.setDim(dim); // +1 for number of args parameter + e = el_long(TYsize_t, dim); + e = el_param(e, type->getTypeInfo(NULL)->toElem(irs)); + // call _d_arrayliteralTX(ti, dim) + e = el_bin(OPcall,TYnptr,el_var(rtlsym[RTLSYM_ARRAYLITERALTX]),e); + Symbol *stmp = symbol_genauto(Type::tvoid->pointerTo()->toCtype()); + e = el_bin(OPeq,TYnptr,el_var(stmp),e); + + targ_size_t sz = tb->nextOf()->size(); // element size + ::type *te = tb->nextOf()->toCtype(); // element type + for (size_t i = 0; i < dim; i++) + { Expression *el = elements->tdata()[i]; + + /* Generate: *(stmp + i * sz) = element[i] + */ + elem *ep = el->toElem(irs); + elem *ev = el_var(stmp); + ev = el_bin(OPadd, TYnptr, ev, el_long(TYsize_t, i * sz)); + ev = el_una(OPind, te->Tty, ev); + elem *eeq = el_bin(OPeq,te->Tty,ev,ep); + + if (tybasic(te->Tty) == TYstruct) + { + eeq->Eoper = OPstreq; + eeq->ET = te; + } + else if (tybasic(te->Tty) == TYarray) + { + eeq->Eoper = OPstreq; + eeq->Ejty = eeq->Ety = TYstruct; + eeq->ET = te; + } + args.tdata()[i] = eeq; + } + e = el_combine(e, el_combines((void **)args.tdata(), dim)); + e = el_combine(e, el_var(stmp)); + } + else + { dim = 0; + e = el_long(TYsize_t, 0); + } + if (tb->ty == Tarray) + { + e = el_pair(TYdarray, el_long(TYsize_t, dim), e); + } + else if (tb->ty == Tpointer) + { + } + else + { + e = el_una(OPind,TYstruct,e); + e->ET = type->toCtype(); + } + + el_setLoc(e,loc); + e = el_combine(earg, e); + return e; +} + +/************************************************* + * Allocate a static array, and initialize its members with + * exps[]. + * Return the initialization expression, and the symbol for the static array in *psym. + */ +elem *ExpressionsToStaticArray(IRState *irs, Loc loc, Expressions *exps, Type *telem, symbol **psym) +{ + // Create a static array of type telem[dim] + size_t dim = exps->dim; + Type *tsarray = new TypeSArray(telem, new IntegerExp(loc, dim, Type::tsize_t)); + tsarray = tsarray->semantic(loc, NULL); + symbol *stmp = symbol_genauto(tsarray->toCtype()); + targ_size_t szelem = telem->size(); + + Elems elems; + elems.setDim(dim); + + ::type *te = telem->toCtype(); // stmp[] element type + + for (size_t i = 0; i < dim; i++) + { Expression *el = exps->tdata()[i]; + + /* Generate: *(&stmp + i * szelem) = element[i] + */ + elem *ep = el->toElem(irs); + elem *ev = el_ptr(stmp); + ev = el_bin(OPadd, TYnptr, ev, el_long(TYsize_t, i * szelem)); + ev = el_una(OPind, te->Tty, ev); + elem *eeq = el_bin(OPeq,te->Tty,ev,ep); + + if (tybasic(te->Tty) == TYstruct) + { + eeq->Eoper = OPstreq; + eeq->ET = te; + } + else if (tybasic(te->Tty) == TYarray) + { + eeq->Eoper = OPstreq; + eeq->Ejty = eeq->Ety = TYstruct; + eeq->ET = te; + } + elems.tdata()[i] = eeq; + } + + *psym = stmp; + return el_combines((void **)elems.tdata(), dim); +} + +elem *AssocArrayLiteralExp::toElem(IRState *irs) +{ + //printf("AssocArrayLiteralExp::toElem() %s\n", toChars()); + size_t dim = keys->dim; + elem *e; + + // call _d_assocarrayliteralTX(TypeInfo_AssociativeArray ti, void[] keys, void[] values) + // Prefer this to avoid the varargs fiasco in 64 bit code + Type *t = type->toBasetype()->mutableOf(); + assert(t->ty == Taarray); + TypeAArray *ta = (TypeAArray *)t; + + symbol *skeys; + elem *ekeys = ExpressionsToStaticArray(irs, loc, keys, ta->index, &skeys); + + symbol *svalues; + elem *evalues = ExpressionsToStaticArray(irs, loc, values, ta->nextOf(), &svalues); + + e = el_params(el_pair(TYdarray, el_long(TYsize_t, dim), el_ptr(svalues)), + el_pair(TYdarray, el_long(TYsize_t, dim), el_ptr(skeys )), + ta->getTypeInfo(NULL)->toElem(irs), + NULL); + + // call _d_assocarrayliteralTX(ti, keys, values) + e = el_bin(OPcall,TYnptr,el_var(rtlsym[RTLSYM_ASSOCARRAYLITERALTX]),e); + el_setLoc(e,loc); + + e = el_combine(evalues, e); + e = el_combine(ekeys, e); + + return e; +} + + +/******************************************* + * Generate elem to zero fill contents of Symbol stmp + * from *poffset..offset2. + * May store anywhere from 0..maxoff, as this function + * tries to use aligned int stores whereever possible. + * Update *poffset to end of initialized hole; *poffset will be >= offset2. + */ + +elem *fillHole(Symbol *stmp, size_t *poffset, size_t offset2, size_t maxoff) +{ elem *e = NULL; + int basealign = 1; + + while (*poffset < offset2) + { tym_t ty; + elem *e1; + + if (tybasic(stmp->Stype->Tty) == TYnptr) + e1 = el_var(stmp); + else + e1 = el_ptr(stmp); + if (basealign) + *poffset &= ~3; + basealign = 1; + size_t sz = maxoff - *poffset; + switch (sz) + { case 1: ty = TYchar; break; + case 2: ty = TYshort; break; + case 3: + ty = TYshort; + basealign = 0; + break; + default: + ty = TYlong; + break; + } + e1 = el_bin(OPadd, TYnptr, e1, el_long(TYsize_t, *poffset)); + e1 = el_una(OPind, ty, e1); + e1 = el_bin(OPeq, ty, e1, el_long(ty, 0)); + e = el_combine(e, e1); + *poffset += tysize[ty]; + } + return e; +} + +elem *StructLiteralExp::toElem(IRState *irs) +{ elem *e; + size_t dim; + + //printf("StructLiteralExp::toElem() %s\n", toChars()); + + // struct symbol to initialize with the literal + Symbol *stmp = sym ? sym : symbol_genauto(sd->type->toCtype()); + + e = NULL; + + if (fillHoles) + { + /* Initialize all alignment 'holes' to zero. + * Do before initializing fields, as the hole filling process + * can spill over into the fields. + */ + size_t offset = 0; + for (size_t i = 0; i < sd->fields.dim; i++) + { + Dsymbol *s = sd->fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + + e = el_combine(e, fillHole(stmp, &offset, v->offset, sd->structsize)); + size_t vend = v->offset + v->type->size(); + if (offset < vend) + offset = vend; + } + e = el_combine(e, fillHole(stmp, &offset, sd->structsize, sd->structsize)); + } + + if (elements) + { + dim = elements->dim; + assert(dim <= sd->fields.dim); + for (size_t i = 0; i < dim; i++) + { Expression *el = elements->tdata()[i]; + if (!el) + continue; + + Dsymbol *s = sd->fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + assert(!v->isThisDeclaration()); + + elem *e1; + if (tybasic(stmp->Stype->Tty) == TYnptr) + { e1 = el_var(stmp); + e1->EV.sp.Voffset = soffset; + } + else + { e1 = el_ptr(stmp); + if (soffset) + e1 = el_bin(OPadd, TYnptr, e1, el_long(TYsize_t, soffset)); + } + e1 = el_bin(OPadd, TYnptr, e1, el_long(TYsize_t, v->offset)); + elem *ec = e1; // pointer to destination + + elem *ep = el->toElem(irs); + + Type *t1b = v->type->toBasetype(); + Type *t2b = el->type->toBasetype(); + if (t1b->ty == Tsarray) + { + if (t2b->implicitConvTo(t1b)) + { +#if DMDV2 + // Determine if postblit is needed + int postblit = 0; + if (needsPostblit(t1b)) + postblit = 1; + + if (postblit) + { + /* Generate: + * _d_arrayctor(ti, From: ep, To: e1) + */ + Expression *ti = t1b->nextOf()->toBasetype()->getTypeInfo(NULL); + elem *esize = el_long(TYsize_t, ((TypeSArray *)t1b)->dim->toInteger()); + e1 = el_pair(TYdarray, esize, e1); + ep = el_pair(TYdarray, el_copytree(esize), array_toPtr(el->type, ep)); + ep = el_params(e1, ep, ti->toElem(irs), NULL); + int rtl = RTLSYM_ARRAYCTOR; + e1 = el_bin(OPcall, type->totym(), el_var(rtlsym[rtl]), ep); + } + else +#endif + { + elem *esize = el_long(TYsize_t, t1b->size()); + ep = array_toPtr(el->type, ep); + e1 = el_bin(OPmemcpy, TYnptr, e1, el_param(ep, esize)); + } + } + else + { + elem *edim = el_long(TYsize_t, t1b->size() / t2b->size()); + e1 = setArray(e1, edim, t2b, ep, irs, TOKconstruct); + } + } + else + { + tym_t ty = v->type->totym(); + e1 = el_una(OPind, ty, e1); + if (tybasic(ty) == TYstruct) + e1->ET = v->type->toCtype(); + e1 = el_bin(OPeq, ty, e1, ep); + if (tybasic(ty) == TYstruct) + { e1->Eoper = OPstreq; + e1->ET = v->type->toCtype(); + } +#if DMDV2 + /* Call postblit() on e1 + */ + StructDeclaration *sd = needsPostblit(v->type); + if (sd) + { FuncDeclaration *fd = sd->postblit; + ec = el_copytree(ec); + ec = callfunc(loc, irs, 1, Type::tvoid, ec, sd->type->pointerTo(), fd, fd->type, NULL, NULL); + e1 = el_bin(OPcomma, ec->Ety, e1, ec); + } +#endif + } + e = el_combine(e, e1); + } + } + +#if DMDV2 + if (sd->isnested) + { // Initialize the hidden 'this' pointer + assert(sd->fields.dim); + Dsymbol *s = sd->fields.tdata()[sd->fields.dim - 1]; + ThisDeclaration *v = s->isThisDeclaration(); + assert(v); + + elem *e1; + if (tybasic(stmp->Stype->Tty) == TYnptr) + { e1 = el_var(stmp); + e1->EV.sp.Voffset = soffset; + } + else + { e1 = el_ptr(stmp); + if (soffset) + e1 = el_bin(OPadd, TYnptr, e1, el_long(TYsize_t, soffset)); + } + e1 = setEthis(loc, irs, e1, sd); + + e = el_combine(e, e1); + } +#endif + + elem *ev = el_var(stmp); + ev->ET = sd->type->toCtype(); + e = el_combine(e, ev); + el_setLoc(e,loc); + return e; +} + +/******************************************** + * Add destructors + */ + +elem *appendDtors(IRState *irs, elem *er, size_t starti, size_t endi) +{ + //printf("appendDtors(%d .. %d)\n", starti, endi); + + /* Code gen can be improved by determining if no exceptions can be thrown + * between the OPdctor and OPddtor, and eliminating the OPdctor and OPddtor. + */ + + /* Build edtors, an expression that calls destructors on all the variables + * going out of the scope starti..endi + */ + elem *edtors = NULL; + for (size_t i = starti; i != endi; ++i) + { + VarDeclaration *vd = irs->varsInScope->tdata()[i]; + if (vd) + { + //printf("appending dtor\n"); + irs->varsInScope->tdata()[i] = NULL; + elem *ed = vd->edtor->toElem(irs); + ed = el_ddtor(ed, vd); + edtors = el_combine(ed, edtors); // execute in reverse order + } + } + + if (edtors) + { +#if TARGET_WINDOS + Blockx *blx = irs->blx; + nteh_declarvars(blx); +#endif + /* Append edtors to er, while preserving the value of er + */ + if (tybasic(er->Ety) == TYvoid) + { /* No value to preserve, so simply append + */ + er = el_combine(er, edtors); + } + else + { + elem **pe; + for (pe = &er; (*pe)->Eoper == OPcomma; pe = &(*pe)->E2) + ; + elem *erx = *pe; + + if (erx->Eoper == OPconst || erx->Eoper == OPrelconst) + { + *pe = el_combine(edtors, erx); + } + else if (tybasic(erx->Ety) == TYstruct || tybasic(erx->Ety) == TYarray) + { + /* Expensive to copy, to take a pointer to it instead + */ + elem *ep = el_una(OPaddr, TYnptr, erx); + elem *e = el_same(&ep); + ep = el_combine(ep, edtors); + ep = el_combine(ep, e); + e = el_una(OPind, erx->Ety, ep); + e->ET = erx->ET; + *pe = e; + } + else + { + elem *e = el_same(&erx); + erx = el_combine(erx, edtors); + *pe = el_combine(erx, e); + } + } + } + return er; +} + diff --git a/eh.c b/eh.c new file mode 100644 index 00000000..2896b745 --- /dev/null +++ b/eh.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 1994-1998 by Symantec + * Copyright (c) 2000-2011 by Digital Mars + * All Rights Reserved + * http://www.digitalmars.com + * http://www.dsource.org/projects/dmd/browser/branches/dmd-1.x/src/eh.c + * http://www.dsource.org/projects/dmd/browser/trunk/src/eh.c + * Written by Walter Bright + * + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +// Support for D exception handling + +#include +#include +#include +#include + +#include "cc.h" +#include "el.h" +#include "code.h" +#include "oper.h" +#include "global.h" +#include "type.h" +#include "dt.h" +#include "exh.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +/* If we do our own EH tables and stack walking scheme + * (Otherwise use NT Structured Exception Handling) + */ +#define OUREH (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) + + +/**************************** + * Generate and output scope table. + */ + +symbol *except_gentables() +{ + //printf("except_gentables()\n"); +#if OUREH + + // BUG: alloca() changes the stack size, which is not reflected + // in the fixed eh tables. + assert(!usedalloca); + + symbol *s = symbol_generate(SCstatic,tsint); + s->Sseg = UNKNOWN; + symbol_keep(s); + symbol_debug(s); + + except_fillInEHTable(s); + + outdata(s); // output the scope table + + obj_ehtables(funcsym_p,funcsym_p->Ssize,s); +#endif + return NULL; +} + +/********************************************** + * Initializes the symbol s with the contents of the exception handler table. + */ + +struct Guard +{ +#if OUREH + unsigned offset; // offset of start of guarded section (Linux) + unsigned endoffset; // ending offset of guarded section (Linux) +#endif + int last_index; // previous index (enclosing guarded section) + unsigned catchoffset; // offset to catch block from symbol + void *finally; // finally code to execute +}; + +void except_fillInEHTable(symbol *s) +{ + unsigned fsize = NPTRSIZE; // target size of function pointer + dt_t **pdt = &s->Sdt; + + /* + void* pointer to start of function + unsigned offset of ESP from EBP + unsigned offset from start of function to return code + unsigned nguards; // dimension of guard[] (Linux) + Guard guard[]; // sorted such that the enclosing guarded sections come first + catchoffset: + unsigned ncatches; // number of catch blocks + { void *type; // symbol representing type + unsigned bpoffset; // EBP offset of catch variable + void *handler; // catch handler code + } catch[]; + */ + +/* Be careful of this, as we need the sizeof Guard on the target, not + * in the compiler. + */ +#if OUREH +#define GUARD_SIZE (I64 ? 3*8 : 5*4) // sizeof(Guard) +#else +#define GUARD_SIZE (sizeof(Guard)) +#endif + + int sz = 0; + + // Address of start of function + symbol_debug(funcsym_p); + pdt = dtxoff(pdt,funcsym_p,0,TYnptr); + sz += fsize; + + //printf("ehtables: func = %s, offset = x%x, startblock->Boffset = x%x\n", funcsym_p->Sident, funcsym_p->Soffset, startblock->Boffset); + + // Get offset of ESP from EBP + long spoff = cod3_spoff(); + pdt = dtdword(pdt,spoff); + sz += 4; + + // Offset from start of function to return code + pdt = dtdword(pdt,retoffset); + sz += 4; + + // First, calculate starting catch offset + int guarddim = 0; // max dimension of guard[] + int ndctors = 0; // number of ESCdctor's + for (block *b = startblock; b; b = b->Bnext) + { + if (b->BC == BC_try && b->Bscope_index >= guarddim) + guarddim = b->Bscope_index + 1; +// printf("b->BC = %2d, Bscope_index = %2d, last_index = %2d, offset = x%x\n", +// b->BC, b->Bscope_index, b->Blast_index, b->Boffset); + if (usednteh & EHcleanup) + for (code *c = b->Bcode; c; c = code_next(c)) + { + if (c->Iop == (ESCAPE | ESCddtor)) + ndctors++; + } + } + //printf("guarddim = %d, ndctors = %d\n", guarddim, ndctors); + +#if OUREH + pdt = dtsize_t(pdt,guarddim + ndctors); + sz += NPTRSIZE; +#endif + + unsigned catchoffset = sz + (guarddim + ndctors) * GUARD_SIZE; + + // Generate guard[] + int i = 0; + for (block *b = startblock; b; b = b->Bnext) + { + //printf("b = %p, b->Btry = %p, b->offset = %x\n", b, b->Btry, b->Boffset); + if (b->BC == BC_try) + { + assert(b->Bscope_index >= i); + if (i < b->Bscope_index) + { int fillsize = (b->Bscope_index - i) * GUARD_SIZE; + pdt = dtnzeros(pdt, fillsize); + sz += fillsize; + } + i = b->Bscope_index + 1; + + int nsucc = list_nitems(b->Bsucc); + +#if OUREH + //printf("DHandlerInfo: offset = %x", (int)(b->Boffset - startblock->Boffset)); + pdt = dtdword(pdt,b->Boffset - startblock->Boffset); // offset to start of block + + // Compute ending offset + unsigned endoffset; + for (block *bn = b->Bnext; 1; bn = bn->Bnext) + { + //printf("\tbn = %p, bn->Btry = %p, bn->offset = %x\n", bn, bn->Btry, bn->Boffset); + assert(bn); + if (bn->Btry == b->Btry) + { endoffset = bn->Boffset - startblock->Boffset; + break; + } + } + //printf(" endoffset = %x, prev_index = %d\n", endoffset, b->Blast_index); + pdt = dtdword(pdt,endoffset); // offset past end of guarded block +#endif + + pdt = dtdword(pdt,b->Blast_index); // parent index + + if (b->jcatchvar) // if try-catch + { + pdt = dtdword(pdt,catchoffset); + pdt = dtsize_t(pdt,0); // no finally handler + + catchoffset += NPTRSIZE + (nsucc - 1) * (3 * NPTRSIZE); + } + else // else try-finally + { + assert(nsucc == 2); + pdt = dtdword(pdt,0); // no catch offset + block *bhandler = list_block(list_next(b->Bsucc)); + assert(bhandler->BC == BC_finally); + // To successor of BC_finally block + bhandler = list_block(bhandler->Bsucc); +#if OUREH + pdt = dtxoff(pdt,funcsym_p,bhandler->Boffset - startblock->Boffset, TYnptr); // finally handler address +#else + pdt = dtcoff(pdt,bhandler->Boffset); // finally handler address +#endif + } + sz += GUARD_SIZE; + } + } + + /* Append to guard[] the guard blocks for temporaries that are created and destroyed + * within a single expression. These are marked by the special instruction pairs + * (ESCAPE | ESCdctor) and (ESCAPE | ESCddtor). + */ + if (usednteh & EHcleanup) + { + int scopeindex = guarddim; + for (block *b = startblock; b; b = b->Bnext) + { + /* Set up stack of scope indices + */ + #define STACKINC 16 + int stackbuf[STACKINC]; + int *stack = stackbuf; + int stackmax = STACKINC; + stack[0] = b->Btry ? b->Btry->Bscope_index : -1; + int stacki = 1; + + unsigned boffset = b->Boffset; + for (code *c = b->Bcode; c; c = code_next(c)) + { + if (c->Iop == (ESCAPE | ESCdctor)) + { + code *c2 = code_next(c); + if (config.flags2 & CFG2seh) + c2->IEV2.Vsize_t = scopeindex; +#if OUREH + pdt = dtdword(pdt,boffset - startblock->Boffset); // guard offset +#endif + // Find corresponding ddtor instruction + int n = 0; + unsigned eoffset = boffset; + unsigned foffset; + for (; 1; c2 = code_next(c2)) + { + assert(c2); + if (c2->Iop == (ESCAPE | ESCddtor)) + { + if (n) + n--; + else + { + foffset = eoffset; + code *cf = code_next(c2); + if (config.flags2 & CFG2seh) + { cf->IEV2.Vsize_t = stack[stacki - 1]; + foffset += calccodsize(cf); + cf = code_next(cf); + } + foffset += calccodsize(cf); + while (cf->Iop != JMP && cf->Iop != JMPS) + { + cf = code_next(cf); + foffset += calccodsize(cf); + } + cf = code_next(cf); + foffset += calccodsize(cf); +#if OUREH + pdt = dtdword(pdt,eoffset - startblock->Boffset); // guard offset +#endif + break; + } + } + else if (c2->Iop == (ESCAPE | ESCdctor)) + { + n++; + } + else + eoffset += calccodsize(c2); + } + //printf("boffset = %x, eoffset = %x, foffset = %x\n", boffset, eoffset, foffset); + pdt = dtdword(pdt,stack[stacki - 1]); // parent index + pdt = dtdword(pdt,0); // no catch offset +#if OUREH + pdt = dtxoff(pdt,funcsym_p,foffset - startblock->Boffset, TYnptr); // finally handler offset +#else + pdt = dtcoff(pdt,foffset); // finally handler address +#endif + if (stacki == stackmax) + { // stack[] is out of space; enlarge it + int *pi = (int *)alloca((stackmax + STACKINC) * sizeof(int)); + assert(pi); + memcpy(pi, stack, stackmax * sizeof(int)); + stack = pi; + stackmax += STACKINC; + } + stack[stacki++] = scopeindex; + ++scopeindex; + sz += GUARD_SIZE; + } + else if (c->Iop == (ESCAPE | ESCddtor)) + { + stacki--; + assert(stacki != 0); + } + boffset += calccodsize(c); + } + } + } + + // Generate catch[] + for (block *b = startblock; b; b = b->Bnext) + { + if (b->BC == BC_try && b->jcatchvar) // if try-catch + { + int nsucc = list_nitems(b->Bsucc); + pdt = dtsize_t(pdt,nsucc - 1); // # of catch blocks + sz += NPTRSIZE; + + for (list_t bl = list_next(b->Bsucc); bl; bl = list_next(bl)) + { + block *bcatch = list_block(bl); + + pdt = dtxoff(pdt,bcatch->Bcatchtype,0,TYjhandle); + + pdt = dtsize_t(pdt,cod3_bpoffset(b->jcatchvar)); // EBP offset + +#if OUREH + pdt = dtxoff(pdt,funcsym_p,bcatch->Boffset - startblock->Boffset, TYnptr); // catch handler address +#else + pdt = dtcoff(pdt,bcatch->Boffset); // catch handler address +#endif + sz += 3 * NPTRSIZE; + } + } + } + assert(sz != 0); +} + diff --git a/entity.c b/entity.c new file mode 100644 index 00000000..98b81141 --- /dev/null +++ b/entity.c @@ -0,0 +1,2391 @@ + +// Copyright (c) 1999-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#include +#include + +/********************************************* + * Convert from named entity to its encoding. + * For reference: + * http://www.htmlhelp.com/reference/html40/entities/ + * http://www.w3.org/2003/entities/2007/w3centities-f.ent + */ + +struct NameId +{ + const char *name; + unsigned value; +}; + +static NameId namesA[]={ + "Aacgr", 0x00386, // GREEK CAPITAL LETTER ALPHA WITH TONOS + "aacgr", 0x003AC, // GREEK SMALL LETTER ALPHA WITH TONOS + "Aacute", 0x000C1, // LATIN CAPITAL LETTER A WITH ACUTE + "aacute", 0x000E1, // LATIN SMALL LETTER A WITH ACUTE + "Abreve", 0x00102, // LATIN CAPITAL LETTER A WITH BREVE + "abreve", 0x00103, // LATIN SMALL LETTER A WITH BREVE + "ac", 0x0223E, // INVERTED LAZY S + "acd", 0x0223F, // SINE WAVE +// "acE", 0x0223E;0x00333, // INVERTED LAZY S with double underline + "Acirc", 0x000C2, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + "acirc", 0x000E2, // LATIN SMALL LETTER A WITH CIRCUMFLEX + "acute", 0x000B4, // ACUTE ACCENT + "Acy", 0x00410, // CYRILLIC CAPITAL LETTER A + "acy", 0x00430, // CYRILLIC SMALL LETTER A + "AElig", 0x000C6, // LATIN CAPITAL LETTER AE + "aelig", 0x000E6, // LATIN SMALL LETTER AE + "af", 0x02061, // FUNCTION APPLICATION + "Afr", 0x1D504, // MATHEMATICAL FRAKTUR CAPITAL A + "afr", 0x1D51E, // MATHEMATICAL FRAKTUR SMALL A + "Agr", 0x00391, // GREEK CAPITAL LETTER ALPHA + "agr", 0x003B1, // GREEK SMALL LETTER ALPHA + "Agrave", 0x000C0, // LATIN CAPITAL LETTER A WITH GRAVE + "agrave", 0x000E0, // LATIN SMALL LETTER A WITH GRAVE + "alefsym", 0x02135, // ALEF SYMBOL + "aleph", 0x02135, // ALEF SYMBOL + "Alpha", 0x00391, // GREEK CAPITAL LETTER ALPHA + "alpha", 0x003B1, // GREEK SMALL LETTER ALPHA + "Amacr", 0x00100, // LATIN CAPITAL LETTER A WITH MACRON + "amacr", 0x00101, // LATIN SMALL LETTER A WITH MACRON + "amalg", 0x02A3F, // AMALGAMATION OR COPRODUCT + "amp", 0x00026, // AMPERSAND + "AMP", 0x00026, // AMPERSAND + "and", 0x02227, // LOGICAL AND + "And", 0x02A53, // DOUBLE LOGICAL AND + "andand", 0x02A55, // TWO INTERSECTING LOGICAL AND + "andd", 0x02A5C, // LOGICAL AND WITH HORIZONTAL DASH + "andslope", 0x02A58, // SLOPING LARGE AND + "andv", 0x02A5A, // LOGICAL AND WITH MIDDLE STEM + "ang", 0x02220, // ANGLE + "ange", 0x029A4, // ANGLE WITH UNDERBAR + "angle", 0x02220, // ANGLE + "angmsd", 0x02221, // MEASURED ANGLE + "angmsdaa", 0x029A8, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT + "angmsdab", 0x029A9, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT + "angmsdac", 0x029AA, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT + "angmsdad", 0x029AB, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT + "angmsdae", 0x029AC, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP + "angmsdaf", 0x029AD, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP + "angmsdag", 0x029AE, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN + "angmsdah", 0x029AF, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN + "angrt", 0x0221F, // RIGHT ANGLE + "angrtvb", 0x022BE, // RIGHT ANGLE WITH ARC + "angrtvbd", 0x0299D, // MEASURED RIGHT ANGLE WITH DOT + "angsph", 0x02222, // SPHERICAL ANGLE + "angst", 0x000C5, // LATIN CAPITAL LETTER A WITH RING ABOVE + "angzarr", 0x0237C, // RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW + "Aogon", 0x00104, // LATIN CAPITAL LETTER A WITH OGONEK + "aogon", 0x00105, // LATIN SMALL LETTER A WITH OGONEK + "Aopf", 0x1D538, // MATHEMATICAL DOUBLE-STRUCK CAPITAL A + "aopf", 0x1D552, // MATHEMATICAL DOUBLE-STRUCK SMALL A + "ap", 0x02248, // ALMOST EQUAL TO + "apacir", 0x02A6F, // ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT + "ape", 0x0224A, // ALMOST EQUAL OR EQUAL TO + "apE", 0x02A70, // APPROXIMATELY EQUAL OR EQUAL TO + "apid", 0x0224B, // TRIPLE TILDE + "apos", 0x00027, // APOSTROPHE + "ApplyFunction", 0x02061, // FUNCTION APPLICATION + "approx", 0x02248, // ALMOST EQUAL TO + "approxeq", 0x0224A, // ALMOST EQUAL OR EQUAL TO + "Aring", 0x000C5, // LATIN CAPITAL LETTER A WITH RING ABOVE + "aring", 0x000E5, // LATIN SMALL LETTER A WITH RING ABOVE + "Ascr", 0x1D49C, // MATHEMATICAL SCRIPT CAPITAL A + "ascr", 0x1D4B6, // MATHEMATICAL SCRIPT SMALL A + "Assign", 0x02254, // COLON EQUALS + "ast", 0x0002A, // ASTERISK + "asymp", 0x02248, // ALMOST EQUAL TO + "asympeq", 0x0224D, // EQUIVALENT TO + "Atilde", 0x000C3, // LATIN CAPITAL LETTER A WITH TILDE + "atilde", 0x000E3, // LATIN SMALL LETTER A WITH TILDE + "Auml", 0x000C4, // LATIN CAPITAL LETTER A WITH DIAERESIS + "auml", 0x000E4, // LATIN SMALL LETTER A WITH DIAERESIS + "awconint", 0x02233, // ANTICLOCKWISE CONTOUR INTEGRAL + "awint", 0x02A11, // ANTICLOCKWISE INTEGRATION + NULL, 0 +}; + +static NameId namesB[]={ + "backcong", 0x0224C, // ALL EQUAL TO + "backepsilon", 0x003F6, // GREEK REVERSED LUNATE EPSILON SYMBOL + "backprime", 0x02035, // REVERSED PRIME + "backsim", 0x0223D, // REVERSED TILDE + "backsimeq", 0x022CD, // REVERSED TILDE EQUALS + "Backslash", 0x02216, // SET MINUS +// "b.alpha", 0x1D6C2, // MATHEMATICAL BOLD SMALL ALPHA + "Barv", 0x02AE7, // SHORT DOWN TACK WITH OVERBAR + "barvee", 0x022BD, // NOR + "barwed", 0x02305, // PROJECTIVE + "Barwed", 0x02306, // PERSPECTIVE + "barwedge", 0x02305, // PROJECTIVE +// "b.beta", 0x1D6C3, // MATHEMATICAL BOLD SMALL BETA + "bbrk", 0x023B5, // BOTTOM SQUARE BRACKET + "bbrktbrk", 0x023B6, // BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET +// "b.chi", 0x1D6D8, // MATHEMATICAL BOLD SMALL CHI + "bcong", 0x0224C, // ALL EQUAL TO + "Bcy", 0x00411, // CYRILLIC CAPITAL LETTER BE + "bcy", 0x00431, // CYRILLIC SMALL LETTER BE +// "b.Delta", 0x1D6AB, // MATHEMATICAL BOLD CAPITAL DELTA +// "b.delta", 0x1D6C5, // MATHEMATICAL BOLD SMALL DELTA + "bdquo", 0x0201E, // DOUBLE LOW-9 QUOTATION MARK + "becaus", 0x02235, // BECAUSE + "because", 0x02235, // BECAUSE + "Because", 0x02235, // BECAUSE + "bemptyv", 0x029B0, // REVERSED EMPTY SET + "bepsi", 0x003F6, // GREEK REVERSED LUNATE EPSILON SYMBOL +// "b.epsi", 0x1D6C6, // MATHEMATICAL BOLD SMALL EPSILON +// "b.epsiv", 0x1D6DC, // MATHEMATICAL BOLD EPSILON SYMBOL + "bernou", 0x0212C, // SCRIPT CAPITAL B + "Bernoullis", 0x0212C, // SCRIPT CAPITAL B + "Beta", 0x00392, // GREEK CAPITAL LETTER BETA + "beta", 0x003B2, // GREEK SMALL LETTER BETA +// "b.eta", 0x1D6C8, // MATHEMATICAL BOLD SMALL ETA + "beth", 0x02136, // BET SYMBOL + "between", 0x0226C, // BETWEEN + "Bfr", 0x1D505, // MATHEMATICAL FRAKTUR CAPITAL B + "bfr", 0x1D51F, // MATHEMATICAL FRAKTUR SMALL B +// "b.Gamma", 0x1D6AA, // MATHEMATICAL BOLD CAPITAL GAMMA +// "b.gamma", 0x1D6C4, // MATHEMATICAL BOLD SMALL GAMMA +// "b.Gammad", 0x1D7CA, // MATHEMATICAL BOLD CAPITAL DIGAMMA +// "b.gammad", 0x1D7CB, // MATHEMATICAL BOLD SMALL DIGAMMA + "Bgr", 0x00392, // GREEK CAPITAL LETTER BETA + "bgr", 0x003B2, // GREEK SMALL LETTER BETA + "bigcap", 0x022C2, // N-ARY INTERSECTION + "bigcirc", 0x025EF, // LARGE CIRCLE + "bigcup", 0x022C3, // N-ARY UNION + "bigodot", 0x02A00, // N-ARY CIRCLED DOT OPERATOR + "bigoplus", 0x02A01, // N-ARY CIRCLED PLUS OPERATOR + "bigotimes", 0x02A02, // N-ARY CIRCLED TIMES OPERATOR + "bigsqcup", 0x02A06, // N-ARY SQUARE UNION OPERATOR + "bigstar", 0x02605, // BLACK STAR + "bigtriangledown", 0x025BD, // WHITE DOWN-POINTING TRIANGLE + "bigtriangleup", 0x025B3, // WHITE UP-POINTING TRIANGLE + "biguplus", 0x02A04, // N-ARY UNION OPERATOR WITH PLUS + "bigvee", 0x022C1, // N-ARY LOGICAL OR + "bigwedge", 0x022C0, // N-ARY LOGICAL AND +// "b.iota", 0x1D6CA, // MATHEMATICAL BOLD SMALL IOTA +// "b.kappa", 0x1D6CB, // MATHEMATICAL BOLD SMALL KAPPA +// "b.kappav", 0x1D6DE, // MATHEMATICAL BOLD KAPPA SYMBOL + "bkarow", 0x0290D, // RIGHTWARDS DOUBLE DASH ARROW + "blacklozenge", 0x029EB, // BLACK LOZENGE + "blacksquare", 0x025AA, // BLACK SMALL SQUARE + "blacktriangle", 0x025B4, // BLACK UP-POINTING SMALL TRIANGLE + "blacktriangledown", 0x025BE, // BLACK DOWN-POINTING SMALL TRIANGLE + "blacktriangleleft", 0x025C2, // BLACK LEFT-POINTING SMALL TRIANGLE + "blacktriangleright", 0x025B8, // BLACK RIGHT-POINTING SMALL TRIANGLE +// "b.Lambda", 0x1D6B2, // MATHEMATICAL BOLD CAPITAL LAMDA +// "b.lambda", 0x1D6CC, // MATHEMATICAL BOLD SMALL LAMDA + "blank", 0x02423, // OPEN BOX + "blk12", 0x02592, // MEDIUM SHADE + "blk14", 0x02591, // LIGHT SHADE + "blk34", 0x02593, // DARK SHADE + "block", 0x02588, // FULL BLOCK +// "b.mu", 0x1D6CD, // MATHEMATICAL BOLD SMALL MU +// "bne", 0x0003D;0x020E5, // EQUALS SIGN with reverse slash +// "bnequiv", 0x02261;0x020E5, // IDENTICAL TO with reverse slash + "bnot", 0x02310, // REVERSED NOT SIGN + "bNot", 0x02AED, // REVERSED DOUBLE STROKE NOT SIGN +// "b.nu", 0x1D6CE, // MATHEMATICAL BOLD SMALL NU +// "b.Omega", 0x1D6C0, // MATHEMATICAL BOLD CAPITAL OMEGA +// "b.omega", 0x1D6DA, // MATHEMATICAL BOLD SMALL OMEGA + "Bopf", 0x1D539, // MATHEMATICAL DOUBLE-STRUCK CAPITAL B + "bopf", 0x1D553, // MATHEMATICAL DOUBLE-STRUCK SMALL B + "bot", 0x022A5, // UP TACK + "bottom", 0x022A5, // UP TACK + "bowtie", 0x022C8, // BOWTIE + "boxbox", 0x029C9, // TWO JOINED SQUARES + "boxdl", 0x02510, // BOX DRAWINGS LIGHT DOWN AND LEFT + "boxdL", 0x02555, // BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE + "boxDl", 0x02556, // BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE + "boxDL", 0x02557, // BOX DRAWINGS DOUBLE DOWN AND LEFT + "boxdr", 0x0250C, // BOX DRAWINGS LIGHT DOWN AND RIGHT + "boxdR", 0x02552, // BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE + "boxDr", 0x02553, // BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE + "boxDR", 0x02554, // BOX DRAWINGS DOUBLE DOWN AND RIGHT + "boxh", 0x02500, // BOX DRAWINGS LIGHT HORIZONTAL + "boxH", 0x02550, // BOX DRAWINGS DOUBLE HORIZONTAL + "boxhd", 0x0252C, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + "boxHd", 0x02564, // BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE + "boxhD", 0x02565, // BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE + "boxHD", 0x02566, // BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL + "boxhu", 0x02534, // BOX DRAWINGS LIGHT UP AND HORIZONTAL + "boxHu", 0x02567, // BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE + "boxhU", 0x02568, // BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE + "boxHU", 0x02569, // BOX DRAWINGS DOUBLE UP AND HORIZONTAL + "boxminus", 0x0229F, // SQUARED MINUS + "boxplus", 0x0229E, // SQUARED PLUS + "boxtimes", 0x022A0, // SQUARED TIMES + "boxul", 0x02518, // BOX DRAWINGS LIGHT UP AND LEFT + "boxuL", 0x0255B, // BOX DRAWINGS UP SINGLE AND LEFT DOUBLE + "boxUl", 0x0255C, // BOX DRAWINGS UP DOUBLE AND LEFT SINGLE + "boxUL", 0x0255D, // BOX DRAWINGS DOUBLE UP AND LEFT + "boxur", 0x02514, // BOX DRAWINGS LIGHT UP AND RIGHT + "boxuR", 0x02558, // BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE + "boxUr", 0x02559, // BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE + "boxUR", 0x0255A, // BOX DRAWINGS DOUBLE UP AND RIGHT + "boxv", 0x02502, // BOX DRAWINGS LIGHT VERTICAL + "boxV", 0x02551, // BOX DRAWINGS DOUBLE VERTICAL + "boxvh", 0x0253C, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + "boxvH", 0x0256A, // BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE + "boxVh", 0x0256B, // BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE + "boxVH", 0x0256C, // BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL + "boxvl", 0x02524, // BOX DRAWINGS LIGHT VERTICAL AND LEFT + "boxvL", 0x02561, // BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE + "boxVl", 0x02562, // BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE + "boxVL", 0x02563, // BOX DRAWINGS DOUBLE VERTICAL AND LEFT + "boxvr", 0x0251C, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT + "boxvR", 0x0255E, // BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE + "boxVr", 0x0255F, // BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE + "boxVR", 0x02560, // BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +// "b.Phi", 0x1D6BD, // MATHEMATICAL BOLD CAPITAL PHI +// "b.phi", 0x1D6D7, // MATHEMATICAL BOLD SMALL PHI +// "b.phiv", 0x1D6DF, // MATHEMATICAL BOLD PHI SYMBOL +// "b.Pi", 0x1D6B7, // MATHEMATICAL BOLD CAPITAL PI +// "b.pi", 0x1D6D1, // MATHEMATICAL BOLD SMALL PI +// "b.piv", 0x1D6E1, // MATHEMATICAL BOLD PI SYMBOL + "bprime", 0x02035, // REVERSED PRIME +// "b.Psi", 0x1D6BF, // MATHEMATICAL BOLD CAPITAL PSI +// "b.psi", 0x1D6D9, // MATHEMATICAL BOLD SMALL PSI + "breve", 0x002D8, // BREVE + "Breve", 0x002D8, // BREVE +// "b.rho", 0x1D6D2, // MATHEMATICAL BOLD SMALL RHO +// "b.rhov", 0x1D6E0, // MATHEMATICAL BOLD RHO SYMBOL + "brvbar", 0x000A6, // BROKEN BAR + "Bscr", 0x0212C, // SCRIPT CAPITAL B + "bscr", 0x1D4B7, // MATHEMATICAL SCRIPT SMALL B + "bsemi", 0x0204F, // REVERSED SEMICOLON +// "b.Sigma", 0x1D6BA, // MATHEMATICAL BOLD CAPITAL SIGMA +// "b.sigma", 0x1D6D4, // MATHEMATICAL BOLD SMALL SIGMA +// "b.sigmav", 0x1D6D3, // MATHEMATICAL BOLD SMALL FINAL SIGMA + "bsim", 0x0223D, // REVERSED TILDE + "bsime", 0x022CD, // REVERSED TILDE EQUALS + "bsol", 0x0005C, // REVERSE SOLIDUS + "bsolb", 0x029C5, // SQUARED FALLING DIAGONAL SLASH + "bsolhsub", 0x027C8, // REVERSE SOLIDUS PRECEDING SUBSET +// "b.tau", 0x1D6D5, // MATHEMATICAL BOLD SMALL TAU +// "b.Theta", 0x1D6AF, // MATHEMATICAL BOLD CAPITAL THETA +// "b.thetas", 0x1D6C9, // MATHEMATICAL BOLD SMALL THETA +// "b.thetav", 0x1D6DD, // MATHEMATICAL BOLD THETA SYMBOL + "bull", 0x02022, // BULLET + "bullet", 0x02022, // BULLET + "bump", 0x0224E, // GEOMETRICALLY EQUIVALENT TO + "bumpe", 0x0224F, // DIFFERENCE BETWEEN + "bumpE", 0x02AAE, // EQUALS SIGN WITH BUMPY ABOVE + "Bumpeq", 0x0224E, // GEOMETRICALLY EQUIVALENT TO + "bumpeq", 0x0224F, // DIFFERENCE BETWEEN +// "b.Upsi", 0x1D6BC, // MATHEMATICAL BOLD CAPITAL UPSILON +// "b.upsi", 0x1D6D6, // MATHEMATICAL BOLD SMALL UPSILON +// "b.Xi", 0x1D6B5, // MATHEMATICAL BOLD CAPITAL XI +// "b.xi", 0x1D6CF, // MATHEMATICAL BOLD SMALL XI +// "b.zeta", 0x1D6C7, // MATHEMATICAL BOLD SMALL ZETA + NULL, 0 +}; + +static NameId namesC[]={ + "Cacute", 0x00106, // LATIN CAPITAL LETTER C WITH ACUTE + "cacute", 0x00107, // LATIN SMALL LETTER C WITH ACUTE + "cap", 0x02229, // INTERSECTION + "Cap", 0x022D2, // DOUBLE INTERSECTION + "capand", 0x02A44, // INTERSECTION WITH LOGICAL AND + "capbrcup", 0x02A49, // INTERSECTION ABOVE BAR ABOVE UNION + "capcap", 0x02A4B, // INTERSECTION BESIDE AND JOINED WITH INTERSECTION + "capcup", 0x02A47, // INTERSECTION ABOVE UNION + "capdot", 0x02A40, // INTERSECTION WITH DOT + "CapitalDifferentialD", 0x02145, // DOUBLE-STRUCK ITALIC CAPITAL D +// "caps", 0x02229;0x0FE00, // INTERSECTION with serifs + "caret", 0x02041, // CARET INSERTION POINT + "caron", 0x002C7, // CARON + "Cayleys", 0x0212D, // BLACK-LETTER CAPITAL C + "ccaps", 0x02A4D, // CLOSED INTERSECTION WITH SERIFS + "Ccaron", 0x0010C, // LATIN CAPITAL LETTER C WITH CARON + "ccaron", 0x0010D, // LATIN SMALL LETTER C WITH CARON + "Ccedil", 0x000C7, // LATIN CAPITAL LETTER C WITH CEDILLA + "ccedil", 0x000E7, // LATIN SMALL LETTER C WITH CEDILLA + "Ccirc", 0x00108, // LATIN CAPITAL LETTER C WITH CIRCUMFLEX + "ccirc", 0x00109, // LATIN SMALL LETTER C WITH CIRCUMFLEX + "Cconint", 0x02230, // VOLUME INTEGRAL + "ccups", 0x02A4C, // CLOSED UNION WITH SERIFS + "ccupssm", 0x02A50, // CLOSED UNION WITH SERIFS AND SMASH PRODUCT + "Cdot", 0x0010A, // LATIN CAPITAL LETTER C WITH DOT ABOVE + "cdot", 0x0010B, // LATIN SMALL LETTER C WITH DOT ABOVE + "cedil", 0x000B8, // CEDILLA + "Cedilla", 0x000B8, // CEDILLA + "cemptyv", 0x029B2, // EMPTY SET WITH SMALL CIRCLE ABOVE + "cent", 0x000A2, // CENT SIGN + "centerdot", 0x000B7, // MIDDLE DOT + "CenterDot", 0x000B7, // MIDDLE DOT + "Cfr", 0x0212D, // BLACK-LETTER CAPITAL C + "cfr", 0x1D520, // MATHEMATICAL FRAKTUR SMALL C + "CHcy", 0x00427, // CYRILLIC CAPITAL LETTER CHE + "chcy", 0x00447, // CYRILLIC SMALL LETTER CHE + "check", 0x02713, // CHECK MARK + "checkmark", 0x02713, // CHECK MARK + "Chi", 0x003A7, // GREEK CAPITAL LETTER CHI + "chi", 0x003C7, // GREEK SMALL LETTER CHI + "cir", 0x025CB, // WHITE CIRCLE + "circ", 0x002C6, // MODIFIER LETTER CIRCUMFLEX ACCENT + "circeq", 0x02257, // RING EQUAL TO + "circlearrowleft", 0x021BA, // ANTICLOCKWISE OPEN CIRCLE ARROW + "circlearrowright", 0x021BB, // CLOCKWISE OPEN CIRCLE ARROW + "circledast", 0x0229B, // CIRCLED ASTERISK OPERATOR + "circledcirc", 0x0229A, // CIRCLED RING OPERATOR + "circleddash", 0x0229D, // CIRCLED DASH + "CircleDot", 0x02299, // CIRCLED DOT OPERATOR + "circledR", 0x000AE, // REGISTERED SIGN + "circledS", 0x024C8, // CIRCLED LATIN CAPITAL LETTER S + "CircleMinus", 0x02296, // CIRCLED MINUS + "CirclePlus", 0x02295, // CIRCLED PLUS + "CircleTimes", 0x02297, // CIRCLED TIMES + "cire", 0x02257, // RING EQUAL TO + "cirE", 0x029C3, // CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT + "cirfnint", 0x02A10, // CIRCULATION FUNCTION + "cirmid", 0x02AEF, // VERTICAL LINE WITH CIRCLE ABOVE + "cirscir", 0x029C2, // CIRCLE WITH SMALL CIRCLE TO THE RIGHT + "ClockwiseContourIntegral", 0x02232, // CLOCKWISE CONTOUR INTEGRAL + "CloseCurlyDoubleQuote", 0x0201D, // RIGHT DOUBLE QUOTATION MARK + "CloseCurlyQuote", 0x02019, // RIGHT SINGLE QUOTATION MARK + "clubs", 0x02663, // BLACK CLUB SUIT + "clubsuit", 0x02663, // BLACK CLUB SUIT + "colon", 0x0003A, // COLON + "Colon", 0x02237, // PROPORTION + "colone", 0x02254, // COLON EQUALS + "Colone", 0x02A74, // DOUBLE COLON EQUAL + "coloneq", 0x02254, // COLON EQUALS + "comma", 0x0002C, // COMMA + "commat", 0x00040, // COMMERCIAL AT + "comp", 0x02201, // COMPLEMENT + "compfn", 0x02218, // RING OPERATOR + "complement", 0x02201, // COMPLEMENT + "complexes", 0x02102, // DOUBLE-STRUCK CAPITAL C + "cong", 0x02245, // APPROXIMATELY EQUAL TO + "congdot", 0x02A6D, // CONGRUENT WITH DOT ABOVE + "Congruent", 0x02261, // IDENTICAL TO + "conint", 0x0222E, // CONTOUR INTEGRAL + "Conint", 0x0222F, // SURFACE INTEGRAL + "ContourIntegral", 0x0222E, // CONTOUR INTEGRAL + "Copf", 0x02102, // DOUBLE-STRUCK CAPITAL C + "copf", 0x1D554, // MATHEMATICAL DOUBLE-STRUCK SMALL C + "coprod", 0x02210, // N-ARY COPRODUCT + "Coproduct", 0x02210, // N-ARY COPRODUCT + "copy", 0x000A9, // COPYRIGHT SIGN + "COPY", 0x000A9, // COPYRIGHT SIGN + "copysr", 0x02117, // SOUND RECORDING COPYRIGHT + "CounterClockwiseContourIntegral", 0x02233, // ANTICLOCKWISE CONTOUR INTEGRAL + "crarr", 0x021B5, // DOWNWARDS ARROW WITH CORNER LEFTWARDS + "cross", 0x02717, // BALLOT X + "Cross", 0x02A2F, // VECTOR OR CROSS PRODUCT + "Cscr", 0x1D49E, // MATHEMATICAL SCRIPT CAPITAL C + "cscr", 0x1D4B8, // MATHEMATICAL SCRIPT SMALL C + "csub", 0x02ACF, // CLOSED SUBSET + "csube", 0x02AD1, // CLOSED SUBSET OR EQUAL TO + "csup", 0x02AD0, // CLOSED SUPERSET + "csupe", 0x02AD2, // CLOSED SUPERSET OR EQUAL TO + "ctdot", 0x022EF, // MIDLINE HORIZONTAL ELLIPSIS + "cudarrl", 0x02938, // RIGHT-SIDE ARC CLOCKWISE ARROW + "cudarrr", 0x02935, // ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS + "cuepr", 0x022DE, // EQUAL TO OR PRECEDES + "cuesc", 0x022DF, // EQUAL TO OR SUCCEEDS + "cularr", 0x021B6, // ANTICLOCKWISE TOP SEMICIRCLE ARROW + "cularrp", 0x0293D, // TOP ARC ANTICLOCKWISE ARROW WITH PLUS + "cup", 0x0222A, // UNION + "Cup", 0x022D3, // DOUBLE UNION + "cupbrcap", 0x02A48, // UNION ABOVE BAR ABOVE INTERSECTION + "CupCap", 0x0224D, // EQUIVALENT TO + "cupcap", 0x02A46, // UNION ABOVE INTERSECTION + "cupcup", 0x02A4A, // UNION BESIDE AND JOINED WITH UNION + "cupdot", 0x0228D, // MULTISET MULTIPLICATION + "cupor", 0x02A45, // UNION WITH LOGICAL OR +// "cups", 0x0222A;0x0FE00, // UNION with serifs + "curarr", 0x021B7, // CLOCKWISE TOP SEMICIRCLE ARROW + "curarrm", 0x0293C, // TOP ARC CLOCKWISE ARROW WITH MINUS + "curlyeqprec", 0x022DE, // EQUAL TO OR PRECEDES + "curlyeqsucc", 0x022DF, // EQUAL TO OR SUCCEEDS + "curlyvee", 0x022CE, // CURLY LOGICAL OR + "curlywedge", 0x022CF, // CURLY LOGICAL AND + "curren", 0x000A4, // CURRENCY SIGN + "curvearrowleft", 0x021B6, // ANTICLOCKWISE TOP SEMICIRCLE ARROW + "curvearrowright", 0x021B7, // CLOCKWISE TOP SEMICIRCLE ARROW + "cuvee", 0x022CE, // CURLY LOGICAL OR + "cuwed", 0x022CF, // CURLY LOGICAL AND + "cwconint", 0x02232, // CLOCKWISE CONTOUR INTEGRAL + "cwint", 0x02231, // CLOCKWISE INTEGRAL + "cylcty", 0x0232D, // CYLINDRICITY + NULL, 0 +}; + +static NameId namesD[]={ + "dagger", 0x02020, // DAGGER + "Dagger", 0x02021, // DOUBLE DAGGER + "daleth", 0x02138, // DALET SYMBOL + "darr", 0x02193, // DOWNWARDS ARROW + "Darr", 0x021A1, // DOWNWARDS TWO HEADED ARROW + "dArr", 0x021D3, // DOWNWARDS DOUBLE ARROW + "dash", 0x02010, // HYPHEN + "dashv", 0x022A3, // LEFT TACK + "Dashv", 0x02AE4, // VERTICAL BAR DOUBLE LEFT TURNSTILE + "dbkarow", 0x0290F, // RIGHTWARDS TRIPLE DASH ARROW + "dblac", 0x002DD, // DOUBLE ACUTE ACCENT + "Dcaron", 0x0010E, // LATIN CAPITAL LETTER D WITH CARON + "dcaron", 0x0010F, // LATIN SMALL LETTER D WITH CARON + "Dcy", 0x00414, // CYRILLIC CAPITAL LETTER DE + "dcy", 0x00434, // CYRILLIC SMALL LETTER DE + "DD", 0x02145, // DOUBLE-STRUCK ITALIC CAPITAL D + "dd", 0x02146, // DOUBLE-STRUCK ITALIC SMALL D + "ddagger", 0x02021, // DOUBLE DAGGER + "ddarr", 0x021CA, // DOWNWARDS PAIRED ARROWS + "DDotrahd", 0x02911, // RIGHTWARDS ARROW WITH DOTTED STEM + "ddotseq", 0x02A77, // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW + "deg", 0x000B0, // DEGREE SIGN + "Del", 0x02207, // NABLA + "Delta", 0x00394, // GREEK CAPITAL LETTER DELTA + "delta", 0x003B4, // GREEK SMALL LETTER DELTA + "demptyv", 0x029B1, // EMPTY SET WITH OVERBAR + "dfisht", 0x0297F, // DOWN FISH TAIL + "Dfr", 0x1D507, // MATHEMATICAL FRAKTUR CAPITAL D + "dfr", 0x1D521, // MATHEMATICAL FRAKTUR SMALL D + "Dgr", 0x00394, // GREEK CAPITAL LETTER DELTA + "dgr", 0x003B4, // GREEK SMALL LETTER DELTA + "dHar", 0x02965, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT + "dharl", 0x021C3, // DOWNWARDS HARPOON WITH BARB LEFTWARDS + "dharr", 0x021C2, // DOWNWARDS HARPOON WITH BARB RIGHTWARDS + "DiacriticalAcute", 0x000B4, // ACUTE ACCENT + "DiacriticalDot", 0x002D9, // DOT ABOVE + "DiacriticalDoubleAcute", 0x002DD, // DOUBLE ACUTE ACCENT + "DiacriticalGrave", 0x00060, // GRAVE ACCENT + "DiacriticalTilde", 0x002DC, // SMALL TILDE + "diam", 0x022C4, // DIAMOND OPERATOR + "diamond", 0x022C4, // DIAMOND OPERATOR + "Diamond", 0x022C4, // DIAMOND OPERATOR + "diamondsuit", 0x02666, // BLACK DIAMOND SUIT + "diams", 0x02666, // BLACK DIAMOND SUIT + "die", 0x000A8, // DIAERESIS + "DifferentialD", 0x02146, // DOUBLE-STRUCK ITALIC SMALL D + "digamma", 0x003DD, // GREEK SMALL LETTER DIGAMMA + "disin", 0x022F2, // ELEMENT OF WITH LONG HORIZONTAL STROKE + "div", 0x000F7, // DIVISION SIGN + "divide", 0x000F7, // DIVISION SIGN + "divideontimes", 0x022C7, // DIVISION TIMES + "divonx", 0x022C7, // DIVISION TIMES + "DJcy", 0x00402, // CYRILLIC CAPITAL LETTER DJE + "djcy", 0x00452, // CYRILLIC SMALL LETTER DJE + "dlcorn", 0x0231E, // BOTTOM LEFT CORNER + "dlcrop", 0x0230D, // BOTTOM LEFT CROP + "dollar", 0x00024, // DOLLAR SIGN + "Dopf", 0x1D53B, // MATHEMATICAL DOUBLE-STRUCK CAPITAL D + "dopf", 0x1D555, // MATHEMATICAL DOUBLE-STRUCK SMALL D + "Dot", 0x000A8, // DIAERESIS + "dot", 0x002D9, // DOT ABOVE + "DotDot", 0x020DC, // COMBINING FOUR DOTS ABOVE + "doteq", 0x02250, // APPROACHES THE LIMIT + "doteqdot", 0x02251, // GEOMETRICALLY EQUAL TO + "DotEqual", 0x02250, // APPROACHES THE LIMIT + "dotminus", 0x02238, // DOT MINUS + "dotplus", 0x02214, // DOT PLUS + "dotsquare", 0x022A1, // SQUARED DOT OPERATOR + "doublebarwedge", 0x02306, // PERSPECTIVE + "DoubleContourIntegral", 0x0222F, // SURFACE INTEGRAL + "DoubleDot", 0x000A8, // DIAERESIS + "DoubleDownArrow", 0x021D3, // DOWNWARDS DOUBLE ARROW + "DoubleLeftArrow", 0x021D0, // LEFTWARDS DOUBLE ARROW + "DoubleLeftRightArrow", 0x021D4, // LEFT RIGHT DOUBLE ARROW + "DoubleLeftTee", 0x02AE4, // VERTICAL BAR DOUBLE LEFT TURNSTILE + "DoubleLongLeftArrow", 0x027F8, // LONG LEFTWARDS DOUBLE ARROW + "DoubleLongLeftRightArrow", 0x027FA, // LONG LEFT RIGHT DOUBLE ARROW + "DoubleLongRightArrow", 0x027F9, // LONG RIGHTWARDS DOUBLE ARROW + "DoubleRightArrow", 0x021D2, // RIGHTWARDS DOUBLE ARROW + "DoubleRightTee", 0x022A8, // TRUE + "DoubleUpArrow", 0x021D1, // UPWARDS DOUBLE ARROW + "DoubleUpDownArrow", 0x021D5, // UP DOWN DOUBLE ARROW + "DoubleVerticalBar", 0x02225, // PARALLEL TO + "downarrow", 0x02193, // DOWNWARDS ARROW + "DownArrow", 0x02193, // DOWNWARDS ARROW + "Downarrow", 0x021D3, // DOWNWARDS DOUBLE ARROW + "DownArrowBar", 0x02913, // DOWNWARDS ARROW TO BAR + "DownArrowUpArrow", 0x021F5, // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW + "DownBreve", 0x00311, // COMBINING INVERTED BREVE + "downdownarrows", 0x021CA, // DOWNWARDS PAIRED ARROWS + "downharpoonleft", 0x021C3, // DOWNWARDS HARPOON WITH BARB LEFTWARDS + "downharpoonright", 0x021C2, // DOWNWARDS HARPOON WITH BARB RIGHTWARDS + "DownLeftRightVector", 0x02950, // LEFT BARB DOWN RIGHT BARB DOWN HARPOON + "DownLeftTeeVector", 0x0295E, // LEFTWARDS HARPOON WITH BARB DOWN FROM BAR + "DownLeftVector", 0x021BD, // LEFTWARDS HARPOON WITH BARB DOWNWARDS + "DownLeftVectorBar", 0x02956, // LEFTWARDS HARPOON WITH BARB DOWN TO BAR + "DownRightTeeVector", 0x0295F, // RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR + "DownRightVector", 0x021C1, // RIGHTWARDS HARPOON WITH BARB DOWNWARDS + "DownRightVectorBar", 0x02957, // RIGHTWARDS HARPOON WITH BARB DOWN TO BAR + "DownTee", 0x022A4, // DOWN TACK + "DownTeeArrow", 0x021A7, // DOWNWARDS ARROW FROM BAR + "drbkarow", 0x02910, // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW + "drcorn", 0x0231F, // BOTTOM RIGHT CORNER + "drcrop", 0x0230C, // BOTTOM RIGHT CROP + "Dscr", 0x1D49F, // MATHEMATICAL SCRIPT CAPITAL D + "dscr", 0x1D4B9, // MATHEMATICAL SCRIPT SMALL D + "DScy", 0x00405, // CYRILLIC CAPITAL LETTER DZE + "dscy", 0x00455, // CYRILLIC SMALL LETTER DZE + "dsol", 0x029F6, // SOLIDUS WITH OVERBAR + "Dstrok", 0x00110, // LATIN CAPITAL LETTER D WITH STROKE + "dstrok", 0x00111, // LATIN SMALL LETTER D WITH STROKE + "dtdot", 0x022F1, // DOWN RIGHT DIAGONAL ELLIPSIS + "dtri", 0x025BF, // WHITE DOWN-POINTING SMALL TRIANGLE + "dtrif", 0x025BE, // BLACK DOWN-POINTING SMALL TRIANGLE + "duarr", 0x021F5, // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW + "duhar", 0x0296F, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT + "dwangle", 0x029A6, // OBLIQUE ANGLE OPENING UP + "DZcy", 0x0040F, // CYRILLIC CAPITAL LETTER DZHE + "dzcy", 0x0045F, // CYRILLIC SMALL LETTER DZHE + "dzigrarr", 0x027FF, // LONG RIGHTWARDS SQUIGGLE ARROW + NULL, 0 +}; + +static NameId namesE[]={ + "Eacgr", 0x00388, // GREEK CAPITAL LETTER EPSILON WITH TONOS + "eacgr", 0x003AD, // GREEK SMALL LETTER EPSILON WITH TONOS + "Eacute", 0x000C9, // LATIN CAPITAL LETTER E WITH ACUTE + "eacute", 0x000E9, // LATIN SMALL LETTER E WITH ACUTE + "easter", 0x02A6E, // EQUALS WITH ASTERISK + "Ecaron", 0x0011A, // LATIN CAPITAL LETTER E WITH CARON + "ecaron", 0x0011B, // LATIN SMALL LETTER E WITH CARON + "ecir", 0x02256, // RING IN EQUAL TO + "Ecirc", 0x000CA, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX + "ecirc", 0x000EA, // LATIN SMALL LETTER E WITH CIRCUMFLEX + "ecolon", 0x02255, // EQUALS COLON + "Ecy", 0x0042D, // CYRILLIC CAPITAL LETTER E + "ecy", 0x0044D, // CYRILLIC SMALL LETTER E + "eDDot", 0x02A77, // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW + "Edot", 0x00116, // LATIN CAPITAL LETTER E WITH DOT ABOVE + "edot", 0x00117, // LATIN SMALL LETTER E WITH DOT ABOVE + "eDot", 0x02251, // GEOMETRICALLY EQUAL TO + "ee", 0x02147, // DOUBLE-STRUCK ITALIC SMALL E + "EEacgr", 0x00389, // GREEK CAPITAL LETTER ETA WITH TONOS + "eeacgr", 0x003AE, // GREEK SMALL LETTER ETA WITH TONOS + "EEgr", 0x00397, // GREEK CAPITAL LETTER ETA + "eegr", 0x003B7, // GREEK SMALL LETTER ETA + "efDot", 0x02252, // APPROXIMATELY EQUAL TO OR THE IMAGE OF + "Efr", 0x1D508, // MATHEMATICAL FRAKTUR CAPITAL E + "efr", 0x1D522, // MATHEMATICAL FRAKTUR SMALL E + "eg", 0x02A9A, // DOUBLE-LINE EQUAL TO OR GREATER-THAN + "Egr", 0x00395, // GREEK CAPITAL LETTER EPSILON + "egr", 0x003B5, // GREEK SMALL LETTER EPSILON + "Egrave", 0x000C8, // LATIN CAPITAL LETTER E WITH GRAVE + "egrave", 0x000E8, // LATIN SMALL LETTER E WITH GRAVE + "egs", 0x02A96, // SLANTED EQUAL TO OR GREATER-THAN + "egsdot", 0x02A98, // SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE + "el", 0x02A99, // DOUBLE-LINE EQUAL TO OR LESS-THAN + "Element", 0x02208, // ELEMENT OF + "elinters", 0x023E7, // ELECTRICAL INTERSECTION + "ell", 0x02113, // SCRIPT SMALL L + "els", 0x02A95, // SLANTED EQUAL TO OR LESS-THAN + "elsdot", 0x02A97, // SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE + "Emacr", 0x00112, // LATIN CAPITAL LETTER E WITH MACRON + "emacr", 0x00113, // LATIN SMALL LETTER E WITH MACRON + "empty", 0x02205, // EMPTY SET + "emptyset", 0x02205, // EMPTY SET + "EmptySmallSquare", 0x025FB, // WHITE MEDIUM SQUARE + "emptyv", 0x02205, // EMPTY SET + "EmptyVerySmallSquare", 0x025AB, // WHITE SMALL SQUARE + "emsp", 0x02003, // EM SPACE + "emsp13", 0x02004, // THREE-PER-EM SPACE + "emsp14", 0x02005, // FOUR-PER-EM SPACE + "ENG", 0x0014A, // LATIN CAPITAL LETTER ENG + "eng", 0x0014B, // LATIN SMALL LETTER ENG + "ensp", 0x02002, // EN SPACE + "Eogon", 0x00118, // LATIN CAPITAL LETTER E WITH OGONEK + "eogon", 0x00119, // LATIN SMALL LETTER E WITH OGONEK + "Eopf", 0x1D53C, // MATHEMATICAL DOUBLE-STRUCK CAPITAL E + "eopf", 0x1D556, // MATHEMATICAL DOUBLE-STRUCK SMALL E + "epar", 0x022D5, // EQUAL AND PARALLEL TO + "eparsl", 0x029E3, // EQUALS SIGN AND SLANTED PARALLEL + "eplus", 0x02A71, // EQUALS SIGN ABOVE PLUS SIGN + "epsi", 0x003B5, // GREEK SMALL LETTER EPSILON + "Epsilon", 0x00395, // GREEK CAPITAL LETTER EPSILON + "epsilon", 0x003B5, // GREEK SMALL LETTER EPSILON + "epsiv", 0x003F5, // GREEK LUNATE EPSILON SYMBOL + "eqcirc", 0x02256, // RING IN EQUAL TO + "eqcolon", 0x02255, // EQUALS COLON + "eqsim", 0x02242, // MINUS TILDE + "eqslantgtr", 0x02A96, // SLANTED EQUAL TO OR GREATER-THAN + "eqslantless", 0x02A95, // SLANTED EQUAL TO OR LESS-THAN + "Equal", 0x02A75, // TWO CONSECUTIVE EQUALS SIGNS + "equals", 0x0003D, // EQUALS SIGN + "EqualTilde", 0x02242, // MINUS TILDE + "equest", 0x0225F, // QUESTIONED EQUAL TO + "Equilibrium", 0x021CC, // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON + "equiv", 0x02261, // IDENTICAL TO + "equivDD", 0x02A78, // EQUIVALENT WITH FOUR DOTS ABOVE + "eqvparsl", 0x029E5, // IDENTICAL TO AND SLANTED PARALLEL + "erarr", 0x02971, // EQUALS SIGN ABOVE RIGHTWARDS ARROW + "erDot", 0x02253, // IMAGE OF OR APPROXIMATELY EQUAL TO + "escr", 0x0212F, // SCRIPT SMALL E + "Escr", 0x02130, // SCRIPT CAPITAL E + "esdot", 0x02250, // APPROACHES THE LIMIT + "esim", 0x02242, // MINUS TILDE + "Esim", 0x02A73, // EQUALS SIGN ABOVE TILDE OPERATOR + "Eta", 0x00397, // GREEK CAPITAL LETTER ETA + "eta", 0x003B7, // GREEK SMALL LETTER ETA + "ETH", 0x000D0, // LATIN CAPITAL LETTER ETH + "eth", 0x000F0, // LATIN SMALL LETTER ETH + "Euml", 0x000CB, // LATIN CAPITAL LETTER E WITH DIAERESIS + "euml", 0x000EB, // LATIN SMALL LETTER E WITH DIAERESIS + "euro", 0x020AC, // EURO SIGN + "excl", 0x00021, // EXCLAMATION MARK + "exist", 0x02203, // THERE EXISTS + "Exists", 0x02203, // THERE EXISTS + "expectation", 0x02130, // SCRIPT CAPITAL E + "exponentiale", 0x02147, // DOUBLE-STRUCK ITALIC SMALL E + "ExponentialE", 0x02147, // DOUBLE-STRUCK ITALIC SMALL E + NULL, 0 +}; + +static NameId namesF[]={ + "fallingdotseq", 0x02252, // APPROXIMATELY EQUAL TO OR THE IMAGE OF + "Fcy", 0x00424, // CYRILLIC CAPITAL LETTER EF + "fcy", 0x00444, // CYRILLIC SMALL LETTER EF + "female", 0x02640, // FEMALE SIGN + "ffilig", 0x0FB03, // LATIN SMALL LIGATURE FFI + "fflig", 0x0FB00, // LATIN SMALL LIGATURE FF + "ffllig", 0x0FB04, // LATIN SMALL LIGATURE FFL + "Ffr", 0x1D509, // MATHEMATICAL FRAKTUR CAPITAL F + "ffr", 0x1D523, // MATHEMATICAL FRAKTUR SMALL F + "filig", 0x0FB01, // LATIN SMALL LIGATURE FI + "FilledSmallSquare", 0x025FC, // BLACK MEDIUM SQUARE + "FilledVerySmallSquare", 0x025AA, // BLACK SMALL SQUARE +// "fjlig", 0x00066;0x0006A, // fj ligature + "flat", 0x0266D, // MUSIC FLAT SIGN + "fllig", 0x0FB02, // LATIN SMALL LIGATURE FL + "fltns", 0x025B1, // WHITE PARALLELOGRAM + "fnof", 0x00192, // LATIN SMALL LETTER F WITH HOOK + "Fopf", 0x1D53D, // MATHEMATICAL DOUBLE-STRUCK CAPITAL F + "fopf", 0x1D557, // MATHEMATICAL DOUBLE-STRUCK SMALL F + "forall", 0x02200, // FOR ALL + "ForAll", 0x02200, // FOR ALL + "fork", 0x022D4, // PITCHFORK + "forkv", 0x02AD9, // ELEMENT OF OPENING DOWNWARDS + "Fouriertrf", 0x02131, // SCRIPT CAPITAL F + "fpartint", 0x02A0D, // FINITE PART INTEGRAL + "frac12", 0x000BD, // VULGAR FRACTION ONE HALF + "frac13", 0x02153, // VULGAR FRACTION ONE THIRD + "frac14", 0x000BC, // VULGAR FRACTION ONE QUARTER + "frac15", 0x02155, // VULGAR FRACTION ONE FIFTH + "frac16", 0x02159, // VULGAR FRACTION ONE SIXTH + "frac18", 0x0215B, // VULGAR FRACTION ONE EIGHTH + "frac23", 0x02154, // VULGAR FRACTION TWO THIRDS + "frac25", 0x02156, // VULGAR FRACTION TWO FIFTHS + "frac34", 0x000BE, // VULGAR FRACTION THREE QUARTERS + "frac35", 0x02157, // VULGAR FRACTION THREE FIFTHS + "frac38", 0x0215C, // VULGAR FRACTION THREE EIGHTHS + "frac45", 0x02158, // VULGAR FRACTION FOUR FIFTHS + "frac56", 0x0215A, // VULGAR FRACTION FIVE SIXTHS + "frac58", 0x0215D, // VULGAR FRACTION FIVE EIGHTHS + "frac78", 0x0215E, // VULGAR FRACTION SEVEN EIGHTHS + "frasl", 0x02044, // FRACTION SLASH + "frown", 0x02322, // FROWN + "Fscr", 0x02131, // SCRIPT CAPITAL F + "fscr", 0x1D4BB, // MATHEMATICAL SCRIPT SMALL F + NULL, 0 +}; + +static NameId namesG[]={ + "gacute", 0x001F5, // LATIN SMALL LETTER G WITH ACUTE + "Gamma", 0x00393, // GREEK CAPITAL LETTER GAMMA + "gamma", 0x003B3, // GREEK SMALL LETTER GAMMA + "Gammad", 0x003DC, // GREEK LETTER DIGAMMA + "gammad", 0x003DD, // GREEK SMALL LETTER DIGAMMA + "gap", 0x02A86, // GREATER-THAN OR APPROXIMATE + "Gbreve", 0x0011E, // LATIN CAPITAL LETTER G WITH BREVE + "gbreve", 0x0011F, // LATIN SMALL LETTER G WITH BREVE + "Gcedil", 0x00122, // LATIN CAPITAL LETTER G WITH CEDILLA + "Gcirc", 0x0011C, // LATIN CAPITAL LETTER G WITH CIRCUMFLEX + "gcirc", 0x0011D, // LATIN SMALL LETTER G WITH CIRCUMFLEX + "Gcy", 0x00413, // CYRILLIC CAPITAL LETTER GHE + "gcy", 0x00433, // CYRILLIC SMALL LETTER GHE + "Gdot", 0x00120, // LATIN CAPITAL LETTER G WITH DOT ABOVE + "gdot", 0x00121, // LATIN SMALL LETTER G WITH DOT ABOVE + "ge", 0x02265, // GREATER-THAN OR EQUAL TO + "gE", 0x02267, // GREATER-THAN OVER EQUAL TO + "gel", 0x022DB, // GREATER-THAN EQUAL TO OR LESS-THAN + "gEl", 0x02A8C, // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN + "geq", 0x02265, // GREATER-THAN OR EQUAL TO + "geqq", 0x02267, // GREATER-THAN OVER EQUAL TO + "geqslant", 0x02A7E, // GREATER-THAN OR SLANTED EQUAL TO + "ges", 0x02A7E, // GREATER-THAN OR SLANTED EQUAL TO + "gescc", 0x02AA9, // GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL + "gesdot", 0x02A80, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE + "gesdoto", 0x02A82, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE + "gesdotol", 0x02A84, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT +// "gesl", 0x022DB;0x0FE00, // GREATER-THAN slanted EQUAL TO OR LESS-THAN + "gesles", 0x02A94, // GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL + "Gfr", 0x1D50A, // MATHEMATICAL FRAKTUR CAPITAL G + "gfr", 0x1D524, // MATHEMATICAL FRAKTUR SMALL G + "gg", 0x0226B, // MUCH GREATER-THAN + "Gg", 0x022D9, // VERY MUCH GREATER-THAN + "ggg", 0x022D9, // VERY MUCH GREATER-THAN + "Ggr", 0x00393, // GREEK CAPITAL LETTER GAMMA + "ggr", 0x003B3, // GREEK SMALL LETTER GAMMA + "gimel", 0x02137, // GIMEL SYMBOL + "GJcy", 0x00403, // CYRILLIC CAPITAL LETTER GJE + "gjcy", 0x00453, // CYRILLIC SMALL LETTER GJE + "gl", 0x02277, // GREATER-THAN OR LESS-THAN + "gla", 0x02AA5, // GREATER-THAN BESIDE LESS-THAN + "glE", 0x02A92, // GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL + "glj", 0x02AA4, // GREATER-THAN OVERLAPPING LESS-THAN + "gnap", 0x02A8A, // GREATER-THAN AND NOT APPROXIMATE + "gnapprox", 0x02A8A, // GREATER-THAN AND NOT APPROXIMATE + "gnE", 0x02269, // GREATER-THAN BUT NOT EQUAL TO + "gne", 0x02A88, // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO + "gneq", 0x02A88, // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO + "gneqq", 0x02269, // GREATER-THAN BUT NOT EQUAL TO + "gnsim", 0x022E7, // GREATER-THAN BUT NOT EQUIVALENT TO + "Gopf", 0x1D53E, // MATHEMATICAL DOUBLE-STRUCK CAPITAL G + "gopf", 0x1D558, // MATHEMATICAL DOUBLE-STRUCK SMALL G + "grave", 0x00060, // GRAVE ACCENT + "GreaterEqual", 0x02265, // GREATER-THAN OR EQUAL TO + "GreaterEqualLess", 0x022DB, // GREATER-THAN EQUAL TO OR LESS-THAN + "GreaterFullEqual", 0x02267, // GREATER-THAN OVER EQUAL TO + "GreaterGreater", 0x02AA2, // DOUBLE NESTED GREATER-THAN + "GreaterLess", 0x02277, // GREATER-THAN OR LESS-THAN + "GreaterSlantEqual", 0x02A7E, // GREATER-THAN OR SLANTED EQUAL TO + "GreaterTilde", 0x02273, // GREATER-THAN OR EQUIVALENT TO + "gscr", 0x0210A, // SCRIPT SMALL G + "Gscr", 0x1D4A2, // MATHEMATICAL SCRIPT CAPITAL G + "gsim", 0x02273, // GREATER-THAN OR EQUIVALENT TO + "gsime", 0x02A8E, // GREATER-THAN ABOVE SIMILAR OR EQUAL + "gsiml", 0x02A90, // GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN + "gt", 0x0003E, // GREATER-THAN SIGN + "GT", 0x0003E, // GREATER-THAN SIGN + "Gt", 0x0226B, // MUCH GREATER-THAN + "gtcc", 0x02AA7, // GREATER-THAN CLOSED BY CURVE + "gtcir", 0x02A7A, // GREATER-THAN WITH CIRCLE INSIDE + "gtdot", 0x022D7, // GREATER-THAN WITH DOT + "gtlPar", 0x02995, // DOUBLE LEFT ARC GREATER-THAN BRACKET + "gtquest", 0x02A7C, // GREATER-THAN WITH QUESTION MARK ABOVE + "gtrapprox", 0x02A86, // GREATER-THAN OR APPROXIMATE + "gtrarr", 0x02978, // GREATER-THAN ABOVE RIGHTWARDS ARROW + "gtrdot", 0x022D7, // GREATER-THAN WITH DOT + "gtreqless", 0x022DB, // GREATER-THAN EQUAL TO OR LESS-THAN + "gtreqqless", 0x02A8C, // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN + "gtrless", 0x02277, // GREATER-THAN OR LESS-THAN + "gtrsim", 0x02273, // GREATER-THAN OR EQUIVALENT TO +// "gvertneqq", 0x02269;0x0FE00, // GREATER-THAN BUT NOT EQUAL TO - with vertical stroke +// "gvnE", 0x02269;0x0FE00, // GREATER-THAN BUT NOT EQUAL TO - with vertical stroke + NULL, 0 +}; + +static NameId namesH[]={ + "Hacek", 0x002C7, // CARON + "hairsp", 0x0200A, // HAIR SPACE + "half", 0x000BD, // VULGAR FRACTION ONE HALF + "hamilt", 0x0210B, // SCRIPT CAPITAL H + "HARDcy", 0x0042A, // CYRILLIC CAPITAL LETTER HARD SIGN + "hardcy", 0x0044A, // CYRILLIC SMALL LETTER HARD SIGN + "harr", 0x02194, // LEFT RIGHT ARROW + "hArr", 0x021D4, // LEFT RIGHT DOUBLE ARROW + "harrcir", 0x02948, // LEFT RIGHT ARROW THROUGH SMALL CIRCLE + "harrw", 0x021AD, // LEFT RIGHT WAVE ARROW + "Hat", 0x0005E, // CIRCUMFLEX ACCENT + "hbar", 0x0210F, // PLANCK CONSTANT OVER TWO PI + "Hcirc", 0x00124, // LATIN CAPITAL LETTER H WITH CIRCUMFLEX + "hcirc", 0x00125, // LATIN SMALL LETTER H WITH CIRCUMFLEX + "hearts", 0x02665, // BLACK HEART SUIT + "heartsuit", 0x02665, // BLACK HEART SUIT + "hellip", 0x02026, // HORIZONTAL ELLIPSIS + "hercon", 0x022B9, // HERMITIAN CONJUGATE MATRIX + "Hfr", 0x0210C, // BLACK-LETTER CAPITAL H + "hfr", 0x1D525, // MATHEMATICAL FRAKTUR SMALL H + "HilbertSpace", 0x0210B, // SCRIPT CAPITAL H + "hksearow", 0x02925, // SOUTH EAST ARROW WITH HOOK + "hkswarow", 0x02926, // SOUTH WEST ARROW WITH HOOK + "hoarr", 0x021FF, // LEFT RIGHT OPEN-HEADED ARROW + "homtht", 0x0223B, // HOMOTHETIC + "hookleftarrow", 0x021A9, // LEFTWARDS ARROW WITH HOOK + "hookrightarrow", 0x021AA, // RIGHTWARDS ARROW WITH HOOK + "Hopf", 0x0210D, // DOUBLE-STRUCK CAPITAL H + "hopf", 0x1D559, // MATHEMATICAL DOUBLE-STRUCK SMALL H + "horbar", 0x02015, // HORIZONTAL BAR + "HorizontalLine", 0x02500, // BOX DRAWINGS LIGHT HORIZONTAL + "Hscr", 0x0210B, // SCRIPT CAPITAL H + "hscr", 0x1D4BD, // MATHEMATICAL SCRIPT SMALL H + "hslash", 0x0210F, // PLANCK CONSTANT OVER TWO PI + "Hstrok", 0x00126, // LATIN CAPITAL LETTER H WITH STROKE + "hstrok", 0x00127, // LATIN SMALL LETTER H WITH STROKE + "HumpDownHump", 0x0224E, // GEOMETRICALLY EQUIVALENT TO + "HumpEqual", 0x0224F, // DIFFERENCE BETWEEN + "hybull", 0x02043, // HYPHEN BULLET + "hyphen", 0x02010, // HYPHEN + NULL, 0 +}; + +static NameId namesI[]={ + "Iacgr", 0x0038A, // GREEK CAPITAL LETTER IOTA WITH TONOS + "iacgr", 0x003AF, // GREEK SMALL LETTER IOTA WITH TONOS + "Iacute", 0x000CD, // LATIN CAPITAL LETTER I WITH ACUTE + "iacute", 0x000ED, // LATIN SMALL LETTER I WITH ACUTE + "ic", 0x02063, // INVISIBLE SEPARATOR + "Icirc", 0x000CE, // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + "icirc", 0x000EE, // LATIN SMALL LETTER I WITH CIRCUMFLEX + "Icy", 0x00418, // CYRILLIC CAPITAL LETTER I + "icy", 0x00438, // CYRILLIC SMALL LETTER I + "idiagr", 0x00390, // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS + "Idigr", 0x003AA, // GREEK CAPITAL LETTER IOTA WITH DIALYTIKA + "idigr", 0x003CA, // GREEK SMALL LETTER IOTA WITH DIALYTIKA + "Idot", 0x00130, // LATIN CAPITAL LETTER I WITH DOT ABOVE + "IEcy", 0x00415, // CYRILLIC CAPITAL LETTER IE + "iecy", 0x00435, // CYRILLIC SMALL LETTER IE + "iexcl", 0x000A1, // INVERTED EXCLAMATION MARK + "iff", 0x021D4, // LEFT RIGHT DOUBLE ARROW + "Ifr", 0x02111, // BLACK-LETTER CAPITAL I + "ifr", 0x1D526, // MATHEMATICAL FRAKTUR SMALL I + "Igr", 0x00399, // GREEK CAPITAL LETTER IOTA + "igr", 0x003B9, // GREEK SMALL LETTER IOTA + "Igrave", 0x000CC, // LATIN CAPITAL LETTER I WITH GRAVE + "igrave", 0x000EC, // LATIN SMALL LETTER I WITH GRAVE + "ii", 0x02148, // DOUBLE-STRUCK ITALIC SMALL I + "iiiint", 0x02A0C, // QUADRUPLE INTEGRAL OPERATOR + "iiint", 0x0222D, // TRIPLE INTEGRAL + "iinfin", 0x029DC, // INCOMPLETE INFINITY + "iiota", 0x02129, // TURNED GREEK SMALL LETTER IOTA + "IJlig", 0x00132, // LATIN CAPITAL LIGATURE IJ + "ijlig", 0x00133, // LATIN SMALL LIGATURE IJ + "Im", 0x02111, // BLACK-LETTER CAPITAL I + "Imacr", 0x0012A, // LATIN CAPITAL LETTER I WITH MACRON + "imacr", 0x0012B, // LATIN SMALL LETTER I WITH MACRON + "image", 0x02111, // BLACK-LETTER CAPITAL I + "ImaginaryI", 0x02148, // DOUBLE-STRUCK ITALIC SMALL I + "imagline", 0x02110, // SCRIPT CAPITAL I + "imagpart", 0x02111, // BLACK-LETTER CAPITAL I + "imath", 0x00131, // LATIN SMALL LETTER DOTLESS I + "imof", 0x022B7, // IMAGE OF + "imped", 0x001B5, // LATIN CAPITAL LETTER Z WITH STROKE + "Implies", 0x021D2, // RIGHTWARDS DOUBLE ARROW + "in", 0x02208, // ELEMENT OF + "incare", 0x02105, // CARE OF + "infin", 0x0221E, // INFINITY + "infintie", 0x029DD, // TIE OVER INFINITY + "inodot", 0x00131, // LATIN SMALL LETTER DOTLESS I + "int", 0x0222B, // INTEGRAL + "Int", 0x0222C, // DOUBLE INTEGRAL + "intcal", 0x022BA, // INTERCALATE + "integers", 0x02124, // DOUBLE-STRUCK CAPITAL Z + "Integral", 0x0222B, // INTEGRAL + "intercal", 0x022BA, // INTERCALATE + "Intersection", 0x022C2, // N-ARY INTERSECTION + "intlarhk", 0x02A17, // INTEGRAL WITH LEFTWARDS ARROW WITH HOOK + "intprod", 0x02A3C, // INTERIOR PRODUCT + "InvisibleComma", 0x02063, // INVISIBLE SEPARATOR + "InvisibleTimes", 0x02062, // INVISIBLE TIMES + "IOcy", 0x00401, // CYRILLIC CAPITAL LETTER IO + "iocy", 0x00451, // CYRILLIC SMALL LETTER IO + "Iogon", 0x0012E, // LATIN CAPITAL LETTER I WITH OGONEK + "iogon", 0x0012F, // LATIN SMALL LETTER I WITH OGONEK + "Iopf", 0x1D540, // MATHEMATICAL DOUBLE-STRUCK CAPITAL I + "iopf", 0x1D55A, // MATHEMATICAL DOUBLE-STRUCK SMALL I + "Iota", 0x00399, // GREEK CAPITAL LETTER IOTA + "iota", 0x003B9, // GREEK SMALL LETTER IOTA + "iprod", 0x02A3C, // INTERIOR PRODUCT + "iquest", 0x000BF, // INVERTED QUESTION MARK + "Iscr", 0x02110, // SCRIPT CAPITAL I + "iscr", 0x1D4BE, // MATHEMATICAL SCRIPT SMALL I + "isin", 0x02208, // ELEMENT OF + "isindot", 0x022F5, // ELEMENT OF WITH DOT ABOVE + "isinE", 0x022F9, // ELEMENT OF WITH TWO HORIZONTAL STROKES + "isins", 0x022F4, // SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + "isinsv", 0x022F3, // ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + "isinv", 0x02208, // ELEMENT OF + "it", 0x02062, // INVISIBLE TIMES + "Itilde", 0x00128, // LATIN CAPITAL LETTER I WITH TILDE + "itilde", 0x00129, // LATIN SMALL LETTER I WITH TILDE + "Iukcy", 0x00406, // CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I + "iukcy", 0x00456, // CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I + "Iuml", 0x000CF, // LATIN CAPITAL LETTER I WITH DIAERESIS + "iuml", 0x000EF, // LATIN SMALL LETTER I WITH DIAERESIS + NULL, 0 +}; + +static NameId namesJ[]={ + "Jcirc", 0x00134, // LATIN CAPITAL LETTER J WITH CIRCUMFLEX + "jcirc", 0x00135, // LATIN SMALL LETTER J WITH CIRCUMFLEX + "Jcy", 0x00419, // CYRILLIC CAPITAL LETTER SHORT I + "jcy", 0x00439, // CYRILLIC SMALL LETTER SHORT I + "Jfr", 0x1D50D, // MATHEMATICAL FRAKTUR CAPITAL J + "jfr", 0x1D527, // MATHEMATICAL FRAKTUR SMALL J + "jmath", 0x00237, // LATIN SMALL LETTER DOTLESS J + "Jopf", 0x1D541, // MATHEMATICAL DOUBLE-STRUCK CAPITAL J + "jopf", 0x1D55B, // MATHEMATICAL DOUBLE-STRUCK SMALL J + "Jscr", 0x1D4A5, // MATHEMATICAL SCRIPT CAPITAL J + "jscr", 0x1D4BF, // MATHEMATICAL SCRIPT SMALL J + "Jsercy", 0x00408, // CYRILLIC CAPITAL LETTER JE + "jsercy", 0x00458, // CYRILLIC SMALL LETTER JE + "Jukcy", 0x00404, // CYRILLIC CAPITAL LETTER UKRAINIAN IE + "jukcy", 0x00454, // CYRILLIC SMALL LETTER UKRAINIAN IE + NULL, 0 +}; + +static NameId namesK[]={ + "Kappa", 0x0039A, // GREEK CAPITAL LETTER KAPPA + "kappa", 0x003BA, // GREEK SMALL LETTER KAPPA + "kappav", 0x003F0, // GREEK KAPPA SYMBOL + "Kcedil", 0x00136, // LATIN CAPITAL LETTER K WITH CEDILLA + "kcedil", 0x00137, // LATIN SMALL LETTER K WITH CEDILLA + "Kcy", 0x0041A, // CYRILLIC CAPITAL LETTER KA + "kcy", 0x0043A, // CYRILLIC SMALL LETTER KA + "Kfr", 0x1D50E, // MATHEMATICAL FRAKTUR CAPITAL K + "kfr", 0x1D528, // MATHEMATICAL FRAKTUR SMALL K + "Kgr", 0x0039A, // GREEK CAPITAL LETTER KAPPA + "kgr", 0x003BA, // GREEK SMALL LETTER KAPPA + "kgreen", 0x00138, // LATIN SMALL LETTER KRA + "KHcy", 0x00425, // CYRILLIC CAPITAL LETTER HA + "khcy", 0x00445, // CYRILLIC SMALL LETTER HA + "KHgr", 0x003A7, // GREEK CAPITAL LETTER CHI + "khgr", 0x003C7, // GREEK SMALL LETTER CHI + "KJcy", 0x0040C, // CYRILLIC CAPITAL LETTER KJE + "kjcy", 0x0045C, // CYRILLIC SMALL LETTER KJE + "Kopf", 0x1D542, // MATHEMATICAL DOUBLE-STRUCK CAPITAL K + "kopf", 0x1D55C, // MATHEMATICAL DOUBLE-STRUCK SMALL K + "Kscr", 0x1D4A6, // MATHEMATICAL SCRIPT CAPITAL K + "kscr", 0x1D4C0, // MATHEMATICAL SCRIPT SMALL K + NULL, 0 +}; + +static NameId namesL[]={ + "lAarr", 0x021DA, // LEFTWARDS TRIPLE ARROW + "Lacute", 0x00139, // LATIN CAPITAL LETTER L WITH ACUTE + "lacute", 0x0013A, // LATIN SMALL LETTER L WITH ACUTE + "laemptyv", 0x029B4, // EMPTY SET WITH LEFT ARROW ABOVE + "lagran", 0x02112, // SCRIPT CAPITAL L + "Lambda", 0x0039B, // GREEK CAPITAL LETTER LAMDA + "lambda", 0x003BB, // GREEK SMALL LETTER LAMDA + "lang", 0x027E8, // MATHEMATICAL LEFT ANGLE BRACKET + "Lang", 0x027EA, // MATHEMATICAL LEFT DOUBLE ANGLE BRACKET + "langd", 0x02991, // LEFT ANGLE BRACKET WITH DOT + "langle", 0x027E8, // MATHEMATICAL LEFT ANGLE BRACKET + "lap", 0x02A85, // LESS-THAN OR APPROXIMATE + "Laplacetrf", 0x02112, // SCRIPT CAPITAL L + "laquo", 0x000AB, // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + "larr", 0x02190, // LEFTWARDS ARROW + "Larr", 0x0219E, // LEFTWARDS TWO HEADED ARROW + "lArr", 0x021D0, // LEFTWARDS DOUBLE ARROW + "larrb", 0x021E4, // LEFTWARDS ARROW TO BAR + "larrbfs", 0x0291F, // LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND + "larrfs", 0x0291D, // LEFTWARDS ARROW TO BLACK DIAMOND + "larrhk", 0x021A9, // LEFTWARDS ARROW WITH HOOK + "larrlp", 0x021AB, // LEFTWARDS ARROW WITH LOOP + "larrpl", 0x02939, // LEFT-SIDE ARC ANTICLOCKWISE ARROW + "larrsim", 0x02973, // LEFTWARDS ARROW ABOVE TILDE OPERATOR + "larrtl", 0x021A2, // LEFTWARDS ARROW WITH TAIL + "lat", 0x02AAB, // LARGER THAN + "latail", 0x02919, // LEFTWARDS ARROW-TAIL + "lAtail", 0x0291B, // LEFTWARDS DOUBLE ARROW-TAIL + "late", 0x02AAD, // LARGER THAN OR EQUAL TO +// "lates", 0x02AAD;0x0FE00, // LARGER THAN OR slanted EQUAL + "lbarr", 0x0290C, // LEFTWARDS DOUBLE DASH ARROW + "lBarr", 0x0290E, // LEFTWARDS TRIPLE DASH ARROW + "lbbrk", 0x02772, // LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT + "lbrace", 0x0007B, // LEFT CURLY BRACKET + "lbrack", 0x0005B, // LEFT SQUARE BRACKET + "lbrke", 0x0298B, // LEFT SQUARE BRACKET WITH UNDERBAR + "lbrksld", 0x0298F, // LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER + "lbrkslu", 0x0298D, // LEFT SQUARE BRACKET WITH TICK IN TOP CORNER + "Lcaron", 0x0013D, // LATIN CAPITAL LETTER L WITH CARON + "lcaron", 0x0013E, // LATIN SMALL LETTER L WITH CARON + "Lcedil", 0x0013B, // LATIN CAPITAL LETTER L WITH CEDILLA + "lcedil", 0x0013C, // LATIN SMALL LETTER L WITH CEDILLA + "lceil", 0x02308, // LEFT CEILING + "lcub", 0x0007B, // LEFT CURLY BRACKET + "Lcy", 0x0041B, // CYRILLIC CAPITAL LETTER EL + "lcy", 0x0043B, // CYRILLIC SMALL LETTER EL + "ldca", 0x02936, // ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS + "ldquo", 0x0201C, // LEFT DOUBLE QUOTATION MARK + "ldquor", 0x0201E, // DOUBLE LOW-9 QUOTATION MARK + "ldrdhar", 0x02967, // LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN + "ldrushar", 0x0294B, // LEFT BARB DOWN RIGHT BARB UP HARPOON + "ldsh", 0x021B2, // DOWNWARDS ARROW WITH TIP LEFTWARDS + "le", 0x02264, // LESS-THAN OR EQUAL TO + "lE", 0x02266, // LESS-THAN OVER EQUAL TO + "LeftAngleBracket", 0x027E8, // MATHEMATICAL LEFT ANGLE BRACKET + "leftarrow", 0x02190, // LEFTWARDS ARROW + "LeftArrow", 0x02190, // LEFTWARDS ARROW + "Leftarrow", 0x021D0, // LEFTWARDS DOUBLE ARROW + "LeftArrowBar", 0x021E4, // LEFTWARDS ARROW TO BAR + "LeftArrowRightArrow", 0x021C6, // LEFTWARDS ARROW OVER RIGHTWARDS ARROW + "leftarrowtail", 0x021A2, // LEFTWARDS ARROW WITH TAIL + "LeftCeiling", 0x02308, // LEFT CEILING + "LeftDoubleBracket", 0x027E6, // MATHEMATICAL LEFT WHITE SQUARE BRACKET + "LeftDownTeeVector", 0x02961, // DOWNWARDS HARPOON WITH BARB LEFT FROM BAR + "LeftDownVector", 0x021C3, // DOWNWARDS HARPOON WITH BARB LEFTWARDS + "LeftDownVectorBar", 0x02959, // DOWNWARDS HARPOON WITH BARB LEFT TO BAR + "LeftFloor", 0x0230A, // LEFT FLOOR + "leftharpoondown", 0x021BD, // LEFTWARDS HARPOON WITH BARB DOWNWARDS + "leftharpoonup", 0x021BC, // LEFTWARDS HARPOON WITH BARB UPWARDS + "leftleftarrows", 0x021C7, // LEFTWARDS PAIRED ARROWS + "leftrightarrow", 0x02194, // LEFT RIGHT ARROW + "LeftRightArrow", 0x02194, // LEFT RIGHT ARROW + "Leftrightarrow", 0x021D4, // LEFT RIGHT DOUBLE ARROW + "leftrightarrows", 0x021C6, // LEFTWARDS ARROW OVER RIGHTWARDS ARROW + "leftrightharpoons", 0x021CB, // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON + "leftrightsquigarrow", 0x021AD, // LEFT RIGHT WAVE ARROW + "LeftRightVector", 0x0294E, // LEFT BARB UP RIGHT BARB UP HARPOON + "LeftTee", 0x022A3, // LEFT TACK + "LeftTeeArrow", 0x021A4, // LEFTWARDS ARROW FROM BAR + "LeftTeeVector", 0x0295A, // LEFTWARDS HARPOON WITH BARB UP FROM BAR + "leftthreetimes", 0x022CB, // LEFT SEMIDIRECT PRODUCT + "LeftTriangle", 0x022B2, // NORMAL SUBGROUP OF + "LeftTriangleBar", 0x029CF, // LEFT TRIANGLE BESIDE VERTICAL BAR + "LeftTriangleEqual", 0x022B4, // NORMAL SUBGROUP OF OR EQUAL TO + "LeftUpDownVector", 0x02951, // UP BARB LEFT DOWN BARB LEFT HARPOON + "LeftUpTeeVector", 0x02960, // UPWARDS HARPOON WITH BARB LEFT FROM BAR + "LeftUpVector", 0x021BF, // UPWARDS HARPOON WITH BARB LEFTWARDS + "LeftUpVectorBar", 0x02958, // UPWARDS HARPOON WITH BARB LEFT TO BAR + "LeftVector", 0x021BC, // LEFTWARDS HARPOON WITH BARB UPWARDS + "LeftVectorBar", 0x02952, // LEFTWARDS HARPOON WITH BARB UP TO BAR + "leg", 0x022DA, // LESS-THAN EQUAL TO OR GREATER-THAN + "lEg", 0x02A8B, // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN + "leq", 0x02264, // LESS-THAN OR EQUAL TO + "leqq", 0x02266, // LESS-THAN OVER EQUAL TO + "leqslant", 0x02A7D, // LESS-THAN OR SLANTED EQUAL TO + "les", 0x02A7D, // LESS-THAN OR SLANTED EQUAL TO + "lescc", 0x02AA8, // LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL + "lesdot", 0x02A7F, // LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE + "lesdoto", 0x02A81, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE + "lesdotor", 0x02A83, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT +// "lesg", 0x022DA;0x0FE00, // LESS-THAN slanted EQUAL TO OR GREATER-THAN + "lesges", 0x02A93, // LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL + "lessapprox", 0x02A85, // LESS-THAN OR APPROXIMATE + "lessdot", 0x022D6, // LESS-THAN WITH DOT + "lesseqgtr", 0x022DA, // LESS-THAN EQUAL TO OR GREATER-THAN + "lesseqqgtr", 0x02A8B, // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN + "LessEqualGreater", 0x022DA, // LESS-THAN EQUAL TO OR GREATER-THAN + "LessFullEqual", 0x02266, // LESS-THAN OVER EQUAL TO + "LessGreater", 0x02276, // LESS-THAN OR GREATER-THAN + "lessgtr", 0x02276, // LESS-THAN OR GREATER-THAN + "LessLess", 0x02AA1, // DOUBLE NESTED LESS-THAN + "lesssim", 0x02272, // LESS-THAN OR EQUIVALENT TO + "LessSlantEqual", 0x02A7D, // LESS-THAN OR SLANTED EQUAL TO + "LessTilde", 0x02272, // LESS-THAN OR EQUIVALENT TO + "lfisht", 0x0297C, // LEFT FISH TAIL + "lfloor", 0x0230A, // LEFT FLOOR + "Lfr", 0x1D50F, // MATHEMATICAL FRAKTUR CAPITAL L + "lfr", 0x1D529, // MATHEMATICAL FRAKTUR SMALL L + "lg", 0x02276, // LESS-THAN OR GREATER-THAN + "lgE", 0x02A91, // LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL + "Lgr", 0x0039B, // GREEK CAPITAL LETTER LAMDA + "lgr", 0x003BB, // GREEK SMALL LETTER LAMDA + "lHar", 0x02962, // LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN + "lhard", 0x021BD, // LEFTWARDS HARPOON WITH BARB DOWNWARDS + "lharu", 0x021BC, // LEFTWARDS HARPOON WITH BARB UPWARDS + "lharul", 0x0296A, // LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH + "lhblk", 0x02584, // LOWER HALF BLOCK + "LJcy", 0x00409, // CYRILLIC CAPITAL LETTER LJE + "ljcy", 0x00459, // CYRILLIC SMALL LETTER LJE + "ll", 0x0226A, // MUCH LESS-THAN + "Ll", 0x022D8, // VERY MUCH LESS-THAN + "llarr", 0x021C7, // LEFTWARDS PAIRED ARROWS + "llcorner", 0x0231E, // BOTTOM LEFT CORNER + "Lleftarrow", 0x021DA, // LEFTWARDS TRIPLE ARROW + "llhard", 0x0296B, // LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH + "lltri", 0x025FA, // LOWER LEFT TRIANGLE + "Lmidot", 0x0013F, // LATIN CAPITAL LETTER L WITH MIDDLE DOT + "lmidot", 0x00140, // LATIN SMALL LETTER L WITH MIDDLE DOT + "lmoust", 0x023B0, // UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION + "lmoustache", 0x023B0, // UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION + "lnap", 0x02A89, // LESS-THAN AND NOT APPROXIMATE + "lnapprox", 0x02A89, // LESS-THAN AND NOT APPROXIMATE + "lnE", 0x02268, // LESS-THAN BUT NOT EQUAL TO + "lne", 0x02A87, // LESS-THAN AND SINGLE-LINE NOT EQUAL TO + "lneq", 0x02A87, // LESS-THAN AND SINGLE-LINE NOT EQUAL TO + "lneqq", 0x02268, // LESS-THAN BUT NOT EQUAL TO + "lnsim", 0x022E6, // LESS-THAN BUT NOT EQUIVALENT TO + "loang", 0x027EC, // MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET + "loarr", 0x021FD, // LEFTWARDS OPEN-HEADED ARROW + "lobrk", 0x027E6, // MATHEMATICAL LEFT WHITE SQUARE BRACKET + "longleftarrow", 0x027F5, // LONG LEFTWARDS ARROW + "LongLeftArrow", 0x027F5, // LONG LEFTWARDS ARROW + "Longleftarrow", 0x027F8, // LONG LEFTWARDS DOUBLE ARROW + "longleftrightarrow", 0x027F7, // LONG LEFT RIGHT ARROW + "LongLeftRightArrow", 0x027F7, // LONG LEFT RIGHT ARROW + "Longleftrightarrow", 0x027FA, // LONG LEFT RIGHT DOUBLE ARROW + "longmapsto", 0x027FC, // LONG RIGHTWARDS ARROW FROM BAR + "longrightarrow", 0x027F6, // LONG RIGHTWARDS ARROW + "LongRightArrow", 0x027F6, // LONG RIGHTWARDS ARROW + "Longrightarrow", 0x027F9, // LONG RIGHTWARDS DOUBLE ARROW + "looparrowleft", 0x021AB, // LEFTWARDS ARROW WITH LOOP + "looparrowright", 0x021AC, // RIGHTWARDS ARROW WITH LOOP + "lopar", 0x02985, // LEFT WHITE PARENTHESIS + "Lopf", 0x1D543, // MATHEMATICAL DOUBLE-STRUCK CAPITAL L + "lopf", 0x1D55D, // MATHEMATICAL DOUBLE-STRUCK SMALL L + "loplus", 0x02A2D, // PLUS SIGN IN LEFT HALF CIRCLE + "lotimes", 0x02A34, // MULTIPLICATION SIGN IN LEFT HALF CIRCLE + "lowast", 0x02217, // ASTERISK OPERATOR + "lowbar", 0x0005F, // LOW LINE + "LowerLeftArrow", 0x02199, // SOUTH WEST ARROW + "LowerRightArrow", 0x02198, // SOUTH EAST ARROW + "loz", 0x025CA, // LOZENGE + "lozenge", 0x025CA, // LOZENGE + "lozf", 0x029EB, // BLACK LOZENGE + "lpar", 0x00028, // LEFT PARENTHESIS + "lparlt", 0x02993, // LEFT ARC LESS-THAN BRACKET + "lrarr", 0x021C6, // LEFTWARDS ARROW OVER RIGHTWARDS ARROW + "lrcorner", 0x0231F, // BOTTOM RIGHT CORNER + "lrhar", 0x021CB, // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON + "lrhard", 0x0296D, // RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH + "lrm", 0x0200E, // LEFT-TO-RIGHT MARK + "lrtri", 0x022BF, // RIGHT TRIANGLE + "lsaquo", 0x02039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK + "Lscr", 0x02112, // SCRIPT CAPITAL L + "lscr", 0x1D4C1, // MATHEMATICAL SCRIPT SMALL L + "lsh", 0x021B0, // UPWARDS ARROW WITH TIP LEFTWARDS + "Lsh", 0x021B0, // UPWARDS ARROW WITH TIP LEFTWARDS + "lsim", 0x02272, // LESS-THAN OR EQUIVALENT TO + "lsime", 0x02A8D, // LESS-THAN ABOVE SIMILAR OR EQUAL + "lsimg", 0x02A8F, // LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN + "lsqb", 0x0005B, // LEFT SQUARE BRACKET + "lsquo", 0x02018, // LEFT SINGLE QUOTATION MARK + "lsquor", 0x0201A, // SINGLE LOW-9 QUOTATION MARK + "Lstrok", 0x00141, // LATIN CAPITAL LETTER L WITH STROKE + "lstrok", 0x00142, // LATIN SMALL LETTER L WITH STROKE + "lt", 0x0003C, // LESS-THAN SIGN + "LT", 0x0003C, // LESS-THAN SIGN + "Lt", 0x0226A, // MUCH LESS-THAN + "ltcc", 0x02AA6, // LESS-THAN CLOSED BY CURVE + "ltcir", 0x02A79, // LESS-THAN WITH CIRCLE INSIDE + "ltdot", 0x022D6, // LESS-THAN WITH DOT + "lthree", 0x022CB, // LEFT SEMIDIRECT PRODUCT + "ltimes", 0x022C9, // LEFT NORMAL FACTOR SEMIDIRECT PRODUCT + "ltlarr", 0x02976, // LESS-THAN ABOVE LEFTWARDS ARROW + "ltquest", 0x02A7B, // LESS-THAN WITH QUESTION MARK ABOVE + "ltri", 0x025C3, // WHITE LEFT-POINTING SMALL TRIANGLE + "ltrie", 0x022B4, // NORMAL SUBGROUP OF OR EQUAL TO + "ltrif", 0x025C2, // BLACK LEFT-POINTING SMALL TRIANGLE + "ltrPar", 0x02996, // DOUBLE RIGHT ARC LESS-THAN BRACKET + "lurdshar", 0x0294A, // LEFT BARB UP RIGHT BARB DOWN HARPOON + "luruhar", 0x02966, // LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP +// "lvertneqq", 0x02268;0x0FE00, // LESS-THAN BUT NOT EQUAL TO - with vertical stroke +// "lvnE", 0x02268;0x0FE00, // LESS-THAN BUT NOT EQUAL TO - with vertical stroke + NULL, 0 +}; + +static NameId namesM[]={ + "macr", 0x000AF, // MACRON + "male", 0x02642, // MALE SIGN + "malt", 0x02720, // MALTESE CROSS + "maltese", 0x02720, // MALTESE CROSS + "map", 0x021A6, // RIGHTWARDS ARROW FROM BAR + "Map", 0x02905, // RIGHTWARDS TWO-HEADED ARROW FROM BAR + "mapsto", 0x021A6, // RIGHTWARDS ARROW FROM BAR + "mapstodown", 0x021A7, // DOWNWARDS ARROW FROM BAR + "mapstoleft", 0x021A4, // LEFTWARDS ARROW FROM BAR + "mapstoup", 0x021A5, // UPWARDS ARROW FROM BAR + "marker", 0x025AE, // BLACK VERTICAL RECTANGLE + "mcomma", 0x02A29, // MINUS SIGN WITH COMMA ABOVE + "Mcy", 0x0041C, // CYRILLIC CAPITAL LETTER EM + "mcy", 0x0043C, // CYRILLIC SMALL LETTER EM + "mdash", 0x02014, // EM DASH + "mDDot", 0x0223A, // GEOMETRIC PROPORTION + "measuredangle", 0x02221, // MEASURED ANGLE + "MediumSpace", 0x0205F, // MEDIUM MATHEMATICAL SPACE + "Mellintrf", 0x02133, // SCRIPT CAPITAL M + "Mfr", 0x1D510, // MATHEMATICAL FRAKTUR CAPITAL M + "mfr", 0x1D52A, // MATHEMATICAL FRAKTUR SMALL M + "Mgr", 0x0039C, // GREEK CAPITAL LETTER MU + "mgr", 0x003BC, // GREEK SMALL LETTER MU + "mho", 0x02127, // INVERTED OHM SIGN + "micro", 0x000B5, // MICRO SIGN + "mid", 0x02223, // DIVIDES + "midast", 0x0002A, // ASTERISK + "midcir", 0x02AF0, // VERTICAL LINE WITH CIRCLE BELOW + "middot", 0x000B7, // MIDDLE DOT + "minus", 0x02212, // MINUS SIGN + "minusb", 0x0229F, // SQUARED MINUS + "minusd", 0x02238, // DOT MINUS + "minusdu", 0x02A2A, // MINUS SIGN WITH DOT BELOW + "MinusPlus", 0x02213, // MINUS-OR-PLUS SIGN + "mlcp", 0x02ADB, // TRANSVERSAL INTERSECTION + "mldr", 0x02026, // HORIZONTAL ELLIPSIS + "mnplus", 0x02213, // MINUS-OR-PLUS SIGN + "models", 0x022A7, // MODELS + "Mopf", 0x1D544, // MATHEMATICAL DOUBLE-STRUCK CAPITAL M + "mopf", 0x1D55E, // MATHEMATICAL DOUBLE-STRUCK SMALL M + "mp", 0x02213, // MINUS-OR-PLUS SIGN + "Mscr", 0x02133, // SCRIPT CAPITAL M + "mscr", 0x1D4C2, // MATHEMATICAL SCRIPT SMALL M + "mstpos", 0x0223E, // INVERTED LAZY S + "Mu", 0x0039C, // GREEK CAPITAL LETTER MU + "mu", 0x003BC, // GREEK SMALL LETTER MU + "multimap", 0x022B8, // MULTIMAP + "mumap", 0x022B8, // MULTIMAP + NULL, 0 +}; + +static NameId namesN[]={ + "nabla", 0x02207, // NABLA + "Nacute", 0x00143, // LATIN CAPITAL LETTER N WITH ACUTE + "nacute", 0x00144, // LATIN SMALL LETTER N WITH ACUTE +// "nang", 0x02220;0x020D2, // ANGLE with vertical line + "nap", 0x02249, // NOT ALMOST EQUAL TO +// "napE", 0x02A70;0x00338, // APPROXIMATELY EQUAL OR EQUAL TO with slash +// "napid", 0x0224B;0x00338, // TRIPLE TILDE with slash + "napos", 0x00149, // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE + "napprox", 0x02249, // NOT ALMOST EQUAL TO + "natur", 0x0266E, // MUSIC NATURAL SIGN + "natural", 0x0266E, // MUSIC NATURAL SIGN + "naturals", 0x02115, // DOUBLE-STRUCK CAPITAL N + "nbsp", 0x000A0, // NO-BREAK SPACE +// "nbump", 0x0224E;0x00338, // GEOMETRICALLY EQUIVALENT TO with slash +// "nbumpe", 0x0224F;0x00338, // DIFFERENCE BETWEEN with slash + "ncap", 0x02A43, // INTERSECTION WITH OVERBAR + "Ncaron", 0x00147, // LATIN CAPITAL LETTER N WITH CARON + "ncaron", 0x00148, // LATIN SMALL LETTER N WITH CARON + "Ncedil", 0x00145, // LATIN CAPITAL LETTER N WITH CEDILLA + "ncedil", 0x00146, // LATIN SMALL LETTER N WITH CEDILLA + "ncong", 0x02247, // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO +// "ncongdot", 0x02A6D;0x00338, // CONGRUENT WITH DOT ABOVE with slash + "ncup", 0x02A42, // UNION WITH OVERBAR + "Ncy", 0x0041D, // CYRILLIC CAPITAL LETTER EN + "ncy", 0x0043D, // CYRILLIC SMALL LETTER EN + "ndash", 0x02013, // EN DASH + "ne", 0x02260, // NOT EQUAL TO + "nearhk", 0x02924, // NORTH EAST ARROW WITH HOOK + "nearr", 0x02197, // NORTH EAST ARROW + "neArr", 0x021D7, // NORTH EAST DOUBLE ARROW + "nearrow", 0x02197, // NORTH EAST ARROW +// "nedot", 0x02250;0x00338, // APPROACHES THE LIMIT with slash + "NegativeMediumSpace", 0x0200B, // ZERO WIDTH SPACE + "NegativeThickSpace", 0x0200B, // ZERO WIDTH SPACE + "NegativeThinSpace", 0x0200B, // ZERO WIDTH SPACE + "NegativeVeryThinSpace", 0x0200B, // ZERO WIDTH SPACE + "nequiv", 0x02262, // NOT IDENTICAL TO + "nesear", 0x02928, // NORTH EAST ARROW AND SOUTH EAST ARROW +// "nesim", 0x02242;0x00338, // MINUS TILDE with slash + "NestedGreaterGreater", 0x0226B, // MUCH GREATER-THAN + "NestedLessLess", 0x0226A, // MUCH LESS-THAN + "NewLine", 0x0000A, // LINE FEED (LF) + "nexist", 0x02204, // THERE DOES NOT EXIST + "nexists", 0x02204, // THERE DOES NOT EXIST + "Nfr", 0x1D511, // MATHEMATICAL FRAKTUR CAPITAL N + "nfr", 0x1D52B, // MATHEMATICAL FRAKTUR SMALL N +// "ngE", 0x02267;0x00338, // GREATER-THAN OVER EQUAL TO with slash + "nge", 0x02271, // NEITHER GREATER-THAN NOR EQUAL TO + "ngeq", 0x02271, // NEITHER GREATER-THAN NOR EQUAL TO +// "ngeqq", 0x02267;0x00338, // GREATER-THAN OVER EQUAL TO with slash +// "ngeqslant", 0x02A7E;0x00338, // GREATER-THAN OR SLANTED EQUAL TO with slash +// "nges", 0x02A7E;0x00338, // GREATER-THAN OR SLANTED EQUAL TO with slash +// "nGg", 0x022D9;0x00338, // VERY MUCH GREATER-THAN with slash + "Ngr", 0x0039D, // GREEK CAPITAL LETTER NU + "ngr", 0x003BD, // GREEK SMALL LETTER NU + "ngsim", 0x02275, // NEITHER GREATER-THAN NOR EQUIVALENT TO +// "nGt", 0x0226B;0x020D2, // MUCH GREATER THAN with vertical line + "ngt", 0x0226F, // NOT GREATER-THAN + "ngtr", 0x0226F, // NOT GREATER-THAN +// "nGtv", 0x0226B;0x00338, // MUCH GREATER THAN with slash + "nharr", 0x021AE, // LEFT RIGHT ARROW WITH STROKE + "nhArr", 0x021CE, // LEFT RIGHT DOUBLE ARROW WITH STROKE + "nhpar", 0x02AF2, // PARALLEL WITH HORIZONTAL STROKE + "ni", 0x0220B, // CONTAINS AS MEMBER + "nis", 0x022FC, // SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + "nisd", 0x022FA, // CONTAINS WITH LONG HORIZONTAL STROKE + "niv", 0x0220B, // CONTAINS AS MEMBER + "NJcy", 0x0040A, // CYRILLIC CAPITAL LETTER NJE + "njcy", 0x0045A, // CYRILLIC SMALL LETTER NJE + "nlarr", 0x0219A, // LEFTWARDS ARROW WITH STROKE + "nlArr", 0x021CD, // LEFTWARDS DOUBLE ARROW WITH STROKE + "nldr", 0x02025, // TWO DOT LEADER +// "nlE", 0x02266;0x00338, // LESS-THAN OVER EQUAL TO with slash + "nle", 0x02270, // NEITHER LESS-THAN NOR EQUAL TO + "nleftarrow", 0x0219A, // LEFTWARDS ARROW WITH STROKE + "nLeftarrow", 0x021CD, // LEFTWARDS DOUBLE ARROW WITH STROKE + "nleftrightarrow", 0x021AE, // LEFT RIGHT ARROW WITH STROKE + "nLeftrightarrow", 0x021CE, // LEFT RIGHT DOUBLE ARROW WITH STROKE + "nleq", 0x02270, // NEITHER LESS-THAN NOR EQUAL TO +// "nleqq", 0x02266;0x00338, // LESS-THAN OVER EQUAL TO with slash +// "nleqslant", 0x02A7D;0x00338, // LESS-THAN OR SLANTED EQUAL TO with slash +// "nles", 0x02A7D;0x00338, // LESS-THAN OR SLANTED EQUAL TO with slash + "nless", 0x0226E, // NOT LESS-THAN +// "nLl", 0x022D8;0x00338, // VERY MUCH LESS-THAN with slash + "nlsim", 0x02274, // NEITHER LESS-THAN NOR EQUIVALENT TO +// "nLt", 0x0226A;0x020D2, // MUCH LESS THAN with vertical line + "nlt", 0x0226E, // NOT LESS-THAN + "nltri", 0x022EA, // NOT NORMAL SUBGROUP OF + "nltrie", 0x022EC, // NOT NORMAL SUBGROUP OF OR EQUAL TO +// "nLtv", 0x0226A;0x00338, // MUCH LESS THAN with slash + "nmid", 0x02224, // DOES NOT DIVIDE + "NoBreak", 0x02060, // WORD JOINER + "NonBreakingSpace", 0x000A0, // NO-BREAK SPACE + "Nopf", 0x02115, // DOUBLE-STRUCK CAPITAL N + "nopf", 0x1D55F, // MATHEMATICAL DOUBLE-STRUCK SMALL N + "not", 0x000AC, // NOT SIGN + "Not", 0x02AEC, // DOUBLE STROKE NOT SIGN + "NotCongruent", 0x02262, // NOT IDENTICAL TO + "NotCupCap", 0x0226D, // NOT EQUIVALENT TO + "NotDoubleVerticalBar", 0x02226, // NOT PARALLEL TO + "NotElement", 0x02209, // NOT AN ELEMENT OF + "NotEqual", 0x02260, // NOT EQUAL TO +// "NotEqualTilde", 0x02242;0x00338, // MINUS TILDE with slash + "NotExists", 0x02204, // THERE DOES NOT EXIST + "NotGreater", 0x0226F, // NOT GREATER-THAN + "NotGreaterEqual", 0x02271, // NEITHER GREATER-THAN NOR EQUAL TO +// "NotGreaterFullEqual", 0x02267;0x00338, // GREATER-THAN OVER EQUAL TO with slash +// "NotGreaterGreater", 0x0226B;0x00338, // MUCH GREATER THAN with slash + "NotGreaterLess", 0x02279, // NEITHER GREATER-THAN NOR LESS-THAN +// "NotGreaterSlantEqual", 0x02A7E;0x00338, // GREATER-THAN OR SLANTED EQUAL TO with slash + "NotGreaterTilde", 0x02275, // NEITHER GREATER-THAN NOR EQUIVALENT TO +// "NotHumpDownHump", 0x0224E;0x00338, // GEOMETRICALLY EQUIVALENT TO with slash +// "NotHumpEqual", 0x0224F;0x00338, // DIFFERENCE BETWEEN with slash + "notin", 0x02209, // NOT AN ELEMENT OF +// "notindot", 0x022F5;0x00338, // ELEMENT OF WITH DOT ABOVE with slash +// "notinE", 0x022F9;0x00338, // ELEMENT OF WITH TWO HORIZONTAL STROKES with slash + "notinva", 0x02209, // NOT AN ELEMENT OF + "notinvb", 0x022F7, // SMALL ELEMENT OF WITH OVERBAR + "notinvc", 0x022F6, // ELEMENT OF WITH OVERBAR + "NotLeftTriangle", 0x022EA, // NOT NORMAL SUBGROUP OF +// "NotLeftTriangleBar", 0x029CF;0x00338, // LEFT TRIANGLE BESIDE VERTICAL BAR with slash + "NotLeftTriangleEqual", 0x022EC, // NOT NORMAL SUBGROUP OF OR EQUAL TO + "NotLess", 0x0226E, // NOT LESS-THAN + "NotLessEqual", 0x02270, // NEITHER LESS-THAN NOR EQUAL TO + "NotLessGreater", 0x02278, // NEITHER LESS-THAN NOR GREATER-THAN +// "NotLessLess", 0x0226A;0x00338, // MUCH LESS THAN with slash +// "NotLessSlantEqual", 0x02A7D;0x00338, // LESS-THAN OR SLANTED EQUAL TO with slash + "NotLessTilde", 0x02274, // NEITHER LESS-THAN NOR EQUIVALENT TO +// "NotNestedGreaterGreater", 0x02AA2;0x00338, // DOUBLE NESTED GREATER-THAN with slash +// "NotNestedLessLess", 0x02AA1;0x00338, // DOUBLE NESTED LESS-THAN with slash + "notni", 0x0220C, // DOES NOT CONTAIN AS MEMBER + "notniva", 0x0220C, // DOES NOT CONTAIN AS MEMBER + "notnivb", 0x022FE, // SMALL CONTAINS WITH OVERBAR + "notnivc", 0x022FD, // CONTAINS WITH OVERBAR + "NotPrecedes", 0x02280, // DOES NOT PRECEDE +// "NotPrecedesEqual", 0x02AAF;0x00338, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash + "NotPrecedesSlantEqual", 0x022E0, // DOES NOT PRECEDE OR EQUAL + "NotReverseElement", 0x0220C, // DOES NOT CONTAIN AS MEMBER + "NotRightTriangle", 0x022EB, // DOES NOT CONTAIN AS NORMAL SUBGROUP +// "NotRightTriangleBar", 0x029D0;0x00338, // VERTICAL BAR BESIDE RIGHT TRIANGLE with slash + "NotRightTriangleEqual", 0x022ED, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL +// "NotSquareSubset", 0x0228F;0x00338, // SQUARE IMAGE OF with slash + "NotSquareSubsetEqual", 0x022E2, // NOT SQUARE IMAGE OF OR EQUAL TO +// "NotSquareSuperset", 0x02290;0x00338, // SQUARE ORIGINAL OF with slash + "NotSquareSupersetEqual", 0x022E3, // NOT SQUARE ORIGINAL OF OR EQUAL TO +// "NotSubset", 0x02282;0x020D2, // SUBSET OF with vertical line + "NotSubsetEqual", 0x02288, // NEITHER A SUBSET OF NOR EQUAL TO + "NotSucceeds", 0x02281, // DOES NOT SUCCEED +// "NotSucceedsEqual", 0x02AB0;0x00338, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash + "NotSucceedsSlantEqual", 0x022E1, // DOES NOT SUCCEED OR EQUAL +// "NotSucceedsTilde", 0x0227F;0x00338, // SUCCEEDS OR EQUIVALENT TO with slash +// "NotSuperset", 0x02283;0x020D2, // SUPERSET OF with vertical line + "NotSupersetEqual", 0x02289, // NEITHER A SUPERSET OF NOR EQUAL TO + "NotTilde", 0x02241, // NOT TILDE + "NotTildeEqual", 0x02244, // NOT ASYMPTOTICALLY EQUAL TO + "NotTildeFullEqual", 0x02247, // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO + "NotTildeTilde", 0x02249, // NOT ALMOST EQUAL TO + "NotVerticalBar", 0x02224, // DOES NOT DIVIDE + "npar", 0x02226, // NOT PARALLEL TO + "nparallel", 0x02226, // NOT PARALLEL TO +// "nparsl", 0x02AFD;0x020E5, // DOUBLE SOLIDUS OPERATOR with reverse slash +// "npart", 0x02202;0x00338, // PARTIAL DIFFERENTIAL with slash + "npolint", 0x02A14, // LINE INTEGRATION NOT INCLUDING THE POLE + "npr", 0x02280, // DOES NOT PRECEDE + "nprcue", 0x022E0, // DOES NOT PRECEDE OR EQUAL +// "npre", 0x02AAF;0x00338, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash + "nprec", 0x02280, // DOES NOT PRECEDE +// "npreceq", 0x02AAF;0x00338, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash + "nrarr", 0x0219B, // RIGHTWARDS ARROW WITH STROKE + "nrArr", 0x021CF, // RIGHTWARDS DOUBLE ARROW WITH STROKE +// "nrarrc", 0x02933;0x00338, // WAVE ARROW POINTING DIRECTLY RIGHT with slash +// "nrarrw", 0x0219D;0x00338, // RIGHTWARDS WAVE ARROW with slash + "nrightarrow", 0x0219B, // RIGHTWARDS ARROW WITH STROKE + "nRightarrow", 0x021CF, // RIGHTWARDS DOUBLE ARROW WITH STROKE + "nrtri", 0x022EB, // DOES NOT CONTAIN AS NORMAL SUBGROUP + "nrtrie", 0x022ED, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL + "nsc", 0x02281, // DOES NOT SUCCEED + "nsccue", 0x022E1, // DOES NOT SUCCEED OR EQUAL +// "nsce", 0x02AB0;0x00338, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash + "Nscr", 0x1D4A9, // MATHEMATICAL SCRIPT CAPITAL N + "nscr", 0x1D4C3, // MATHEMATICAL SCRIPT SMALL N + "nshortmid", 0x02224, // DOES NOT DIVIDE + "nshortparallel", 0x02226, // NOT PARALLEL TO + "nsim", 0x02241, // NOT TILDE + "nsime", 0x02244, // NOT ASYMPTOTICALLY EQUAL TO + "nsimeq", 0x02244, // NOT ASYMPTOTICALLY EQUAL TO + "nsmid", 0x02224, // DOES NOT DIVIDE + "nspar", 0x02226, // NOT PARALLEL TO + "nsqsube", 0x022E2, // NOT SQUARE IMAGE OF OR EQUAL TO + "nsqsupe", 0x022E3, // NOT SQUARE ORIGINAL OF OR EQUAL TO + "nsub", 0x02284, // NOT A SUBSET OF + "nsube", 0x02288, // NEITHER A SUBSET OF NOR EQUAL TO +// "nsubE", 0x02AC5;0x00338, // SUBSET OF ABOVE EQUALS SIGN with slash +// "nsubset", 0x02282;0x020D2, // SUBSET OF with vertical line + "nsubseteq", 0x02288, // NEITHER A SUBSET OF NOR EQUAL TO +// "nsubseteqq", 0x02AC5;0x00338, // SUBSET OF ABOVE EQUALS SIGN with slash + "nsucc", 0x02281, // DOES NOT SUCCEED +// "nsucceq", 0x02AB0;0x00338, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash + "nsup", 0x02285, // NOT A SUPERSET OF + "nsupe", 0x02289, // NEITHER A SUPERSET OF NOR EQUAL TO +// "nsupE", 0x02AC6;0x00338, // SUPERSET OF ABOVE EQUALS SIGN with slash +// "nsupset", 0x02283;0x020D2, // SUPERSET OF with vertical line + "nsupseteq", 0x02289, // NEITHER A SUPERSET OF NOR EQUAL TO +// "nsupseteqq", 0x02AC6;0x00338, // SUPERSET OF ABOVE EQUALS SIGN with slash + "ntgl", 0x02279, // NEITHER GREATER-THAN NOR LESS-THAN + "Ntilde", 0x000D1, // LATIN CAPITAL LETTER N WITH TILDE + "ntilde", 0x000F1, // LATIN SMALL LETTER N WITH TILDE + "ntlg", 0x02278, // NEITHER LESS-THAN NOR GREATER-THAN + "ntriangleleft", 0x022EA, // NOT NORMAL SUBGROUP OF + "ntrianglelefteq", 0x022EC, // NOT NORMAL SUBGROUP OF OR EQUAL TO + "ntriangleright", 0x022EB, // DOES NOT CONTAIN AS NORMAL SUBGROUP + "ntrianglerighteq", 0x022ED, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL + "Nu", 0x0039D, // GREEK CAPITAL LETTER NU + "nu", 0x003BD, // GREEK SMALL LETTER NU + "num", 0x00023, // NUMBER SIGN + "numero", 0x02116, // NUMERO SIGN + "numsp", 0x02007, // FIGURE SPACE +// "nvap", 0x0224D;0x020D2, // EQUIVALENT TO with vertical line + "nvdash", 0x022AC, // DOES NOT PROVE + "nvDash", 0x022AD, // NOT TRUE + "nVdash", 0x022AE, // DOES NOT FORCE + "nVDash", 0x022AF, // NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE +// "nvge", 0x02265;0x020D2, // GREATER-THAN OR EQUAL TO with vertical line +// "nvgt", 0x0003E;0x020D2, // GREATER-THAN SIGN with vertical line + "nvHarr", 0x02904, // LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE + "nvinfin", 0x029DE, // INFINITY NEGATED WITH VERTICAL BAR + "nvlArr", 0x02902, // LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE +// "nvle", 0x02264;0x020D2, // LESS-THAN OR EQUAL TO with vertical line +// "nvlt", 0x0003C;0x020D2, // LESS-THAN SIGN with vertical line +// "nvltrie", 0x022B4;0x020D2, // NORMAL SUBGROUP OF OR EQUAL TO with vertical line + "nvrArr", 0x02903, // RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE +// "nvrtrie", 0x022B5;0x020D2, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO with vertical line +// "nvsim", 0x0223C;0x020D2, // TILDE OPERATOR with vertical line + "nwarhk", 0x02923, // NORTH WEST ARROW WITH HOOK + "nwarr", 0x02196, // NORTH WEST ARROW + "nwArr", 0x021D6, // NORTH WEST DOUBLE ARROW + "nwarrow", 0x02196, // NORTH WEST ARROW + "nwnear", 0x02927, // NORTH WEST ARROW AND NORTH EAST ARROW + NULL, 0 +}; + +static NameId namesO[]={ + "Oacgr", 0x0038C, // GREEK CAPITAL LETTER OMICRON WITH TONOS + "oacgr", 0x003CC, // GREEK SMALL LETTER OMICRON WITH TONOS + "Oacute", 0x000D3, // LATIN CAPITAL LETTER O WITH ACUTE + "oacute", 0x000F3, // LATIN SMALL LETTER O WITH ACUTE + "oast", 0x0229B, // CIRCLED ASTERISK OPERATOR + "ocir", 0x0229A, // CIRCLED RING OPERATOR + "Ocirc", 0x000D4, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + "ocirc", 0x000F4, // LATIN SMALL LETTER O WITH CIRCUMFLEX + "Ocy", 0x0041E, // CYRILLIC CAPITAL LETTER O + "ocy", 0x0043E, // CYRILLIC SMALL LETTER O + "odash", 0x0229D, // CIRCLED DASH + "Odblac", 0x00150, // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE + "odblac", 0x00151, // LATIN SMALL LETTER O WITH DOUBLE ACUTE + "odiv", 0x02A38, // CIRCLED DIVISION SIGN + "odot", 0x02299, // CIRCLED DOT OPERATOR + "odsold", 0x029BC, // CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN + "OElig", 0x00152, // LATIN CAPITAL LIGATURE OE + "oelig", 0x00153, // LATIN SMALL LIGATURE OE + "ofcir", 0x029BF, // CIRCLED BULLET + "Ofr", 0x1D512, // MATHEMATICAL FRAKTUR CAPITAL O + "ofr", 0x1D52C, // MATHEMATICAL FRAKTUR SMALL O + "ogon", 0x002DB, // OGONEK + "Ogr", 0x0039F, // GREEK CAPITAL LETTER OMICRON + "ogr", 0x003BF, // GREEK SMALL LETTER OMICRON + "Ograve", 0x000D2, // LATIN CAPITAL LETTER O WITH GRAVE + "ograve", 0x000F2, // LATIN SMALL LETTER O WITH GRAVE + "ogt", 0x029C1, // CIRCLED GREATER-THAN + "OHacgr", 0x0038F, // GREEK CAPITAL LETTER OMEGA WITH TONOS + "ohacgr", 0x003CE, // GREEK SMALL LETTER OMEGA WITH TONOS + "ohbar", 0x029B5, // CIRCLE WITH HORIZONTAL BAR + "OHgr", 0x003A9, // GREEK CAPITAL LETTER OMEGA + "ohgr", 0x003C9, // GREEK SMALL LETTER OMEGA + "ohm", 0x003A9, // GREEK CAPITAL LETTER OMEGA + "oint", 0x0222E, // CONTOUR INTEGRAL + "olarr", 0x021BA, // ANTICLOCKWISE OPEN CIRCLE ARROW + "olcir", 0x029BE, // CIRCLED WHITE BULLET + "olcross", 0x029BB, // CIRCLE WITH SUPERIMPOSED X + "oline", 0x0203E, // OVERLINE + "olt", 0x029C0, // CIRCLED LESS-THAN + "Omacr", 0x0014C, // LATIN CAPITAL LETTER O WITH MACRON + "omacr", 0x0014D, // LATIN SMALL LETTER O WITH MACRON + "Omega", 0x003A9, // GREEK CAPITAL LETTER OMEGA + "omega", 0x003C9, // GREEK SMALL LETTER OMEGA + "Omicron", 0x0039F, // GREEK CAPITAL LETTER OMICRON + "omicron", 0x003BF, // GREEK SMALL LETTER OMICRON + "omid", 0x029B6, // CIRCLED VERTICAL BAR + "ominus", 0x02296, // CIRCLED MINUS + "Oopf", 0x1D546, // MATHEMATICAL DOUBLE-STRUCK CAPITAL O + "oopf", 0x1D560, // MATHEMATICAL DOUBLE-STRUCK SMALL O + "opar", 0x029B7, // CIRCLED PARALLEL + "OpenCurlyDoubleQuote", 0x0201C, // LEFT DOUBLE QUOTATION MARK + "OpenCurlyQuote", 0x02018, // LEFT SINGLE QUOTATION MARK + "operp", 0x029B9, // CIRCLED PERPENDICULAR + "oplus", 0x02295, // CIRCLED PLUS + "or", 0x02228, // LOGICAL OR + "Or", 0x02A54, // DOUBLE LOGICAL OR + "orarr", 0x021BB, // CLOCKWISE OPEN CIRCLE ARROW + "ord", 0x02A5D, // LOGICAL OR WITH HORIZONTAL DASH + "order", 0x02134, // SCRIPT SMALL O + "orderof", 0x02134, // SCRIPT SMALL O + "ordf", 0x000AA, // FEMININE ORDINAL INDICATOR + "ordm", 0x000BA, // MASCULINE ORDINAL INDICATOR + "origof", 0x022B6, // ORIGINAL OF + "oror", 0x02A56, // TWO INTERSECTING LOGICAL OR + "orslope", 0x02A57, // SLOPING LARGE OR + "orv", 0x02A5B, // LOGICAL OR WITH MIDDLE STEM + "oS", 0x024C8, // CIRCLED LATIN CAPITAL LETTER S + "oscr", 0x02134, // SCRIPT SMALL O + "Oscr", 0x1D4AA, // MATHEMATICAL SCRIPT CAPITAL O + "Oslash", 0x000D8, // LATIN CAPITAL LETTER O WITH STROKE + "oslash", 0x000F8, // LATIN SMALL LETTER O WITH STROKE + "osol", 0x02298, // CIRCLED DIVISION SLASH + "Otilde", 0x000D5, // LATIN CAPITAL LETTER O WITH TILDE + "otilde", 0x000F5, // LATIN SMALL LETTER O WITH TILDE + "otimes", 0x02297, // CIRCLED TIMES + "Otimes", 0x02A37, // MULTIPLICATION SIGN IN DOUBLE CIRCLE + "otimesas", 0x02A36, // CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT + "Ouml", 0x000D6, // LATIN CAPITAL LETTER O WITH DIAERESIS + "ouml", 0x000F6, // LATIN SMALL LETTER O WITH DIAERESIS + "ovbar", 0x0233D, // APL FUNCTIONAL SYMBOL CIRCLE STILE + "OverBar", 0x0203E, // OVERLINE + "OverBrace", 0x023DE, // TOP CURLY BRACKET + "OverBracket", 0x023B4, // TOP SQUARE BRACKET + "OverParenthesis", 0x023DC, // TOP PARENTHESIS + NULL, 0 +}; + +static NameId namesP[]={ + "par", 0x02225, // PARALLEL TO + "para", 0x000B6, // PILCROW SIGN + "parallel", 0x02225, // PARALLEL TO + "parsim", 0x02AF3, // PARALLEL WITH TILDE OPERATOR + "parsl", 0x02AFD, // DOUBLE SOLIDUS OPERATOR + "part", 0x02202, // PARTIAL DIFFERENTIAL + "PartialD", 0x02202, // PARTIAL DIFFERENTIAL + "Pcy", 0x0041F, // CYRILLIC CAPITAL LETTER PE + "pcy", 0x0043F, // CYRILLIC SMALL LETTER PE + "percnt", 0x00025, // PERCENT SIGN + "period", 0x0002E, // FULL STOP + "permil", 0x02030, // PER MILLE SIGN + "perp", 0x022A5, // UP TACK + "pertenk", 0x02031, // PER TEN THOUSAND SIGN + "Pfr", 0x1D513, // MATHEMATICAL FRAKTUR CAPITAL P + "pfr", 0x1D52D, // MATHEMATICAL FRAKTUR SMALL P + "Pgr", 0x003A0, // GREEK CAPITAL LETTER PI + "pgr", 0x003C0, // GREEK SMALL LETTER PI + "PHgr", 0x003A6, // GREEK CAPITAL LETTER PHI + "phgr", 0x003C6, // GREEK SMALL LETTER PHI + "Phi", 0x003A6, // GREEK CAPITAL LETTER PHI + "phi", 0x003C6, // GREEK SMALL LETTER PHI + "phiv", 0x003D5, // GREEK PHI SYMBOL + "phmmat", 0x02133, // SCRIPT CAPITAL M + "phone", 0x0260E, // BLACK TELEPHONE + "Pi", 0x003A0, // GREEK CAPITAL LETTER PI + "pi", 0x003C0, // GREEK SMALL LETTER PI + "pitchfork", 0x022D4, // PITCHFORK + "piv", 0x003D6, // GREEK PI SYMBOL + "planck", 0x0210F, // PLANCK CONSTANT OVER TWO PI + "planckh", 0x0210E, // PLANCK CONSTANT + "plankv", 0x0210F, // PLANCK CONSTANT OVER TWO PI + "plus", 0x0002B, // PLUS SIGN + "plusacir", 0x02A23, // PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE + "plusb", 0x0229E, // SQUARED PLUS + "pluscir", 0x02A22, // PLUS SIGN WITH SMALL CIRCLE ABOVE + "plusdo", 0x02214, // DOT PLUS + "plusdu", 0x02A25, // PLUS SIGN WITH DOT BELOW + "pluse", 0x02A72, // PLUS SIGN ABOVE EQUALS SIGN + "PlusMinus", 0x000B1, // PLUS-MINUS SIGN + "plusmn", 0x000B1, // PLUS-MINUS SIGN + "plussim", 0x02A26, // PLUS SIGN WITH TILDE BELOW + "plustwo", 0x02A27, // PLUS SIGN WITH SUBSCRIPT TWO + "pm", 0x000B1, // PLUS-MINUS SIGN + "Poincareplane", 0x0210C, // BLACK-LETTER CAPITAL H + "pointint", 0x02A15, // INTEGRAL AROUND A POINT OPERATOR + "Popf", 0x02119, // DOUBLE-STRUCK CAPITAL P + "popf", 0x1D561, // MATHEMATICAL DOUBLE-STRUCK SMALL P + "pound", 0x000A3, // POUND SIGN + "pr", 0x0227A, // PRECEDES + "Pr", 0x02ABB, // DOUBLE PRECEDES + "prap", 0x02AB7, // PRECEDES ABOVE ALMOST EQUAL TO + "prcue", 0x0227C, // PRECEDES OR EQUAL TO + "pre", 0x02AAF, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN + "prE", 0x02AB3, // PRECEDES ABOVE EQUALS SIGN + "prec", 0x0227A, // PRECEDES + "precapprox", 0x02AB7, // PRECEDES ABOVE ALMOST EQUAL TO + "preccurlyeq", 0x0227C, // PRECEDES OR EQUAL TO + "Precedes", 0x0227A, // PRECEDES + "PrecedesEqual", 0x02AAF, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN + "PrecedesSlantEqual", 0x0227C, // PRECEDES OR EQUAL TO + "PrecedesTilde", 0x0227E, // PRECEDES OR EQUIVALENT TO + "preceq", 0x02AAF, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN + "precnapprox", 0x02AB9, // PRECEDES ABOVE NOT ALMOST EQUAL TO + "precneqq", 0x02AB5, // PRECEDES ABOVE NOT EQUAL TO + "precnsim", 0x022E8, // PRECEDES BUT NOT EQUIVALENT TO + "precsim", 0x0227E, // PRECEDES OR EQUIVALENT TO + "prime", 0x02032, // PRIME + "Prime", 0x02033, // DOUBLE PRIME + "primes", 0x02119, // DOUBLE-STRUCK CAPITAL P + "prnap", 0x02AB9, // PRECEDES ABOVE NOT ALMOST EQUAL TO + "prnE", 0x02AB5, // PRECEDES ABOVE NOT EQUAL TO + "prnsim", 0x022E8, // PRECEDES BUT NOT EQUIVALENT TO + "prod", 0x0220F, // N-ARY PRODUCT + "Product", 0x0220F, // N-ARY PRODUCT + "profalar", 0x0232E, // ALL AROUND-PROFILE + "profline", 0x02312, // ARC + "profsurf", 0x02313, // SEGMENT + "prop", 0x0221D, // PROPORTIONAL TO + "Proportion", 0x02237, // PROPORTION + "Proportional", 0x0221D, // PROPORTIONAL TO + "propto", 0x0221D, // PROPORTIONAL TO + "prsim", 0x0227E, // PRECEDES OR EQUIVALENT TO + "prurel", 0x022B0, // PRECEDES UNDER RELATION + "Pscr", 0x1D4AB, // MATHEMATICAL SCRIPT CAPITAL P + "pscr", 0x1D4C5, // MATHEMATICAL SCRIPT SMALL P + "PSgr", 0x003A8, // GREEK CAPITAL LETTER PSI + "psgr", 0x003C8, // GREEK SMALL LETTER PSI + "Psi", 0x003A8, // GREEK CAPITAL LETTER PSI + "psi", 0x003C8, // GREEK SMALL LETTER PSI + "puncsp", 0x02008, // PUNCTUATION SPACE + NULL, 0 +}; + +static NameId namesQ[]={ + "Qfr", 0x1D514, // MATHEMATICAL FRAKTUR CAPITAL Q + "qfr", 0x1D52E, // MATHEMATICAL FRAKTUR SMALL Q + "qint", 0x02A0C, // QUADRUPLE INTEGRAL OPERATOR + "Qopf", 0x0211A, // DOUBLE-STRUCK CAPITAL Q + "qopf", 0x1D562, // MATHEMATICAL DOUBLE-STRUCK SMALL Q + "qprime", 0x02057, // QUADRUPLE PRIME + "Qscr", 0x1D4AC, // MATHEMATICAL SCRIPT CAPITAL Q + "qscr", 0x1D4C6, // MATHEMATICAL SCRIPT SMALL Q + "quaternions", 0x0210D, // DOUBLE-STRUCK CAPITAL H + "quatint", 0x02A16, // QUATERNION INTEGRAL OPERATOR + "quest", 0x0003F, // QUESTION MARK + "questeq", 0x0225F, // QUESTIONED EQUAL TO + "quot", 0x00022, // QUOTATION MARK + "QUOT", 0x00022, // QUOTATION MARK + NULL, 0 +}; + +static NameId namesR[]={ + "rAarr", 0x021DB, // RIGHTWARDS TRIPLE ARROW +// "race", 0x0223D;0x00331, // REVERSED TILDE with underline + "Racute", 0x00154, // LATIN CAPITAL LETTER R WITH ACUTE + "racute", 0x00155, // LATIN SMALL LETTER R WITH ACUTE + "radic", 0x0221A, // SQUARE ROOT + "raemptyv", 0x029B3, // EMPTY SET WITH RIGHT ARROW ABOVE + "rang", 0x027E9, // MATHEMATICAL RIGHT ANGLE BRACKET + "Rang", 0x027EB, // MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET + "rangd", 0x02992, // RIGHT ANGLE BRACKET WITH DOT + "range", 0x029A5, // REVERSED ANGLE WITH UNDERBAR + "rangle", 0x027E9, // MATHEMATICAL RIGHT ANGLE BRACKET + "raquo", 0x000BB, // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + "rarr", 0x02192, // RIGHTWARDS ARROW + "Rarr", 0x021A0, // RIGHTWARDS TWO HEADED ARROW + "rArr", 0x021D2, // RIGHTWARDS DOUBLE ARROW + "rarrap", 0x02975, // RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO + "rarrb", 0x021E5, // RIGHTWARDS ARROW TO BAR + "rarrbfs", 0x02920, // RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND + "rarrc", 0x02933, // WAVE ARROW POINTING DIRECTLY RIGHT + "rarrfs", 0x0291E, // RIGHTWARDS ARROW TO BLACK DIAMOND + "rarrhk", 0x021AA, // RIGHTWARDS ARROW WITH HOOK + "rarrlp", 0x021AC, // RIGHTWARDS ARROW WITH LOOP + "rarrpl", 0x02945, // RIGHTWARDS ARROW WITH PLUS BELOW + "rarrsim", 0x02974, // RIGHTWARDS ARROW ABOVE TILDE OPERATOR + "rarrtl", 0x021A3, // RIGHTWARDS ARROW WITH TAIL + "Rarrtl", 0x02916, // RIGHTWARDS TWO-HEADED ARROW WITH TAIL + "rarrw", 0x0219D, // RIGHTWARDS WAVE ARROW + "ratail", 0x0291A, // RIGHTWARDS ARROW-TAIL + "rAtail", 0x0291C, // RIGHTWARDS DOUBLE ARROW-TAIL + "ratio", 0x02236, // RATIO + "rationals", 0x0211A, // DOUBLE-STRUCK CAPITAL Q + "rbarr", 0x0290D, // RIGHTWARDS DOUBLE DASH ARROW + "rBarr", 0x0290F, // RIGHTWARDS TRIPLE DASH ARROW + "RBarr", 0x02910, // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW + "rbbrk", 0x02773, // LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT + "rbrace", 0x0007D, // RIGHT CURLY BRACKET + "rbrack", 0x0005D, // RIGHT SQUARE BRACKET + "rbrke", 0x0298C, // RIGHT SQUARE BRACKET WITH UNDERBAR + "rbrksld", 0x0298E, // RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER + "rbrkslu", 0x02990, // RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER + "Rcaron", 0x00158, // LATIN CAPITAL LETTER R WITH CARON + "rcaron", 0x00159, // LATIN SMALL LETTER R WITH CARON + "Rcedil", 0x00156, // LATIN CAPITAL LETTER R WITH CEDILLA + "rcedil", 0x00157, // LATIN SMALL LETTER R WITH CEDILLA + "rceil", 0x02309, // RIGHT CEILING + "rcub", 0x0007D, // RIGHT CURLY BRACKET + "Rcy", 0x00420, // CYRILLIC CAPITAL LETTER ER + "rcy", 0x00440, // CYRILLIC SMALL LETTER ER + "rdca", 0x02937, // ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS + "rdldhar", 0x02969, // RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN + "rdquo", 0x0201D, // RIGHT DOUBLE QUOTATION MARK + "rdquor", 0x0201D, // RIGHT DOUBLE QUOTATION MARK + "rdsh", 0x021B3, // DOWNWARDS ARROW WITH TIP RIGHTWARDS + "Re", 0x0211C, // BLACK-LETTER CAPITAL R + "real", 0x0211C, // BLACK-LETTER CAPITAL R + "realine", 0x0211B, // SCRIPT CAPITAL R + "realpart", 0x0211C, // BLACK-LETTER CAPITAL R + "reals", 0x0211D, // DOUBLE-STRUCK CAPITAL R + "rect", 0x025AD, // WHITE RECTANGLE + "reg", 0x000AE, // REGISTERED SIGN + "REG", 0x000AE, // REGISTERED SIGN + "ReverseElement", 0x0220B, // CONTAINS AS MEMBER + "ReverseEquilibrium", 0x021CB, // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON + "ReverseUpEquilibrium", 0x0296F, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT + "rfisht", 0x0297D, // RIGHT FISH TAIL + "rfloor", 0x0230B, // RIGHT FLOOR + "Rfr", 0x0211C, // BLACK-LETTER CAPITAL R + "rfr", 0x1D52F, // MATHEMATICAL FRAKTUR SMALL R + "Rgr", 0x003A1, // GREEK CAPITAL LETTER RHO + "rgr", 0x003C1, // GREEK SMALL LETTER RHO + "rHar", 0x02964, // RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN + "rhard", 0x021C1, // RIGHTWARDS HARPOON WITH BARB DOWNWARDS + "rharu", 0x021C0, // RIGHTWARDS HARPOON WITH BARB UPWARDS + "rharul", 0x0296C, // RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH + "Rho", 0x003A1, // GREEK CAPITAL LETTER RHO + "rho", 0x003C1, // GREEK SMALL LETTER RHO + "rhov", 0x003F1, // GREEK RHO SYMBOL + "RightAngleBracket", 0x027E9, // MATHEMATICAL RIGHT ANGLE BRACKET + "rightarrow", 0x02192, // RIGHTWARDS ARROW + "RightArrow", 0x02192, // RIGHTWARDS ARROW + "Rightarrow", 0x021D2, // RIGHTWARDS DOUBLE ARROW + "RightArrowBar", 0x021E5, // RIGHTWARDS ARROW TO BAR + "RightArrowLeftArrow", 0x021C4, // RIGHTWARDS ARROW OVER LEFTWARDS ARROW + "rightarrowtail", 0x021A3, // RIGHTWARDS ARROW WITH TAIL + "RightCeiling", 0x02309, // RIGHT CEILING + "RightDoubleBracket", 0x027E7, // MATHEMATICAL RIGHT WHITE SQUARE BRACKET + "RightDownTeeVector", 0x0295D, // DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR + "RightDownVector", 0x021C2, // DOWNWARDS HARPOON WITH BARB RIGHTWARDS + "RightDownVectorBar", 0x02955, // DOWNWARDS HARPOON WITH BARB RIGHT TO BAR + "RightFloor", 0x0230B, // RIGHT FLOOR + "rightharpoondown", 0x021C1, // RIGHTWARDS HARPOON WITH BARB DOWNWARDS + "rightharpoonup", 0x021C0, // RIGHTWARDS HARPOON WITH BARB UPWARDS + "rightleftarrows", 0x021C4, // RIGHTWARDS ARROW OVER LEFTWARDS ARROW + "rightleftharpoons", 0x021CC, // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON + "rightrightarrows", 0x021C9, // RIGHTWARDS PAIRED ARROWS + "rightsquigarrow", 0x0219D, // RIGHTWARDS WAVE ARROW + "RightTee", 0x022A2, // RIGHT TACK + "RightTeeArrow", 0x021A6, // RIGHTWARDS ARROW FROM BAR + "RightTeeVector", 0x0295B, // RIGHTWARDS HARPOON WITH BARB UP FROM BAR + "rightthreetimes", 0x022CC, // RIGHT SEMIDIRECT PRODUCT + "RightTriangle", 0x022B3, // CONTAINS AS NORMAL SUBGROUP + "RightTriangleBar", 0x029D0, // VERTICAL BAR BESIDE RIGHT TRIANGLE + "RightTriangleEqual", 0x022B5, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO + "RightUpDownVector", 0x0294F, // UP BARB RIGHT DOWN BARB RIGHT HARPOON + "RightUpTeeVector", 0x0295C, // UPWARDS HARPOON WITH BARB RIGHT FROM BAR + "RightUpVector", 0x021BE, // UPWARDS HARPOON WITH BARB RIGHTWARDS + "RightUpVectorBar", 0x02954, // UPWARDS HARPOON WITH BARB RIGHT TO BAR + "RightVector", 0x021C0, // RIGHTWARDS HARPOON WITH BARB UPWARDS + "RightVectorBar", 0x02953, // RIGHTWARDS HARPOON WITH BARB UP TO BAR + "ring", 0x002DA, // RING ABOVE + "risingdotseq", 0x02253, // IMAGE OF OR APPROXIMATELY EQUAL TO + "rlarr", 0x021C4, // RIGHTWARDS ARROW OVER LEFTWARDS ARROW + "rlhar", 0x021CC, // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON + "rlm", 0x0200F, // RIGHT-TO-LEFT MARK + "rmoust", 0x023B1, // UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION + "rmoustache", 0x023B1, // UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION + "rnmid", 0x02AEE, // DOES NOT DIVIDE WITH REVERSED NEGATION SLASH + "roang", 0x027ED, // MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET + "roarr", 0x021FE, // RIGHTWARDS OPEN-HEADED ARROW + "robrk", 0x027E7, // MATHEMATICAL RIGHT WHITE SQUARE BRACKET + "ropar", 0x02986, // RIGHT WHITE PARENTHESIS + "Ropf", 0x0211D, // DOUBLE-STRUCK CAPITAL R + "ropf", 0x1D563, // MATHEMATICAL DOUBLE-STRUCK SMALL R + "roplus", 0x02A2E, // PLUS SIGN IN RIGHT HALF CIRCLE + "rotimes", 0x02A35, // MULTIPLICATION SIGN IN RIGHT HALF CIRCLE + "RoundImplies", 0x02970, // RIGHT DOUBLE ARROW WITH ROUNDED HEAD + "rpar", 0x00029, // RIGHT PARENTHESIS + "rpargt", 0x02994, // RIGHT ARC GREATER-THAN BRACKET + "rppolint", 0x02A12, // LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE + "rrarr", 0x021C9, // RIGHTWARDS PAIRED ARROWS + "Rrightarrow", 0x021DB, // RIGHTWARDS TRIPLE ARROW + "rsaquo", 0x0203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + "Rscr", 0x0211B, // SCRIPT CAPITAL R + "rscr", 0x1D4C7, // MATHEMATICAL SCRIPT SMALL R + "rsh", 0x021B1, // UPWARDS ARROW WITH TIP RIGHTWARDS + "Rsh", 0x021B1, // UPWARDS ARROW WITH TIP RIGHTWARDS + "rsqb", 0x0005D, // RIGHT SQUARE BRACKET + "rsquo", 0x02019, // RIGHT SINGLE QUOTATION MARK + "rsquor", 0x02019, // RIGHT SINGLE QUOTATION MARK + "rthree", 0x022CC, // RIGHT SEMIDIRECT PRODUCT + "rtimes", 0x022CA, // RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT + "rtri", 0x025B9, // WHITE RIGHT-POINTING SMALL TRIANGLE + "rtrie", 0x022B5, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO + "rtrif", 0x025B8, // BLACK RIGHT-POINTING SMALL TRIANGLE + "rtriltri", 0x029CE, // RIGHT TRIANGLE ABOVE LEFT TRIANGLE + "RuleDelayed", 0x029F4, // RULE-DELAYED + "ruluhar", 0x02968, // RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP + "rx", 0x0211E, // PRESCRIPTION TAKE + NULL, 0 +}; + +static NameId namesS[]={ + "Sacute", 0x0015A, // LATIN CAPITAL LETTER S WITH ACUTE + "sacute", 0x0015B, // LATIN SMALL LETTER S WITH ACUTE + "sbquo", 0x0201A, // SINGLE LOW-9 QUOTATION MARK + "sc", 0x0227B, // SUCCEEDS + "Sc", 0x02ABC, // DOUBLE SUCCEEDS + "scap", 0x02AB8, // SUCCEEDS ABOVE ALMOST EQUAL TO + "Scaron", 0x00160, // LATIN CAPITAL LETTER S WITH CARON + "scaron", 0x00161, // LATIN SMALL LETTER S WITH CARON + "sccue", 0x0227D, // SUCCEEDS OR EQUAL TO + "sce", 0x02AB0, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN + "scE", 0x02AB4, // SUCCEEDS ABOVE EQUALS SIGN + "Scedil", 0x0015E, // LATIN CAPITAL LETTER S WITH CEDILLA + "scedil", 0x0015F, // LATIN SMALL LETTER S WITH CEDILLA + "Scirc", 0x0015C, // LATIN CAPITAL LETTER S WITH CIRCUMFLEX + "scirc", 0x0015D, // LATIN SMALL LETTER S WITH CIRCUMFLEX + "scnap", 0x02ABA, // SUCCEEDS ABOVE NOT ALMOST EQUAL TO + "scnE", 0x02AB6, // SUCCEEDS ABOVE NOT EQUAL TO + "scnsim", 0x022E9, // SUCCEEDS BUT NOT EQUIVALENT TO + "scpolint", 0x02A13, // LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE + "scsim", 0x0227F, // SUCCEEDS OR EQUIVALENT TO + "Scy", 0x00421, // CYRILLIC CAPITAL LETTER ES + "scy", 0x00441, // CYRILLIC SMALL LETTER ES + "sdot", 0x022C5, // DOT OPERATOR + "sdotb", 0x022A1, // SQUARED DOT OPERATOR + "sdote", 0x02A66, // EQUALS SIGN WITH DOT BELOW + "searhk", 0x02925, // SOUTH EAST ARROW WITH HOOK + "searr", 0x02198, // SOUTH EAST ARROW + "seArr", 0x021D8, // SOUTH EAST DOUBLE ARROW + "searrow", 0x02198, // SOUTH EAST ARROW + "sect", 0x000A7, // SECTION SIGN + "semi", 0x0003B, // SEMICOLON + "seswar", 0x02929, // SOUTH EAST ARROW AND SOUTH WEST ARROW + "setminus", 0x02216, // SET MINUS + "setmn", 0x02216, // SET MINUS + "sext", 0x02736, // SIX POINTED BLACK STAR + "sfgr", 0x003C2, // GREEK SMALL LETTER FINAL SIGMA + "Sfr", 0x1D516, // MATHEMATICAL FRAKTUR CAPITAL S + "sfr", 0x1D530, // MATHEMATICAL FRAKTUR SMALL S + "sfrown", 0x02322, // FROWN + "Sgr", 0x003A3, // GREEK CAPITAL LETTER SIGMA + "sgr", 0x003C3, // GREEK SMALL LETTER SIGMA + "sharp", 0x0266F, // MUSIC SHARP SIGN + "SHCHcy", 0x00429, // CYRILLIC CAPITAL LETTER SHCHA + "shchcy", 0x00449, // CYRILLIC SMALL LETTER SHCHA + "SHcy", 0x00428, // CYRILLIC CAPITAL LETTER SHA + "shcy", 0x00448, // CYRILLIC SMALL LETTER SHA + "ShortDownArrow", 0x02193, // DOWNWARDS ARROW + "ShortLeftArrow", 0x02190, // LEFTWARDS ARROW + "shortmid", 0x02223, // DIVIDES + "shortparallel", 0x02225, // PARALLEL TO + "ShortRightArrow", 0x02192, // RIGHTWARDS ARROW + "ShortUpArrow", 0x02191, // UPWARDS ARROW + "shy", 0x000AD, // SOFT HYPHEN + "Sigma", 0x003A3, // GREEK CAPITAL LETTER SIGMA + "sigma", 0x003C3, // GREEK SMALL LETTER SIGMA + "sigmaf", 0x003C2, // GREEK SMALL LETTER FINAL SIGMA + "sigmav", 0x003C2, // GREEK SMALL LETTER FINAL SIGMA + "sim", 0x0223C, // TILDE OPERATOR + "simdot", 0x02A6A, // TILDE OPERATOR WITH DOT ABOVE + "sime", 0x02243, // ASYMPTOTICALLY EQUAL TO + "simeq", 0x02243, // ASYMPTOTICALLY EQUAL TO + "simg", 0x02A9E, // SIMILAR OR GREATER-THAN + "simgE", 0x02AA0, // SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN + "siml", 0x02A9D, // SIMILAR OR LESS-THAN + "simlE", 0x02A9F, // SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN + "simne", 0x02246, // APPROXIMATELY BUT NOT ACTUALLY EQUAL TO + "simplus", 0x02A24, // PLUS SIGN WITH TILDE ABOVE + "simrarr", 0x02972, // TILDE OPERATOR ABOVE RIGHTWARDS ARROW + "slarr", 0x02190, // LEFTWARDS ARROW + "SmallCircle", 0x02218, // RING OPERATOR + "smallsetminus", 0x02216, // SET MINUS + "smashp", 0x02A33, // SMASH PRODUCT + "smeparsl", 0x029E4, // EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE + "smid", 0x02223, // DIVIDES + "smile", 0x02323, // SMILE + "smt", 0x02AAA, // SMALLER THAN + "smte", 0x02AAC, // SMALLER THAN OR EQUAL TO +// "smtes", 0x02AAC;0x0FE00, // SMALLER THAN OR slanted EQUAL + "SOFTcy", 0x0042C, // CYRILLIC CAPITAL LETTER SOFT SIGN + "softcy", 0x0044C, // CYRILLIC SMALL LETTER SOFT SIGN + "sol", 0x0002F, // SOLIDUS + "solb", 0x029C4, // SQUARED RISING DIAGONAL SLASH + "solbar", 0x0233F, // APL FUNCTIONAL SYMBOL SLASH BAR + "Sopf", 0x1D54A, // MATHEMATICAL DOUBLE-STRUCK CAPITAL S + "sopf", 0x1D564, // MATHEMATICAL DOUBLE-STRUCK SMALL S + "spades", 0x02660, // BLACK SPADE SUIT + "spadesuit", 0x02660, // BLACK SPADE SUIT + "spar", 0x02225, // PARALLEL TO + "sqcap", 0x02293, // SQUARE CAP +// "sqcaps", 0x02293;0x0FE00, // SQUARE CAP with serifs + "sqcup", 0x02294, // SQUARE CUP +// "sqcups", 0x02294;0x0FE00, // SQUARE CUP with serifs + "Sqrt", 0x0221A, // SQUARE ROOT + "sqsub", 0x0228F, // SQUARE IMAGE OF + "sqsube", 0x02291, // SQUARE IMAGE OF OR EQUAL TO + "sqsubset", 0x0228F, // SQUARE IMAGE OF + "sqsubseteq", 0x02291, // SQUARE IMAGE OF OR EQUAL TO + "sqsup", 0x02290, // SQUARE ORIGINAL OF + "sqsupe", 0x02292, // SQUARE ORIGINAL OF OR EQUAL TO + "sqsupset", 0x02290, // SQUARE ORIGINAL OF + "sqsupseteq", 0x02292, // SQUARE ORIGINAL OF OR EQUAL TO + "squ", 0x025A1, // WHITE SQUARE + "square", 0x025A1, // WHITE SQUARE + "Square", 0x025A1, // WHITE SQUARE + "SquareIntersection", 0x02293, // SQUARE CAP + "SquareSubset", 0x0228F, // SQUARE IMAGE OF + "SquareSubsetEqual", 0x02291, // SQUARE IMAGE OF OR EQUAL TO + "SquareSuperset", 0x02290, // SQUARE ORIGINAL OF + "SquareSupersetEqual", 0x02292, // SQUARE ORIGINAL OF OR EQUAL TO + "SquareUnion", 0x02294, // SQUARE CUP + "squarf", 0x025AA, // BLACK SMALL SQUARE + "squf", 0x025AA, // BLACK SMALL SQUARE + "srarr", 0x02192, // RIGHTWARDS ARROW + "Sscr", 0x1D4AE, // MATHEMATICAL SCRIPT CAPITAL S + "sscr", 0x1D4C8, // MATHEMATICAL SCRIPT SMALL S + "ssetmn", 0x02216, // SET MINUS + "ssmile", 0x02323, // SMILE + "sstarf", 0x022C6, // STAR OPERATOR + "Star", 0x022C6, // STAR OPERATOR + "star", 0x02606, // WHITE STAR + "starf", 0x02605, // BLACK STAR + "straightepsilon", 0x003F5, // GREEK LUNATE EPSILON SYMBOL + "straightphi", 0x003D5, // GREEK PHI SYMBOL + "strns", 0x000AF, // MACRON + "sub", 0x02282, // SUBSET OF + "Sub", 0x022D0, // DOUBLE SUBSET + "subdot", 0x02ABD, // SUBSET WITH DOT + "sube", 0x02286, // SUBSET OF OR EQUAL TO + "subE", 0x02AC5, // SUBSET OF ABOVE EQUALS SIGN + "subedot", 0x02AC3, // SUBSET OF OR EQUAL TO WITH DOT ABOVE + "submult", 0x02AC1, // SUBSET WITH MULTIPLICATION SIGN BELOW + "subne", 0x0228A, // SUBSET OF WITH NOT EQUAL TO + "subnE", 0x02ACB, // SUBSET OF ABOVE NOT EQUAL TO + "subplus", 0x02ABF, // SUBSET WITH PLUS SIGN BELOW + "subrarr", 0x02979, // SUBSET ABOVE RIGHTWARDS ARROW + "subset", 0x02282, // SUBSET OF + "Subset", 0x022D0, // DOUBLE SUBSET + "subseteq", 0x02286, // SUBSET OF OR EQUAL TO + "subseteqq", 0x02AC5, // SUBSET OF ABOVE EQUALS SIGN + "SubsetEqual", 0x02286, // SUBSET OF OR EQUAL TO + "subsetneq", 0x0228A, // SUBSET OF WITH NOT EQUAL TO + "subsetneqq", 0x02ACB, // SUBSET OF ABOVE NOT EQUAL TO + "subsim", 0x02AC7, // SUBSET OF ABOVE TILDE OPERATOR + "subsub", 0x02AD5, // SUBSET ABOVE SUBSET + "subsup", 0x02AD3, // SUBSET ABOVE SUPERSET + "succ", 0x0227B, // SUCCEEDS + "succapprox", 0x02AB8, // SUCCEEDS ABOVE ALMOST EQUAL TO + "succcurlyeq", 0x0227D, // SUCCEEDS OR EQUAL TO + "Succeeds", 0x0227B, // SUCCEEDS + "SucceedsEqual", 0x02AB0, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN + "SucceedsSlantEqual", 0x0227D, // SUCCEEDS OR EQUAL TO + "SucceedsTilde", 0x0227F, // SUCCEEDS OR EQUIVALENT TO + "succeq", 0x02AB0, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN + "succnapprox", 0x02ABA, // SUCCEEDS ABOVE NOT ALMOST EQUAL TO + "succneqq", 0x02AB6, // SUCCEEDS ABOVE NOT EQUAL TO + "succnsim", 0x022E9, // SUCCEEDS BUT NOT EQUIVALENT TO + "succsim", 0x0227F, // SUCCEEDS OR EQUIVALENT TO + "SuchThat", 0x0220B, // CONTAINS AS MEMBER + "sum", 0x02211, // N-ARY SUMMATION + "Sum", 0x02211, // N-ARY SUMMATION + "sung", 0x0266A, // EIGHTH NOTE + "sup", 0x02283, // SUPERSET OF + "Sup", 0x022D1, // DOUBLE SUPERSET + "sup1", 0x000B9, // SUPERSCRIPT ONE + "sup2", 0x000B2, // SUPERSCRIPT TWO + "sup3", 0x000B3, // SUPERSCRIPT THREE + "supdot", 0x02ABE, // SUPERSET WITH DOT + "supdsub", 0x02AD8, // SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET + "supe", 0x02287, // SUPERSET OF OR EQUAL TO + "supE", 0x02AC6, // SUPERSET OF ABOVE EQUALS SIGN + "supedot", 0x02AC4, // SUPERSET OF OR EQUAL TO WITH DOT ABOVE + "Superset", 0x02283, // SUPERSET OF + "SupersetEqual", 0x02287, // SUPERSET OF OR EQUAL TO + "suphsol", 0x027C9, // SUPERSET PRECEDING SOLIDUS + "suphsub", 0x02AD7, // SUPERSET BESIDE SUBSET + "suplarr", 0x0297B, // SUPERSET ABOVE LEFTWARDS ARROW + "supmult", 0x02AC2, // SUPERSET WITH MULTIPLICATION SIGN BELOW + "supne", 0x0228B, // SUPERSET OF WITH NOT EQUAL TO + "supnE", 0x02ACC, // SUPERSET OF ABOVE NOT EQUAL TO + "supplus", 0x02AC0, // SUPERSET WITH PLUS SIGN BELOW + "supset", 0x02283, // SUPERSET OF + "Supset", 0x022D1, // DOUBLE SUPERSET + "supseteq", 0x02287, // SUPERSET OF OR EQUAL TO + "supseteqq", 0x02AC6, // SUPERSET OF ABOVE EQUALS SIGN + "supsetneq", 0x0228B, // SUPERSET OF WITH NOT EQUAL TO + "supsetneqq", 0x02ACC, // SUPERSET OF ABOVE NOT EQUAL TO + "supsim", 0x02AC8, // SUPERSET OF ABOVE TILDE OPERATOR + "supsub", 0x02AD4, // SUPERSET ABOVE SUBSET + "supsup", 0x02AD6, // SUPERSET ABOVE SUPERSET + "swarhk", 0x02926, // SOUTH WEST ARROW WITH HOOK + "swarr", 0x02199, // SOUTH WEST ARROW + "swArr", 0x021D9, // SOUTH WEST DOUBLE ARROW + "swarrow", 0x02199, // SOUTH WEST ARROW + "swnwar", 0x0292A, // SOUTH WEST ARROW AND NORTH WEST ARROW + "szlig", 0x000DF, // LATIN SMALL LETTER SHARP S + NULL, 0 +}; + +static NameId namesT[]={ + "Tab", 0x00009, // CHARACTER TABULATION + "target", 0x02316, // POSITION INDICATOR + "Tau", 0x003A4, // GREEK CAPITAL LETTER TAU + "tau", 0x003C4, // GREEK SMALL LETTER TAU + "tbrk", 0x023B4, // TOP SQUARE BRACKET + "Tcaron", 0x00164, // LATIN CAPITAL LETTER T WITH CARON + "tcaron", 0x00165, // LATIN SMALL LETTER T WITH CARON + "Tcedil", 0x00162, // LATIN CAPITAL LETTER T WITH CEDILLA + "tcedil", 0x00163, // LATIN SMALL LETTER T WITH CEDILLA + "Tcy", 0x00422, // CYRILLIC CAPITAL LETTER TE + "tcy", 0x00442, // CYRILLIC SMALL LETTER TE + "tdot", 0x020DB, // COMBINING THREE DOTS ABOVE + "telrec", 0x02315, // TELEPHONE RECORDER + "Tfr", 0x1D517, // MATHEMATICAL FRAKTUR CAPITAL T + "tfr", 0x1D531, // MATHEMATICAL FRAKTUR SMALL T + "Tgr", 0x003A4, // GREEK CAPITAL LETTER TAU + "tgr", 0x003C4, // GREEK SMALL LETTER TAU + "there4", 0x02234, // THEREFORE + "therefore", 0x02234, // THEREFORE + "Therefore", 0x02234, // THEREFORE + "Theta", 0x00398, // GREEK CAPITAL LETTER THETA + "theta", 0x003B8, // GREEK SMALL LETTER THETA + "thetasym", 0x003D1, // GREEK THETA SYMBOL + "thetav", 0x003D1, // GREEK THETA SYMBOL + "THgr", 0x00398, // GREEK CAPITAL LETTER THETA + "thgr", 0x003B8, // GREEK SMALL LETTER THETA + "thickapprox", 0x02248, // ALMOST EQUAL TO + "thicksim", 0x0223C, // TILDE OPERATOR +// "ThickSpace", 0x0205F;0x0200A, // space of width 5/18 em + "thinsp", 0x02009, // THIN SPACE + "ThinSpace", 0x02009, // THIN SPACE + "thkap", 0x02248, // ALMOST EQUAL TO + "thksim", 0x0223C, // TILDE OPERATOR + "THORN", 0x000DE, // LATIN CAPITAL LETTER THORN + "thorn", 0x000FE, // LATIN SMALL LETTER THORN + "tilde", 0x002DC, // SMALL TILDE + "Tilde", 0x0223C, // TILDE OPERATOR + "TildeEqual", 0x02243, // ASYMPTOTICALLY EQUAL TO + "TildeFullEqual", 0x02245, // APPROXIMATELY EQUAL TO + "TildeTilde", 0x02248, // ALMOST EQUAL TO + "times", 0x000D7, // MULTIPLICATION SIGN + "timesb", 0x022A0, // SQUARED TIMES + "timesbar", 0x02A31, // MULTIPLICATION SIGN WITH UNDERBAR + "timesd", 0x02A30, // MULTIPLICATION SIGN WITH DOT ABOVE + "tint", 0x0222D, // TRIPLE INTEGRAL + "toea", 0x02928, // NORTH EAST ARROW AND SOUTH EAST ARROW + "top", 0x022A4, // DOWN TACK + "topbot", 0x02336, // APL FUNCTIONAL SYMBOL I-BEAM + "topcir", 0x02AF1, // DOWN TACK WITH CIRCLE BELOW + "Topf", 0x1D54B, // MATHEMATICAL DOUBLE-STRUCK CAPITAL T + "topf", 0x1D565, // MATHEMATICAL DOUBLE-STRUCK SMALL T + "topfork", 0x02ADA, // PITCHFORK WITH TEE TOP + "tosa", 0x02929, // SOUTH EAST ARROW AND SOUTH WEST ARROW + "tprime", 0x02034, // TRIPLE PRIME + "trade", 0x02122, // TRADE MARK SIGN + "TRADE", 0x02122, // TRADE MARK SIGN + "triangle", 0x025B5, // WHITE UP-POINTING SMALL TRIANGLE + "triangledown", 0x025BF, // WHITE DOWN-POINTING SMALL TRIANGLE + "triangleleft", 0x025C3, // WHITE LEFT-POINTING SMALL TRIANGLE + "trianglelefteq", 0x022B4, // NORMAL SUBGROUP OF OR EQUAL TO + "triangleq", 0x0225C, // DELTA EQUAL TO + "triangleright", 0x025B9, // WHITE RIGHT-POINTING SMALL TRIANGLE + "trianglerighteq", 0x022B5, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO + "tridot", 0x025EC, // WHITE UP-POINTING TRIANGLE WITH DOT + "trie", 0x0225C, // DELTA EQUAL TO + "triminus", 0x02A3A, // MINUS SIGN IN TRIANGLE + "TripleDot", 0x020DB, // COMBINING THREE DOTS ABOVE + "triplus", 0x02A39, // PLUS SIGN IN TRIANGLE + "trisb", 0x029CD, // TRIANGLE WITH SERIFS AT BOTTOM + "tritime", 0x02A3B, // MULTIPLICATION SIGN IN TRIANGLE + "trpezium", 0x023E2, // WHITE TRAPEZIUM + "Tscr", 0x1D4AF, // MATHEMATICAL SCRIPT CAPITAL T + "tscr", 0x1D4C9, // MATHEMATICAL SCRIPT SMALL T + "TScy", 0x00426, // CYRILLIC CAPITAL LETTER TSE + "tscy", 0x00446, // CYRILLIC SMALL LETTER TSE + "TSHcy", 0x0040B, // CYRILLIC CAPITAL LETTER TSHE + "tshcy", 0x0045B, // CYRILLIC SMALL LETTER TSHE + "Tstrok", 0x00166, // LATIN CAPITAL LETTER T WITH STROKE + "tstrok", 0x00167, // LATIN SMALL LETTER T WITH STROKE + "twixt", 0x0226C, // BETWEEN + "twoheadleftarrow", 0x0219E, // LEFTWARDS TWO HEADED ARROW + "twoheadrightarrow", 0x021A0, // RIGHTWARDS TWO HEADED ARROW + NULL, 0 +}; + +static NameId namesU[]={ + "Uacgr", 0x0038E, // GREEK CAPITAL LETTER UPSILON WITH TONOS + "uacgr", 0x003CD, // GREEK SMALL LETTER UPSILON WITH TONOS + "Uacute", 0x000DA, // LATIN CAPITAL LETTER U WITH ACUTE + "uacute", 0x000FA, // LATIN SMALL LETTER U WITH ACUTE + "uarr", 0x02191, // UPWARDS ARROW + "Uarr", 0x0219F, // UPWARDS TWO HEADED ARROW + "uArr", 0x021D1, // UPWARDS DOUBLE ARROW + "Uarrocir", 0x02949, // UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE + "Ubrcy", 0x0040E, // CYRILLIC CAPITAL LETTER SHORT U + "ubrcy", 0x0045E, // CYRILLIC SMALL LETTER SHORT U + "Ubreve", 0x0016C, // LATIN CAPITAL LETTER U WITH BREVE + "ubreve", 0x0016D, // LATIN SMALL LETTER U WITH BREVE + "Ucirc", 0x000DB, // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + "ucirc", 0x000FB, // LATIN SMALL LETTER U WITH CIRCUMFLEX + "Ucy", 0x00423, // CYRILLIC CAPITAL LETTER U + "ucy", 0x00443, // CYRILLIC SMALL LETTER U + "udarr", 0x021C5, // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW + "Udblac", 0x00170, // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE + "udblac", 0x00171, // LATIN SMALL LETTER U WITH DOUBLE ACUTE + "udhar", 0x0296E, // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT + "udiagr", 0x003B0, // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS + "Udigr", 0x003AB, // GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA + "udigr", 0x003CB, // GREEK SMALL LETTER UPSILON WITH DIALYTIKA + "ufisht", 0x0297E, // UP FISH TAIL + "Ufr", 0x1D518, // MATHEMATICAL FRAKTUR CAPITAL U + "ufr", 0x1D532, // MATHEMATICAL FRAKTUR SMALL U + "Ugr", 0x003A5, // GREEK CAPITAL LETTER UPSILON + "ugr", 0x003C5, // GREEK SMALL LETTER UPSILON + "Ugrave", 0x000D9, // LATIN CAPITAL LETTER U WITH GRAVE + "ugrave", 0x000F9, // LATIN SMALL LETTER U WITH GRAVE + "uHar", 0x02963, // UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT + "uharl", 0x021BF, // UPWARDS HARPOON WITH BARB LEFTWARDS + "uharr", 0x021BE, // UPWARDS HARPOON WITH BARB RIGHTWARDS + "uhblk", 0x02580, // UPPER HALF BLOCK + "ulcorn", 0x0231C, // TOP LEFT CORNER + "ulcorner", 0x0231C, // TOP LEFT CORNER + "ulcrop", 0x0230F, // TOP LEFT CROP + "ultri", 0x025F8, // UPPER LEFT TRIANGLE + "Umacr", 0x0016A, // LATIN CAPITAL LETTER U WITH MACRON + "umacr", 0x0016B, // LATIN SMALL LETTER U WITH MACRON + "uml", 0x000A8, // DIAERESIS + "UnderBar", 0x0005F, // LOW LINE + "UnderBrace", 0x023DF, // BOTTOM CURLY BRACKET + "UnderBracket", 0x023B5, // BOTTOM SQUARE BRACKET + "UnderParenthesis", 0x023DD, // BOTTOM PARENTHESIS + "Union", 0x022C3, // N-ARY UNION + "UnionPlus", 0x0228E, // MULTISET UNION + "Uogon", 0x00172, // LATIN CAPITAL LETTER U WITH OGONEK + "uogon", 0x00173, // LATIN SMALL LETTER U WITH OGONEK + "Uopf", 0x1D54C, // MATHEMATICAL DOUBLE-STRUCK CAPITAL U + "uopf", 0x1D566, // MATHEMATICAL DOUBLE-STRUCK SMALL U + "uparrow", 0x02191, // UPWARDS ARROW + "UpArrow", 0x02191, // UPWARDS ARROW + "Uparrow", 0x021D1, // UPWARDS DOUBLE ARROW + "UpArrowBar", 0x02912, // UPWARDS ARROW TO BAR + "UpArrowDownArrow", 0x021C5, // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW + "updownarrow", 0x02195, // UP DOWN ARROW + "UpDownArrow", 0x02195, // UP DOWN ARROW + "Updownarrow", 0x021D5, // UP DOWN DOUBLE ARROW + "UpEquilibrium", 0x0296E, // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT + "upharpoonleft", 0x021BF, // UPWARDS HARPOON WITH BARB LEFTWARDS + "upharpoonright", 0x021BE, // UPWARDS HARPOON WITH BARB RIGHTWARDS + "uplus", 0x0228E, // MULTISET UNION + "UpperLeftArrow", 0x02196, // NORTH WEST ARROW + "UpperRightArrow", 0x02197, // NORTH EAST ARROW + "upsi", 0x003C5, // GREEK SMALL LETTER UPSILON + "Upsi", 0x003D2, // GREEK UPSILON WITH HOOK SYMBOL + "upsih", 0x003D2, // GREEK UPSILON WITH HOOK SYMBOL + "Upsilon", 0x003A5, // GREEK CAPITAL LETTER UPSILON + "upsilon", 0x003C5, // GREEK SMALL LETTER UPSILON + "UpTee", 0x022A5, // UP TACK + "UpTeeArrow", 0x021A5, // UPWARDS ARROW FROM BAR + "upuparrows", 0x021C8, // UPWARDS PAIRED ARROWS + "urcorn", 0x0231D, // TOP RIGHT CORNER + "urcorner", 0x0231D, // TOP RIGHT CORNER + "urcrop", 0x0230E, // TOP RIGHT CROP + "Uring", 0x0016E, // LATIN CAPITAL LETTER U WITH RING ABOVE + "uring", 0x0016F, // LATIN SMALL LETTER U WITH RING ABOVE + "urtri", 0x025F9, // UPPER RIGHT TRIANGLE + "Uscr", 0x1D4B0, // MATHEMATICAL SCRIPT CAPITAL U + "uscr", 0x1D4CA, // MATHEMATICAL SCRIPT SMALL U + "utdot", 0x022F0, // UP RIGHT DIAGONAL ELLIPSIS + "Utilde", 0x00168, // LATIN CAPITAL LETTER U WITH TILDE + "utilde", 0x00169, // LATIN SMALL LETTER U WITH TILDE + "utri", 0x025B5, // WHITE UP-POINTING SMALL TRIANGLE + "utrif", 0x025B4, // BLACK UP-POINTING SMALL TRIANGLE + "uuarr", 0x021C8, // UPWARDS PAIRED ARROWS + "Uuml", 0x000DC, // LATIN CAPITAL LETTER U WITH DIAERESIS + "uuml", 0x000FC, // LATIN SMALL LETTER U WITH DIAERESIS + "uwangle", 0x029A7, // OBLIQUE ANGLE OPENING DOWN + NULL, 0 +}; + +static NameId namesV[]={ + "vangrt", 0x0299C, // RIGHT ANGLE VARIANT WITH SQUARE + "varepsilon", 0x003F5, // GREEK LUNATE EPSILON SYMBOL + "varkappa", 0x003F0, // GREEK KAPPA SYMBOL + "varnothing", 0x02205, // EMPTY SET + "varphi", 0x003D5, // GREEK PHI SYMBOL + "varpi", 0x003D6, // GREEK PI SYMBOL + "varpropto", 0x0221D, // PROPORTIONAL TO + "varr", 0x02195, // UP DOWN ARROW + "vArr", 0x021D5, // UP DOWN DOUBLE ARROW + "varrho", 0x003F1, // GREEK RHO SYMBOL + "varsigma", 0x003C2, // GREEK SMALL LETTER FINAL SIGMA +// "varsubsetneq", 0x0228A;0x0FE00, // SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members +// "varsubsetneqq", 0x02ACB;0x0FE00, // SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members +// "varsupsetneq", 0x0228B;0x0FE00, // SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members +// "varsupsetneqq", 0x02ACC;0x0FE00, // SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members + "vartheta", 0x003D1, // GREEK THETA SYMBOL + "vartriangleleft", 0x022B2, // NORMAL SUBGROUP OF + "vartriangleright", 0x022B3, // CONTAINS AS NORMAL SUBGROUP + "vBar", 0x02AE8, // SHORT UP TACK WITH UNDERBAR + "Vbar", 0x02AEB, // DOUBLE UP TACK + "vBarv", 0x02AE9, // SHORT UP TACK ABOVE SHORT DOWN TACK + "Vcy", 0x00412, // CYRILLIC CAPITAL LETTER VE + "vcy", 0x00432, // CYRILLIC SMALL LETTER VE + "vdash", 0x022A2, // RIGHT TACK + "vDash", 0x022A8, // TRUE + "Vdash", 0x022A9, // FORCES + "VDash", 0x022AB, // DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE + "Vdashl", 0x02AE6, // LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL + "vee", 0x02228, // LOGICAL OR + "Vee", 0x022C1, // N-ARY LOGICAL OR + "veebar", 0x022BB, // XOR + "veeeq", 0x0225A, // EQUIANGULAR TO + "vellip", 0x022EE, // VERTICAL ELLIPSIS + "verbar", 0x0007C, // VERTICAL LINE + "Verbar", 0x02016, // DOUBLE VERTICAL LINE + "vert", 0x0007C, // VERTICAL LINE + "Vert", 0x02016, // DOUBLE VERTICAL LINE + "VerticalBar", 0x02223, // DIVIDES + "VerticalLine", 0x0007C, // VERTICAL LINE + "VerticalSeparator", 0x02758, // LIGHT VERTICAL BAR + "VerticalTilde", 0x02240, // WREATH PRODUCT + "VeryThinSpace", 0x0200A, // HAIR SPACE + "Vfr", 0x1D519, // MATHEMATICAL FRAKTUR CAPITAL V + "vfr", 0x1D533, // MATHEMATICAL FRAKTUR SMALL V + "vltri", 0x022B2, // NORMAL SUBGROUP OF +// "vnsub", 0x02282;0x020D2, // SUBSET OF with vertical line +// "vnsup", 0x02283;0x020D2, // SUPERSET OF with vertical line + "Vopf", 0x1D54D, // MATHEMATICAL DOUBLE-STRUCK CAPITAL V + "vopf", 0x1D567, // MATHEMATICAL DOUBLE-STRUCK SMALL V + "vprop", 0x0221D, // PROPORTIONAL TO + "vrtri", 0x022B3, // CONTAINS AS NORMAL SUBGROUP + "Vscr", 0x1D4B1, // MATHEMATICAL SCRIPT CAPITAL V + "vscr", 0x1D4CB, // MATHEMATICAL SCRIPT SMALL V +// "vsubne", 0x0228A;0x0FE00, // SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members +// "vsubnE", 0x02ACB;0x0FE00, // SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members +// "vsupne", 0x0228B;0x0FE00, // SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members +// "vsupnE", 0x02ACC;0x0FE00, // SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members + "Vvdash", 0x022AA, // TRIPLE VERTICAL BAR RIGHT TURNSTILE + "vzigzag", 0x0299A, // VERTICAL ZIGZAG LINE + NULL, 0 +}; + +static NameId namesW[]={ + "Wcirc", 0x00174, // LATIN CAPITAL LETTER W WITH CIRCUMFLEX + "wcirc", 0x00175, // LATIN SMALL LETTER W WITH CIRCUMFLEX + "wedbar", 0x02A5F, // LOGICAL AND WITH UNDERBAR + "wedge", 0x02227, // LOGICAL AND + "Wedge", 0x022C0, // N-ARY LOGICAL AND + "wedgeq", 0x02259, // ESTIMATES + "weierp", 0x02118, // SCRIPT CAPITAL P + "Wfr", 0x1D51A, // MATHEMATICAL FRAKTUR CAPITAL W + "wfr", 0x1D534, // MATHEMATICAL FRAKTUR SMALL W + "Wopf", 0x1D54E, // MATHEMATICAL DOUBLE-STRUCK CAPITAL W + "wopf", 0x1D568, // MATHEMATICAL DOUBLE-STRUCK SMALL W + "wp", 0x02118, // SCRIPT CAPITAL P + "wr", 0x02240, // WREATH PRODUCT + "wreath", 0x02240, // WREATH PRODUCT + "Wscr", 0x1D4B2, // MATHEMATICAL SCRIPT CAPITAL W + "wscr", 0x1D4CC, // MATHEMATICAL SCRIPT SMALL W + NULL, 0 +}; + +static NameId namesX[]={ + "xcap", 0x022C2, // N-ARY INTERSECTION + "xcirc", 0x025EF, // LARGE CIRCLE + "xcup", 0x022C3, // N-ARY UNION + "xdtri", 0x025BD, // WHITE DOWN-POINTING TRIANGLE + "Xfr", 0x1D51B, // MATHEMATICAL FRAKTUR CAPITAL X + "xfr", 0x1D535, // MATHEMATICAL FRAKTUR SMALL X + "Xgr", 0x0039E, // GREEK CAPITAL LETTER XI + "xgr", 0x003BE, // GREEK SMALL LETTER XI + "xharr", 0x027F7, // LONG LEFT RIGHT ARROW + "xhArr", 0x027FA, // LONG LEFT RIGHT DOUBLE ARROW + "Xi", 0x0039E, // GREEK CAPITAL LETTER XI + "xi", 0x003BE, // GREEK SMALL LETTER XI + "xlarr", 0x027F5, // LONG LEFTWARDS ARROW + "xlArr", 0x027F8, // LONG LEFTWARDS DOUBLE ARROW + "xmap", 0x027FC, // LONG RIGHTWARDS ARROW FROM BAR + "xnis", 0x022FB, // CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + "xodot", 0x02A00, // N-ARY CIRCLED DOT OPERATOR + "Xopf", 0x1D54F, // MATHEMATICAL DOUBLE-STRUCK CAPITAL X + "xopf", 0x1D569, // MATHEMATICAL DOUBLE-STRUCK SMALL X + "xoplus", 0x02A01, // N-ARY CIRCLED PLUS OPERATOR + "xotime", 0x02A02, // N-ARY CIRCLED TIMES OPERATOR + "xrarr", 0x027F6, // LONG RIGHTWARDS ARROW + "xrArr", 0x027F9, // LONG RIGHTWARDS DOUBLE ARROW + "Xscr", 0x1D4B3, // MATHEMATICAL SCRIPT CAPITAL X + "xscr", 0x1D4CD, // MATHEMATICAL SCRIPT SMALL X + "xsqcup", 0x02A06, // N-ARY SQUARE UNION OPERATOR + "xuplus", 0x02A04, // N-ARY UNION OPERATOR WITH PLUS + "xutri", 0x025B3, // WHITE UP-POINTING TRIANGLE + "xvee", 0x022C1, // N-ARY LOGICAL OR + "xwedge", 0x022C0, // N-ARY LOGICAL AND + NULL, 0 +}; + +static NameId namesY[]={ + "Yacute", 0x000DD, // LATIN CAPITAL LETTER Y WITH ACUTE + "yacute", 0x000FD, // LATIN SMALL LETTER Y WITH ACUTE + "YAcy", 0x0042F, // CYRILLIC CAPITAL LETTER YA + "yacy", 0x0044F, // CYRILLIC SMALL LETTER YA + "Ycirc", 0x00176, // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX + "ycirc", 0x00177, // LATIN SMALL LETTER Y WITH CIRCUMFLEX + "Ycy", 0x0042B, // CYRILLIC CAPITAL LETTER YERU + "ycy", 0x0044B, // CYRILLIC SMALL LETTER YERU + "yen", 0x000A5, // YEN SIGN + "Yfr", 0x1D51C, // MATHEMATICAL FRAKTUR CAPITAL Y + "yfr", 0x1D536, // MATHEMATICAL FRAKTUR SMALL Y + "YIcy", 0x00407, // CYRILLIC CAPITAL LETTER YI + "yicy", 0x00457, // CYRILLIC SMALL LETTER YI + "Yopf", 0x1D550, // MATHEMATICAL DOUBLE-STRUCK CAPITAL Y + "yopf", 0x1D56A, // MATHEMATICAL DOUBLE-STRUCK SMALL Y + "Yscr", 0x1D4B4, // MATHEMATICAL SCRIPT CAPITAL Y + "yscr", 0x1D4CE, // MATHEMATICAL SCRIPT SMALL Y + "YUcy", 0x0042E, // CYRILLIC CAPITAL LETTER YU + "yucy", 0x0044E, // CYRILLIC SMALL LETTER YU + "yuml", 0x000FF, // LATIN SMALL LETTER Y WITH DIAERESIS + "Yuml", 0x00178, // LATIN CAPITAL LETTER Y WITH DIAERESIS + NULL, 0 +}; + +static NameId namesZ[]={ + "Zacute", 0x00179, // LATIN CAPITAL LETTER Z WITH ACUTE + "zacute", 0x0017A, // LATIN SMALL LETTER Z WITH ACUTE + "Zcaron", 0x0017D, // LATIN CAPITAL LETTER Z WITH CARON + "zcaron", 0x0017E, // LATIN SMALL LETTER Z WITH CARON + "Zcy", 0x00417, // CYRILLIC CAPITAL LETTER ZE + "zcy", 0x00437, // CYRILLIC SMALL LETTER ZE + "Zdot", 0x0017B, // LATIN CAPITAL LETTER Z WITH DOT ABOVE + "zdot", 0x0017C, // LATIN SMALL LETTER Z WITH DOT ABOVE + "zeetrf", 0x02128, // BLACK-LETTER CAPITAL Z + "ZeroWidthSpace", 0x0200B, // ZERO WIDTH SPACE + "Zeta", 0x00396, // GREEK CAPITAL LETTER ZETA + "zeta", 0x003B6, // GREEK SMALL LETTER ZETA + "Zfr", 0x02128, // BLACK-LETTER CAPITAL Z + "zfr", 0x1D537, // MATHEMATICAL FRAKTUR SMALL Z + "Zgr", 0x00396, // GREEK CAPITAL LETTER ZETA + "zgr", 0x003B6, // GREEK SMALL LETTER ZETA + "ZHcy", 0x00416, // CYRILLIC CAPITAL LETTER ZHE + "zhcy", 0x00436, // CYRILLIC SMALL LETTER ZHE + "zigrarr", 0x021DD, // RIGHTWARDS SQUIGGLE ARROW + "Zopf", 0x02124, // DOUBLE-STRUCK CAPITAL Z + "zopf", 0x1D56B, // MATHEMATICAL DOUBLE-STRUCK SMALL Z + "Zscr", 0x1D4B5, // MATHEMATICAL SCRIPT CAPITAL Z + "zscr", 0x1D4CF, // MATHEMATICAL SCRIPT SMALL Z + "zwj", 0x0200D, // ZERO WIDTH JOINER + "zwnj", 0x0200C, // ZERO WIDTH NON-JOINER + NULL, 0 +}; + +// @todo@ order namesTable and names? by frequency +static NameId* namesTable[] = { + namesA, namesB, namesC, namesD, namesE, namesF, namesG, namesH, namesI, + namesJ, namesK, namesL, namesM, namesN, namesO, namesP, namesQ, namesR, + namesS, namesT, namesU, namesV, namesW, namesX, namesY, namesZ, NULL +}; + +int HtmlNamedEntity(unsigned char *p, int length) +{ + int tableIndex = tolower(*p) - 'a'; + if (tableIndex >= 0 && tableIndex < 26) + { + NameId* names = namesTable[tableIndex]; + int i; + + for (i = 0; names[i].name; i++) + { + if (strncmp(names[i].name, (char *)p, length) == 0) + return names[i].value; + } + } + return -1; +} + diff --git a/enum.c b/enum.c new file mode 100644 index 00000000..d88d6e3d --- /dev/null +++ b/enum.c @@ -0,0 +1,425 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "root.h" +#include "enum.h" +#include "mtype.h" +#include "scope.h" +#include "id.h" +#include "expression.h" +#include "module.h" +#include "declaration.h" + +/********************************* EnumDeclaration ****************************/ + +EnumDeclaration::EnumDeclaration(Loc loc, Identifier *id, Type *memtype) + : ScopeDsymbol(id) +{ + this->loc = loc; + type = new TypeEnum(this); + this->memtype = memtype; + maxval = NULL; + minval = NULL; + defaultval = NULL; + sinit = NULL; + isdeprecated = 0; + isdone = 0; +} + +Dsymbol *EnumDeclaration::syntaxCopy(Dsymbol *s) +{ + Type *t = NULL; + if (memtype) + t = memtype->syntaxCopy(); + + EnumDeclaration *ed; + if (s) + { ed = (EnumDeclaration *)s; + ed->memtype = t; + } + else + ed = new EnumDeclaration(loc, ident, t); + ScopeDsymbol::syntaxCopy(ed); + return ed; +} + +void EnumDeclaration::semantic0(Scope *sc) +{ + /* This function is a hack to get around a significant problem. + * The members of anonymous enums, like: + * enum { A, B, C } + * don't get installed into the symbol table until after they are + * semantically analyzed, yet they're supposed to go into the enclosing + * scope's table. Hence, when forward referenced, they come out as + * 'undefined'. The real fix is to add them in at addSymbol() time. + * But to get code to compile, we'll just do this quick hack at the moment + * to compile it if it doesn't depend on anything else. + */ + + if (isdone || !scope) + return; + if (!isAnonymous() || memtype) + return; + for (size_t i = 0; i < members->dim; i++) + { + EnumMember *em = (*members)[i]->isEnumMember(); + if (em && (em->type || em->value)) + return; + } + + // Can do it + semantic(sc); +} + +void EnumDeclaration::semantic(Scope *sc) +{ + Type *t; + Scope *sce; + + //printf("EnumDeclaration::semantic(sd = %p, '%s') %s\n", sc->scopesym, sc->scopesym->toChars(), toChars()); + //printf("EnumDeclaration::semantic() %s\n", toChars()); + if (!members) // enum ident; + return; + + if (!memtype && !isAnonymous()) + { // Set memtype if we can to reduce fwd reference errors + memtype = Type::tint32; // case 1) enum ident { ... } + } + + if (symtab) // if already done + { if (isdone || !scope) + return; // semantic() already completed + } + else + symtab = new DsymbolTable(); + + Scope *scx = NULL; + if (scope) + { sc = scope; + scx = scope; // save so we don't make redundant copies + scope = NULL; + } + + unsigned dprogress_save = Module::dprogress; + + if (sc->stc & STCdeprecated) + isdeprecated = 1; + + parent = sc->parent; + + /* The separate, and distinct, cases are: + * 1. enum { ... } + * 2. enum : memtype { ... } + * 3. enum ident { ... } + * 4. enum ident : memtype { ... } + */ + + if (memtype) + { + memtype = memtype->semantic(loc, sc); + + /* Check to see if memtype is forward referenced + */ + if (memtype->ty == Tenum) + { EnumDeclaration *sym = (EnumDeclaration *)memtype->toDsymbol(sc); + if (!sym->memtype || !sym->members || !sym->symtab || sym->scope) + { // memtype is forward referenced, so try again later + scope = scx ? scx : new Scope(*sc); + scope->setNoFree(); + scope->module->addDeferredSemantic(this); + Module::dprogress = dprogress_save; + //printf("\tdeferring %s\n", toChars()); + return; + } + } +#if 0 // Decided to abandon this restriction for D 2.0 + if (!memtype->isintegral()) + { error("base type must be of integral type, not %s", memtype->toChars()); + memtype = Type::tint32; + } +#endif + } + + isdone = 1; + Module::dprogress++; + + type = type->semantic(loc, sc); + if (isAnonymous()) + sce = sc; + else + { sce = sc->push(this); + sce->parent = this; + } + if (members->dim == 0) + error("enum %s must have at least one member", toChars()); + int first = 1; + Expression *elast = NULL; + for (size_t i = 0; i < members->dim; i++) + { + EnumMember *em = (*members)[i]->isEnumMember(); + Expression *e; + + if (!em) + /* The e->semantic(sce) can insert other symbols, such as + * template instances and function literals. + */ + continue; + + //printf(" Enum member '%s'\n",em->toChars()); + if (em->type) + em->type = em->type->semantic(em->loc, sce); + e = em->value; + if (e) + { + assert(e->dyncast() == DYNCAST_EXPRESSION); + e = e->semantic(sce); + e = e->optimize(WANTvalue | WANTinterpret); + if (memtype) + { + e = e->implicitCastTo(sce, memtype); + e = e->optimize(WANTvalue | WANTinterpret); + if (!isAnonymous()) + e = e->castTo(sce, type); + t = memtype; + } + else if (em->type) + { + e = e->implicitCastTo(sce, em->type); + e = e->optimize(WANTvalue | WANTinterpret); + assert(isAnonymous()); + t = e->type; + } + else + t = e->type; + } + else if (first) + { + if (memtype) + t = memtype; + else if (em->type) + t = em->type; + else + t = Type::tint32; + e = new IntegerExp(em->loc, 0, Type::tint32); + e = e->implicitCastTo(sce, t); + e = e->optimize(WANTvalue | WANTinterpret); + if (!isAnonymous()) + e = e->castTo(sce, type); + } + else + { + // Set value to (elast + 1). + // But first check that (elast != t.max) + assert(elast); + e = new EqualExp(TOKequal, em->loc, elast, t->getProperty(0, Id::max)); + e = e->semantic(sce); + e = e->optimize(WANTvalue | WANTinterpret); + if (e->toInteger()) + error("overflow of enum value %s", elast->toChars()); + + // Now set e to (elast + 1) + e = new AddExp(em->loc, elast, new IntegerExp(em->loc, 1, Type::tint32)); + e = e->semantic(sce); + e = e->castTo(sce, elast->type); + e = e->optimize(WANTvalue | WANTinterpret); + } + elast = e; + em->value = e; + + // Add to symbol table only after evaluating 'value' + if (isAnonymous()) + { + /* Anonymous enum members get added to enclosing scope. + */ + for (Scope *sct = sce; sct; sct = sct->enclosing) + { + if (sct->scopesym) + { + if (!sct->scopesym->symtab) + sct->scopesym->symtab = new DsymbolTable(); + em->addMember(sce, sct->scopesym, 1); + break; + } + } + } + else + em->addMember(sc, this, 1); + + /* Compute .min, .max and .default values. + * If enum doesn't have a name, we can never identify the enum type, + * so there is no purpose for a .min, .max or .default + */ + if (!isAnonymous()) + { + if (first) + { defaultval = e; + minval = e; + maxval = e; + } + else + { Expression *ec; + + /* In order to work successfully with UDTs, + * build expressions to do the comparisons, + * and let the semantic analyzer and constant + * folder give us the result. + */ + + // Compute if(e < minval) + ec = new CmpExp(TOKlt, em->loc, e, minval); + ec = ec->semantic(sce); + ec = ec->optimize(WANTvalue | WANTinterpret); + if (ec->toInteger()) + minval = e; + + ec = new CmpExp(TOKgt, em->loc, e, maxval); + ec = ec->semantic(sce); + ec = ec->optimize(WANTvalue | WANTinterpret); + if (ec->toInteger()) + maxval = e; + } + } + first = 0; + } + //printf("defaultval = %lld\n", defaultval); + + //if (defaultval) printf("defaultval: %s %s\n", defaultval->toChars(), defaultval->type->toChars()); + if (sc != sce) + sce->pop(); + //members->print(); +} + +int EnumDeclaration::oneMember(Dsymbol **ps, Identifier *ident) +{ + if (isAnonymous()) + return Dsymbol::oneMembers(members, ps, ident); + return Dsymbol::oneMember(ps, ident); +} + +void EnumDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("enum "); + if (ident) + { buf->writestring(ident->toChars()); + buf->writeByte(' '); + } + if (memtype) + { + buf->writestring(": "); + memtype->toCBuffer(buf, NULL, hgs); + } + if (!members) + { + buf->writeByte(';'); + buf->writenl(); + return; + } + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + for (size_t i = 0; i < members->dim; i++) + { + EnumMember *em = (*members)[i]->isEnumMember(); + if (!em) + continue; + //buf->writestring(" "); + em->toCBuffer(buf, hgs); + buf->writeByte(','); + buf->writenl(); + } + buf->writeByte('}'); + buf->writenl(); +} + +Type *EnumDeclaration::getType() +{ + return type; +} + +const char *EnumDeclaration::kind() +{ + return "enum"; +} + +int EnumDeclaration::isDeprecated() +{ + return isdeprecated; +} + +Dsymbol *EnumDeclaration::search(Loc loc, Identifier *ident, int flags) +{ + //printf("%s.EnumDeclaration::search('%s')\n", toChars(), ident->toChars()); + if (scope) + // Try one last time to resolve this enum + semantic(scope); + + if (!members || !symtab || scope) + { error("is forward referenced when looking for '%s'", ident->toChars()); + //*(char*)0=0; + return NULL; + } + + Dsymbol *s = ScopeDsymbol::search(loc, ident, flags); + return s; +} + +/********************************* EnumMember ****************************/ + +EnumMember::EnumMember(Loc loc, Identifier *id, Expression *value, Type *type) + : Dsymbol(id) +{ + this->value = value; + this->type = type; + this->loc = loc; +} + +Dsymbol *EnumMember::syntaxCopy(Dsymbol *s) +{ + Expression *e = NULL; + if (value) + e = value->syntaxCopy(); + + Type *t = NULL; + if (type) + t = type->syntaxCopy(); + + EnumMember *em; + if (s) + { em = (EnumMember *)s; + em->loc = loc; + em->value = e; + em->type = t; + } + else + em = new EnumMember(loc, ident, e, t); + return em; +} + +void EnumMember::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (type) + type->toCBuffer(buf, ident, hgs); + else + buf->writestring(ident->toChars()); + if (value) + { + buf->writestring(" = "); + value->toCBuffer(buf, hgs); + } +} + +const char *EnumMember::kind() +{ + return "enum member"; +} + + diff --git a/enum.h b/enum.h new file mode 100644 index 00000000..0d617630 --- /dev/null +++ b/enum.h @@ -0,0 +1,91 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2008 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_ENUM_H +#define DMD_ENUM_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "root.h" +#include "dsymbol.h" + +struct Identifier; +struct Type; +struct Expression; +struct HdrGenState; + + +struct EnumDeclaration : ScopeDsymbol +{ /* enum ident : memtype { ... } + */ + Type *type; // the TypeEnum + Type *memtype; // type of the members + +#if DMDV1 + dinteger_t maxval; + dinteger_t minval; + dinteger_t defaultval; // default initializer +#else + Expression *maxval; + Expression *minval; + Expression *defaultval; // default initializer +#endif + int isdeprecated; + int isdone; // 0: not done + // 1: semantic() successfully completed + + EnumDeclaration(Loc loc, Identifier *id, Type *memtype); + Dsymbol *syntaxCopy(Dsymbol *s); + void semantic0(Scope *sc); + void semantic(Scope *sc); + int oneMember(Dsymbol **ps, Identifier *ident = NULL); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Type *getType(); + const char *kind(); +#if DMDV2 + Dsymbol *search(Loc, Identifier *ident, int flags); +#endif + int isDeprecated(); // is Dsymbol deprecated? + + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf); + + EnumDeclaration *isEnumDeclaration() { return this; } + + void toObjFile(int multiobj); // compile to .obj file + void toDebug(); + int cvMember(unsigned char *p); + + Symbol *sinit; + Symbol *toInitializer(); +}; + + +struct EnumMember : Dsymbol +{ + Expression *value; + Type *type; + + EnumMember(Loc loc, Identifier *id, Expression *value, Type *type); + Dsymbol *syntaxCopy(Dsymbol *s); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + const char *kind(); + + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf); + + EnumMember *isEnumMember() { return this; } +}; + +#endif /* DMD_ENUM_H */ diff --git a/expression.c b/expression.c new file mode 100644 index 00000000..3b41fd35 --- /dev/null +++ b/expression.c @@ -0,0 +1,12367 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include +#include +#if _MSC_VER +#include +#else +#include +#endif + +#if _WIN32 && __DMC__ +extern "C" char * __cdecl __locale_decpoint; +#endif + +#include "rmem.h" +#include "port.h" +#include "root.h" + +#include "mtype.h" +#include "init.h" +#include "expression.h" +#include "template.h" +#include "utf.h" +#include "enum.h" +#include "scope.h" +#include "statement.h" +#include "declaration.h" +#include "aggregate.h" +#include "import.h" +#include "id.h" +#include "dsymbol.h" +#include "module.h" +#include "attrib.h" +#include "hdrgen.h" +#include "parse.h" +#include "doc.h" + + +Expression *createTypeInfoArray(Scope *sc, Expression *args[], unsigned dim); +Expression *expandVar(int result, VarDeclaration *v); + +#define LOGSEMANTIC 0 + +/************************************************************* + * Given var, we need to get the + * right 'this' pointer if var is in an outer class, but our + * existing 'this' pointer is in an inner class. + * Input: + * e1 existing 'this' + * ad struct or class we need the correct 'this' for + * var the specific member of ad we're accessing + */ + +Expression *getRightThis(Loc loc, Scope *sc, AggregateDeclaration *ad, + Expression *e1, Declaration *var) +{ + //printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1->toChars(), ad->toChars(), var->toChars()); + L1: + Type *t = e1->type->toBasetype(); + //printf("e1->type = %s, var->type = %s\n", e1->type->toChars(), var->type->toChars()); + + /* If e1 is not the 'this' pointer for ad + */ + if (ad && + !(t->ty == Tpointer && t->nextOf()->ty == Tstruct && + ((TypeStruct *)t->nextOf())->sym == ad) + && + !(t->ty == Tstruct && + ((TypeStruct *)t)->sym == ad) + ) + { + ClassDeclaration *cd = ad->isClassDeclaration(); + ClassDeclaration *tcd = t->isClassHandle(); + + /* e1 is the right this if ad is a base class of e1 + */ + if (!cd || !tcd || + !(tcd == cd || cd->isBaseOf(tcd, NULL)) + ) + { + /* Only classes can be inner classes with an 'outer' + * member pointing to the enclosing class instance + */ + if (tcd && tcd->isNested()) + { /* e1 is the 'this' pointer for an inner class: tcd. + * Rewrite it as the 'this' pointer for the outer class. + */ + + e1 = new DotVarExp(loc, e1, tcd->vthis); + e1->type = tcd->vthis->type; + // Do not call checkNestedRef() + //e1 = e1->semantic(sc); + + // Skip up over nested functions, and get the enclosing + // class type. + int n = 0; + Dsymbol *s; + for (s = tcd->toParent(); + s && s->isFuncDeclaration(); + s = s->toParent()) + { FuncDeclaration *f = s->isFuncDeclaration(); + if (f->vthis) + { + //printf("rewriting e1 to %s's this\n", f->toChars()); + n++; + e1 = new VarExp(loc, f->vthis); + } + else + { + e1->error("need 'this' of type %s to access member %s" + " from static function %s", + ad->toChars(), var->toChars(), f->toChars()); + e1 = new ErrorExp(); + return e1; + } + } + if (s && s->isClassDeclaration()) + { e1->type = s->isClassDeclaration()->type; + if (n > 1) + e1 = e1->semantic(sc); + } + else + e1 = e1->semantic(sc); + goto L1; + } + /* Can't find a path from e1 to ad + */ + e1->error("this for %s needs to be type %s not type %s", + var->toChars(), ad->toChars(), t->toChars()); + e1 = new ErrorExp(); + } + } + return e1; +} + +/***************************************** + * Determine if 'this' is available. + * If it is, return the FuncDeclaration that has it. + */ + +FuncDeclaration *hasThis(Scope *sc) +{ FuncDeclaration *fd; + FuncDeclaration *fdthis; + + //printf("hasThis()\n"); + fdthis = sc->parent->isFuncDeclaration(); + //printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis->toChars() : ""); + + /* Special case for inside template constraint + */ + if (fdthis && (sc->flags & SCOPEstaticif) && fdthis->parent->isTemplateDeclaration()) + { + //TemplateDeclaration *td = fdthis->parent->isTemplateDeclaration(); + //printf("[%s] td = %s, fdthis->vthis = %p\n", td->loc.toChars(), td->toChars(), fdthis->vthis); + return fdthis->vthis ? fdthis : NULL; + } + + // Go upwards until we find the enclosing member function + fd = fdthis; + while (1) + { + if (!fd) + { + goto Lno; + } + if (!fd->isNested()) + break; + + Dsymbol *parent = fd->parent; + while (1) + { + if (!parent) + goto Lno; + TemplateInstance *ti = parent->isTemplateInstance(); + if (ti) + parent = ti->parent; + else + break; + } + fd = parent->isFuncDeclaration(); + } + + if (!fd->isThis()) + { //printf("test '%s'\n", fd->toChars()); + goto Lno; + } + + assert(fd->vthis); + return fd; + +Lno: + return NULL; // don't have 'this' available +} + + +/*************************************** + * Pull out any properties. + */ + +Expression *resolveProperties(Scope *sc, Expression *e) +{ + //printf("resolveProperties(%s)\n", e->toChars()); + + TemplateDeclaration *td; + Objects *targsi; + Expression *ethis; + if (e->op == TOKdotti) + { + DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e; + td = dti->getTempdecl(sc); + dti->ti->semanticTiargs(sc); + targsi = dti->ti->tiargs; + ethis = dti->e1; + goto L1; + } + else if (e->op == TOKdottd) + { + DotTemplateExp *dte = (DotTemplateExp *)e; + td = dte->td; + targsi = NULL; + ethis = dte->e1; + goto L1; + } + else if (e->op == TOKtemplate) + { + td = ((TemplateExp *)e)->td; + targsi = NULL; + ethis = NULL; + L1: + assert(td); + unsigned errors = global.startGagging(); + FuncDeclaration *fd = td->deduceFunctionTemplate(sc, e->loc, targsi, ethis, NULL, 1); + if (global.endGagging(errors)) + fd = NULL; // eat "is not a function template" error + if (fd && fd->type) + { assert(fd->type->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)fd->type; + if (!tf->isproperty && global.params.enforcePropertySyntax) + { error(e->loc, "not a property %s", e->toChars()); + return new ErrorExp(); + } + e = new CallExp(e->loc, e); + e = e->semantic(sc); + } + goto return_expr; + } + + if (e->type) + { + Type *t = e->type->toBasetype(); + + if (t->ty == Tfunction || e->op == TOKoverloadset) + { + if (t->ty == Tfunction && !((TypeFunction *)t)->isproperty && + global.params.enforcePropertySyntax) + { + error(e->loc, "not a property %s", e->toChars()); + return new ErrorExp(); + } + e = new CallExp(e->loc, e); + e = e->semantic(sc); + } + + /* Look for e being a lazy parameter; rewrite as delegate call + */ + else if (e->op == TOKvar) + { VarExp *ve = (VarExp *)e; + + if (ve->var->storage_class & STClazy) + { + e = new CallExp(e->loc, e); + e = e->semantic(sc); + } + } + + else if (e->op == TOKdotexp) + { + e->error("expression has no value"); + return new ErrorExp(); + } + + } + +return_expr: + if (!e->type) + { + error(e->loc, "cannot resolve type for %s", e->toChars()); + e->type = new TypeError(); + } + return e; +} + +/****************************** + * Perform semantic() on an array of Expressions. + */ + +Expressions *arrayExpressionSemantic(Expressions *exps, Scope *sc) +{ + if (exps) + { + for (size_t i = 0; i < exps->dim; i++) + { Expression *e = (*exps)[i]; + if (e) + { e = e->semantic(sc); + (*exps)[i] = e; + } + } + } + return exps; +} + + +/****************************** + * Perform canThrow() on an array of Expressions. + */ + +#if DMDV2 +int arrayExpressionCanThrow(Expressions *exps, bool mustNotThrow) +{ + if (exps) + { + for (size_t i = 0; i < exps->dim; i++) + { Expression *e = (*exps)[i]; + if (e && e->canThrow(mustNotThrow)) + return 1; + } + } + return 0; +} +#endif + +/**************************************** + * Expand tuples. + */ + +void expandTuples(Expressions *exps) +{ + //printf("expandTuples()\n"); + if (exps) + { + for (size_t i = 0; i < exps->dim; i++) + { Expression *arg = (*exps)[i]; + if (!arg) + continue; + + // Look for tuple with 0 members + if (arg->op == TOKtype) + { TypeExp *e = (TypeExp *)arg; + if (e->type->toBasetype()->ty == Ttuple) + { TypeTuple *tt = (TypeTuple *)e->type->toBasetype(); + + if (!tt->arguments || tt->arguments->dim == 0) + { + exps->remove(i); + if (i == exps->dim) + return; + i--; + continue; + } + } + } + + // Inline expand all the tuples + while (arg->op == TOKtuple) + { TupleExp *te = (TupleExp *)arg; + + exps->remove(i); // remove arg + exps->insert(i, te->exps); // replace with tuple contents + if (i == exps->dim) + return; // empty tuple, no more arguments + arg = (*exps)[i]; + } + } + } +} + +/**************************************** + * Expand alias this tuples. + */ + +TupleDeclaration *isAliasThisTuple(Expression *e) +{ + if (e->type) + { + Type *t = e->type->toBasetype(); + AggregateDeclaration *ad; + if (t->ty == Tstruct) + { + ad = ((TypeStruct *)t)->sym; + goto L1; + } + else if (t->ty == Tclass) + { + ad = ((TypeClass *)t)->sym; + L1: + Dsymbol *s = ad->aliasthis; + if (s && s->isVarDeclaration()) + { + TupleDeclaration *td = s->isVarDeclaration()->toAlias()->isTupleDeclaration(); + if (td && td->isexp) + return td; + } + } + } + return NULL; +} + +int expandAliasThisTuples(Expressions *exps, int starti) +{ + if (!exps || exps->dim == 0) + return -1; + + for (size_t u = starti; u < exps->dim; u++) + { + Expression *exp = exps->tdata()[u]; + TupleDeclaration *td = isAliasThisTuple(exp); + if (td) + { + exps->remove(u); + for (size_t i = 0; iobjects->dim; ++i) + { + Expression *e = isExpression(td->objects->tdata()[i]); + assert(e); + assert(e->op == TOKdsymbol); + DsymbolExp *se = (DsymbolExp *)e; + Declaration *d = se->s->isDeclaration(); + assert(d); + e = new DotVarExp(exp->loc, exp, d); + assert(d->type); + e->type = d->type; + exps->insert(u + i, e); + } + #if 0 + printf("expansion ->\n"); + for (size_t i = 0; idim; ++i) + { + Expression *e = exps->tdata()[i]; + printf("\texps[%d] e = %s %s\n", i, Token::tochars[e->op], e->toChars()); + } + #endif + return u; + } + } + + return -1; +} + +Expressions *arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt) +{ +#if DMDV1 + /* The first element sets the type + */ + Type *t0 = NULL; + for (size_t i = 0; i < exps->dim; i++) + { Expression *e = (*exps)[i]; + + if (!e->type) + { error("%s has no value", e->toChars()); + e = new ErrorExp(); + } + e = resolveProperties(sc, e); + + if (!t0) + t0 = e->type; + else + e = e->implicitCastTo(sc, t0); + (*exps)[i] = e; + } + + if (!t0) + t0 = Type::tvoid; + if (pt) + *pt = t0; + + // Eventually, we want to make this copy-on-write + return exps; +#endif +#if DMDV2 + /* The type is determined by applying ?: to each pair. + */ + /* Still have a problem with: + * ubyte[][] = [ cast(ubyte[])"hello", [1]]; + * which works if the array literal is initialized top down with the ubyte[][] + * type, but fails with this function doing bottom up typing. + */ + //printf("arrayExpressionToCommonType()\n"); + IntegerExp integerexp(0); + CondExp condexp(0, &integerexp, NULL, NULL); + + Type *t0 = NULL; + Expression *e0; + int j0; + for (size_t i = 0; i < exps->dim; i++) + { Expression *e = (*exps)[i]; + + e = resolveProperties(sc, e); + if (!e->type) + { error("%s has no value", e->toChars()); + e = new ErrorExp(); + } + + if (t0) + { if (t0 != e->type) + { + /* This applies ?: to merge the types. It's backwards; + * ?: should call this function to merge types. + */ + condexp.type = NULL; + condexp.e1 = e0; + condexp.e2 = e; + condexp.loc = e->loc; + condexp.semantic(sc); + (*exps)[j0] = condexp.e1; + e = condexp.e2; + j0 = i; + e0 = e; + t0 = e0->type; + } + } + else + { j0 = i; + e0 = e; + t0 = e->type; + } + (*exps)[i] = e; + } + + if (t0) + { + for (size_t i = 0; i < exps->dim; i++) + { Expression *e = (*exps)[i]; + e = e->implicitCastTo(sc, t0); + (*exps)[i] = e; + } + } + else + t0 = Type::tvoid; // [] is typed as void[] + if (pt) + *pt = t0; + + // Eventually, we want to make this copy-on-write + return exps; +#endif +} + +/**************************************** + * Get TemplateDeclaration enclosing FuncDeclaration. + */ + +TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s) +{ + FuncDeclaration *f = s->isFuncDeclaration(); + if (f && f->parent) + { TemplateInstance *ti = f->parent->isTemplateInstance(); + + if (ti && + !ti->isTemplateMixin() && + (ti->name == f->ident || + ti->toAlias()->ident == f->ident) + && + ti->tempdecl && ti->tempdecl->onemember) + { + return ti->tempdecl; + } + } + return NULL; +} + +/**************************************** + * Preprocess arguments to function. + */ + +void preFunctionParameters(Loc loc, Scope *sc, Expressions *exps) +{ + if (exps) + { + expandTuples(exps); + + for (size_t i = 0; i < exps->dim; i++) + { Expression *arg = (*exps)[i]; + + arg = resolveProperties(sc, arg); + (*exps)[i] = arg; + + //arg->rvalue(); +#if 0 + if (arg->type->ty == Tfunction) + { + arg = new AddrExp(arg->loc, arg); + arg = arg->semantic(sc); + (*exps)[i] = arg; + } +#endif + } + } +} + +/************************************************ + * If we want the value of this expression, but do not want to call + * the destructor on it. + */ + +void valueNoDtor(Expression *e) +{ + if (e->op == TOKcall) + { + /* The struct value returned from the function is transferred + * so do not call the destructor on it. + * Recognize: + * ((S _ctmp = S.init), _ctmp).this(...) + * and make sure the destructor is not called on _ctmp + * BUG: if e is a CommaExp, we should go down the right side. + */ + CallExp *ce = (CallExp *)e; + if (ce->e1->op == TOKdotvar) + { DotVarExp *dve = (DotVarExp *)ce->e1; + if (dve->var->isCtorDeclaration()) + { // It's a constructor call + if (dve->e1->op == TOKcomma) + { CommaExp *comma = (CommaExp *)dve->e1; + if (comma->e2->op == TOKvar) + { VarExp *ve = (VarExp *)comma->e2; + VarDeclaration *ctmp = ve->var->isVarDeclaration(); + if (ctmp) + ctmp->noscope = 1; + } + } + } + } + } +} + +/********************************************* + * Call copy constructor for struct value argument. + */ +#if DMDV2 +Expression *callCpCtor(Loc loc, Scope *sc, Expression *e, int noscope) +{ + Type *tb = e->type->toBasetype(); + assert(tb->ty == Tstruct); + StructDeclaration *sd = ((TypeStruct *)tb)->sym; + if (sd->cpctor) + { + /* Create a variable tmp, and replace the argument e with: + * (tmp = e),tmp + * and let AssignExp() handle the construction. + * This is not the most efficent, ideally tmp would be constructed + * directly onto the stack. + */ + Identifier *idtmp = Lexer::uniqueId("__cpcttmp"); + VarDeclaration *tmp = new VarDeclaration(loc, tb, idtmp, new ExpInitializer(0, e)); + tmp->storage_class |= STCctfe; + tmp->noscope = noscope; + Expression *ae = new DeclarationExp(loc, tmp); + e = new CommaExp(loc, ae, new VarExp(loc, tmp)); + e = e->semantic(sc); + } + return e; +} +#endif + +// Check if this function is a member of a template which has only been +// instantiated speculatively, eg from inside is(typeof()). +// Return the speculative template instance it is part of, +// or NULL if not speculative. +TemplateInstance *isSpeculativeFunction(FuncDeclaration *fd) +{ + Dsymbol * par = fd->parent; + while (par) + { + TemplateInstance *ti = par->isTemplateInstance(); + if (ti && ti->speculative) + return ti; + par = par->toParent(); + } + return NULL; +} + +/**************************************** + * Now that we know the exact type of the function we're calling, + * the arguments[] need to be adjusted: + * 1. implicitly convert argument to the corresponding parameter type + * 2. add default arguments for any missing arguments + * 3. do default promotions on arguments corresponding to ... + * 4. add hidden _arguments[] argument + * 5. call copy constructor for struct value arguments + * Returns: + * return type from function + */ + +Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, + Expression *ethis, Expressions *arguments, FuncDeclaration *fd) +{ + //printf("functionParameters()\n"); + assert(arguments); + assert(fd || tf->next); + size_t nargs = arguments ? arguments->dim : 0; + size_t nparams = Parameter::dim(tf->parameters); + + if (nargs > nparams && tf->varargs == 0) + { error(loc, "expected %zu arguments, not %zu for non-variadic function type %s", nparams, nargs, tf->toChars()); + return Type::terror; + } + + // If inferring return type, and semantic3() needs to be run if not already run + if (!tf->next && fd->inferRetType) + { + TemplateInstance *spec = isSpeculativeFunction(fd); + int olderrs = global.errors; + fd->semantic3(fd->scope); + // Update the template instantiation with the number + // of errors which occured. + if (spec && global.errors != olderrs) + spec->errors = global.errors - olderrs; + } + + unsigned n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams) + + unsigned wildmatch = 0; + if (ethis && tf->isWild()) + { + Type *t = ethis->type; + if (t->isWild()) + wildmatch |= MODwild; + else if (t->isConst()) + wildmatch |= MODconst; + else if (t->isImmutable()) + wildmatch |= MODimmutable; + else + wildmatch |= MODmutable; + } + + int done = 0; + for (size_t i = 0; i < n; i++) + { + Expression *arg; + + if (i < nargs) + arg = arguments->tdata()[i]; + else + arg = NULL; + + if (i < nparams) + { + Parameter *p = Parameter::getNth(tf->parameters, i); + + if (!arg) + { + if (!p->defaultArg) + { + if (tf->varargs == 2 && i + 1 == nparams) + goto L2; + error(loc, "expected %zu function arguments, not %zu", nparams, nargs); + return Type::terror; + } + arg = p->defaultArg; + arg = arg->inlineCopy(sc); +#if DMDV2 + arg = arg->resolveLoc(loc, sc); // __FILE__ and __LINE__ +#endif + arguments->push(arg); + nargs++; + } + else if (arg->op == TOKfunction) + { FuncExp *fe = (FuncExp *)arg; + Type *pt = p->type; + if (tf->varargs == 2 && i + 1 == nparams && pt->nextOf()) + pt = pt->nextOf(); + fe->setType(pt); + arg = fe->semantic(sc); + arguments->tdata()[i] = arg; + } + + if (tf->varargs == 2 && i + 1 == nparams) + { + //printf("\t\tvarargs == 2, p->type = '%s'\n", p->type->toChars()); + if (arg->implicitConvTo(p->type)) + { + if (p->type->nextOf() && arg->implicitConvTo(p->type->nextOf())) + goto L2; + else if (nargs != nparams) + { error(loc, "expected %zu function arguments, not %zu", nparams, nargs); + return Type::terror; + } + goto L1; + } + L2: + Type *tb = p->type->toBasetype(); + Type *tret = p->isLazyArray(); + switch (tb->ty) + { + case Tsarray: + case Tarray: + { // Create a static array variable v of type arg->type +#ifdef IN_GCC + /* GCC 4.0 does not like zero length arrays used like + this; pass a null array value instead. Could also + just make a one-element array. */ + if (nargs - i == 0) + { + arg = new NullExp(loc); + break; + } +#endif + Identifier *id = Lexer::uniqueId("__arrayArg"); + Type *t = new TypeSArray(((TypeArray *)tb)->next, new IntegerExp(nargs - i)); + t = t->semantic(loc, sc); + bool isSafe = fd ? fd->isSafe() : tf->trust == TRUSTsafe; + VarDeclaration *v = new VarDeclaration(loc, t, id, + isSafe ? NULL : new VoidInitializer(loc)); + v->storage_class |= STCctfe; + v->semantic(sc); + v->parent = sc->parent; + //sc->insert(v); + + Expression *c = new DeclarationExp(0, v); + c->type = v->type; + + for (size_t u = i; u < nargs; u++) + { Expression *a = arguments->tdata()[u]; + if (tret && !((TypeArray *)tb)->next->equals(a->type)) + a = a->toDelegate(sc, tret); + + Expression *e = new VarExp(loc, v); + e = new IndexExp(loc, e, new IntegerExp(u + 1 - nparams)); + ConstructExp *ae = new ConstructExp(loc, e, a); + if (c) + c = new CommaExp(loc, c, ae); + else + c = ae; + } + arg = new VarExp(loc, v); + if (c) + arg = new CommaExp(loc, c, arg); + break; + } + case Tclass: + { /* Set arg to be: + * new Tclass(arg0, arg1, ..., argn) + */ + Expressions *args = new Expressions(); + args->setDim(nargs - i); + for (size_t u = i; u < nargs; u++) + args->tdata()[u - i] = arguments->tdata()[u]; + arg = new NewExp(loc, NULL, NULL, p->type, args); + break; + } + default: + if (!arg) + { error(loc, "not enough arguments"); + return Type::terror; + } + break; + } + arg = arg->semantic(sc); + //printf("\targ = '%s'\n", arg->toChars()); + arguments->setDim(i + 1); + arguments->tdata()[i] = arg; + nargs = i + 1; + done = 1; + } + + L1: + if (!(p->storageClass & STClazy && p->type->ty == Tvoid)) + { + unsigned mod = arg->type->wildConvTo(p->type); + if (mod) + { + wildmatch |= mod; + } + } + } + if (done) + break; + } + if (wildmatch) + { /* Calculate wild matching modifier + */ + if (wildmatch & MODconst || wildmatch & (wildmatch - 1)) + wildmatch = MODconst; + else if (wildmatch & MODimmutable) + wildmatch = MODimmutable; + else if (wildmatch & MODwild) + wildmatch = MODwild; + else + { assert(wildmatch & MODmutable); + wildmatch = MODmutable; + } + } + + assert(nargs >= nparams); + for (size_t i = 0; i < nargs; i++) + { + Expression *arg = arguments->tdata()[i]; + assert(arg); + + if (i < nparams) + { + Parameter *p = Parameter::getNth(tf->parameters, i); + + if (!(p->storageClass & STClazy && p->type->ty == Tvoid)) + { + if (p->type->hasWild()) + { + arg = arg->implicitCastTo(sc, p->type->substWildTo(wildmatch)); + arg = arg->optimize(WANTvalue); + } + else if (p->type != arg->type) + { + //printf("arg->type = %s, p->type = %s\n", arg->type->toChars(), p->type->toChars()); + if (arg->op == TOKtype) + { arg->error("cannot pass type %s as function argument", arg->toChars()); + arg = new ErrorExp(); + goto L3; + } + else + arg = arg->implicitCastTo(sc, p->type); + arg = arg->optimize(WANTvalue); + } + } + if (p->storageClass & STCref) + { + arg = arg->toLvalue(sc, arg); + } + else if (p->storageClass & STCout) + { + arg = arg->modifiableLvalue(sc, arg); + } + + Type *tb = arg->type->toBasetype(); +#if !SARRAYVALUE + // Convert static arrays to pointers + if (tb->ty == Tsarray) + { + arg = arg->checkToPointer(); + } +#endif +#if DMDV2 + if (tb->ty == Tstruct && !(p->storageClass & (STCref | STCout))) + { + if (arg->op == TOKcall) + { + /* The struct value returned from the function is transferred + * to the function, so the callee should not call the destructor + * on it. + */ + valueNoDtor(arg); + } + else + { /* Not transferring it, so call the copy constructor + */ + arg = callCpCtor(loc, sc, arg, 1); + } + } +#endif + + //printf("arg: %s\n", arg->toChars()); + //printf("type: %s\n", arg->type->toChars()); + + // Convert lazy argument to a delegate + if (p->storageClass & STClazy) + { + arg = arg->toDelegate(sc, p->type); + } +#if DMDV2 + /* Look for arguments that cannot 'escape' from the called + * function. + */ + if (!tf->parameterEscapes(p)) + { + Expression *a = arg; + if (a->op == TOKcast) + a = ((CastExp *)a)->e1; + + /* Function literals can only appear once, so if this + * appearance was scoped, there cannot be any others. + */ + if (a->op == TOKfunction) + { FuncExp *fe = (FuncExp *)a; + fe->fd->tookAddressOf = 0; + } + + /* For passing a delegate to a scoped parameter, + * this doesn't count as taking the address of it. + * We only worry about 'escaping' references to the function. + */ + else if (a->op == TOKdelegate) + { DelegateExp *de = (DelegateExp *)a; + if (de->e1->op == TOKvar) + { VarExp *ve = (VarExp *)de->e1; + FuncDeclaration *f = ve->var->isFuncDeclaration(); + if (f) + { f->tookAddressOf--; + //printf("tookAddressOf = %d\n", f->tookAddressOf); + } + } + } + } +#endif + } + else + { + + // If not D linkage, do promotions + if (tf->linkage != LINKd) + { + // Promote bytes, words, etc., to ints + arg = arg->integralPromotions(sc); + + // Promote floats to doubles + switch (arg->type->ty) + { + case Tfloat32: + arg = arg->castTo(sc, Type::tfloat64); + break; + + case Timaginary32: + arg = arg->castTo(sc, Type::timaginary64); + break; + } + } + + // Do not allow types that need destructors + if (arg->type->needsDestruction()) + { arg->error("cannot pass types that need destruction as variadic arguments"); + arg = new ErrorExp(); + } + + // Convert static arrays to dynamic arrays + // BUG: I don't think this is right for D2 + Type *tb = arg->type->toBasetype(); + if (tb->ty == Tsarray) + { TypeSArray *ts = (TypeSArray *)tb; + Type *ta = ts->next->arrayOf(); + if (ts->size(arg->loc) == 0) + arg = new NullExp(arg->loc, ta); + else + arg = arg->castTo(sc, ta); + } +#if DMDV2 + if (tb->ty == Tstruct) + { + arg = callCpCtor(loc, sc, arg, 1); + } +#endif + + // Give error for overloaded function addresses + if (arg->op == TOKsymoff) + { SymOffExp *se = (SymOffExp *)arg; + if ( +#if DMDV2 + se->hasOverloads && +#endif + !se->var->isFuncDeclaration()->isUnique()) + { arg->error("function %s is overloaded", arg->toChars()); + arg = new ErrorExp(); + } + } + arg->rvalue(); + } + arg = arg->optimize(WANTvalue); + L3: + arguments->tdata()[i] = arg; + } + + // If D linkage and variadic, add _arguments[] as first argument + if (tf->linkage == LINKd && tf->varargs == 1) + { + assert(arguments->dim >= nparams); + Expression *e = createTypeInfoArray(sc, (Expression **)&arguments->tdata()[nparams], + arguments->dim - nparams); + arguments->insert(0, e); + } + + Type *tret = tf->next; + if (wildmatch) + { /* Adjust function return type based on wildmatch + */ + //printf("wildmatch = x%x, tret = %s\n", wildmatch, tret->toChars()); + tret = tret->substWildTo(wildmatch); + } + return tret; +} + +/************************************************** + * Write expression out to buf, but wrap it + * in ( ) if its precedence is less than pr. + */ + +void expToCBuffer(OutBuffer *buf, HdrGenState *hgs, Expression *e, enum PREC pr) +{ +#ifdef DEBUG + if (precedence[e->op] == PREC_zero) + printf("precedence not defined for token '%s'\n",Token::tochars[e->op]); +#endif + assert(precedence[e->op] != PREC_zero); + assert(pr != PREC_zero); + + //if (precedence[e->op] == 0) e->dump(0); + if (precedence[e->op] < pr || + /* Despite precedence, we don't allow aop] == pr)) + { + buf->writeByte('('); + e->toCBuffer(buf, hgs); + buf->writeByte(')'); + } + else + e->toCBuffer(buf, hgs); +} + +/************************************************** + * Write out argument list to buf. + */ + +void argsToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs) +{ + if (arguments) + { + for (size_t i = 0; i < arguments->dim; i++) + { Expression *arg = arguments->tdata()[i]; + + if (arg) + { if (i) + buf->writeByte(','); + expToCBuffer(buf, hgs, arg, PREC_assign); + } + } + } +} + +/************************************************** + * Write out argument types to buf. + */ + +void argExpTypesToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs) +{ + if (arguments) + { OutBuffer argbuf; + + for (size_t i = 0; i < arguments->dim; i++) + { Expression *arg = arguments->tdata()[i]; + + if (i) + buf->writeByte(','); + argbuf.reset(); + arg->type->toCBuffer2(&argbuf, hgs, 0); + buf->write(&argbuf); + } + } +} + +/******************************** Expression **************************/ + +Expression::Expression(Loc loc, enum TOK op, int size) + : loc(loc) +{ + //printf("Expression::Expression(op = %d) this = %p\n", op, this); + this->loc = loc; + this->op = op; + this->size = size; + this->parens = 0; + type = NULL; +} + +Expression *Expression::syntaxCopy() +{ + //printf("Expression::syntaxCopy()\n"); + //dump(0); + return copy(); +} + +/********************************* + * Does *not* do a deep copy. + */ + +Expression *Expression::copy() +{ + Expression *e; + if (!size) + { +#ifdef DEBUG + fprintf(stdmsg, "No expression copy for: %s\n", toChars()); + printf("op = %d\n", op); + dump(0); +#endif + assert(0); + } + e = (Expression *)mem.malloc(size); + //printf("Expression::copy(op = %d) e = %p\n", op, e); + return (Expression *)memcpy(e, this, size); +} + +/************************** + * Semantically analyze Expression. + * Determine types, fold constants, etc. + */ + +Expression *Expression::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("Expression::semantic() %s\n", toChars()); +#endif + if (type) + type = type->semantic(loc, sc); + else + type = Type::tvoid; + return this; +} + +/********************************** + * Try to run semantic routines. + * If they fail, return NULL. + */ + +Expression *Expression::trySemantic(Scope *sc) +{ + //printf("+trySemantic(%s)\n", toChars()); + unsigned errors = global.startGagging(); + Expression *e = semantic(sc); + if (global.endGagging(errors)) + { + e = NULL; + } + //printf("-trySemantic(%s)\n", toChars()); + return e; +} + +void Expression::print() +{ + fprintf(stdmsg, "%s\n", toChars()); + fflush(stdmsg); +} + +char *Expression::toChars() +{ OutBuffer *buf; + HdrGenState hgs; + + memset(&hgs, 0, sizeof(hgs)); + buf = new OutBuffer(); + toCBuffer(buf, &hgs); + return buf->toChars(); +} + +void Expression::error(const char *format, ...) +{ + if (type != Type::terror) + { + va_list ap; + va_start(ap, format); + ::verror(loc, format, ap); + va_end( ap ); + } +} + +void Expression::warning(const char *format, ...) +{ + if (type != Type::terror) + { + va_list ap; + va_start(ap, format); + ::vwarning(loc, format, ap); + va_end( ap ); + } +} + +int Expression::rvalue() +{ + if (type && type->toBasetype()->ty == Tvoid) + { error("expression %s is void and has no value", toChars()); +#if 0 + dump(0); + halt(); +#endif + if (!global.gag) + type = Type::terror; + return 0; + } + return 1; +} + +Expression *Expression::combine(Expression *e1, Expression *e2) +{ + if (e1) + { + if (e2) + { + e1 = new CommaExp(e1->loc, e1, e2); + e1->type = e2->type; + } + } + else + e1 = e2; + return e1; +} + +dinteger_t Expression::toInteger() +{ + //printf("Expression %s\n", Token::toChars(op)); + error("Integer constant expression expected instead of %s", toChars()); + return 0; +} + +uinteger_t Expression::toUInteger() +{ + //printf("Expression %s\n", Token::toChars(op)); + return (uinteger_t)toInteger(); +} + +real_t Expression::toReal() +{ + error("Floating point constant expression expected instead of %s", toChars()); + return 0; +} + +real_t Expression::toImaginary() +{ + error("Floating point constant expression expected instead of %s", toChars()); + return 0; +} + +complex_t Expression::toComplex() +{ + error("Floating point constant expression expected instead of %s", toChars()); +#ifdef IN_GCC + return complex_t(real_t(0)); // %% nicer +#else + return 0; +#endif +} + +StringExp *Expression::toString() +{ + return NULL; +} + +void Expression::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(Token::toChars(op)); +} + +void Expression::toMangleBuffer(OutBuffer *buf) +{ + error("expression %s is not a valid template value argument", toChars()); +#ifdef DEBUG +dump(0); +#endif +} + +/*************************************** + * Return !=0 if expression is an lvalue. + */ +#if DMDV2 +int Expression::isLvalue() +{ + return 0; +} +#endif + +/******************************* + * Give error if we're not an lvalue. + * If we can, convert expression to be an lvalue. + */ + +Expression *Expression::toLvalue(Scope *sc, Expression *e) +{ + if (!e) + e = this; + else if (!loc.filename) + loc = e->loc; + error("%s is not an lvalue", e->toChars()); + return new ErrorExp(); +} + +Expression *Expression::modifiableLvalue(Scope *sc, Expression *e) +{ + //printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type->toChars()); + + // See if this expression is a modifiable lvalue (i.e. not const) +#if DMDV2 + if (type && (!type->isMutable() || !type->isAssignable())) + { error("%s is not mutable", e->toChars()); + return new ErrorExp(); + } +#endif + return toLvalue(sc, e); +} + + +/************************************ + * Detect cases where pointers to the stack can 'escape' the + * lifetime of the stack frame. + */ + +void Expression::checkEscape() +{ +} + +void Expression::checkEscapeRef() +{ +} + +void Expression::checkScalar() +{ + if (!type->isscalar() && type->toBasetype() != Type::terror) + error("'%s' is not a scalar, it is a %s", toChars(), type->toChars()); + rvalue(); +} + +void Expression::checkNoBool() +{ + if (type->toBasetype()->ty == Tbool) + error("operation not allowed on bool '%s'", toChars()); +} + +Expression *Expression::checkIntegral() +{ + if (!type->isintegral()) + { if (type->toBasetype() != Type::terror) + error("'%s' is not of integral type, it is a %s", toChars(), type->toChars()); + return new ErrorExp(); + } + if (!rvalue()) + return new ErrorExp(); + return this; +} + +Expression *Expression::checkArithmetic() +{ + if (!type->isintegral() && !type->isfloating()) + { if (type->toBasetype() != Type::terror) + error("'%s' is not of arithmetic type, it is a %s", toChars(), type->toChars()); + return new ErrorExp(); + } + if (!rvalue()) + return new ErrorExp(); + return this; +} + +void Expression::checkDeprecated(Scope *sc, Dsymbol *s) +{ + s->checkDeprecated(loc, sc); +} + +#if DMDV2 +/********************************************* + * Calling function f. + * Check the purity, i.e. if we're in a pure function + * we can only call other pure functions. + */ +void Expression::checkPurity(Scope *sc, FuncDeclaration *f) +{ +#if 1 + if (sc->func) + { + /* Given: + * void f() + * { pure void g() + * { + * void h() + * { + * void i() { } + * } + * } + * } + * g() can call h() but not f() + * i() can call h() and g() but not f() + */ + FuncDeclaration *outerfunc = sc->func; + // Find the closest pure parent of the calling function + while (outerfunc->toParent2() && + !outerfunc->isPureBypassingInference() && + outerfunc->toParent2()->isFuncDeclaration()) + { + outerfunc = outerfunc->toParent2()->isFuncDeclaration(); + } + // Find the closest pure parent of the called function + FuncDeclaration *calledparent = f; + while (calledparent->toParent2() && !calledparent->isPureBypassingInference() + && calledparent->toParent2()->isFuncDeclaration() ) + { + calledparent = calledparent->toParent2()->isFuncDeclaration(); + } + // If the caller has a pure parent, then either the called func must be pure, + // OR, they must have the same pure parent. + if (/*outerfunc->isPure() &&*/ // comment out because we deduce purity now + !sc->intypeof && + !(sc->flags & SCOPEdebug) && + !(f->isPure() || (calledparent == outerfunc))) + { + if (outerfunc->setImpure()) + error("pure function '%s' cannot call impure function '%s'", + outerfunc->toChars(), f->toChars()); + } + } +#else + if (sc->func && sc->func->isPure() && !sc->intypeof && !f->isPure()) + error("pure function '%s' cannot call impure function '%s'", + sc->func->toChars(), f->toChars()); +#endif +} + +/******************************************* + * Accessing variable v. + * Check for purity and safety violations. + * If ethis is not NULL, then ethis is the 'this' pointer as in ethis.v + */ + +void Expression::checkPurity(Scope *sc, VarDeclaration *v, Expression *ethis) +{ + /* Look for purity and safety violations when accessing variable v + * from current function. + */ + if (sc->func && + !sc->intypeof && // allow violations inside typeof(expression) + !(sc->flags & SCOPEdebug) && // allow violations inside debug conditionals + v->ident != Id::ctfe && // magic variable never violates pure and safe + !v->isImmutable() && // always safe and pure to access immutables... + !(v->isConst() && v->isDataseg() && !v->type->hasPointers()) && // const global value types are immutable + !(v->storage_class & STCmanifest) // ...or manifest constants + ) + { + if (v->isDataseg()) + { + /* Accessing global mutable state. + * Therefore, this function and all its immediately enclosing + * functions must be pure. + */ + bool msg = FALSE; + for (Dsymbol *s = sc->func; s; s = s->toParent2()) + { + FuncDeclaration *ff = s->isFuncDeclaration(); + if (!ff) + break; + if (ff->setImpure() && !msg) + { error("pure function '%s' cannot access mutable static data '%s'", + sc->func->toChars(), v->toChars()); + msg = TRUE; // only need the innermost message + } + } + } + else + { + /* Given: + * void f() + * { int fx; + * pure void g() + * { int gx; + * void h() + * { int hx; + * void i() { } + * } + * } + * } + * i() can modify hx and gx but not fx + */ + + Dsymbol *vparent = v->toParent2(); + for (Dsymbol *s = sc->func; s; s = s->toParent2()) + { + if (s == vparent) + break; + FuncDeclaration *ff = s->isFuncDeclaration(); + if (!ff) + break; + if (ff->setImpure()) + { error("pure nested function '%s' cannot access mutable data '%s'", + ff->toChars(), v->toChars()); + break; + } + } + } + + /* Do not allow safe functions to access __gshared data + */ + if (v->storage_class & STCgshared) + { + if (sc->func->setUnsafe()) + error("safe function '%s' cannot access __gshared data '%s'", + sc->func->toChars(), v->toChars()); + } + } +} + +void Expression::checkSafety(Scope *sc, FuncDeclaration *f) +{ + if (sc->func && !sc->intypeof && + !f->isSafe() && !f->isTrusted()) + { + if (sc->func->setUnsafe()) + error("safe function '%s' cannot call system function '%s'", + sc->func->toChars(), f->toChars()); + } +} +#endif + + +/***************************** + * Check that expression can be tested for true or false. + */ + +Expression *Expression::checkToBoolean(Scope *sc) +{ + // Default is 'yes' - do nothing + +#ifdef DEBUG + if (!type) + dump(0); +#endif + + // Structs can be converted to bool using opCast(bool)() + Type *tb = type->toBasetype(); + if (tb->ty == Tstruct) + { AggregateDeclaration *ad = ((TypeStruct *)tb)->sym; + /* Don't really need to check for opCast first, but by doing so we + * get better error messages if it isn't there. + */ + Dsymbol *fd = search_function(ad, Id::cast); + if (fd) + { + Expression *e = new CastExp(loc, this, Type::tbool); + e = e->semantic(sc); + return e; + } + + // Forward to aliasthis. + if (ad->aliasthis) + { + Expression *e = new DotIdExp(loc, this, ad->aliasthis->ident); + e = e->semantic(sc); + e = resolveProperties(sc, e); + e = e->checkToBoolean(sc); + return e; + } + } + + if (!type->checkBoolean()) + { if (type->toBasetype() != Type::terror) + error("expression %s of type %s does not have a boolean value", toChars(), type->toChars()); + return new ErrorExp(); + } + return this; +} + +/**************************** + */ + +Expression *Expression::checkToPointer() +{ + //printf("Expression::checkToPointer()\n"); + Expression *e = this; + +#if !SARRAYVALUE + // If C static array, convert to pointer + Type *tb = type->toBasetype(); + if (tb->ty == Tsarray) + { TypeSArray *ts = (TypeSArray *)tb; + if (ts->size(loc) == 0) + e = new NullExp(loc); + else + e = new AddrExp(loc, this); + e->type = ts->next->pointerTo(); + } +#endif + return e; +} + +/****************************** + * Take address of expression. + */ + +Expression *Expression::addressOf(Scope *sc) +{ + Expression *e; + Type *t = type; + + //printf("Expression::addressOf()\n"); + e = toLvalue(sc, NULL); + e = new AddrExp(loc, e); + e->type = t->pointerTo(); + return e; +} + +/****************************** + * If this is a reference, dereference it. + */ + +Expression *Expression::deref() +{ + //printf("Expression::deref()\n"); + // type could be null if forward referencing an 'auto' variable + if (type && type->ty == Treference) + { + Expression *e = new PtrExp(loc, this); + e->type = ((TypeReference *)type)->next; + return e; + } + return this; +} + +/******************************** + * Does this expression statically evaluate to a boolean TRUE or FALSE? + */ + +int Expression::isBool(int result) +{ + return FALSE; +} + +/******************************** + * Does this expression result in either a 1 or a 0? + */ + +int Expression::isBit() +{ + return FALSE; +} + +/**************************************** + * Resolve __LINE__ and __FILE__ to loc. + */ + +Expression *Expression::resolveLoc(Loc loc, Scope *sc) +{ + return this; +} + +Expressions *Expression::arraySyntaxCopy(Expressions *exps) +{ Expressions *a = NULL; + + if (exps) + { + a = new Expressions(); + a->setDim(exps->dim); + for (size_t i = 0; i < a->dim; i++) + { Expression *e = (*exps)[i]; + + if (e) + e = e->syntaxCopy(); + a->tdata()[i] = e; + } + } + return a; +} + +/*************************************************** + * Recognize expressions of the form: + * ((T v = init), v) + * where v is a temp. + * This is used in optimizing out unnecessary temporary generation. + * Returns initializer expression of v if so, NULL if not. + */ + +Expression *Expression::isTemp() +{ + //printf("isTemp() %s\n", toChars()); + if (op == TOKcomma) + { CommaExp *ec = (CommaExp *)this; + if (ec->e1->op == TOKdeclaration && + ec->e2->op == TOKvar) + { DeclarationExp *de = (DeclarationExp *)ec->e1; + VarExp *ve = (VarExp *)ec->e2; + if (ve->var == de->declaration && ve->var->storage_class & STCctfe) + { VarDeclaration *v = ve->var->isVarDeclaration(); + if (v && v->init) + { + ExpInitializer *ei = v->init->isExpInitializer(); + if (ei) + { Expression *e = ei->exp; + if (e->op == TOKconstruct) + { ConstructExp *ce = (ConstructExp *)e; + if (ce->e1->op == TOKvar && ((VarExp *)ce->e1)->var == ve->var) + e = ce->e2; + } + return e; + } + } + } + } + } + return NULL; +} + +/************************************************ + * Destructors are attached to VarDeclarations. + * Hence, if expression returns a temp that needs a destructor, + * make sure and create a VarDeclaration for that temp. + */ + +Expression *Expression::addDtorHook(Scope *sc) +{ + return this; +} + +/******************************** IntegerExp **************************/ + +IntegerExp::IntegerExp(Loc loc, dinteger_t value, Type *type) + : Expression(loc, TOKint64, sizeof(IntegerExp)) +{ + //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type->toChars() : ""); + if (type && !type->isscalar()) + { + //printf("%s, loc = %d\n", toChars(), loc.linnum); + if (type->ty != Terror) + error("integral constant must be scalar type, not %s", type->toChars()); + type = Type::terror; + } + this->type = type; + this->value = value; +} + +IntegerExp::IntegerExp(dinteger_t value) + : Expression(0, TOKint64, sizeof(IntegerExp)) +{ + this->type = Type::tint32; + this->value = value; +} + +int IntegerExp::equals(Object *o) +{ IntegerExp *ne; + + if (this == o || + (((Expression *)o)->op == TOKint64 && + ((ne = (IntegerExp *)o), type->toHeadMutable()->equals(ne->type->toHeadMutable())) && + value == ne->value)) + return 1; + return 0; +} + +char *IntegerExp::toChars() +{ +#if 1 + return Expression::toChars(); +#else + static char buffer[sizeof(value) * 3 + 1]; + + sprintf(buffer, "%jd", value); + return buffer; +#endif +} + +dinteger_t IntegerExp::toInteger() +{ Type *t; + + t = type; + while (t) + { + switch (t->ty) + { + case Tbool: value = (value != 0); break; + case Tint8: value = (d_int8) value; break; + case Tchar: + case Tuns8: value = (d_uns8) value; break; + case Tint16: value = (d_int16) value; break; + case Twchar: + case Tuns16: value = (d_uns16) value; break; + case Tint32: value = (d_int32) value; break; + case Tdchar: + case Tuns32: value = (d_uns32) value; break; + case Tint64: value = (d_int64) value; break; + case Tuns64: value = (d_uns64) value; break; + case Tpointer: + if (PTRSIZE == 4) + value = (d_uns32) value; + else if (PTRSIZE == 8) + value = (d_uns64) value; + else + assert(0); + break; + + case Tenum: + { + TypeEnum *te = (TypeEnum *)t; + t = te->sym->memtype; + continue; + } + + case Ttypedef: + { + TypeTypedef *tt = (TypeTypedef *)t; + t = tt->sym->basetype; + continue; + } + + default: + /* This can happen if errors, such as + * the type is painted on like in fromConstInitializer(). + */ + if (!global.errors) + { + printf("e = %p, ty = %d\n", this, type->ty); + type->print(); + assert(0); + } + break; + } + break; + } + return value; +} + +real_t IntegerExp::toReal() +{ + Type *t; + + toInteger(); + t = type->toBasetype(); + if (t->ty == Tuns64) + return (real_t)(d_uns64)value; + else + return (real_t)(d_int64)value; +} + +real_t IntegerExp::toImaginary() +{ + return (real_t) 0; +} + +complex_t IntegerExp::toComplex() +{ + return toReal(); +} + +int IntegerExp::isBool(int result) +{ + int r = toInteger() != 0; + return result ? r : !r; +} + +Expression *IntegerExp::semantic(Scope *sc) +{ + if (!type) + { + // Determine what the type of this number is + dinteger_t number = value; + + if (number & 0x8000000000000000LL) + type = Type::tuns64; + else if (number & 0xFFFFFFFF80000000LL) + type = Type::tint64; + else + type = Type::tint32; + } + else + { if (!type->deco) + type = type->semantic(loc, sc); + } + return this; +} + +Expression *IntegerExp::toLvalue(Scope *sc, Expression *e) +{ + if (!e) + e = this; + else if (!loc.filename) + loc = e->loc; + e->error("constant %s is not an lvalue", e->toChars()); + return new ErrorExp(); +} + +void IntegerExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + dinteger_t v = toInteger(); + + if (type) + { Type *t = type; + + L1: + switch (t->ty) + { + case Tenum: + { TypeEnum *te = (TypeEnum *)t; + buf->printf("cast(%s)", te->sym->toChars()); + t = te->sym->memtype; + goto L1; + } + + case Ttypedef: + { TypeTypedef *tt = (TypeTypedef *)t; + buf->printf("cast(%s)", tt->sym->toChars()); + t = tt->sym->basetype; + goto L1; + } + + case Twchar: // BUG: need to cast(wchar) + case Tdchar: // BUG: need to cast(dchar) + if ((uinteger_t)v > 0xFF) + { + buf->printf("'\\U%08x'", v); + break; + } + case Tchar: + { + unsigned o = buf->offset; + if (v == '\'') + buf->writestring("'\\''"); + else if (isprint(v) && v != '\\') + buf->printf("'%c'", (int)v); + else + buf->printf("'\\x%02x'", (int)v); + if (hgs->ddoc) + escapeDdocString(buf, o); + break; + } + + case Tint8: + buf->writestring("cast(byte)"); + goto L2; + + case Tint16: + buf->writestring("cast(short)"); + goto L2; + + case Tint32: + L2: + buf->printf("%d", (int)v); + break; + + case Tuns8: + buf->writestring("cast(ubyte)"); + goto L3; + + case Tuns16: + buf->writestring("cast(ushort)"); + goto L3; + + case Tuns32: + L3: + buf->printf("%du", (unsigned)v); + break; + + case Tint64: + buf->printf("%jdL", v); + break; + + case Tuns64: + L4: + buf->printf("%juLU", v); + break; + + case Tbool: + buf->writestring((char *)(v ? "true" : "false")); + break; + + case Tpointer: + buf->writestring("cast("); + buf->writestring(t->toChars()); + buf->writeByte(')'); + if (PTRSIZE == 4) + goto L3; + else if (PTRSIZE == 8) + goto L4; + else + assert(0); + + default: + /* This can happen if errors, such as + * the type is painted on like in fromConstInitializer(). + */ + if (!global.errors) + { +#ifdef DEBUG + t->print(); +#endif + assert(0); + } + break; + } + } + else if (v & 0x8000000000000000LL) + buf->printf("0x%jx", v); + else + buf->printf("%jd", v); +} + +void IntegerExp::toMangleBuffer(OutBuffer *buf) +{ + if ((sinteger_t)value < 0) + buf->printf("N%jd", -value); + else + { + /* This is an awful hack to maintain backwards compatibility. + * There really always should be an 'i' before a number, but + * there wasn't in earlier implementations, so to maintain + * backwards compatibility it is only done if necessary to disambiguate. + * See bugzilla 3029 + */ + if (buf->offset > 0 && isdigit(buf->data[buf->offset - 1])) + buf->writeByte('i'); + + buf->printf("%jd", value); + } +} + +/******************************** ErrorExp **************************/ + +/* Use this expression for error recovery. + * It should behave as a 'sink' to prevent further cascaded error messages. + */ + +ErrorExp::ErrorExp() + : IntegerExp(0, 0, Type::terror) +{ + op = TOKerror; +} + +Expression *ErrorExp::toLvalue(Scope *sc, Expression *e) +{ + return this; +} + +void ErrorExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("__error"); +} + +/******************************** RealExp **************************/ + +RealExp::RealExp(Loc loc, real_t value, Type *type) + : Expression(loc, TOKfloat64, sizeof(RealExp)) +{ + //printf("RealExp::RealExp(%Lg)\n", value); + this->value = value; + this->type = type; +} + +char *RealExp::toChars() +{ + char buffer[sizeof(value) * 3 + 8 + 1 + 1]; + +#ifdef IN_GCC + value.format(buffer, sizeof(buffer)); + if (type->isimaginary()) + strcat(buffer, "i"); +#else + sprintf(buffer, type->isimaginary() ? "%Lgi" : "%Lg", value); +#endif + assert(strlen(buffer) < sizeof(buffer)); + return mem.strdup(buffer); +} + +dinteger_t RealExp::toInteger() +{ +#ifdef IN_GCC + return toReal().toInt(); +#else + return (sinteger_t) toReal(); +#endif +} + +uinteger_t RealExp::toUInteger() +{ +#ifdef IN_GCC + return (uinteger_t) toReal().toInt(); +#else + return (uinteger_t) toReal(); +#endif +} + +real_t RealExp::toReal() +{ + return type->isreal() ? value : 0; +} + +real_t RealExp::toImaginary() +{ + return type->isreal() ? 0 : value; +} + +complex_t RealExp::toComplex() +{ +#ifdef __DMC__ + return toReal() + toImaginary() * I; +#else + return complex_t(toReal(), toImaginary()); +#endif +} + +/******************************** + * Test to see if two reals are the same. + * Regard NaN's as equivalent. + * Regard +0 and -0 as different. + */ + +int RealEquals(real_t x1, real_t x2) +{ + return (Port::isNan(x1) && Port::isNan(x2)) || + /* In some cases, the REALPAD bytes get garbage in them, + * so be sure and ignore them. + */ + memcmp(&x1, &x2, REALSIZE - REALPAD) == 0; +} + +int RealExp::equals(Object *o) +{ RealExp *ne; + + if (this == o || + (((Expression *)o)->op == TOKfloat64 && + ((ne = (RealExp *)o), type->toHeadMutable()->equals(ne->type->toHeadMutable())) && + RealEquals(value, ne->value) + ) + ) + return 1; + return 0; +} + +Expression *RealExp::semantic(Scope *sc) +{ + if (!type) + type = Type::tfloat64; + else + type = type->semantic(loc, sc); + return this; +} + +int RealExp::isBool(int result) +{ +#ifdef IN_GCC + return result ? (! value.isZero()) : (value.isZero()); +#else + return result ? (value != 0) + : (value == 0); +#endif +} + +void floatToBuffer(OutBuffer *buf, Type *type, real_t value) +{ + /* In order to get an exact representation, try converting it + * to decimal then back again. If it matches, use it. + * If it doesn't, fall back to hex, which is + * always exact. + */ + char buffer[25]; + sprintf(buffer, "%Lg", value); + assert(strlen(buffer) < sizeof(buffer)); +#if _WIN32 && __DMC__ + char *save = __locale_decpoint; + __locale_decpoint = "."; + real_t r = strtold(buffer, NULL); + __locale_decpoint = save; +#else + real_t r = strtold(buffer, NULL); +#endif + if (r == value) // if exact duplication + buf->writestring(buffer); + else + buf->printf("%La", value); // ensure exact duplication + + if (type) + { + Type *t = type->toBasetype(); + switch (t->ty) + { + case Tfloat32: + case Timaginary32: + case Tcomplex32: + buf->writeByte('F'); + break; + + case Tfloat80: + case Timaginary80: + case Tcomplex80: + buf->writeByte('L'); + break; + + default: + break; + } + if (t->isimaginary()) + buf->writeByte('i'); + } +} + +void RealExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + floatToBuffer(buf, type, value); +} + +void realToMangleBuffer(OutBuffer *buf, real_t value) +{ + /* Rely on %A to get portable mangling. + * Must munge result to get only identifier characters. + * + * Possible values from %A => mangled result + * NAN => NAN + * -INF => NINF + * INF => INF + * -0X1.1BC18BA997B95P+79 => N11BC18BA997B95P79 + * 0X1.9P+2 => 19P2 + */ + + if (Port::isNan(value)) + buf->writestring("NAN"); // no -NAN bugs + else + { + char buffer[32]; + int n = sprintf(buffer, "%LA", value); + assert(n > 0 && n < sizeof(buffer)); + for (int i = 0; i < n; i++) + { char c = buffer[i]; + + switch (c) + { + case '-': + buf->writeByte('N'); + break; + + case '+': + case 'X': + case '.': + break; + + case '0': + if (i < 2) + break; // skip leading 0X + default: + buf->writeByte(c); + break; + } + } + } +} + +void RealExp::toMangleBuffer(OutBuffer *buf) +{ + buf->writeByte('e'); + realToMangleBuffer(buf, value); +} + + +/******************************** ComplexExp **************************/ + +ComplexExp::ComplexExp(Loc loc, complex_t value, Type *type) + : Expression(loc, TOKcomplex80, sizeof(ComplexExp)) +{ + this->value = value; + this->type = type; + //printf("ComplexExp::ComplexExp(%s)\n", toChars()); +} + +char *ComplexExp::toChars() +{ + char buffer[sizeof(value) * 3 + 8 + 1]; + +#ifdef IN_GCC + char buf1[sizeof(value) * 3 + 8 + 1]; + char buf2[sizeof(value) * 3 + 8 + 1]; + creall(value).format(buf1, sizeof(buf1)); + cimagl(value).format(buf2, sizeof(buf2)); + sprintf(buffer, "(%s+%si)", buf1, buf2); +#else + sprintf(buffer, "(%Lg+%Lgi)", creall(value), cimagl(value)); + assert(strlen(buffer) < sizeof(buffer)); +#endif + return mem.strdup(buffer); +} + +dinteger_t ComplexExp::toInteger() +{ +#ifdef IN_GCC + return (sinteger_t) toReal().toInt(); +#else + return (sinteger_t) toReal(); +#endif +} + +uinteger_t ComplexExp::toUInteger() +{ +#ifdef IN_GCC + return (uinteger_t) toReal().toInt(); +#else + return (uinteger_t) toReal(); +#endif +} + +real_t ComplexExp::toReal() +{ + return creall(value); +} + +real_t ComplexExp::toImaginary() +{ + return cimagl(value); +} + +complex_t ComplexExp::toComplex() +{ + return value; +} + +int ComplexExp::equals(Object *o) +{ ComplexExp *ne; + + if (this == o || + (((Expression *)o)->op == TOKcomplex80 && + ((ne = (ComplexExp *)o), type->toHeadMutable()->equals(ne->type->toHeadMutable())) && + RealEquals(creall(value), creall(ne->value)) && + RealEquals(cimagl(value), cimagl(ne->value)) + ) + ) + return 1; + return 0; +} + +Expression *ComplexExp::semantic(Scope *sc) +{ + if (!type) + type = Type::tcomplex80; + else + type = type->semantic(loc, sc); + return this; +} + +int ComplexExp::isBool(int result) +{ + if (result) + return (bool)(value); + else + return !value; +} + +void ComplexExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + /* Print as: + * (re+imi) + */ +#ifdef IN_GCC + char buf1[sizeof(value) * 3 + 8 + 1]; + char buf2[sizeof(value) * 3 + 8 + 1]; + creall(value).format(buf1, sizeof(buf1)); + cimagl(value).format(buf2, sizeof(buf2)); + buf->printf("(%s+%si)", buf1, buf2); +#else + buf->writeByte('('); + floatToBuffer(buf, type, creall(value)); + buf->writeByte('+'); + floatToBuffer(buf, type, cimagl(value)); + buf->writestring("i)"); +#endif +} + +void ComplexExp::toMangleBuffer(OutBuffer *buf) +{ + buf->writeByte('c'); + real_t r = toReal(); + realToMangleBuffer(buf, r); + buf->writeByte('c'); // separate the two + r = toImaginary(); + realToMangleBuffer(buf, r); +} + +/******************************** IdentifierExp **************************/ + +IdentifierExp::IdentifierExp(Loc loc, Identifier *ident) + : Expression(loc, TOKidentifier, sizeof(IdentifierExp)) +{ + this->ident = ident; +} + +Expression *IdentifierExp::semantic(Scope *sc) +{ + Dsymbol *s; + Dsymbol *scopesym; + +#if LOGSEMANTIC + printf("IdentifierExp::semantic('%s')\n", ident->toChars()); +#endif + s = sc->search(loc, ident, &scopesym); + if (s) + { Expression *e; + WithScopeSymbol *withsym; + + /* See if the symbol was a member of an enclosing 'with' + */ + withsym = scopesym->isWithScopeSymbol(); + if (withsym) + { +#if DMDV2 + /* Disallow shadowing + */ + // First find the scope of the with + Scope *scwith = sc; + while (scwith->scopesym != scopesym) + { scwith = scwith->enclosing; + assert(scwith); + } + // Look at enclosing scopes for symbols with the same name, + // in the same function + for (Scope *scx = scwith; scx && scx->func == scwith->func; scx = scx->enclosing) + { Dsymbol *s2; + + if (scx->scopesym && scx->scopesym->symtab && + (s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL && + s != s2) + { + error("with symbol %s is shadowing local symbol %s", s->toPrettyChars(), s2->toPrettyChars()); + return new ErrorExp(); + } + } +#endif + s = s->toAlias(); + + // Same as wthis.ident + if (s->needThis() || s->isTemplateDeclaration()) + { + e = new VarExp(loc, withsym->withstate->wthis); + e = new DotIdExp(loc, e, ident); + } + else + { Type *t = withsym->withstate->wthis->type; + if (t->ty == Tpointer) + t = ((TypePointer *)t)->next; + e = typeDotIdExp(loc, t, ident); + } + } + else + { + /* If f is really a function template, + * then replace f with the function template declaration. + */ + FuncDeclaration *f = s->isFuncDeclaration(); + if (f) + { TemplateDeclaration *tempdecl = getFuncTemplateDecl(f); + if (tempdecl) + { + if (tempdecl->overroot) // if not start of overloaded list of TemplateDeclaration's + tempdecl = tempdecl->overroot; // then get the start + e = new TemplateExp(loc, tempdecl); + e = e->semantic(sc); + return e; + } + } + // Haven't done overload resolution yet, so pass 1 + e = new DsymbolExp(loc, s, 1); + } + return e->semantic(sc); + } +#if DMDV2 + if (hasThis(sc)) + { + AggregateDeclaration *ad = sc->getStructClassScope(); + if (ad && ad->aliasthis) + { + Expression *e; + e = new IdentifierExp(loc, Id::This); + e = new DotIdExp(loc, e, ad->aliasthis->ident); + e = new DotIdExp(loc, e, ident); + e = e->trySemantic(sc); + if (e) + return e; + } + } + if (ident == Id::ctfe) + { // Create the magic __ctfe bool variable + VarDeclaration *vd = new VarDeclaration(loc, Type::tbool, Id::ctfe, NULL); + Expression *e = new VarExp(loc, vd); + e = e->semantic(sc); + return e; + } +#endif + const char *n = importHint(ident->toChars()); + if (n) + error("'%s' is not defined, perhaps you need to import %s; ?", ident->toChars(), n); + else + { + s = sc->search_correct(ident); + if (s) + error("undefined identifier %s, did you mean %s %s?", ident->toChars(), s->kind(), s->toChars()); + else + error("undefined identifier %s", ident->toChars()); + } + return new ErrorExp(); +} + +char *IdentifierExp::toChars() +{ + return ident->toChars(); +} + +void IdentifierExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (hgs->hdrgen) + buf->writestring(ident->toHChars2()); + else + buf->writestring(ident->toChars()); +} + +#if DMDV2 +int IdentifierExp::isLvalue() +{ + return 1; +} +#endif + +Expression *IdentifierExp::toLvalue(Scope *sc, Expression *e) +{ +#if 0 + tym = tybasic(e1->ET->Tty); + if (!(tyscalar(tym) || + tym == TYstruct || + tym == TYarray && e->Eoper == TOKaddr)) + synerr(EM_lvalue); // lvalue expected +#endif + return this; +} + +/******************************** DollarExp **************************/ + +DollarExp::DollarExp(Loc loc) + : IdentifierExp(loc, Id::dollar) +{ +} + +/******************************** DsymbolExp **************************/ + +DsymbolExp::DsymbolExp(Loc loc, Dsymbol *s, int hasOverloads) + : Expression(loc, TOKdsymbol, sizeof(DsymbolExp)) +{ + this->s = s; + this->hasOverloads = hasOverloads; +} + +Expression *DsymbolExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("DsymbolExp::semantic('%s')\n", s->toChars()); +#endif + +Lagain: + EnumMember *em; + Expression *e; + VarDeclaration *v; + FuncDeclaration *f; + FuncLiteralDeclaration *fld; + OverloadSet *o; + ClassDeclaration *cd; + ClassDeclaration *thiscd = NULL; + Import *imp; + Package *pkg; + Type *t; + + //printf("DsymbolExp:: %p '%s' is a symbol\n", this, toChars()); + //printf("s = '%s', s->kind = '%s'\n", s->toChars(), s->kind()); + if (type) + return this; + if (!s->isFuncDeclaration()) // functions are checked after overloading + checkDeprecated(sc, s); + Dsymbol *olds = s; + s = s->toAlias(); + //printf("s = '%s', s->kind = '%s', s->needThis() = %p\n", s->toChars(), s->kind(), s->needThis()); + if (s != olds && !s->isFuncDeclaration()) + checkDeprecated(sc, s); + + if (sc->func) + thiscd = sc->func->parent->isClassDeclaration(); + + // BUG: This should happen after overload resolution for functions, not before + if (s->needThis()) + { + if (hasThis(sc) +#if DMDV2 + && !s->isFuncDeclaration() +#endif + ) + { + // Supply an implicit 'this', as in + // this.ident + + DotVarExp *de; + + de = new DotVarExp(loc, new ThisExp(loc), s->isDeclaration()); + return de->semantic(sc); + } + } + + em = s->isEnumMember(); + if (em) + { + e = em->value; + e = e->semantic(sc); + return e; + } + v = s->isVarDeclaration(); + if (v) + { + //printf("Identifier '%s' is a variable, type '%s'\n", toChars(), v->type->toChars()); + if (!type) + { if ((!v->type || !v->type->deco) && v->scope) + v->semantic(v->scope); + type = v->type; + if (!v->type) + { error("forward reference of %s %s", v->kind(), v->toChars()); + return new ErrorExp(); + } + } + + if ((v->storage_class & STCmanifest) && v->init) + { + e = v->init->toExpression(); + if (!e) + { error("cannot make expression out of initializer for %s", v->toChars()); + return new ErrorExp(); + } + e = e->copy(); + e->loc = loc; // for better error message + e = e->semantic(sc); + return e; + } + + e = new VarExp(loc, v); + e->type = type; + e = e->semantic(sc); + return e->deref(); + } + fld = s->isFuncLiteralDeclaration(); + if (fld) + { //printf("'%s' is a function literal\n", fld->toChars()); + e = new FuncExp(loc, fld); + return e->semantic(sc); + } + f = s->isFuncDeclaration(); + if (f) + { //printf("'%s' is a function\n", f->toChars()); + + if (!f->originalType && f->scope) // semantic not yet run + f->semantic(f->scope); + + // if inferring return type, sematic3 needs to be run + if (f->inferRetType && f->scope && f->type && !f->type->nextOf()) + { + TemplateInstance *spec = isSpeculativeFunction(f); + int olderrs = global.errors; + f->semantic3(f->scope); + // Update the template instantiation with the number + // of errors which occured. + if (spec && global.errors != olderrs) + spec->errors = global.errors - olderrs; + } + + if (f->isUnitTestDeclaration()) + { + error("cannot call unittest function %s", toChars()); + return new ErrorExp(); + } + if (!f->type->deco) + { + error("forward reference to %s", toChars()); + return new ErrorExp(); + } + return new VarExp(loc, f, hasOverloads); + } + o = s->isOverloadSet(); + if (o) + { //printf("'%s' is an overload set\n", o->toChars()); + return new OverExp(o); + } + cd = s->isClassDeclaration(); + if (cd && thiscd && cd->isBaseOf(thiscd, NULL) && sc->func->needThis()) + { + // We need to add an implicit 'this' if cd is this class or a base class. + DotTypeExp *dte; + + dte = new DotTypeExp(loc, new ThisExp(loc), s); + return dte->semantic(sc); + } + imp = s->isImport(); + if (imp) + { + if (!imp->pkg) + { error("forward reference of import %s", imp->toChars()); + return new ErrorExp(); + } + ScopeExp *ie = new ScopeExp(loc, imp->pkg); + return ie->semantic(sc); + } + pkg = s->isPackage(); + if (pkg) + { + ScopeExp *ie; + + ie = new ScopeExp(loc, pkg); + return ie->semantic(sc); + } + Module *mod = s->isModule(); + if (mod) + { + ScopeExp *ie; + + ie = new ScopeExp(loc, mod); + return ie->semantic(sc); + } + + t = s->getType(); + if (t) + { + TypeExp *te = new TypeExp(loc, t); + return te->semantic(sc); + } + + TupleDeclaration *tup = s->isTupleDeclaration(); + if (tup) + { + e = new TupleExp(loc, tup); + e = e->semantic(sc); + return e; + } + + TemplateInstance *ti = s->isTemplateInstance(); + if (ti && !global.errors) + { if (!ti->semanticRun) + ti->semantic(sc); + s = ti->inst->toAlias(); + if (!s->isTemplateInstance()) + goto Lagain; + e = new ScopeExp(loc, ti); + e = e->semantic(sc); + return e; + } + + TemplateDeclaration *td = s->isTemplateDeclaration(); + if (td) + { +#if 0 // This was the fix for Bugzilla 6738, but it breaks 7498 + Dsymbol *p = td->toParent2(); + if (hasThis(sc) && p && p->isAggregateDeclaration()) + e = new DotTemplateExp(loc, new ThisExp(loc), td); + else +#endif + e = new TemplateExp(loc, td); + e = e->semantic(sc); + return e; + } + + error("%s '%s' is not a variable", s->kind(), s->toChars()); + return new ErrorExp(); +} + +char *DsymbolExp::toChars() +{ + return s->toChars(); +} + +void DsymbolExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(s->toChars()); +} + +#if DMDV2 +int DsymbolExp::isLvalue() +{ + return 1; +} +#endif + +Expression *DsymbolExp::toLvalue(Scope *sc, Expression *e) +{ +#if 0 + tym = tybasic(e1->ET->Tty); + if (!(tyscalar(tym) || + tym == TYstruct || + tym == TYarray && e->Eoper == TOKaddr)) + synerr(EM_lvalue); // lvalue expected +#endif + return this; +} + +/******************************** ThisExp **************************/ + +ThisExp::ThisExp(Loc loc) + : Expression(loc, TOKthis, sizeof(ThisExp)) +{ + //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum); + var = NULL; +} + +Expression *ThisExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("ThisExp::semantic()\n"); +#endif + if (type) + { //assert(global.errors || var); + return this; + } + + FuncDeclaration *fd = hasThis(sc); // fd is the uplevel function with the 'this' variable + + /* Special case for typeof(this) and typeof(super) since both + * should work even if they are not inside a non-static member function + */ + if (!fd && sc->intypeof) + { + // Find enclosing struct or class + for (Dsymbol *s = sc->getStructClassScope(); 1; s = s->parent) + { + if (!s) + { + error("%s is not in a class or struct scope", toChars()); + goto Lerr; + } + ClassDeclaration *cd = s->isClassDeclaration(); + if (cd) + { + type = cd->type; + return this; + } + StructDeclaration *sd = s->isStructDeclaration(); + if (sd) + { +#if STRUCTTHISREF + type = sd->type; +#else + type = sd->type->pointerTo(); +#endif + return this; + } + } + } + if (!fd) + goto Lerr; + + assert(fd->vthis); + var = fd->vthis; + assert(var->parent); + type = var->type; + var->isVarDeclaration()->checkNestedReference(sc, loc); + if (!sc->intypeof) + sc->callSuper |= CSXthis; + return this; + +Lerr: + error("'this' is only defined in non-static member functions, not %s", sc->parent->toChars()); + return new ErrorExp(); +} + +int ThisExp::isBool(int result) +{ + return result ? TRUE : FALSE; +} + +void ThisExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("this"); +} + +#if DMDV2 +int ThisExp::isLvalue() +{ + return 1; +} +#endif + +Expression *ThisExp::toLvalue(Scope *sc, Expression *e) +{ + return this; +} + +/******************************** SuperExp **************************/ + +SuperExp::SuperExp(Loc loc) + : ThisExp(loc) +{ + op = TOKsuper; +} + +Expression *SuperExp::semantic(Scope *sc) +{ + ClassDeclaration *cd; + Dsymbol *s; + +#if LOGSEMANTIC + printf("SuperExp::semantic('%s')\n", toChars()); +#endif + if (type) + return this; + + FuncDeclaration *fd = hasThis(sc); + + /* Special case for typeof(this) and typeof(super) since both + * should work even if they are not inside a non-static member function + */ + if (!fd && sc->intypeof) + { + // Find enclosing class + for (Dsymbol *s = sc->getStructClassScope(); 1; s = s->parent) + { + if (!s) + { + error("%s is not in a class scope", toChars()); + goto Lerr; + } + ClassDeclaration *cd = s->isClassDeclaration(); + if (cd) + { + cd = cd->baseClass; + if (!cd) + { error("class %s has no 'super'", s->toChars()); + goto Lerr; + } + type = cd->type; + return this; + } + } + } + if (!fd) + goto Lerr; + + assert(fd->vthis); + var = fd->vthis; + assert(var->parent); + + s = fd->toParent(); + while (s && s->isTemplateInstance()) + s = s->toParent(); + assert(s); + cd = s->isClassDeclaration(); +//printf("parent is %s %s\n", fd->toParent()->kind(), fd->toParent()->toChars()); + if (!cd) + goto Lerr; + if (!cd->baseClass) + { + error("no base class for %s", cd->toChars()); + type = fd->vthis->type; + } + else + { + type = cd->baseClass->type; + type = type->castMod(var->type->mod); + } + + var->isVarDeclaration()->checkNestedReference(sc, loc); + + if (!sc->intypeof) + sc->callSuper |= CSXsuper; + return this; + + +Lerr: + error("'super' is only allowed in non-static class member functions"); + return new ErrorExp(); +} + +void SuperExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("super"); +} + + +/******************************** NullExp **************************/ + +NullExp::NullExp(Loc loc, Type *type) + : Expression(loc, TOKnull, sizeof(NullExp)) +{ + committed = 0; + this->type = type; +} + +int NullExp::equals(Object *o) +{ + if (o && o->dyncast() == DYNCAST_EXPRESSION) + { Expression *e = (Expression *)o; + + if (e->op == TOKnull) + return TRUE; + } + return FALSE; +} + +Expression *NullExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("NullExp::semantic('%s')\n", toChars()); +#endif + // NULL is the same as (void *)0 + if (!type) + type = Type::tnull; + return this; +} + +int NullExp::isBool(int result) +{ + return result ? FALSE : TRUE; +} + +StringExp *NullExp::toString() +{ + if (implicitConvTo(Type::tstring)) + { + StringExp *se = new StringExp(loc, (char*)mem.calloc(1, 1), 0); + se->type = Type::tstring; + return se; + } + return NULL; +} + +void NullExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("null"); +} + +void NullExp::toMangleBuffer(OutBuffer *buf) +{ + buf->writeByte('n'); +} + +/******************************** StringExp **************************/ + +StringExp::StringExp(Loc loc, char *string) + : Expression(loc, TOKstring, sizeof(StringExp)) +{ + this->string = string; + this->len = strlen(string); + this->sz = 1; + this->committed = 0; + this->postfix = 0; + this->ownedByCtfe = false; +} + +StringExp::StringExp(Loc loc, void *string, size_t len) + : Expression(loc, TOKstring, sizeof(StringExp)) +{ + this->string = string; + this->len = len; + this->sz = 1; + this->committed = 0; + this->postfix = 0; + this->ownedByCtfe = false; +} + +StringExp::StringExp(Loc loc, void *string, size_t len, unsigned char postfix) + : Expression(loc, TOKstring, sizeof(StringExp)) +{ + this->string = string; + this->len = len; + this->sz = 1; + this->committed = 0; + this->postfix = postfix; + this->ownedByCtfe = false; +} + +#if 0 +Expression *StringExp::syntaxCopy() +{ + printf("StringExp::syntaxCopy() %s\n", toChars()); + return copy(); +} +#endif + +int StringExp::equals(Object *o) +{ + //printf("StringExp::equals('%s') %s\n", o->toChars(), toChars()); + if (o && o->dyncast() == DYNCAST_EXPRESSION) + { Expression *e = (Expression *)o; + + if (e->op == TOKstring) + { + return compare(o) == 0; + } + } + return FALSE; +} + +char *StringExp::toChars() +{ + OutBuffer buf; + HdrGenState hgs; + char *p; + + memset(&hgs, 0, sizeof(hgs)); + toCBuffer(&buf, &hgs); + buf.writeByte(0); + p = (char *)buf.data; + buf.data = NULL; + return p; +} + +Expression *StringExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("StringExp::semantic() %s\n", toChars()); +#endif + if (!type) + { OutBuffer buffer; + size_t newlen = 0; + const char *p; + size_t u; + unsigned c; + + switch (postfix) + { + case 'd': + for (u = 0; u < len;) + { + p = utf_decodeChar((unsigned char *)string, len, &u, &c); + if (p) + { error("%s", p); + return new ErrorExp(); + } + else + { buffer.write4(c); + newlen++; + } + } + buffer.write4(0); + string = buffer.extractData(); + len = newlen; + sz = 4; + //type = new TypeSArray(Type::tdchar, new IntegerExp(loc, len, Type::tindex)); + type = new TypeDArray(Type::tdchar->invariantOf()); + committed = 1; + break; + + case 'w': + for (u = 0; u < len;) + { + p = utf_decodeChar((unsigned char *)string, len, &u, &c); + if (p) + { error("%s", p); + return new ErrorExp(); + } + else + { buffer.writeUTF16(c); + newlen++; + if (c >= 0x10000) + newlen++; + } + } + buffer.writeUTF16(0); + string = buffer.extractData(); + len = newlen; + sz = 2; + //type = new TypeSArray(Type::twchar, new IntegerExp(loc, len, Type::tindex)); + type = new TypeDArray(Type::twchar->invariantOf()); + committed = 1; + break; + + case 'c': + committed = 1; + default: + //type = new TypeSArray(Type::tchar, new IntegerExp(loc, len, Type::tindex)); + type = new TypeDArray(Type::tchar->invariantOf()); + break; + } + type = type->semantic(loc, sc); + //type = type->invariantOf(); + //printf("type = %s\n", type->toChars()); + } + return this; +} + +/********************************** + * Return length of string. + */ + +size_t StringExp::length() +{ + size_t result = 0; + dchar_t c; + const char *p; + + switch (sz) + { + case 1: + for (size_t u = 0; u < len;) + { + p = utf_decodeChar((unsigned char *)string, len, &u, &c); + if (p) + { error("%s", p); + return 0; + } + else + result++; + } + break; + + case 2: + for (size_t u = 0; u < len;) + { + p = utf_decodeWchar((unsigned short *)string, len, &u, &c); + if (p) + { error("%s", p); + return 0; + } + else + result++; + } + break; + + case 4: + result = len; + break; + + default: + assert(0); + } + return result; +} + +StringExp *StringExp::toString() +{ + return this; +} + +/**************************************** + * Convert string to char[]. + */ + +StringExp *StringExp::toUTF8(Scope *sc) +{ + if (sz != 1) + { // Convert to UTF-8 string + committed = 0; + Expression *e = castTo(sc, Type::tchar->arrayOf()); + e = e->optimize(WANTvalue); + assert(e->op == TOKstring); + StringExp *se = (StringExp *)e; + assert(se->sz == 1); + return se; + } + return this; +} + +int StringExp::compare(Object *obj) +{ + //printf("StringExp::compare()\n"); + // Used to sort case statement expressions so we can do an efficient lookup + StringExp *se2 = (StringExp *)(obj); + + // This is a kludge so isExpression() in template.c will return 5 + // for StringExp's. + if (!se2) + return 5; + + assert(se2->op == TOKstring); + + int len1 = len; + int len2 = se2->len; + + //printf("sz = %d, len1 = %d, len2 = %d\n", sz, len1, len2); + if (len1 == len2) + { + switch (sz) + { + case 1: + return memcmp((char *)string, (char *)se2->string, len1); + + case 2: + { unsigned u; + d_wchar *s1 = (d_wchar *)string; + d_wchar *s2 = (d_wchar *)se2->string; + + for (u = 0; u < len; u++) + { + if (s1[u] != s2[u]) + return s1[u] - s2[u]; + } + } + + case 4: + { unsigned u; + d_dchar *s1 = (d_dchar *)string; + d_dchar *s2 = (d_dchar *)se2->string; + + for (u = 0; u < len; u++) + { + if (s1[u] != s2[u]) + return s1[u] - s2[u]; + } + } + break; + + default: + assert(0); + } + } + return len1 - len2; +} + +int StringExp::isBool(int result) +{ + return result ? TRUE : FALSE; +} + +#if DMDV2 +int StringExp::isLvalue() +{ + /* string literal is rvalue in default, but + * conversion to reference of static array is only allowed. + */ + return 0; +} +#endif + +Expression *StringExp::toLvalue(Scope *sc, Expression *e) +{ + //printf("StringExp::toLvalue(%s)\n", toChars()); + return this; +} + +Expression *StringExp::modifiableLvalue(Scope *sc, Expression *e) +{ + error("Cannot modify '%s'", toChars()); + return new ErrorExp(); +} + +unsigned StringExp::charAt(size_t i) +{ unsigned value; + + switch (sz) + { + case 1: + value = ((unsigned char *)string)[i]; + break; + + case 2: + value = ((unsigned short *)string)[i]; + break; + + case 4: + value = ((unsigned int *)string)[i]; + break; + + default: + assert(0); + break; + } + return value; +} + +void StringExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writeByte('"'); + unsigned o = buf->offset; + for (size_t i = 0; i < len; i++) + { unsigned c = charAt(i); + + switch (c) + { + case '"': + case '\\': + if (!hgs->console) + buf->writeByte('\\'); + default: + if (c <= 0xFF) + { if (c <= 0x7F && (isprint(c) || hgs->console)) + buf->writeByte(c); + else + buf->printf("\\x%02x", c); + } + else if (c <= 0xFFFF) + buf->printf("\\x%02x\\x%02x", c & 0xFF, c >> 8); + else + buf->printf("\\x%02x\\x%02x\\x%02x\\x%02x", + c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF, c >> 24); + break; + } + } + if (hgs->ddoc) + escapeDdocString(buf, o); + buf->writeByte('"'); + if (postfix) + buf->writeByte(postfix); +} + +void StringExp::toMangleBuffer(OutBuffer *buf) +{ char m; + OutBuffer tmp; + const char *p; + unsigned c; + size_t u; + unsigned char *q; + unsigned qlen; + + /* Write string in UTF-8 format + */ + switch (sz) + { case 1: + m = 'a'; + q = (unsigned char *)string; + qlen = len; + break; + case 2: + m = 'w'; + for (u = 0; u < len; ) + { + p = utf_decodeWchar((unsigned short *)string, len, &u, &c); + if (p) + error("%s", p); + else + tmp.writeUTF8(c); + } + q = tmp.data; + qlen = tmp.offset; + break; + case 4: + m = 'd'; + for (u = 0; u < len; u++) + { + c = ((unsigned *)string)[u]; + if (!utf_isValidDchar(c)) + error("invalid UCS-32 char \\U%08x", c); + else + tmp.writeUTF8(c); + } + q = tmp.data; + qlen = tmp.offset; + break; + default: + assert(0); + } + buf->reserve(1 + 11 + 2 * qlen); + buf->writeByte(m); + buf->printf("%d_", qlen); // nbytes <= 11 + + for (unsigned char *p = buf->data + buf->offset, *pend = p + 2 * qlen; + p < pend; p += 2, ++q) + { + unsigned char hi = *q >> 4 & 0xF; + p[0] = (hi < 10 ? hi + '0' : hi - 10 + 'a'); + unsigned char lo = *q & 0xF; + p[1] = (lo < 10 ? lo + '0' : lo - 10 + 'a'); + } + buf->offset += 2 * qlen; +} + +/************************ ArrayLiteralExp ************************************/ + +// [ e1, e2, e3, ... ] + +ArrayLiteralExp::ArrayLiteralExp(Loc loc, Expressions *elements) + : Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp)) +{ + this->elements = elements; + this->ownedByCtfe = false; +} + +ArrayLiteralExp::ArrayLiteralExp(Loc loc, Expression *e) + : Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp)) +{ + elements = new Expressions; + elements->push(e); +} + +Expression *ArrayLiteralExp::syntaxCopy() +{ + return new ArrayLiteralExp(loc, arraySyntaxCopy(elements)); +} + +Expression *ArrayLiteralExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("ArrayLiteralExp::semantic('%s')\n", toChars()); +#endif + if (type) + return this; + + /* Perhaps an empty array literal [ ] should be rewritten as null? + */ + + arrayExpressionSemantic(elements, sc); // run semantic() on each element + expandTuples(elements); + + Type *t0; + elements = arrayExpressionToCommonType(sc, elements, &t0); + + type = t0->arrayOf(); + //type = new TypeSArray(t0, new IntegerExp(elements->dim)); + type = type->semantic(loc, sc); + + /* Disallow array literals of type void being used. + */ + if (elements->dim > 0 && t0->ty == Tvoid) + { error("%s of type %s has no value", toChars(), type->toChars()); + return new ErrorExp(); + } + + return this; +} + +int ArrayLiteralExp::isBool(int result) +{ + size_t dim = elements ? elements->dim : 0; + return result ? (dim != 0) : (dim == 0); +} + +StringExp *ArrayLiteralExp::toString() +{ + TY telem = type->nextOf()->toBasetype()->ty; + + if (telem == Tchar || telem == Twchar || telem == Tdchar || + (telem == Tvoid && (!elements || elements->dim == 0))) + { + OutBuffer buf; + if (elements) + for (int i = 0; i < elements->dim; ++i) + { + Expression *ch = elements->tdata()[i]; + if (ch->op != TOKint64) + return NULL; + buf.writedchar(ch->toInteger()); + } + buf.writebyte(0); + + char prefix = 'c'; + if (telem == Twchar) prefix = 'w'; + else if (telem == Tdchar) prefix = 'd'; + + StringExp *se = new StringExp(loc, buf.extractData(), buf.size - 1, prefix); + se->type = type; + return se; + } + return NULL; +} + +void ArrayLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writeByte('['); + argsToCBuffer(buf, elements, hgs); + buf->writeByte(']'); +} + +void ArrayLiteralExp::toMangleBuffer(OutBuffer *buf) +{ + size_t dim = elements ? elements->dim : 0; + buf->printf("A%u", dim); + for (size_t i = 0; i < dim; i++) + { Expression *e = elements->tdata()[i]; + e->toMangleBuffer(buf); + } +} + +/************************ AssocArrayLiteralExp ************************************/ + +// [ key0 : value0, key1 : value1, ... ] + +AssocArrayLiteralExp::AssocArrayLiteralExp(Loc loc, + Expressions *keys, Expressions *values) + : Expression(loc, TOKassocarrayliteral, sizeof(AssocArrayLiteralExp)) +{ + assert(keys->dim == values->dim); + this->keys = keys; + this->values = values; + this->ownedByCtfe = false; +} + +Expression *AssocArrayLiteralExp::syntaxCopy() +{ + return new AssocArrayLiteralExp(loc, + arraySyntaxCopy(keys), arraySyntaxCopy(values)); +} + +Expression *AssocArrayLiteralExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("AssocArrayLiteralExp::semantic('%s')\n", toChars()); +#endif + + if (type) + return this; + + // Run semantic() on each element + arrayExpressionSemantic(keys, sc); + arrayExpressionSemantic(values, sc); + expandTuples(keys); + expandTuples(values); + if (keys->dim != values->dim) + { + error("number of keys is %u, must match number of values %u", keys->dim, values->dim); + return new ErrorExp(); + } + + Type *tkey = NULL; + Type *tvalue = NULL; + keys = arrayExpressionToCommonType(sc, keys, &tkey); + values = arrayExpressionToCommonType(sc, values, &tvalue); + + type = new TypeAArray(tvalue, tkey); + type = type->semantic(loc, sc); + return this; +} + + +int AssocArrayLiteralExp::isBool(int result) +{ + size_t dim = keys->dim; + return result ? (dim != 0) : (dim == 0); +} + +void AssocArrayLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writeByte('['); + for (size_t i = 0; i < keys->dim; i++) + { Expression *key = keys->tdata()[i]; + Expression *value = values->tdata()[i]; + + if (i) + buf->writeByte(','); + expToCBuffer(buf, hgs, key, PREC_assign); + buf->writeByte(':'); + expToCBuffer(buf, hgs, value, PREC_assign); + } + buf->writeByte(']'); +} + +void AssocArrayLiteralExp::toMangleBuffer(OutBuffer *buf) +{ + size_t dim = keys->dim; + buf->printf("A%u", dim); + for (size_t i = 0; i < dim; i++) + { Expression *key = keys->tdata()[i]; + Expression *value = values->tdata()[i]; + + key->toMangleBuffer(buf); + value->toMangleBuffer(buf); + } +} + +/************************ StructLiteralExp ************************************/ + +// sd( e1, e2, e3, ... ) + +StructLiteralExp::StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions *elements, Type *stype) + : Expression(loc, TOKstructliteral, sizeof(StructLiteralExp)) +{ + this->sd = sd; + this->elements = elements; + this->stype = stype; + this->sym = NULL; + this->soffset = 0; + this->fillHoles = 1; + this->ownedByCtfe = false; +} + +Expression *StructLiteralExp::syntaxCopy() +{ + return new StructLiteralExp(loc, sd, arraySyntaxCopy(elements), stype); +} + +Expression *StructLiteralExp::semantic(Scope *sc) +{ Expression *e; + size_t nfields = sd->fields.dim - sd->isnested; + +#if LOGSEMANTIC + printf("StructLiteralExp::semantic('%s')\n", toChars()); +#endif + if (type) + return this; + + elements = arrayExpressionSemantic(elements, sc); // run semantic() on each element + expandTuples(elements); + size_t offset = 0; + for (size_t i = 0; i < elements->dim; i++) + { e = elements->tdata()[i]; + if (!e) + continue; + + e = resolveProperties(sc, e); + if (i >= nfields) + { error("more initializers than fields of %s", sd->toChars()); + return new ErrorExp(); + } + Dsymbol *s = sd->fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + if (v->offset < offset) + { error("overlapping initialization for %s", v->toChars()); + return new ErrorExp(); + } + offset = v->offset + v->type->size(); + + Type *telem = v->type; + if (stype) + telem = telem->addMod(stype->mod); + while (!e->implicitConvTo(telem) && telem->toBasetype()->ty == Tsarray) + { /* Static array initialization, as in: + * T[3][5] = e; + */ + telem = telem->toBasetype()->nextOf(); + } + + if (e->op == TOKfunction) + { e = ((FuncExp *)e)->inferType(sc, telem); + if (!e) + { error("cannot infer function literal type from %s", telem->toChars()); + e = new ErrorExp(); + } + } + + e = e->implicitCastTo(sc, telem); + + elements->tdata()[i] = e; + } + + /* Fill out remainder of elements[] with default initializers for fields[] + */ + for (size_t i = elements->dim; i < nfields; i++) + { Dsymbol *s = sd->fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + assert(!v->isThisDeclaration()); + + if (v->offset < offset) + { e = NULL; + sd->hasUnions = 1; + } + else + { + if (v->init) + { if (v->init->isVoidInitializer()) + e = NULL; + else + { e = v->init->toExpression(); + if (!e) + { error("cannot make expression out of initializer for %s", v->toChars()); + return new ErrorExp(); + } + else if (v->scope) + { // Do deferred semantic analysis + Initializer *i2 = v->init->syntaxCopy(); + i2 = i2->semantic(v->scope, v->type, WANTinterpret); + e = i2->toExpression(); + // remove v->scope (see bug 3426) + // but not if gagged, for we might be called again. + if (!global.gag) + v->scope = NULL; + } + } + } + else + e = v->type->defaultInitLiteral(loc); + offset = v->offset + v->type->size(); + } + elements->push(e); + } + + type = stype ? stype : sd->type; + + /* If struct requires a destructor, rewrite as: + * (S tmp = S()),tmp + * so that the destructor can be hung on tmp. + */ + if (sd->dtor && sc->func) + { + Identifier *idtmp = Lexer::uniqueId("__sl"); + VarDeclaration *tmp = new VarDeclaration(loc, type, idtmp, new ExpInitializer(0, this)); + tmp->storage_class |= STCctfe; + Expression *ae = new DeclarationExp(loc, tmp); + Expression *e = new CommaExp(loc, ae, new VarExp(loc, tmp)); + e = e->semantic(sc); + return e; + } + + return this; +} + +/************************************** + * Gets expression at offset of type. + * Returns NULL if not found. + */ + +Expression *StructLiteralExp::getField(Type *type, unsigned offset) +{ + //printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n", +// /*toChars()*/"", type->toChars(), offset); + Expression *e = NULL; + int i = getFieldIndex(type, offset); + + if (i != -1) + { + //printf("\ti = %d\n", i); + assert(i < elements->dim); + e = elements->tdata()[i]; + if (e) + { + //printf("e = %s, e->type = %s\n", e->toChars(), e->type->toChars()); + + /* If type is a static array, and e is an initializer for that array, + * then the field initializer should be an array literal of e. + */ + if (e->type->castMod(0) != type->castMod(0) && type->ty == Tsarray) + { TypeSArray *tsa = (TypeSArray *)type; + uinteger_t length = tsa->dim->toInteger(); + Expressions *z = new Expressions; + z->setDim(length); + for (int q = 0; q < length; ++q) + z->tdata()[q] = e->copy(); + e = new ArrayLiteralExp(loc, z); + e->type = type; + } + else + { + e = e->copy(); + e->type = type; + } + } + } + return e; +} + +/************************************ + * Get index of field. + * Returns -1 if not found. + */ + +int StructLiteralExp::getFieldIndex(Type *type, unsigned offset) +{ + /* Find which field offset is by looking at the field offsets + */ + if (elements->dim) + { + for (size_t i = 0; i < sd->fields.dim; i++) + { + Dsymbol *s = sd->fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + + if (offset == v->offset && + type->size() == v->type->size()) + { Expression *e = elements->tdata()[i]; + if (e) + { + return i; + } + break; + } + } + } + return -1; +} + +#if DMDV2 +int StructLiteralExp::isLvalue() +{ + return 1; +} +#endif + +Expression *StructLiteralExp::toLvalue(Scope *sc, Expression *e) +{ + return this; +} + + +void StructLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(sd->toChars()); + buf->writeByte('('); + argsToCBuffer(buf, elements, hgs); + buf->writeByte(')'); +} + +void StructLiteralExp::toMangleBuffer(OutBuffer *buf) +{ + size_t dim = elements ? elements->dim : 0; + buf->printf("S%u", dim); + for (size_t i = 0; i < dim; i++) + { Expression *e = elements->tdata()[i]; + if (e) + e->toMangleBuffer(buf); + else + buf->writeByte('v'); // 'v' for void + } +} + +/************************ TypeDotIdExp ************************************/ + +/* Things like: + * int.size + * foo.size + * (foo).size + * cast(foo).size + */ + +Expression *typeDotIdExp(Loc loc, Type *type, Identifier *ident) +{ + return new DotIdExp(loc, new TypeExp(loc, type), ident); +} + + +/************************************************************/ + +// Mainly just a placeholder + +TypeExp::TypeExp(Loc loc, Type *type) + : Expression(loc, TOKtype, sizeof(TypeExp)) +{ + //printf("TypeExp::TypeExp(%s)\n", type->toChars()); + this->type = type; +} + +Expression *TypeExp::syntaxCopy() +{ + //printf("TypeExp::syntaxCopy()\n"); + return new TypeExp(loc, type->syntaxCopy()); +} + +Expression *TypeExp::semantic(Scope *sc) +{ + //printf("TypeExp::semantic(%s)\n", type->toChars()); + type = type->semantic(loc, sc); + return this; +} + +int TypeExp::rvalue() +{ + error("type %s has no value", toChars()); + return 0; +} + +void TypeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + type->toCBuffer(buf, NULL, hgs); +} + +/************************************************************/ + +// Mainly just a placeholder + +ScopeExp::ScopeExp(Loc loc, ScopeDsymbol *pkg) + : Expression(loc, TOKimport, sizeof(ScopeExp)) +{ + //printf("ScopeExp::ScopeExp(pkg = '%s')\n", pkg->toChars()); + //static int count; if (++count == 38) *(char*)0=0; + this->sds = pkg; +} + +Expression *ScopeExp::syntaxCopy() +{ + ScopeExp *se = new ScopeExp(loc, (ScopeDsymbol *)sds->syntaxCopy(NULL)); + return se; +} + +Expression *ScopeExp::semantic(Scope *sc) +{ + TemplateInstance *ti; + ScopeDsymbol *sds2; + +#if LOGSEMANTIC + printf("+ScopeExp::semantic('%s')\n", toChars()); +#endif +Lagain: + ti = sds->isTemplateInstance(); + if (ti && !global.errors) + { + if (!ti->semanticRun) + ti->semantic(sc); + if (ti->inst) + { + Dsymbol *s = ti->inst->toAlias(); + sds2 = s->isScopeDsymbol(); + if (!sds2) + { Expression *e; + + //printf("s = %s, '%s'\n", s->kind(), s->toChars()); + if (ti->withsym) + { + // Same as wthis.s + e = new VarExp(loc, ti->withsym->withstate->wthis); + e = new DotVarExp(loc, e, s->isDeclaration()); + } + else + e = new DsymbolExp(loc, s); + e = e->semantic(sc); + //printf("-1ScopeExp::semantic()\n"); + return e; + } + if (sds2 != sds) + { + sds = sds2; + goto Lagain; + } + //printf("sds = %s, '%s'\n", sds->kind(), sds->toChars()); + } + if (global.errors) + return new ErrorExp(); + } + else + { + //printf("sds = %s, '%s'\n", sds->kind(), sds->toChars()); + //printf("\tparent = '%s'\n", sds->parent->toChars()); + sds->semantic(sc); + + AggregateDeclaration *ad = sds->isAggregateDeclaration(); + if (ad) + return (new TypeExp(loc, ad->type))->semantic(sc); + } + type = Type::tvoid; + //printf("-2ScopeExp::semantic() %s\n", toChars()); + return this; +} + +void ScopeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (sds->isTemplateInstance()) + { + sds->toCBuffer(buf, hgs); + } + else if (hgs != NULL && hgs->ddoc) + { // fixes bug 6491 + Module *module = sds->isModule(); + if (module) + buf->writestring(module->md->toChars()); + else + buf->writestring(sds->toChars()); + } + else + { + buf->writestring(sds->kind()); + buf->writestring(" "); + buf->writestring(sds->toChars()); + } +} + +/********************** TemplateExp **************************************/ + +// Mainly just a placeholder + +TemplateExp::TemplateExp(Loc loc, TemplateDeclaration *td) + : Expression(loc, TOKtemplate, sizeof(TemplateExp)) +{ + //printf("TemplateExp(): %s\n", td->toChars()); + this->td = td; +} + +void TemplateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(td->toChars()); +} + +int TemplateExp::rvalue() +{ + error("template %s has no value", toChars()); + return 0; +} + +/********************** NewExp **************************************/ + +/* thisexp.new(newargs) newtype(arguments) */ + +NewExp::NewExp(Loc loc, Expression *thisexp, Expressions *newargs, + Type *newtype, Expressions *arguments) + : Expression(loc, TOKnew, sizeof(NewExp)) +{ + this->thisexp = thisexp; + this->newargs = newargs; + this->newtype = newtype; + this->arguments = arguments; + member = NULL; + allocator = NULL; + onstack = 0; +} + +Expression *NewExp::syntaxCopy() +{ + return new NewExp(loc, + thisexp ? thisexp->syntaxCopy() : NULL, + arraySyntaxCopy(newargs), + newtype->syntaxCopy(), arraySyntaxCopy(arguments)); +} + + +Expression *NewExp::semantic(Scope *sc) +{ + Type *tb; + ClassDeclaration *cdthis = NULL; + +#if LOGSEMANTIC + printf("NewExp::semantic() %s\n", toChars()); + if (thisexp) + printf("\tthisexp = %s\n", thisexp->toChars()); + printf("\tnewtype: %s\n", newtype->toChars()); +#endif + if (type) // if semantic() already run + return this; + +Lagain: + if (thisexp) + { thisexp = thisexp->semantic(sc); + cdthis = thisexp->type->isClassHandle(); + if (cdthis) + { + sc = sc->push(cdthis); + type = newtype->semantic(loc, sc); + sc = sc->pop(); + } + else + { + error("'this' for nested class must be a class type, not %s", thisexp->type->toChars()); + goto Lerr; + } + } + else + type = newtype->semantic(loc, sc); + newtype = type; // in case type gets cast to something else + tb = type->toBasetype(); + //printf("tb: %s, deco = %s\n", tb->toChars(), tb->deco); + + arrayExpressionSemantic(newargs, sc); + preFunctionParameters(loc, sc, newargs); + arrayExpressionSemantic(arguments, sc); + preFunctionParameters(loc, sc, arguments); + + if (thisexp && tb->ty != Tclass) + { error("e.new is only for allocating nested classes, not %s", tb->toChars()); + goto Lerr; + } + + if (tb->ty == Tclass) + { + TypeClass *tc = (TypeClass *)(tb); + ClassDeclaration *cd = tc->sym->isClassDeclaration(); + if (cd->isInterfaceDeclaration()) + { error("cannot create instance of interface %s", cd->toChars()); + goto Lerr; + } + else if (cd->isAbstract()) + { error("cannot create instance of abstract class %s", cd->toChars()); + for (size_t i = 0; i < cd->vtbl.dim; i++) + { FuncDeclaration *fd = cd->vtbl.tdata()[i]->isFuncDeclaration(); + if (fd && fd->isAbstract()) + error("function %s is abstract", fd->toChars()); + } + goto Lerr; + } + + if (cd->noDefaultCtor && (!arguments || !arguments->dim)) + { error("default construction is disabled for type %s", cd->toChars()); + goto Lerr; + } + checkDeprecated(sc, cd); + if (cd->isNested()) + { /* We need a 'this' pointer for the nested class. + * Ensure we have the right one. + */ + Dsymbol *s = cd->toParent2(); + ClassDeclaration *cdn = s->isClassDeclaration(); + FuncDeclaration *fdn = s->isFuncDeclaration(); + + //printf("cd isNested, cdn = %s\n", cdn ? cdn->toChars() : "null"); + if (cdn) + { + if (!cdthis) + { + // Supply an implicit 'this' and try again + thisexp = new ThisExp(loc); + for (Dsymbol *sp = sc->parent; 1; sp = sp->parent) + { if (!sp) + { + error("outer class %s 'this' needed to 'new' nested class %s", cdn->toChars(), cd->toChars()); + goto Lerr; + } + ClassDeclaration *cdp = sp->isClassDeclaration(); + if (!cdp) + continue; + if (cdp == cdn || cdn->isBaseOf(cdp, NULL)) + break; + // Add a '.outer' and try again + thisexp = new DotIdExp(loc, thisexp, Id::outer); + } + if (!global.errors) + goto Lagain; + } + if (cdthis) + { + //printf("cdthis = %s\n", cdthis->toChars()); + if (cdthis != cdn && !cdn->isBaseOf(cdthis, NULL)) + { error("'this' for nested class must be of type %s, not %s", cdn->toChars(), thisexp->type->toChars()); + goto Lerr; + } + } +#if 0 + else + { + for (Dsymbol *sf = sc->func; 1; sf= sf->toParent2()->isFuncDeclaration()) + { + if (!sf) + { + error("outer class %s 'this' needed to 'new' nested class %s", cdn->toChars(), cd->toChars()); + goto Lerr; + } + printf("sf = %s\n", sf->toChars()); + AggregateDeclaration *ad = sf->isThis(); + if (ad && (ad == cdn || cdn->isBaseOf(ad->isClassDeclaration(), NULL))) + break; + } + } +#endif + } +#if 1 + else if (thisexp) + { error("e.new is only for allocating nested classes"); + goto Lerr; + } + else if (fdn) + { + // make sure the parent context fdn of cd is reachable from sc + for (Dsymbol *sp = sc->parent; 1; sp = sp->parent) + { + if (fdn == sp) + break; + FuncDeclaration *fsp = sp ? sp->isFuncDeclaration() : NULL; + if (!sp || (fsp && fsp->isStatic())) + { + error("outer function context of %s is needed to 'new' nested class %s", fdn->toPrettyChars(), cd->toPrettyChars()); + goto Lerr; + } + } + } +#else + else if (fdn) + { /* The nested class cd is nested inside a function, + * we'll let getEthis() look for errors. + */ + //printf("nested class %s is nested inside function %s, we're in %s\n", cd->toChars(), fdn->toChars(), sc->func->toChars()); + if (thisexp) + { // Because thisexp cannot be a function frame pointer + error("e.new is only for allocating nested classes"); + goto Lerr; + } + } +#endif + else + assert(0); + } + else if (thisexp) + { error("e.new is only for allocating nested classes"); + goto Lerr; + } + + FuncDeclaration *f = NULL; + if (cd->ctor) + f = resolveFuncCall(sc, loc, cd->ctor, NULL, NULL, arguments, 0); + if (f) + { + checkDeprecated(sc, f); + member = f->isCtorDeclaration(); + assert(member); + + cd->accessCheck(loc, sc, member); + + TypeFunction *tf = (TypeFunction *)f->type; + + if (!arguments) + arguments = new Expressions(); + unsigned olderrors = global.errors; + functionParameters(loc, sc, tf, NULL, arguments, f); + if (olderrors != global.errors) + return new ErrorExp(); + type = type->addMod(tf->nextOf()->mod); + } + else + { + if (arguments && arguments->dim) + { error("no constructor for %s", cd->toChars()); + goto Lerr; + } + } + + if (cd->aggNew) + { + // Prepend the size argument to newargs[] + Expression *e = new IntegerExp(loc, cd->size(loc), Type::tsize_t); + if (!newargs) + newargs = new Expressions(); + newargs->shift(e); + + f = cd->aggNew->overloadResolve(loc, NULL, newargs); + allocator = f->isNewDeclaration(); + assert(allocator); + + TypeFunction *tf = (TypeFunction *)f->type; + unsigned olderrors = global.errors; + functionParameters(loc, sc, tf, NULL, newargs, f); + if (olderrors != global.errors) + return new ErrorExp(); + + } + else + { + if (newargs && newargs->dim) + { error("no allocator for %s", cd->toChars()); + goto Lerr; + } + } + } + else if (tb->ty == Tstruct) + { + TypeStruct *ts = (TypeStruct *)tb; + StructDeclaration *sd = ts->sym; + TypeFunction *tf; + + if (sd->noDefaultCtor && (!arguments || !arguments->dim)) + { error("default construction is disabled for type %s", sd->toChars()); + goto Lerr; + } + FuncDeclaration *f = NULL; + if (sd->ctor) + f = resolveFuncCall(sc, loc, sd->ctor, NULL, NULL, arguments, 0); + if (f) + { + checkDeprecated(sc, f); + member = f->isCtorDeclaration(); + assert(member); + + sd->accessCheck(loc, sc, member); + + tf = (TypeFunction *)f->type; + type = tf->next; + + if (!arguments) + arguments = new Expressions(); + unsigned olderrors = global.errors; + functionParameters(loc, sc, tf, NULL, arguments, f); + if (olderrors != global.errors) + return new ErrorExp(); + + } + else + { + if (arguments && arguments->dim) + { error("no constructor for %s", sd->toChars()); + goto Lerr; + } + } + + + if (sd->aggNew) + { + // Prepend the uint size argument to newargs[] + Expression *e = new IntegerExp(loc, sd->size(loc), Type::tuns32); + if (!newargs) + newargs = new Expressions(); + newargs->shift(e); + + f = sd->aggNew->overloadResolve(loc, NULL, newargs); + allocator = f->isNewDeclaration(); + assert(allocator); + + tf = (TypeFunction *)f->type; + unsigned olderrors = global.errors; + functionParameters(loc, sc, tf, NULL, newargs, f); + if (olderrors != global.errors) + return new ErrorExp(); + +#if 0 + e = new VarExp(loc, f); + e = new CallExp(loc, e, newargs); + e = e->semantic(sc); + e->type = type->pointerTo(); + return e; +#endif + } + else + { + if (newargs && newargs->dim) + { error("no allocator for %s", sd->toChars()); + goto Lerr; + } + } + + type = type->pointerTo(); + } + else if (tb->ty == Tarray && (arguments && arguments->dim)) + { + for (size_t i = 0; i < arguments->dim; i++) + { + if (tb->ty != Tarray) + { error("too many arguments for array"); + goto Lerr; + } + + Expression *arg = arguments->tdata()[i]; + arg = resolveProperties(sc, arg); + arg = arg->implicitCastTo(sc, Type::tsize_t); + arg = arg->optimize(WANTvalue); + if (arg->op == TOKint64 && (sinteger_t)arg->toInteger() < 0) + { error("negative array index %s", arg->toChars()); + goto Lerr; + } + arguments->tdata()[i] = arg; + tb = ((TypeDArray *)tb)->next->toBasetype(); + } + } + else if (tb->isscalar()) + { + if (arguments && arguments->dim) + { error("no constructor for %s", type->toChars()); + goto Lerr; + } + + type = type->pointerTo(); + } + else + { + error("new can only create structs, dynamic arrays or class objects, not %s's", type->toChars()); + goto Lerr; + } + +//printf("NewExp: '%s'\n", toChars()); +//printf("NewExp:type '%s'\n", type->toChars()); + + return this; + +Lerr: + return new ErrorExp(); +} + + +void NewExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (thisexp) + { expToCBuffer(buf, hgs, thisexp, PREC_primary); + buf->writeByte('.'); + } + buf->writestring("new "); + if (newargs && newargs->dim) + { + buf->writeByte('('); + argsToCBuffer(buf, newargs, hgs); + buf->writeByte(')'); + } + newtype->toCBuffer(buf, NULL, hgs); + if (arguments && arguments->dim) + { + buf->writeByte('('); + argsToCBuffer(buf, arguments, hgs); + buf->writeByte(')'); + } +} + +/********************** NewAnonClassExp **************************************/ + +NewAnonClassExp::NewAnonClassExp(Loc loc, Expression *thisexp, + Expressions *newargs, ClassDeclaration *cd, Expressions *arguments) + : Expression(loc, TOKnewanonclass, sizeof(NewAnonClassExp)) +{ + this->thisexp = thisexp; + this->newargs = newargs; + this->cd = cd; + this->arguments = arguments; +} + +Expression *NewAnonClassExp::syntaxCopy() +{ + return new NewAnonClassExp(loc, + thisexp ? thisexp->syntaxCopy() : NULL, + arraySyntaxCopy(newargs), + (ClassDeclaration *)cd->syntaxCopy(NULL), + arraySyntaxCopy(arguments)); +} + + +Expression *NewAnonClassExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("NewAnonClassExp::semantic() %s\n", toChars()); + //printf("thisexp = %p\n", thisexp); + //printf("type: %s\n", type->toChars()); +#endif + + Expression *d = new DeclarationExp(loc, cd); + d = d->semantic(sc); + + Expression *n = new NewExp(loc, thisexp, newargs, cd->type, arguments); + + Expression *c = new CommaExp(loc, d, n); + return c->semantic(sc); +} + + +void NewAnonClassExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (thisexp) + { expToCBuffer(buf, hgs, thisexp, PREC_primary); + buf->writeByte('.'); + } + buf->writestring("new"); + if (newargs && newargs->dim) + { + buf->writeByte('('); + argsToCBuffer(buf, newargs, hgs); + buf->writeByte(')'); + } + buf->writestring(" class "); + if (arguments && arguments->dim) + { + buf->writeByte('('); + argsToCBuffer(buf, arguments, hgs); + buf->writeByte(')'); + } + //buf->writestring(" { }"); + if (cd) + { + cd->toCBuffer(buf, hgs); + } +} + +/********************** SymbolExp **************************************/ + +#if DMDV2 +SymbolExp::SymbolExp(Loc loc, enum TOK op, int size, Declaration *var, int hasOverloads) + : Expression(loc, op, size) +{ + assert(var); + this->var = var; + this->hasOverloads = hasOverloads; +} +#endif + +/********************** SymOffExp **************************************/ + +SymOffExp::SymOffExp(Loc loc, Declaration *var, unsigned offset, int hasOverloads) + : SymbolExp(loc, TOKsymoff, sizeof(SymOffExp), var, hasOverloads) +{ + this->offset = offset; + VarDeclaration *v = var->isVarDeclaration(); + if (v && v->needThis()) + error("need 'this' for address of %s", v->toChars()); +} + +Expression *SymOffExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("SymOffExp::semantic('%s')\n", toChars()); +#endif + //var->semantic(sc); + if (!type) + type = var->type->pointerTo(); + VarDeclaration *v = var->isVarDeclaration(); + if (v) + v->checkNestedReference(sc, loc); + FuncDeclaration *f = var->isFuncDeclaration(); + if (f) + f->checkNestedReference(sc, loc); + return this; +} + +int SymOffExp::isBool(int result) +{ + return result ? TRUE : FALSE; +} + +void SymOffExp::checkEscape() +{ + VarDeclaration *v = var->isVarDeclaration(); + if (v) + { + if (!v->isDataseg() && !(v->storage_class & (STCref | STCout))) + { /* BUG: This should be allowed: + * void foo() + * { int a; + * int* bar() { return &a; } + * } + */ + error("escaping reference to local %s", v->toChars()); + } + } +} + +void SymOffExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (offset) + buf->printf("(& %s+%u)", var->toChars(), offset); + else + buf->printf("& %s", var->toChars()); +} + +/******************************** VarExp **************************/ + +VarExp::VarExp(Loc loc, Declaration *var, int hasOverloads) + : SymbolExp(loc, TOKvar, sizeof(VarExp), var, hasOverloads) +{ + //printf("VarExp(this = %p, '%s', loc = %s)\n", this, var->toChars(), loc.toChars()); + //if (strcmp(var->ident->toChars(), "func") == 0) halt(); + this->type = var->type; +} + +int VarExp::equals(Object *o) +{ VarExp *ne; + + if (this == o || + (((Expression *)o)->op == TOKvar && + ((ne = (VarExp *)o), type->toHeadMutable()->equals(ne->type->toHeadMutable())) && + var == ne->var)) + return 1; + return 0; +} + +Expression *VarExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("VarExp::semantic(%s)\n", toChars()); +#endif +// if (var->sem == SemanticStart && var->scope) // if forward referenced +// var->semantic(sc); + if (!type) + { type = var->type; +#if 0 + if (var->storage_class & STClazy) + { + TypeFunction *tf = new TypeFunction(NULL, type, 0, LINKd); + type = new TypeDelegate(tf); + type = type->semantic(loc, sc); + } +#endif + } + + if (type && !type->deco) + type = type->semantic(loc, sc); + + /* Fix for 1161 doesn't work because it causes protection + * problems when instantiating imported templates passing private + * variables as alias template parameters. + */ + //accessCheck(loc, sc, NULL, var); + + VarDeclaration *v = var->isVarDeclaration(); + if (v) + { + v->checkNestedReference(sc, loc); +#if DMDV2 + checkPurity(sc, v, NULL); +#endif + } + FuncDeclaration *f = var->isFuncDeclaration(); + if (f) + f->checkNestedReference(sc, loc); +#if 0 + else if ((fd = var->isFuncLiteralDeclaration()) != NULL) + { Expression *e; + e = new FuncExp(loc, fd); + e->type = type; + return e; + } +#endif + + return this; +} + +char *VarExp::toChars() +{ + return var->toChars(); +} + +void VarExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(var->toChars()); +} + +void VarExp::checkEscape() +{ + VarDeclaration *v = var->isVarDeclaration(); + if (v) + { Type *tb = v->type->toBasetype(); + // if reference type + if (tb->ty == Tarray || tb->ty == Tsarray || tb->ty == Tclass || tb->ty == Tdelegate) + { + if (v->isScope() && (!v->noscope || tb->ty == Tclass)) + error("escaping reference to scope local %s", v->toChars()); + else if (v->storage_class & STCvariadic) + error("escaping reference to variadic parameter %s", v->toChars()); + } + } +} + +void VarExp::checkEscapeRef() +{ + VarDeclaration *v = var->isVarDeclaration(); + if (v) + { + if (!v->isDataseg() && !(v->storage_class & (STCref | STCout))) + error("escaping reference to local variable %s", v->toChars()); + } +} + +#if DMDV2 +int VarExp::isLvalue() +{ + if (var->storage_class & STClazy) + return 0; + return 1; +} +#endif + +Expression *VarExp::toLvalue(Scope *sc, Expression *e) +{ +#if 0 + tym = tybasic(e1->ET->Tty); + if (!(tyscalar(tym) || + tym == TYstruct || + tym == TYarray && e->Eoper == TOKaddr)) + synerr(EM_lvalue); // lvalue expected +#endif + if (var->storage_class & STClazy) + { error("lazy variables cannot be lvalues"); + return new ErrorExp(); + } + return this; +} + +Expression *VarExp::modifiableLvalue(Scope *sc, Expression *e) +{ + //printf("VarExp::modifiableLvalue('%s')\n", var->toChars()); + //if (type && type->toBasetype()->ty == Tsarray) + //error("cannot change reference to static array '%s'", var->toChars()); + + var->checkModify(loc, sc, type); + + // See if this expression is a modifiable lvalue (i.e. not const) + return toLvalue(sc, e); +} + + +/******************************** OverExp **************************/ + +#if DMDV2 +OverExp::OverExp(OverloadSet *s) + : Expression(loc, TOKoverloadset, sizeof(OverExp)) +{ + //printf("OverExp(this = %p, '%s')\n", this, var->toChars()); + vars = s; + type = Type::tvoid; +} + +int OverExp::isLvalue() +{ + return 1; +} + +Expression *OverExp::toLvalue(Scope *sc, Expression *e) +{ + return this; +} +#endif + + +/******************************** TupleExp **************************/ + +TupleExp::TupleExp(Loc loc, Expressions *exps) + : Expression(loc, TOKtuple, sizeof(TupleExp)) +{ + //printf("TupleExp(this = %p)\n", this); + this->exps = exps; + this->type = NULL; +} + + +TupleExp::TupleExp(Loc loc, TupleDeclaration *tup) + : Expression(loc, TOKtuple, sizeof(TupleExp)) +{ + exps = new Expressions(); + type = NULL; + + exps->reserve(tup->objects->dim); + for (size_t i = 0; i < tup->objects->dim; i++) + { Object *o = tup->objects->tdata()[i]; + if (o->dyncast() == DYNCAST_EXPRESSION) + { + Expression *e = (Expression *)o; + if (e->op == TOKdsymbol) + e = e->syntaxCopy(); + exps->push(e); + } + else if (o->dyncast() == DYNCAST_DSYMBOL) + { + Dsymbol *s = (Dsymbol *)o; + Expression *e = new DsymbolExp(loc, s); + exps->push(e); + } + else if (o->dyncast() == DYNCAST_TYPE) + { + Type *t = (Type *)o; + Expression *e = new TypeExp(loc, t); + exps->push(e); + } + else + { + error("%s is not an expression", o->toChars()); + } + } +} + +int TupleExp::equals(Object *o) +{ + if (this == o) + return 1; + if (((Expression *)o)->op == TOKtuple) + { + TupleExp *te = (TupleExp *)o; + if (exps->dim != te->exps->dim) + return 0; + for (size_t i = 0; i < exps->dim; i++) + { Expression *e1 = (*exps)[i]; + Expression *e2 = (*te->exps)[i]; + + if (!e1->equals(e2)) + return 0; + } + return 1; + } + return 0; +} + +Expression *TupleExp::syntaxCopy() +{ + return new TupleExp(loc, arraySyntaxCopy(exps)); +} + +Expression *TupleExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("+TupleExp::semantic(%s)\n", toChars()); +#endif + if (type) + return this; + + // Run semantic() on each argument + for (size_t i = 0; i < exps->dim; i++) + { Expression *e = (*exps)[i]; + + e = e->semantic(sc); + if (!e->type) + { error("%s has no value", e->toChars()); + return new ErrorExp(); + } + (*exps)[i] = e; + } + + expandTuples(exps); + type = new TypeTuple(exps); + type = type->semantic(loc, sc); + //printf("-TupleExp::semantic(%s)\n", toChars()); + return this; +} + +void TupleExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("tuple("); + argsToCBuffer(buf, exps, hgs); + buf->writeByte(')'); +} + + +void TupleExp::checkEscape() +{ + for (size_t i = 0; i < exps->dim; i++) + { Expression *e = (*exps)[i]; + e->checkEscape(); + } +} + +/******************************** FuncExp *********************************/ + +FuncExp::FuncExp(Loc loc, FuncLiteralDeclaration *fd, TemplateDeclaration *td) + : Expression(loc, TOKfunction, sizeof(FuncExp)) +{ + this->fd = fd; + this->td = td; + tok = fd->tok; // save original kind of function/delegate/(infer) + tded = NULL; + scope = NULL; +} + +Expression *FuncExp::syntaxCopy() +{ + return new FuncExp(loc, (FuncLiteralDeclaration *)fd->syntaxCopy(NULL)); +} + +Expression *FuncExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("FuncExp::semantic(%s)\n", toChars()); +#endif + if (!type || type == Type::tvoid) + { + // save for later use + scope = sc; + + //printf("td = %p, tded = %p\n", td, tded); + if (td) + { + assert(td->parameters && td->parameters->dim); + td->semantic(sc); + + if (!tded) + { // defer type determination + type = Type::tvoid; // temporary type + return this; + } + else + { + Expression *e = inferType(sc, tded); + if (e) + { e = e->castTo(sc, tded); + e = e->semantic(sc); + } + if (!e) + { error("cannot infer function literal type"); + e = new ErrorExp(); + } + return e; + } + } + + unsigned olderrors = global.errors; + fd->semantic(sc); + //fd->parent = sc->parent; + if (olderrors != global.errors) + { + } + else + { + fd->semantic2(sc); + if ( (olderrors == global.errors) || + // need to infer return type + (fd->type && fd->type->ty == Tfunction && !fd->type->nextOf())) + { + fd->semantic3(sc); + + if ( (olderrors == global.errors) && global.params.useInline) + fd->inlineScan(); + } + } + + // need to infer return type + if ((olderrors != global.errors) && fd->type && fd->type->ty == Tfunction && !fd->type->nextOf()) + ((TypeFunction *)fd->type)->next = Type::terror; + + // Type is a "delegate to" or "pointer to" the function literal + if ((fd->isNested() && fd->tok == TOKdelegate) || + (tok == TOKreserved && tded && tded->ty == Tdelegate)) + { + type = new TypeDelegate(fd->type); + type = type->semantic(loc, sc); + } + else + { + type = fd->type->pointerTo(); + } + fd->tookAddressOf++; + } + return this; +} + +// used from CallExp::semantic() +Expression *FuncExp::semantic(Scope *sc, Expressions *arguments) +{ + assert(!tded); + assert(!scope); + + if ((!type || type == Type::tvoid) && td && arguments && arguments->dim) + { + for (size_t k = 0; k < arguments->dim; k++) + { Expression *checkarg = arguments->tdata()[k]; + if (checkarg->op == TOKerror) + return checkarg; + } + + assert(td->parameters && td->parameters->dim); + td->semantic(sc); + + TypeFunction *tfl = (TypeFunction *)fd->type; + size_t dim = Parameter::dim(tfl->parameters); + + if ((!tfl->varargs && arguments->dim == dim) || + ( tfl->varargs && arguments->dim >= dim)) + { + Objects *tiargs = new Objects(); + tiargs->reserve(td->parameters->dim); + + for (size_t i = 0; i < td->parameters->dim; i++) + { + TemplateParameter *tp = (*td->parameters)[i]; + for (size_t u = 0; u < dim; u++) + { Parameter *p = Parameter::getNth(tfl->parameters, u); + if (p->type->ty == Tident && + ((TypeIdentifier *)p->type)->ident == tp->ident) + { Expression *e = (*arguments)[u]; + tiargs->push(e->type); + u = dim; // break inner loop + } + } + } + + TemplateInstance *ti = new TemplateInstance(loc, td, tiargs); + return (new ScopeExp(loc, ti))->semantic(sc); + } + error("cannot infer function literal type"); + return new ErrorExp(); + } + return semantic(sc); +} + +Expression *FuncExp::inferType(Scope *sc, Type *to) +{ + //printf("inferType sc = %p, to = %s\n", sc, to->toChars()); + if (!sc) + { // used from TypeFunction::callMatch() + assert(scope); + sc = scope; + } + + Expression *e = NULL; + if (td) + { /// Parameter types inference from + assert(!type || type == Type::tvoid); + Type *t = to; + if (t->ty == Tdelegate || + t->ty == Tpointer && t->nextOf()->ty == Tfunction) + { t = t->nextOf(); + } + if (t->ty == Tfunction) + { + TypeFunction *tfv = (TypeFunction *)t; + TypeFunction *tfl = (TypeFunction *)fd->type; + size_t dim = Parameter::dim(tfl->parameters); + + if (Parameter::dim(tfv->parameters) == dim && + tfv->varargs == tfl->varargs) + { + Objects *tiargs = new Objects(); + tiargs->reserve(td->parameters->dim); + + for (size_t i = 0; i < td->parameters->dim; i++) + { + TemplateParameter *tp = (*td->parameters)[i]; + for (size_t u = 0; u < dim; u++) + { Parameter *p = Parameter::getNth(tfl->parameters, u); + if (p->type->ty == Tident && + ((TypeIdentifier *)p->type)->ident == tp->ident) + { p = Parameter::getNth(tfv->parameters, u); + if (p->type->ty == Tident) + return NULL; + tiargs->push(p->type); + u = dim; // break inner loop + } + } + } + + TemplateInstance *ti = new TemplateInstance(loc, td, tiargs); + e = (new ScopeExp(loc, ti))->semantic(sc); + } + } + } + else + { + assert(type && type != Type::tvoid); // semantic is already done + e = this; + } + + if (e) + { // Check implicit function to delegate conversion + if (e->implicitConvTo(to)) + e = e->castTo(sc, to); + else + e = NULL; + } + return e; +} + +void FuncExp::setType(Type *t) +{ + assert(t); + + if (t->ty == Tdelegate || + t->ty == Tpointer && t->nextOf()->ty == Tfunction) + { tded = t; + } +} + +char *FuncExp::toChars() +{ + return fd->toChars(); +} + +void FuncExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + fd->toCBuffer(buf, hgs); + //buf->writestring(fd->toChars()); +} + + +/******************************** DeclarationExp **************************/ + +DeclarationExp::DeclarationExp(Loc loc, Dsymbol *declaration) + : Expression(loc, TOKdeclaration, sizeof(DeclarationExp)) +{ + this->declaration = declaration; +} + +Expression *DeclarationExp::syntaxCopy() +{ + return new DeclarationExp(loc, declaration->syntaxCopy(NULL)); +} + +Expression *DeclarationExp::semantic(Scope *sc) +{ + if (type) + return this; + +#if LOGSEMANTIC + printf("DeclarationExp::semantic() %s\n", toChars()); +#endif + + unsigned olderrors = global.errors; + + /* This is here to support extern(linkage) declaration, + * where the extern(linkage) winds up being an AttribDeclaration + * wrapper. + */ + Dsymbol *s = declaration; + + AttribDeclaration *ad = declaration->isAttribDeclaration(); + if (ad) + { + if (ad->decl && ad->decl->dim == 1) + s = ad->decl->tdata()[0]; + } + + if (s->isVarDeclaration()) + { // Do semantic() on initializer first, so: + // int a = a; + // will be illegal. + declaration->semantic(sc); + s->parent = sc->parent; + } + + //printf("inserting '%s' %p into sc = %p\n", s->toChars(), s, sc); + // Insert into both local scope and function scope. + // Must be unique in both. + if (s->ident) + { + if (!sc->insert(s)) + { error("declaration %s is already defined", s->toPrettyChars()); + return new ErrorExp(); + } + else if (sc->func) + { VarDeclaration *v = s->isVarDeclaration(); + if ( (s->isFuncDeclaration() || s->isTypedefDeclaration() || + s->isAggregateDeclaration() || s->isEnumDeclaration() || + s->isInterfaceDeclaration()) && + !sc->func->localsymtab->insert(s)) + { + error("declaration %s is already defined in another scope in %s", + s->toPrettyChars(), sc->func->toChars()); + return new ErrorExp(); + } + else if (!global.params.useDeprecated) + { // Disallow shadowing + + for (Scope *scx = sc->enclosing; scx && scx->func == sc->func; scx = scx->enclosing) + { Dsymbol *s2; + + if (scx->scopesym && scx->scopesym->symtab && + (s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL && + s != s2) + { + error("shadowing declaration %s is deprecated", s->toPrettyChars()); + return new ErrorExp(); + } + } + } + } + } + if (!s->isVarDeclaration()) + { + Scope *sc2 = sc; + if (sc2->stc & (STCpure | STCnothrow)) + sc2 = sc->push(); + sc2->stc &= ~(STCpure | STCnothrow); + declaration->semantic(sc2); + if (sc2 != sc) + sc2->pop(); + s->parent = sc->parent; + } + if (global.errors == olderrors) + { + declaration->semantic2(sc); + if (global.errors == olderrors) + { + declaration->semantic3(sc); + + if ((global.errors == olderrors) && global.params.useInline) + declaration->inlineScan(); + } + } + + type = Type::tvoid; + return this; +} + + +void DeclarationExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + declaration->toCBuffer(buf, hgs); +} + + +/************************ TypeidExp ************************************/ + +/* + * typeid(int) + */ + +TypeidExp::TypeidExp(Loc loc, Object *o) + : Expression(loc, TOKtypeid, sizeof(TypeidExp)) +{ + this->obj = o; +} + + +Expression *TypeidExp::syntaxCopy() +{ + return new TypeidExp(loc, objectSyntaxCopy(obj)); +} + + +Expression *TypeidExp::semantic(Scope *sc) +{ Expression *e; + +#if LOGSEMANTIC + printf("TypeidExp::semantic() %s\n", toChars()); +#endif + Type *ta = isType(obj); + Expression *ea = isExpression(obj); + Dsymbol *sa = isDsymbol(obj); + + //printf("ta %p ea %p sa %p\n", ta, ea, sa); + + if (ta) + { + ta->resolve(loc, sc, &ea, &ta, &sa); + } + + if (ea) + { + ea = ea->semantic(sc); + ea = resolveProperties(sc, ea); + ta = ea->type; + if (ea->op == TOKtype) + ea = NULL; + } + + if (!ta) + { + //printf("ta %p ea %p sa %p\n", ta, ea, sa); + error("no type for typeid(%s)", ea ? ea->toChars() : (sa ? sa->toChars() : "")); + return new ErrorExp(); + } + + if (ea && ta->toBasetype()->ty == Tclass) + { /* Get the dynamic type, which is .classinfo + */ + e = new DotIdExp(ea->loc, ea, Id::classinfo); + e = e->semantic(sc); + } + else + { /* Get the static type + */ + e = ta->getTypeInfo(sc); + if (e->loc.linnum == 0) + e->loc = loc; // so there's at least some line number info + if (ea) + { + e = new CommaExp(loc, ea, e); // execute ea + e = e->semantic(sc); + } + } + return e; +} + +void TypeidExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("typeid("); + ObjectToCBuffer(buf, hgs, obj); + buf->writeByte(')'); +} + +/************************ TraitsExp ************************************/ +#if DMDV2 +/* + * __traits(identifier, args...) + */ + +TraitsExp::TraitsExp(Loc loc, Identifier *ident, Objects *args) + : Expression(loc, TOKtraits, sizeof(TraitsExp)) +{ + this->ident = ident; + this->args = args; +} + + +Expression *TraitsExp::syntaxCopy() +{ + return new TraitsExp(loc, ident, TemplateInstance::arraySyntaxCopy(args)); +} + + +void TraitsExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("__traits("); + buf->writestring(ident->toChars()); + if (args) + { + for (size_t i = 0; i < args->dim; i++) + { + buf->writeByte(','); + Object *oarg = args->tdata()[i]; + ObjectToCBuffer(buf, hgs, oarg); + } + } + buf->writeByte(')'); +} +#endif + +/************************************************************/ + +HaltExp::HaltExp(Loc loc) + : Expression(loc, TOKhalt, sizeof(HaltExp)) +{ +} + +Expression *HaltExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("HaltExp::semantic()\n"); +#endif + type = Type::tvoid; + return this; +} + + +void HaltExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("halt"); +} + +/************************************************************/ + +IsExp::IsExp(Loc loc, Type *targ, Identifier *id, enum TOK tok, + Type *tspec, enum TOK tok2, TemplateParameters *parameters) + : Expression(loc, TOKis, sizeof(IsExp)) +{ + this->targ = targ; + this->id = id; + this->tok = tok; + this->tspec = tspec; + this->tok2 = tok2; + this->parameters = parameters; +} + +Expression *IsExp::syntaxCopy() +{ + // This section is identical to that in TemplateDeclaration::syntaxCopy() + TemplateParameters *p = NULL; + if (parameters) + { + p = new TemplateParameters(); + p->setDim(parameters->dim); + for (size_t i = 0; i < p->dim; i++) + { TemplateParameter *tp = parameters->tdata()[i]; + p->tdata()[i] = tp->syntaxCopy(); + } + } + + return new IsExp(loc, + targ->syntaxCopy(), + id, + tok, + tspec ? tspec->syntaxCopy() : NULL, + tok2, + p); +} + +Expression *IsExp::semantic(Scope *sc) +{ Type *tded; + + /* is(targ id tok tspec) + * is(targ id : tok2) + * is(targ id == tok2) + */ + + //printf("IsExp::semantic(%s)\n", toChars()); + if (id && !(sc->flags & (SCOPEstaticif | SCOPEstaticassert))) + { error("can only declare type aliases within static if conditionals or static asserts"); + return new ErrorExp(); + } + + Type *t = targ->trySemantic(loc, sc); + if (!t) + goto Lno; // errors, so condition is false + targ = t; + if (tok2 != TOKreserved) + { + switch (tok2) + { + case TOKtypedef: + if (targ->ty != Ttypedef) + goto Lno; + tded = ((TypeTypedef *)targ)->sym->basetype; + break; + + case TOKstruct: + if (targ->ty != Tstruct) + goto Lno; + if (((TypeStruct *)targ)->sym->isUnionDeclaration()) + goto Lno; + tded = targ; + break; + + case TOKunion: + if (targ->ty != Tstruct) + goto Lno; + if (!((TypeStruct *)targ)->sym->isUnionDeclaration()) + goto Lno; + tded = targ; + break; + + case TOKclass: + if (targ->ty != Tclass) + goto Lno; + if (((TypeClass *)targ)->sym->isInterfaceDeclaration()) + goto Lno; + tded = targ; + break; + + case TOKinterface: + if (targ->ty != Tclass) + goto Lno; + if (!((TypeClass *)targ)->sym->isInterfaceDeclaration()) + goto Lno; + tded = targ; + break; +#if DMDV2 + case TOKconst: + if (!targ->isConst()) + goto Lno; + tded = targ; + break; + + case TOKinvariant: + if (!global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); + case TOKimmutable: + if (!targ->isImmutable()) + goto Lno; + tded = targ; + break; + + case TOKshared: + if (!targ->isShared()) + goto Lno; + tded = targ; + break; + + case TOKwild: + if (!targ->isWild()) + goto Lno; + tded = targ; + break; +#endif + + case TOKsuper: + // If class or interface, get the base class and interfaces + if (targ->ty != Tclass) + goto Lno; + else + { ClassDeclaration *cd = ((TypeClass *)targ)->sym; + Parameters *args = new Parameters; + args->reserve(cd->baseclasses->dim); + for (size_t i = 0; i < cd->baseclasses->dim; i++) + { BaseClass *b = cd->baseclasses->tdata()[i]; + args->push(new Parameter(STCin, b->type, NULL, NULL)); + } + tded = new TypeTuple(args); + } + break; + + case TOKenum: + if (targ->ty != Tenum) + goto Lno; + tded = ((TypeEnum *)targ)->sym->memtype; + break; + + case TOKdelegate: + if (targ->ty != Tdelegate) + goto Lno; + tded = ((TypeDelegate *)targ)->next; // the underlying function type + break; + + case TOKfunction: + { + if (targ->ty != Tfunction) + goto Lno; + tded = targ; + + /* Generate tuple from function parameter types. + */ + assert(tded->ty == Tfunction); + Parameters *params = ((TypeFunction *)tded)->parameters; + size_t dim = Parameter::dim(params); + Parameters *args = new Parameters; + args->reserve(dim); + for (size_t i = 0; i < dim; i++) + { Parameter *arg = Parameter::getNth(params, i); + assert(arg && arg->type); + args->push(new Parameter(arg->storageClass, arg->type, NULL, NULL)); + } + tded = new TypeTuple(args); + break; + } + case TOKreturn: + /* Get the 'return type' for the function, + * delegate, or pointer to function. + */ + if (targ->ty == Tfunction) + tded = ((TypeFunction *)targ)->next; + else if (targ->ty == Tdelegate) + { tded = ((TypeDelegate *)targ)->next; + tded = ((TypeFunction *)tded)->next; + } + else if (targ->ty == Tpointer && + ((TypePointer *)targ)->next->ty == Tfunction) + { tded = ((TypePointer *)targ)->next; + tded = ((TypeFunction *)tded)->next; + } + else + goto Lno; + break; + + case TOKargTypes: + /* Generate a type tuple of the equivalent types used to determine if a + * function argument of this type can be passed in registers. + * The results of this are highly platform dependent, and intended + * primarly for use in implementing va_arg(). + */ + tded = targ->toArgTypes(); + if (!tded) + goto Lno; // not valid for a parameter + break; + + default: + assert(0); + } + goto Lyes; + } + else if (id && tspec) + { + /* Evaluate to TRUE if targ matches tspec. + * If TRUE, declare id as an alias for the specialized type. + */ + + assert(parameters && parameters->dim); + + Objects dedtypes; + dedtypes.setDim(parameters->dim); + dedtypes.zero(); + + MATCH m = targ->deduceType(sc, tspec, parameters, &dedtypes); +//printf("targ: %s\n", targ->toChars()); +//printf("tspec: %s\n", tspec->toChars()); + if (m == MATCHnomatch || + (m != MATCHexact && tok == TOKequal)) + { + goto Lno; + } + else + { + tded = (Type *)dedtypes.tdata()[0]; + if (!tded) + tded = targ; +#if DMDV2 + Objects tiargs; + tiargs.setDim(1); + tiargs.tdata()[0] = targ; + + /* Declare trailing parameters + */ + for (size_t i = 1; i < parameters->dim; i++) + { TemplateParameter *tp = (*parameters)[i]; + Declaration *s = NULL; + + m = tp->matchArg(sc, &tiargs, i, parameters, &dedtypes, &s); + if (m == MATCHnomatch) + goto Lno; + s->semantic(sc); + if (sc->sd) + s->addMember(sc, sc->sd, 1); + else if (!sc->insert(s)) + error("declaration %s is already defined", s->toChars()); + } +#endif + goto Lyes; + } + } + else if (id) + { + /* Declare id as an alias for type targ. Evaluate to TRUE + */ + tded = targ; + goto Lyes; + } + else if (tspec) + { + /* Evaluate to TRUE if targ matches tspec + * is(targ == tspec) + * is(targ : tspec) + */ + tspec = tspec->semantic(loc, sc); + //printf("targ = %s, %s\n", targ->toChars(), targ->deco); + //printf("tspec = %s, %s\n", tspec->toChars(), tspec->deco); + if (tok == TOKcolon) + { if (targ->implicitConvTo(tspec)) + goto Lyes; + else + goto Lno; + } + else /* == */ + { if (targ->equals(tspec)) + goto Lyes; + else + goto Lno; + } + } + +Lyes: + if (id) + { + Dsymbol *s; + Tuple *tup = isTuple(tded); + if (tup) + s = new TupleDeclaration(loc, id, &(tup->objects)); + else + s = new AliasDeclaration(loc, id, tded); + s->semantic(sc); + /* The reason for the !tup is unclear. It fails Phobos unittests if it is not there. + * More investigation is needed. + */ + if (!tup && !sc->insert(s)) + error("declaration %s is already defined", s->toChars()); + if (sc->sd) + s->addMember(sc, sc->sd, 1); + } + //printf("Lyes\n"); + return new IntegerExp(loc, 1, Type::tbool); + +Lno: + //printf("Lno\n"); + return new IntegerExp(loc, 0, Type::tbool); +} + +void IsExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("is("); + targ->toCBuffer(buf, id, hgs); + if (tok2 != TOKreserved) + { + buf->printf(" %s %s", Token::toChars(tok), Token::toChars(tok2)); + } + else if (tspec) + { + if (tok == TOKcolon) + buf->writestring(" : "); + else + buf->writestring(" == "); + tspec->toCBuffer(buf, NULL, hgs); + } +#if DMDV2 + if (parameters) + { // First parameter is already output, so start with second + for (size_t i = 1; i < parameters->dim; i++) + { + buf->writeByte(','); + TemplateParameter *tp = parameters->tdata()[i]; + tp->toCBuffer(buf, hgs); + } + } +#endif + buf->writeByte(')'); +} + + +/************************************************************/ + +UnaExp::UnaExp(Loc loc, enum TOK op, int size, Expression *e1) + : Expression(loc, op, size) +{ + this->e1 = e1; +} + +Expression *UnaExp::syntaxCopy() +{ + UnaExp *e = (UnaExp *)copy(); + e->type = NULL; + e->e1 = e->e1->syntaxCopy(); + return e; +} + +Expression *UnaExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("UnaExp::semantic('%s')\n", toChars()); +#endif + e1 = e1->semantic(sc); +// if (!e1->type) +// error("%s has no value", e1->toChars()); + return this; +} + +Expression *UnaExp::resolveLoc(Loc loc, Scope *sc) +{ + e1 = e1->resolveLoc(loc, sc); + return this; +} + +void UnaExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(Token::toChars(op)); + expToCBuffer(buf, hgs, e1, precedence[op]); +} + +/************************************************************/ + +BinExp::BinExp(Loc loc, enum TOK op, int size, Expression *e1, Expression *e2) + : Expression(loc, op, size) +{ + this->e1 = e1; + this->e2 = e2; +} + +Expression *BinExp::syntaxCopy() +{ + BinExp *e = (BinExp *)copy(); + e->type = NULL; + e->e1 = e->e1->syntaxCopy(); + e->e2 = e->e2->syntaxCopy(); + return e; +} + +Expression *BinExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("BinExp::semantic('%s')\n", toChars()); +#endif + e1 = e1->semantic(sc); + e2 = e2->semantic(sc); + if (e1->op == TOKerror || e2->op == TOKerror) + return new ErrorExp(); + return this; +} + +Expression *BinExp::semanticp(Scope *sc) +{ + BinExp::semantic(sc); + e1 = resolveProperties(sc, e1); + e2 = resolveProperties(sc, e2); + return this; +} + + +// generate an error if this is a nonsensical *=,/=, or %=, eg real *= imaginary +void BinExp::checkComplexMulAssign() +{ + // Any multiplication by an imaginary or complex number yields a complex result. + // r *= c, i*=c, r*=i, i*=i are all forbidden operations. + const char *opstr = Token::toChars(op); + if ( e1->type->isreal() && e2->type->iscomplex()) + { + error("%s %s %s is undefined. Did you mean %s %s %s.re ?", + e1->type->toChars(), opstr, e2->type->toChars(), + e1->type->toChars(), opstr, e2->type->toChars()); + } + else if (e1->type->isimaginary() && e2->type->iscomplex()) + { + error("%s %s %s is undefined. Did you mean %s %s %s.im ?", + e1->type->toChars(), opstr, e2->type->toChars(), + e1->type->toChars(), opstr, e2->type->toChars()); + } + else if ((e1->type->isreal() || e1->type->isimaginary()) && + e2->type->isimaginary()) + { + error("%s %s %s is an undefined operation", e1->type->toChars(), + opstr, e2->type->toChars()); + } +} + +// generate an error if this is a nonsensical += or -=, eg real += imaginary +void BinExp::checkComplexAddAssign() +{ + // Addition or subtraction of a real and an imaginary is a complex result. + // Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations. + if ( (e1->type->isreal() && (e2->type->isimaginary() || e2->type->iscomplex())) || + (e1->type->isimaginary() && (e2->type->isreal() || e2->type->iscomplex())) + ) + { + error("%s %s %s is undefined (result is complex)", + e1->type->toChars(), Token::toChars(op), e2->type->toChars()); + } +} + +void BinExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, precedence[op]); + buf->writeByte(' '); + buf->writestring(Token::toChars(op)); + buf->writeByte(' '); + expToCBuffer(buf, hgs, e2, (enum PREC)(precedence[op] + 1)); +} + +int BinExp::isunsigned() +{ + return e1->type->isunsigned() || e2->type->isunsigned(); +} + +Expression *BinExp::incompatibleTypes() +{ + if (e1->type->toBasetype() != Type::terror && + e2->type->toBasetype() != Type::terror + ) + { error("incompatible types for ((%s) %s (%s)): '%s' and '%s'", + e1->toChars(), Token::toChars(op), e2->toChars(), + e1->type->toChars(), e2->type->toChars()); + return new ErrorExp(); + } + return this; +} + +/********************** BinAssignExp **************************************/ + +/*************************** + * Common semantic routine for some xxxAssignExp's. + */ + +Expression *BinAssignExp::commonSemanticAssign(Scope *sc) +{ Expression *e; + + if (!type) + { + if (e1->op == TOKarraylength) + { + e = ArrayLengthExp::rewriteOpAssign(this); + e = e->semantic(sc); + return e; + } + + if (e1->op == TOKslice) + { // T[] op= ... + e = typeCombine(sc); + if (e->op == TOKerror) + return e; + type = e1->type; + return arrayOp(sc); + } + + e1 = e1->modifiableLvalue(sc, e1); + e1->checkScalar(); + type = e1->type; + if (type->toBasetype()->ty == Tbool) + { + error("operator not allowed on bool expression %s", toChars()); + return new ErrorExp(); + } + typeCombine(sc); + e1->checkArithmetic(); + e2->checkArithmetic(); + + if (op == TOKmodass) + { + if (e2->type->iscomplex()) + { error("cannot perform modulo complex arithmetic"); + return new ErrorExp(); + } + else if (type->toBasetype()->ty == Tvector) + return incompatibleTypes(); + } + } + return this; +} + +Expression *BinAssignExp::commonSemanticAssignIntegral(Scope *sc) +{ Expression *e; + + if (!type) + { + e = op_overload(sc); + if (e) + return e; + + if (e1->op == TOKarraylength) + { + e = ArrayLengthExp::rewriteOpAssign(this); + e = e->semantic(sc); + return e; + } + + if (e1->op == TOKslice) + { // T[] op= ... + e = typeCombine(sc); + if (e->op == TOKerror) + return e; + type = e1->type; + return arrayOp(sc); + } + + e1 = e1->modifiableLvalue(sc, e1); + e1->checkScalar(); + type = e1->type; + if (type->toBasetype()->ty == Tbool) + { + e2 = e2->implicitCastTo(sc, type); + } + + typeCombine(sc); + e1->checkIntegral(); + e2->checkIntegral(); + } + return this; +} + +#if DMDV2 +int BinAssignExp::isLvalue() +{ + return 1; +} + +Expression *BinAssignExp::toLvalue(Scope *sc, Expression *ex) +{ Expression *e; + + if (e1->op == TOKvar) + { + /* Convert (e1 op= e2) to + * e1 op= e2; + * e1 + */ + e = e1->copy(); + e = new CommaExp(loc, this, e); + e = e->semantic(sc); + } + else + { + /* Convert (e1 op= e2) to + * ref v = e1; + * v op= e2; + * v + */ + + // ref v = e1; + Identifier *id = Lexer::uniqueId("__assignop"); + ExpInitializer *ei = new ExpInitializer(loc, e1); + VarDeclaration *v = new VarDeclaration(loc, e1->type, id, ei); + v->storage_class |= STCref | STCforeach; + Expression *de = new DeclarationExp(loc, v); + + // v op= e2 + e1 = new VarExp(e1->loc, v); + + e = new CommaExp(loc, de, this); + e = new CommaExp(loc, e, new VarExp(loc, v)); + e = e->semantic(sc); + } + return e; +} + +Expression *BinAssignExp::modifiableLvalue(Scope *sc, Expression *e) +{ + return toLvalue(sc, this); +} + +#endif + +/************************************************************/ + +CompileExp::CompileExp(Loc loc, Expression *e) + : UnaExp(loc, TOKmixin, sizeof(CompileExp), e) +{ +} + +Expression *CompileExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("CompileExp::semantic('%s')\n", toChars()); +#endif + UnaExp::semantic(sc); + e1 = resolveProperties(sc, e1); + if (e1->op == TOKerror) + return e1; + if (!e1->type->isString()) + { + error("argument to mixin must be a string type, not %s\n", e1->type->toChars()); + return new ErrorExp(); + } + e1 = e1->optimize(WANTvalue | WANTinterpret); + StringExp *se = e1->toString(); + if (!se) + { error("argument to mixin must be a string, not (%s)", e1->toChars()); + return new ErrorExp(); + } + se = se->toUTF8(sc); + Parser p(sc->module, (unsigned char *)se->string, se->len, 0); + p.loc = loc; + p.nextToken(); + //printf("p.loc.linnum = %d\n", p.loc.linnum); + Expression *e = p.parseExpression(); + if (p.token.value != TOKeof) + { error("incomplete mixin expression (%s)", se->toChars()); + return new ErrorExp(); + } + return e->semantic(sc); +} + +void CompileExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("mixin("); + expToCBuffer(buf, hgs, e1, PREC_assign); + buf->writeByte(')'); +} + +/************************************************************/ + +FileExp::FileExp(Loc loc, Expression *e) + : UnaExp(loc, TOKmixin, sizeof(FileExp), e) +{ +} + +Expression *FileExp::semantic(Scope *sc) +{ char *name; + StringExp *se; + +#if LOGSEMANTIC + printf("FileExp::semantic('%s')\n", toChars()); +#endif + UnaExp::semantic(sc); + e1 = resolveProperties(sc, e1); + e1 = e1->optimize(WANTvalue | WANTinterpret); + if (e1->op != TOKstring) + { error("file name argument must be a string, not (%s)", e1->toChars()); + goto Lerror; + } + se = (StringExp *)e1; + se = se->toUTF8(sc); + name = (char *)se->string; + + if (!global.params.fileImppath) + { error("need -Jpath switch to import text file %s", name); + goto Lerror; + } + + /* Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory + * ('Path Traversal') attacks. + * http://cwe.mitre.org/data/definitions/22.html + */ + + name = FileName::safeSearchPath(global.filePath, name); + if (!name) + { error("file %s cannot be found or not in a path specified with -J", se->toChars()); + goto Lerror; + } + + if (global.params.verbose) + printf("file %s\t(%s)\n", (char *)se->string, name); + + { File f(name); + if (f.read()) + { error("cannot read file %s", f.toChars()); + goto Lerror; + } + else + { + f.ref = 1; + se = new StringExp(loc, f.buffer, f.len); + } + } + return se->semantic(sc); + + Lerror: + return new ErrorExp(); +} + +void FileExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("import("); + expToCBuffer(buf, hgs, e1, PREC_assign); + buf->writeByte(')'); +} + +/************************************************************/ + +AssertExp::AssertExp(Loc loc, Expression *e, Expression *msg) + : UnaExp(loc, TOKassert, sizeof(AssertExp), e) +{ + this->msg = msg; +} + +Expression *AssertExp::syntaxCopy() +{ + AssertExp *ae = new AssertExp(loc, e1->syntaxCopy(), + msg ? msg->syntaxCopy() : NULL); + return ae; +} + +Expression *AssertExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("AssertExp::semantic('%s')\n", toChars()); +#endif + UnaExp::semantic(sc); + e1 = resolveProperties(sc, e1); + // BUG: see if we can do compile time elimination of the Assert + e1 = e1->optimize(WANTvalue); + e1 = e1->checkToBoolean(sc); + if (msg) + { + msg = msg->semantic(sc); + msg = resolveProperties(sc, msg); + msg = msg->implicitCastTo(sc, Type::tchar->constOf()->arrayOf()); + msg = msg->optimize(WANTvalue); + } + if (e1->isBool(FALSE)) + { + FuncDeclaration *fd = sc->parent->isFuncDeclaration(); + if (fd) + fd->hasReturnExp |= 4; + + if (!global.params.useAssert) + { Expression *e = new HaltExp(loc); + e = e->semantic(sc); + return e; + } + } + type = Type::tvoid; + return this; +} + + +void AssertExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("assert("); + expToCBuffer(buf, hgs, e1, PREC_assign); + if (msg) + { + buf->writeByte(','); + expToCBuffer(buf, hgs, msg, PREC_assign); + } + buf->writeByte(')'); +} + +/************************************************************/ + +DotIdExp::DotIdExp(Loc loc, Expression *e, Identifier *ident) + : UnaExp(loc, TOKdot, sizeof(DotIdExp), e) +{ + this->ident = ident; +} + +Expression *DotIdExp::semantic(Scope *sc) +{ + // Indicate we didn't come from CallExp::semantic() + return semantic(sc, 0); +} + +Expression *DotIdExp::semantic(Scope *sc, int flag) +{ Expression *e; + Expression *eleft; + Expression *eright; + +#if LOGSEMANTIC + printf("DotIdExp::semantic(this = %p, '%s')\n", this, toChars()); + //printf("e1->op = %d, '%s'\n", e1->op, Token::toChars(e1->op)); +#endif + +//{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; } + +#if 0 + /* Don't do semantic analysis if we'll be converting + * it to a string. + */ + if (ident == Id::stringof) + { char *s = e1->toChars(); + e = new StringExp(loc, s, strlen(s), 'c'); + e = e->semantic(sc); + return e; + } +#endif + + /* Special case: rewrite this.id and super.id + * to be classtype.id and baseclasstype.id + * if we have no this pointer. + */ + if ((e1->op == TOKthis || e1->op == TOKsuper) && !hasThis(sc)) + { ClassDeclaration *cd; + StructDeclaration *sd; + AggregateDeclaration *ad; + + ad = sc->getStructClassScope(); + if (ad) + { + cd = ad->isClassDeclaration(); + if (cd) + { + if (e1->op == TOKthis) + { + e = typeDotIdExp(loc, cd->type, ident); + return e->semantic(sc); + } + else if (cd->baseClass && e1->op == TOKsuper) + { + e = typeDotIdExp(loc, cd->baseClass->type, ident); + return e->semantic(sc); + } + } + else + { + sd = ad->isStructDeclaration(); + if (sd) + { + if (e1->op == TOKthis) + { + e = typeDotIdExp(loc, sd->type, ident); + return e->semantic(sc); + } + } + } + } + } + + UnaExp::semantic(sc); + + if (ident == Id::mangleof) + { // symbol.mangleof + Dsymbol *ds; + switch (e1->op) + { + case TOKimport: ds = ((ScopeExp *)e1)->sds; goto L1; + case TOKvar: ds = ((VarExp *)e1)->var; goto L1; + case TOKdotvar: ds = ((DotVarExp *)e1)->var; goto L1; + L1: + char* s = ds->mangle(); + e = new StringExp(loc, s, strlen(s), 'c'); + e = e->semantic(sc); + return e; + } + } + + if (e1->op == TOKdotexp) + { + DotExp *de = (DotExp *)e1; + eleft = de->e1; + eright = de->e2; + } + else + { + if (e1->op != TOKtype) + e1 = resolveProperties(sc, e1); + eleft = NULL; + eright = e1; + } +#if DMDV2 + if (e1->op == TOKtuple && ident == Id::offsetof) + { /* 'distribute' the .offsetof to each of the tuple elements. + */ + TupleExp *te = (TupleExp *)e1; + Expressions *exps = new Expressions(); + exps->setDim(te->exps->dim); + for (size_t i = 0; i < exps->dim; i++) + { Expression *e = (*te->exps)[i]; + e = e->semantic(sc); + e = new DotIdExp(e->loc, e, Id::offsetof); + (*exps)[i] = e; + } + e = new TupleExp(loc, exps); + e = e->semantic(sc); + return e; + } +#endif + + if (e1->op == TOKtuple && ident == Id::length) + { + TupleExp *te = (TupleExp *)e1; + e = new IntegerExp(loc, te->exps->dim, Type::tsize_t); + return e; + } + + if (e1->op == TOKdottd) + { + error("template %s does not have property %s", e1->toChars(), ident->toChars()); + return new ErrorExp(); + } + + if (!e1->type) + { + error("expression %s does not have property %s", e1->toChars(), ident->toChars()); + return new ErrorExp(); + } + + Type *t1b = e1->type->toBasetype(); + + if (eright->op == TOKimport) // also used for template alias's + { + ScopeExp *ie = (ScopeExp *)eright; + + /* Disable access to another module's private imports. + * The check for 'is sds our current module' is because + * the current module should have access to its own imports. + */ + Dsymbol *s = ie->sds->search(loc, ident, + (ie->sds->isModule() && ie->sds != sc->module) ? 1 : 0); + if (s) + { + /* Check for access before resolving aliases because public + * aliases to private symbols are public. + */ + if (Declaration *d = s->isDeclaration()) + accessCheck(loc, sc, 0, d); + + s = s->toAlias(); + checkDeprecated(sc, s); + + EnumMember *em = s->isEnumMember(); + if (em) + { + e = em->value; + e = e->semantic(sc); + return e; + } + + VarDeclaration *v = s->isVarDeclaration(); + if (v) + { + //printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v->type->toChars()); + if (v->inuse) + { + error("circular reference to '%s'", v->toChars()); + return new ErrorExp(); + } + type = v->type; + if (v->needThis()) + { + if (!eleft) + eleft = new ThisExp(loc); + e = new DotVarExp(loc, eleft, v); + e = e->semantic(sc); + } + else + { + e = new VarExp(loc, v); + if (eleft) + { e = new CommaExp(loc, eleft, e); + e->type = v->type; + } + } + e = e->deref(); + return e->semantic(sc); + } + + FuncDeclaration *f = s->isFuncDeclaration(); + if (f) + { + //printf("it's a function\n"); + if (f->needThis()) + { + if (!eleft) + eleft = new ThisExp(loc); + e = new DotVarExp(loc, eleft, f); + e = e->semantic(sc); + } + else + { + e = new VarExp(loc, f, 1); + if (eleft) + { e = new CommaExp(loc, eleft, e); + e->type = f->type; + } + } + return e; + } +#if DMDV2 + OverloadSet *o = s->isOverloadSet(); + if (o) + { //printf("'%s' is an overload set\n", o->toChars()); + return new OverExp(o); + } +#endif + + Type *t = s->getType(); + if (t) + { + return new TypeExp(loc, t); + } + + TupleDeclaration *tup = s->isTupleDeclaration(); + if (tup) + { + if (eleft) + { error("cannot have e.tuple"); + return new ErrorExp(); + } + e = new TupleExp(loc, tup); + e = e->semantic(sc); + return e; + } + + ScopeDsymbol *sds = s->isScopeDsymbol(); + if (sds) + { + //printf("it's a ScopeDsymbol\n"); + e = new ScopeExp(loc, sds); + e = e->semantic(sc); + if (eleft) + e = new DotExp(loc, eleft, e); + return e; + } + + Import *imp = s->isImport(); + if (imp) + { + ScopeExp *ie; + + ie = new ScopeExp(loc, imp->pkg); + return ie->semantic(sc); + } + + // BUG: handle other cases like in IdentifierExp::semantic() +#ifdef DEBUG + printf("s = '%s', kind = '%s'\n", s->toChars(), s->kind()); +#endif + assert(0); + } + else if (ident == Id::stringof) + { char *s = ie->toChars(); + e = new StringExp(loc, s, strlen(s), 'c'); + e = e->semantic(sc); + return e; + } + error("undefined identifier %s", toChars()); + return new ErrorExp(); + } + else if (t1b->ty == Tpointer && + ident != Id::init && ident != Id::__sizeof && + ident != Id::__xalignof && ident != Id::offsetof && + ident != Id::mangleof && ident != Id::stringof) + { /* Rewrite: + * p.ident + * as: + * (*p).ident + */ + e = new PtrExp(loc, e1); + e->type = ((TypePointer *)t1b)->next; + return e->type->dotExp(sc, e, ident); + } +#if DMDV2 + else if ((t1b->ty == Tarray || t1b->ty == Tsarray || + t1b->ty == Taarray) && + ident != Id::sort && ident != Id::reverse && + ident != Id::dup && ident != Id::idup) + { /* If ident is not a valid property, rewrite: + * e1.ident + * as: + * .ident(e1) + */ + unsigned errors = global.startGagging(); + Type *t1 = e1->type; + e = e1->type->dotExp(sc, e1, ident); + if (global.endGagging(errors)) // if failed to find the property + { + e1->type = t1; // kludge to restore type + e = new DotIdExp(loc, new IdentifierExp(loc, Id::empty), ident); + e = new CallExp(loc, e, e1); + } + e = e->semantic(sc); + return e; + } +#endif + else + { + e = e1->type->dotExp(sc, e1, ident); + if (!(flag && e->op == TOKdotti)) // let CallExp::semantic() handle this + e = e->semantic(sc); + return e; + } +} + +void DotIdExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + //printf("DotIdExp::toCBuffer()\n"); + expToCBuffer(buf, hgs, e1, PREC_primary); + buf->writeByte('.'); + buf->writestring(ident->toChars()); +} + +/********************** DotTemplateExp ***********************************/ + +// Mainly just a placeholder + +DotTemplateExp::DotTemplateExp(Loc loc, Expression *e, TemplateDeclaration *td) + : UnaExp(loc, TOKdottd, sizeof(DotTemplateExp), e) + +{ + this->td = td; +} + +void DotTemplateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, PREC_primary); + buf->writeByte('.'); + buf->writestring(td->toChars()); +} + + +/************************************************************/ + +DotVarExp::DotVarExp(Loc loc, Expression *e, Declaration *v, int hasOverloads) + : UnaExp(loc, TOKdotvar, sizeof(DotVarExp), e) +{ + //printf("DotVarExp()\n"); + this->var = v; + this->hasOverloads = hasOverloads; +} + +Expression *DotVarExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("DotVarExp::semantic('%s')\n", toChars()); +#endif + if (!type) + { + var = var->toAlias()->isDeclaration(); + + TupleDeclaration *tup = var->isTupleDeclaration(); + if (tup) + { /* Replace: + * e1.tuple(a, b, c) + * with: + * tuple(e1.a, e1.b, e1.c) + */ + Expressions *exps = new Expressions; + Expression *ev = e1; + + exps->reserve(tup->objects->dim); + for (size_t i = 0; i < tup->objects->dim; i++) + { Object *o = tup->objects->tdata()[i]; + if (o->dyncast() != DYNCAST_EXPRESSION) + { + error("%s is not an expression", o->toChars()); + goto Lerr; + } + + Expression *e = (Expression *)o; + if (e->op != TOKdsymbol) + { error("%s is not a member", e->toChars()); + goto Lerr; + } + + Dsymbol *s = ((DsymbolExp *)e)->s; + if (i == 0 && sc->func && tup->objects->dim > 1 && + e1->hasSideEffect()) + { + Identifier *id = Lexer::uniqueId("__tup"); + ExpInitializer *ei = new ExpInitializer(e1->loc, e1); + VarDeclaration *v = new VarDeclaration(e1->loc, NULL, id, ei); + v->storage_class |= STCctfe | STCref | STCforeach; + + ev = new VarExp(e->loc, v); + e = new CommaExp(e1->loc, new DeclarationExp(e1->loc, v), ev); + e = new DotVarExp(loc, e, s->isDeclaration()); + } + else + e = new DotVarExp(loc, ev, s->isDeclaration()); + exps->push(e); + } + Expression *e = new TupleExp(loc, exps); + e = e->semantic(sc); + return e; + } + + e1 = e1->semantic(sc); + e1 = e1->addDtorHook(sc); + type = var->type; + if (!type && global.errors) + { // var is goofed up, just return 0 + return new ErrorExp(); + } + assert(type); + + Type *t1 = e1->type; + if (!var->isFuncDeclaration()) // for functions, do checks after overload resolution + { + if (t1->ty == Tpointer) + t1 = t1->nextOf(); + + type = type->addMod(t1->mod); + + Dsymbol *vparent = var->toParent(); + AggregateDeclaration *ad = vparent ? vparent->isAggregateDeclaration() : NULL; + e1 = getRightThis(loc, sc, ad, e1, var); + if (!sc->noaccesscheck) + accessCheck(loc, sc, e1, var); + + VarDeclaration *v = var->isVarDeclaration(); + Expression *e = expandVar(WANTvalue, v); + if (e) + return e; + } + Dsymbol *s; + if (sc->func && !sc->intypeof && t1->hasPointers() && + (s = t1->toDsymbol(sc)) != NULL) + { + AggregateDeclaration *ad = s->isAggregateDeclaration(); + if (ad && ad->hasUnions) + { + if (sc->func->setUnsafe()) + { error("union %s containing pointers are not allowed in @safe functions", t1->toChars()); + goto Lerr; + } + } + } + } + + //printf("-DotVarExp::semantic('%s')\n", toChars()); + return this; + +Lerr: + return new ErrorExp(); +} + +#if DMDV2 +int DotVarExp::isLvalue() +{ + return 1; +} +#endif + +Expression *DotVarExp::toLvalue(Scope *sc, Expression *e) +{ + //printf("DotVarExp::toLvalue(%s)\n", toChars()); + return this; +} + +/*********************************************** + * Mark variable v as modified if it is inside a constructor that var + * is a field in. + */ +void modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1) +{ + //printf("modifyFieldVar(var = %s)\n", var->toChars()); + Dsymbol *s = sc->func; + while (1) + { + FuncDeclaration *fd = NULL; + if (s) + fd = s->isFuncDeclaration(); + if (fd && + ((fd->isCtorDeclaration() && var->storage_class & STCfield) || + (fd->isStaticCtorDeclaration() && !(var->storage_class & STCfield))) && + fd->toParent2() == var->toParent2() && + (!e1 || e1->op == TOKthis) + ) + { + var->ctorinit = 1; + //printf("setting ctorinit\n"); + } + else + { + if (s) + { s = s->toParent2(); + continue; + } + else if (var->storage_class & STCctorinit) + { + const char *p = var->isStatic() ? "static " : ""; + error(loc, "can only initialize %sconst member %s inside %sconstructor", + p, var->toChars(), p); + } + } + break; + } +} + +Expression *DotVarExp::modifiableLvalue(Scope *sc, Expression *e) +{ +#if 0 + printf("DotVarExp::modifiableLvalue(%s)\n", toChars()); + printf("e1->type = %s\n", e1->type->toChars()); + printf("var->type = %s\n", var->type->toChars()); +#endif + + Type *t1 = e1->type->toBasetype(); + + if (!t1->isMutable() || + (t1->ty == Tpointer && !t1->nextOf()->isMutable()) || + !var->type->isMutable() || + !var->type->isAssignable() || + var->storage_class & STCmanifest + ) + { + if (var->isCtorinit()) + { // It's only modifiable if inside the right constructor + modifyFieldVar(loc, sc, var->isVarDeclaration(), e1); + } + else + { + error("cannot modify const/immutable/inout expression %s", toChars()); + } + } + else if (var->storage_class & STCnodefaultctor) + { + modifyFieldVar(loc, sc, var->isVarDeclaration(), e1); + } + return this; +} + +void DotVarExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, PREC_primary); + buf->writeByte('.'); + buf->writestring(var->toChars()); +} + +/************************************************************/ + +/* Things like: + * foo.bar!(args) + */ + +DotTemplateInstanceExp::DotTemplateInstanceExp(Loc loc, Expression *e, Identifier *name, Objects *tiargs) + : UnaExp(loc, TOKdotti, sizeof(DotTemplateInstanceExp), e) +{ + //printf("DotTemplateInstanceExp()\n"); + this->ti = new TemplateInstance(loc, name); + this->ti->tiargs = tiargs; +} + +Expression *DotTemplateInstanceExp::syntaxCopy() +{ + DotTemplateInstanceExp *de = new DotTemplateInstanceExp(loc, + e1->syntaxCopy(), + ti->name, + TemplateInstance::arraySyntaxCopy(ti->tiargs)); + return de; +} + +TemplateDeclaration *DotTemplateInstanceExp::getTempdecl(Scope *sc) +{ +#if LOGSEMANTIC + printf("DotTemplateInstanceExp::getTempdecl('%s')\n", toChars()); +#endif + if (!ti->tempdecl) + { + Expression *e = new DotIdExp(loc, e1, ti->name); + e = e->semantic(sc); + if (e->op == TOKdottd) + { + DotTemplateExp *dte = (DotTemplateExp *)e; + ti->tempdecl = dte->td; + } + else if (e->op == TOKimport) + { ScopeExp *se = (ScopeExp *)e; + ti->tempdecl = se->sds->isTemplateDeclaration(); + } + } + return ti->tempdecl; +} + +Expression *DotTemplateInstanceExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("DotTemplateInstanceExp::semantic('%s')\n", toChars()); +#endif + Expression *eleft; + Expression *e = new DotIdExp(loc, e1, ti->name); +L1: + e = e->semantic(sc); + if (e->op == TOKerror) + return e; + if (e->op == TOKdottd) + { + if (global.errors) + return new ErrorExp(); // TemplateInstance::semantic() will fail anyway + DotTemplateExp *dte = (DotTemplateExp *)e; + TemplateDeclaration *td = dte->td; + eleft = dte->e1; + ti->tempdecl = td; + if (ti->needsTypeInference(sc)) + { + e1 = eleft; // save result of semantic() + return this; + } + else + ti->semantic(sc); + if (!ti->inst) // if template failed to expand + return new ErrorExp(); + Dsymbol *s = ti->inst->toAlias(); + Declaration *v = s->isDeclaration(); + if (v) + { + /* Fix for Bugzilla 4003 + * The problem is a class template member function v returning a reference to the same + * type as the enclosing template instantiation. This results in a nested instantiation, + * which of course gets short circuited. The return type then gets set to + * the template instance type before instantiation, rather than after. + * We can detect this by the deco not being set. If so, go ahead and retry + * the return type semantic. + * The offending code is the return type from std.typecons.Tuple.slice: + * ref Tuple!(Types[from .. to]) slice(uint from, uint to)() + * { + * return *cast(typeof(return) *) &(field[from]); + * } + * and this line from the following unittest: + * auto s = a.slice!(1, 3); + * where s's type wound up not having semantic() run on it. + */ + if (v->type && !v->type->deco) + v->type = v->type->semantic(v->loc, sc); + + e = new DotVarExp(loc, eleft, v); + e = e->semantic(sc); + return e; + } + e = new ScopeExp(loc, ti); + e = new DotExp(loc, eleft, e); + e = e->semantic(sc); + return e; + } + else if (e->op == TOKimport) + { ScopeExp *se = (ScopeExp *)e; + TemplateDeclaration *td = se->sds->isTemplateDeclaration(); + if (!td) + { error("%s is not a template", e->toChars()); + return new ErrorExp(); + } + ti->tempdecl = td; + e = new ScopeExp(loc, ti); + e = e->semantic(sc); + return e; + } + else if (e->op == TOKdotexp) + { DotExp *de = (DotExp *)e; + + if (de->e2->op == TOKoverloadset) + { + return e; + } + + if (de->e2->op == TOKimport) + { // This should *really* be moved to ScopeExp::semantic() + ScopeExp *se = (ScopeExp *)de->e2; + de->e2 = new DsymbolExp(loc, se->sds); + de->e2 = de->e2->semantic(sc); + } + + if (de->e2->op == TOKtemplate) + { TemplateExp *te = (TemplateExp *) de->e2; + e = new DotTemplateExp(loc,de->e1,te->td); + } + goto L1; + } + error("%s isn't a template", e->toChars()); + return new ErrorExp(); +} + +void DotTemplateInstanceExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, PREC_primary); + buf->writeByte('.'); + ti->toCBuffer(buf, hgs); +} + +/************************************************************/ + +DelegateExp::DelegateExp(Loc loc, Expression *e, FuncDeclaration *f, int hasOverloads) + : UnaExp(loc, TOKdelegate, sizeof(DelegateExp), e) +{ + this->func = f; + this->hasOverloads = hasOverloads; +} + +Expression *DelegateExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("DelegateExp::semantic('%s')\n", toChars()); +#endif + if (!type) + { + e1 = e1->semantic(sc); + type = new TypeDelegate(func->type); + type = type->semantic(loc, sc); + AggregateDeclaration *ad = func->toParent()->isAggregateDeclaration(); + if (func->needThis()) + e1 = getRightThis(loc, sc, ad, e1, func); + if (ad && ad->isClassDeclaration() && ad->type != e1->type) + { // A downcast is required for interfaces, see Bugzilla 3706 + e1 = new CastExp(loc, e1, ad->type); + e1 = e1->semantic(sc); + } + } + return this; +} + +void DelegateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writeByte('&'); + if (!func->isNested()) + { + expToCBuffer(buf, hgs, e1, PREC_primary); + buf->writeByte('.'); + } + buf->writestring(func->toChars()); +} + +/************************************************************/ + +DotTypeExp::DotTypeExp(Loc loc, Expression *e, Dsymbol *s) + : UnaExp(loc, TOKdottype, sizeof(DotTypeExp), e) +{ + this->sym = s; + this->type = s->getType(); +} + +Expression *DotTypeExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("DotTypeExp::semantic('%s')\n", toChars()); +#endif + UnaExp::semantic(sc); + return this; +} + +void DotTypeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, PREC_primary); + buf->writeByte('.'); + buf->writestring(sym->toChars()); +} + +/************************************************************/ + +CallExp::CallExp(Loc loc, Expression *e, Expressions *exps) + : UnaExp(loc, TOKcall, sizeof(CallExp), e) +{ + this->arguments = exps; + this->f = NULL; +} + +CallExp::CallExp(Loc loc, Expression *e) + : UnaExp(loc, TOKcall, sizeof(CallExp), e) +{ + this->arguments = NULL; +} + +CallExp::CallExp(Loc loc, Expression *e, Expression *earg1) + : UnaExp(loc, TOKcall, sizeof(CallExp), e) +{ + Expressions *arguments = new Expressions(); + if (earg1) + { arguments->setDim(1); + arguments->tdata()[0] = earg1; + } + this->arguments = arguments; +} + +CallExp::CallExp(Loc loc, Expression *e, Expression *earg1, Expression *earg2) + : UnaExp(loc, TOKcall, sizeof(CallExp), e) +{ + Expressions *arguments = new Expressions(); + arguments->setDim(2); + arguments->tdata()[0] = earg1; + arguments->tdata()[1] = earg2; + + this->arguments = arguments; +} + +Expression *CallExp::syntaxCopy() +{ + return new CallExp(loc, e1->syntaxCopy(), arraySyntaxCopy(arguments)); +} + + +Expression *CallExp::resolveUFCS(Scope *sc) +{ + Expression *ethis = NULL; + DotIdExp *dotid; + DotTemplateInstanceExp *dotti; + Identifier *ident; + + if (e1->op == TOKdot) + { + dotid = (DotIdExp *)e1; + ident = dotid->ident; + ethis = dotid->e1 = dotid->e1->semantic(sc); + if (ethis->op == TOKdotexp) + return NULL; + ethis = resolveProperties(sc, ethis); + } + else if (e1->op == TOKdotti) + { + dotti = (DotTemplateInstanceExp *)e1; + ident = dotti->ti->name; + ethis = dotti->e1 = dotti->e1->semantic(sc); + if (ethis->op == TOKdotexp) + return NULL; + ethis = resolveProperties(sc, ethis); + } + + if (ethis && ethis->type) + { + AggregateDeclaration *ad; +Lagain: + Type *tthis = ethis->type->toBasetype(); + if (tthis->ty == Tclass) + { + ad = ((TypeClass *)tthis)->sym; + if (search_function(ad, ident)) + return NULL; + goto L1; + } + else if (tthis->ty == Tstruct) + { + ad = ((TypeStruct *)tthis)->sym; + if (search_function(ad, ident)) + return NULL; + L1: + if (ad->aliasthis) + { + ethis = new DotIdExp(ethis->loc, ethis, ad->aliasthis->ident); + ethis = ethis->semantic(sc); + ethis = resolveProperties(sc, ethis); + goto Lagain; + } + } + else if (tthis->ty == Taarray && e1->op == TOKdot) + { + if (ident == Id::remove) + { + /* Transform: + * aa.remove(arg) into delete aa[arg] + */ + if (!arguments || arguments->dim != 1) + { error("expected key as argument to aa.remove()"); + return new ErrorExp(); + } + Expression *key = arguments->tdata()[0]; + key = key->semantic(sc); + key = resolveProperties(sc, key); + if (!key->rvalue()) + return new ErrorExp(); + + TypeAArray *taa = (TypeAArray *)tthis; + key = key->implicitCastTo(sc, taa->index); + + return new RemoveExp(loc, ethis, key); + } + else if (ident == Id::apply || ident == Id::applyReverse) + { + return NULL; + } + else + { TypeAArray *taa = (TypeAArray *)tthis; + assert(taa->ty == Taarray); + StructDeclaration *sd = taa->getImpl(); + Dsymbol *s = sd->search(0, ident, 2); + if (s) + return NULL; + goto Lshift; + } + } + else if (tthis->ty == Tarray || tthis->ty == Tsarray) + { +Lshift: + if (!arguments) + arguments = new Expressions(); + arguments->shift(ethis); + if (e1->op == TOKdot) + { + /* Transform: + * array.id(args) into .id(array,args) + */ +#if DMDV2 + e1 = new DotIdExp(dotid->loc, + new IdentifierExp(dotid->loc, Id::empty), + ident); +#else + e1 = new IdentifierExp(dotid->loc, ident); +#endif + } + else if (e1->op == TOKdotti) + { + /* Transform: + * array.foo!(tiargs)(args) into .foo!(tiargs)(array,args) + */ +#if DMDV2 + e1 = new DotTemplateInstanceExp(dotti->loc, + new IdentifierExp(dotti->loc, Id::empty), + dotti->ti->name, dotti->ti->tiargs); +#else + e1 = new ScopeExp(dotti->loc, dotti->ti); +#endif + } + //printf("-> this = %s\n", toChars()); + } + } + return NULL; +} + +Expression *CallExp::semantic(Scope *sc) +{ + TypeFunction *tf; + Type *t1; + int istemp; + Objects *targsi = NULL; // initial list of template arguments + TemplateInstance *tierror = NULL; + Expression *ethis = NULL; + +#if LOGSEMANTIC + printf("CallExp::semantic() %s\n", toChars()); +#endif + if (type) + return this; // semantic() already run +#if 0 + if (arguments && arguments->dim) + { + Expression *earg = arguments->tdata()[0]; + earg->print(); + if (earg->type) earg->type->print(); + } +#endif + + if (e1->op == TOKcomma) + { /* Rewrite (a,b)(args) as (a,(b(args))) + */ + CommaExp *ce = (CommaExp *)e1; + + e1 = ce->e2; + e1->type = ce->type; + ce->e2 = this; + ce->type = NULL; + return ce->semantic(sc); + } + + if (e1->op == TOKdelegate) + { DelegateExp *de = (DelegateExp *)e1; + + e1 = new DotVarExp(de->loc, de->e1, de->func); + return semantic(sc); + } + + if (e1->op == TOKfunction) + { FuncExp *fe = (FuncExp *)e1; + + arguments = arrayExpressionSemantic(arguments, sc); + preFunctionParameters(loc, sc, arguments); + e1 = fe->semantic(sc, arguments); + if (e1->op == TOKerror) + return e1; + } + + Expression *e = resolveUFCS(sc); + if (e) + return e; + +#if 1 + /* This recognizes: + * foo!(tiargs)(funcargs) + */ + if (e1->op == TOKimport && !e1->type) + { ScopeExp *se = (ScopeExp *)e1; + TemplateInstance *ti = se->sds->isTemplateInstance(); + if (ti && !ti->semanticRun) + { + /* Attempt to instantiate ti. If that works, go with it. + * If not, go with partial explicit specialization. + */ + ti->semanticTiargs(sc); + if (ti->needsTypeInference(sc)) + { + /* Go with partial explicit specialization + */ + targsi = ti->tiargs; + tierror = ti; // for error reporting + e1 = new IdentifierExp(loc, ti->name); + } + else + { + ti->semantic(sc); + } + } + } + + /* This recognizes: + * expr.foo!(tiargs)(funcargs) + */ +Ldotti: + if (e1->op == TOKdotti && !e1->type) + { DotTemplateInstanceExp *se = (DotTemplateInstanceExp *)e1; + TemplateInstance *ti = se->ti; + if (!ti->semanticRun) + { + /* Attempt to instantiate ti. If that works, go with it. + * If not, go with partial explicit specialization. + */ + ti->semanticTiargs(sc); +#if 0 + Expression *etmp = e1->trySemantic(sc); + if (etmp) + e1 = etmp; // it worked + else // didn't work + { + targsi = ti->tiargs; + tierror = ti; // for error reporting + e1 = new DotIdExp(loc, se->e1, ti->name); + } +#else + if (!ti->tempdecl) + { + se->getTempdecl(sc); + } + if (ti->tempdecl && ti->needsTypeInference(sc)) + { + /* Go with partial explicit specialization + */ + targsi = ti->tiargs; + tierror = ti; // for error reporting + e1 = new DotIdExp(loc, se->e1, ti->name); + } + else + { + e1 = e1->semantic(sc); + } +#endif + } + } +#endif + + istemp = 0; +Lagain: + //printf("Lagain: %s\n", toChars()); + f = NULL; + if (e1->op == TOKthis || e1->op == TOKsuper) + { + // semantic() run later for these + } + else + { + if (e1->op == TOKdot) + { DotIdExp *die = (DotIdExp *)e1; + e1 = die->semantic(sc, 1); + /* Look for e1 having been rewritten to expr.opDispatch!(string) + * We handle such earlier, so go back. + * Note that in the rewrite, we carefully did not run semantic() on e1 + */ + if (e1->op == TOKdotti && !e1->type) + { + goto Ldotti; + } + } + else + { + static int nest; + if (++nest > 500) + { + error("recursive evaluation of %s", toChars()); + --nest; + return new ErrorExp(); + } + UnaExp::semantic(sc); + --nest; + } + + /* Look for e1 being a lazy parameter + */ + if (e1->op == TOKvar) + { VarExp *ve = (VarExp *)e1; + + if (ve->var->storage_class & STClazy) + { + // lazy paramaters can be called without violating purity and safety + TypeFunction *tf = new TypeFunction(NULL, ve->var->type, 0, LINKd, STCsafe | STCpure); + TypeDelegate *t = new TypeDelegate(tf); + ve->type = t->semantic(loc, sc); + } + } + + if (e1->op == TOKimport) + { // Perhaps this should be moved to ScopeExp::semantic() + ScopeExp *se = (ScopeExp *)e1; + e1 = new DsymbolExp(loc, se->sds); + e1 = e1->semantic(sc); + } + else if (e1->op == TOKsymoff && ((SymOffExp *)e1)->hasOverloads) + { + SymOffExp *se = (SymOffExp *)e1; + e1 = new VarExp(se->loc, se->var, 1); + e1 = e1->semantic(sc); + } +#if 1 // patch for #540 by Oskar Linde + else if (e1->op == TOKdotexp) + { + DotExp *de = (DotExp *) e1; + + if (de->e2->op == TOKoverloadset) + { + ethis = de->e1; + e1 = de->e2; + } + + if (de->e2->op == TOKimport) + { // This should *really* be moved to ScopeExp::semantic() + ScopeExp *se = (ScopeExp *)de->e2; + de->e2 = new DsymbolExp(loc, se->sds); + de->e2 = de->e2->semantic(sc); + } + + if (de->e2->op == TOKtemplate) + { TemplateExp *te = (TemplateExp *) de->e2; + e1 = new DotTemplateExp(loc,de->e1,te->td); + } + } +#endif + } + + t1 = NULL; + if (e1->type) + t1 = e1->type->toBasetype(); + + // Check for call operator overload + if (t1) + { AggregateDeclaration *ad; + + if (t1->ty == Tstruct) + { + ad = ((TypeStruct *)t1)->sym; +#if DMDV2 + // First look for constructor + if (ad->ctor && arguments && arguments->dim) + { + // Create variable that will get constructed + Identifier *idtmp = Lexer::uniqueId("__ctmp"); + VarDeclaration *tmp = new VarDeclaration(loc, t1, idtmp, NULL); + tmp->storage_class |= STCctfe; + Expression *av = new DeclarationExp(loc, tmp); + av = new CommaExp(loc, av, new VarExp(loc, tmp)); + + Expression *e; + CtorDeclaration *cf = ad->ctor->isCtorDeclaration(); + if (cf) + e = new DotVarExp(loc, av, cf, 1); + else + { TemplateDeclaration *td = ad->ctor->isTemplateDeclaration(); + assert(td); + e = new DotTemplateExp(loc, av, td); + } + e = new CallExp(loc, e, arguments); +#if !STRUCTTHISREF + /* Constructors return a pointer to the instance + */ + e = new PtrExp(loc, e); +#endif + e = e->semantic(sc); + return e; + } +#endif + // No constructor, look for overload of opCall + if (search_function(ad, Id::call)) + goto L1; // overload of opCall, therefore it's a call + + if (e1->op != TOKtype) + { error("%s %s does not overload ()", ad->kind(), ad->toChars()); + return new ErrorExp(); + } + + /* It's a struct literal + */ + Expression *e = new StructLiteralExp(loc, (StructDeclaration *)ad, arguments, e1->type); + e = e->semantic(sc); + return e; + } + else if (t1->ty == Tclass) + { + ad = ((TypeClass *)t1)->sym; + goto L1; + L1: + // Rewrite as e1.call(arguments) + Expression *e = new DotIdExp(loc, e1, Id::call); + e = new CallExp(loc, e, arguments); + e = e->semantic(sc); + return e; + } + } + + arguments = arrayExpressionSemantic(arguments, sc); + preFunctionParameters(loc, sc, arguments); + + // If there was an error processing any argument, or the call, + // return an error without trying to resolve the function call. + if (arguments && arguments->dim) + { + for (size_t k = 0; k < arguments->dim; k++) + { Expression *checkarg = arguments->tdata()[k]; + if (checkarg->op == TOKerror) + return checkarg; + } + } + if (e1->op == TOKerror) + return e1; + + if (e1->op == TOKdotvar && t1->ty == Tfunction || + e1->op == TOKdottd) + { + DotVarExp *dve; + DotTemplateExp *dte; + AggregateDeclaration *ad; + UnaExp *ue = (UnaExp *)(e1); + + if (e1->op == TOKdotvar) + { // Do overload resolution + dve = (DotVarExp *)(e1); + + f = dve->var->isFuncDeclaration(); + assert(f); + f = f->overloadResolve(loc, ue->e1, arguments); + + ad = f->toParent()->isAggregateDeclaration(); + } + else + { dte = (DotTemplateExp *)(e1); + TemplateDeclaration *td = dte->td; + assert(td); + if (!arguments) + // Should fix deduceFunctionTemplate() so it works on NULL argument + arguments = new Expressions(); + f = td->deduceFunctionTemplate(sc, loc, targsi, ue->e1, arguments); + if (!f) + return new ErrorExp(); + ad = td->toParent()->isAggregateDeclaration(); + } + if (f->needThis()) + { + ue->e1 = getRightThis(loc, sc, ad, ue->e1, f); + ethis = ue->e1; + } + + /* Cannot call public functions from inside invariant + * (because then the invariant would have infinite recursion) + */ + if (sc->func && sc->func->isInvariantDeclaration() && + ue->e1->op == TOKthis && + f->addPostInvariant() + ) + { + error("cannot call public/export function %s from invariant", f->toChars()); + return new ErrorExp(); + } + + checkDeprecated(sc, f); +#if DMDV2 + checkPurity(sc, f); + checkSafety(sc, f); +#endif + accessCheck(loc, sc, ue->e1, f); + if (!f->needThis()) + { + VarExp *ve = new VarExp(loc, f); + if ((ue->e1)->op == TOKtype) // just a FQN + e1 = ve; + else // things like (new Foo).bar() + e1 = new CommaExp(loc, ue->e1, ve); + e1->type = f->type; + } + else + { + if (e1->op == TOKdotvar) + { + dve->var = f; + e1->type = f->type; + } + else + { + e1 = new DotVarExp(loc, dte->e1, f); + e1 = e1->semantic(sc); + } +#if 0 + printf("ue->e1 = %s\n", ue->e1->toChars()); + printf("f = %s\n", f->toChars()); + printf("t = %s\n", t->toChars()); + printf("e1 = %s\n", e1->toChars()); + printf("e1->type = %s\n", e1->type->toChars()); +#endif + // Const member function can take const/immutable/mutable/inout this + if (!(f->type->isConst())) + { + // Check for const/immutable compatibility + Type *tthis = ue->e1->type->toBasetype(); + if (tthis->ty == Tpointer) + tthis = tthis->nextOf()->toBasetype(); +#if 0 // this checking should have been already done + if (f->type->isImmutable()) + { + if (tthis->mod != MODimmutable) + error("%s can only be called with an immutable object", e1->toChars()); + } + else if (f->type->isShared()) + { + if (tthis->mod != MODimmutable && + tthis->mod != MODshared && + tthis->mod != (MODshared | MODconst)) + error("shared %s can only be called with a shared or immutable object", e1->toChars()); + } + else + { + if (tthis->mod != 0) + { //printf("mod = %x\n", tthis->mod); + error("%s can only be called with a mutable object, not %s", e1->toChars(), tthis->toChars()); + } + } +#endif + /* Cannot call mutable method on a final struct + */ + if (tthis->ty == Tstruct && + ue->e1->op == TOKvar) + { VarExp *v = (VarExp *)ue->e1; + if (v->var->storage_class & STCfinal) + error("cannot call mutable method on final struct"); + } + } + + // See if we need to adjust the 'this' pointer + AggregateDeclaration *ad = f->isThis(); + ClassDeclaration *cd = ue->e1->type->isClassHandle(); + if (ad && cd && ad->isClassDeclaration() && ad != cd && + ue->e1->op != TOKsuper) + { + ue->e1 = ue->e1->castTo(sc, ad->type); //new CastExp(loc, ue->e1, ad->type); + ue->e1 = ue->e1->semantic(sc); + } + } + t1 = e1->type; + } + else if (e1->op == TOKsuper) + { + // Base class constructor call + ClassDeclaration *cd = NULL; + + if (sc->func) + cd = sc->func->toParent()->isClassDeclaration(); + if (!cd || !cd->baseClass || !sc->func->isCtorDeclaration()) + { + error("super class constructor call must be in a constructor"); + return new ErrorExp(); + } + else + { + if (!cd->baseClass->ctor) + { error("no super class constructor for %s", cd->baseClass->toChars()); + return new ErrorExp(); + } + else + { + if (!sc->intypeof) + { +#if 0 + if (sc->callSuper & (CSXthis | CSXsuper)) + error("reference to this before super()"); +#endif + if (sc->noctor || sc->callSuper & CSXlabel) + error("constructor calls not allowed in loops or after labels"); + if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor)) + error("multiple constructor calls"); + sc->callSuper |= CSXany_ctor | CSXsuper_ctor; + } + + f = resolveFuncCall(sc, loc, cd->baseClass->ctor, NULL, NULL, arguments, 0); + accessCheck(loc, sc, NULL, f); + checkDeprecated(sc, f); +#if DMDV2 + checkPurity(sc, f); + checkSafety(sc, f); +#endif + e1 = new DotVarExp(e1->loc, e1, f); + e1 = e1->semantic(sc); + t1 = e1->type; + } + } + } + else if (e1->op == TOKthis) + { + // same class constructor call + AggregateDeclaration *cd = NULL; + + if (sc->func) + cd = sc->func->toParent()->isAggregateDeclaration(); + if (!cd || !sc->func->isCtorDeclaration()) + { + error("constructor call must be in a constructor"); + return new ErrorExp(); + } + else + { + if (!sc->intypeof) + { +#if 0 + if (sc->callSuper & (CSXthis | CSXsuper)) + error("reference to this before super()"); +#endif + if (sc->noctor || sc->callSuper & CSXlabel) + error("constructor calls not allowed in loops or after labels"); + if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor)) + error("multiple constructor calls"); + sc->callSuper |= CSXany_ctor | CSXthis_ctor; + } + + f = resolveFuncCall(sc, loc, cd->ctor, NULL, NULL, arguments, 0); + checkDeprecated(sc, f); +#if DMDV2 + checkPurity(sc, f); + checkSafety(sc, f); +#endif + e1 = new DotVarExp(e1->loc, e1, f); + e1 = e1->semantic(sc); + t1 = e1->type; + + // BUG: this should really be done by checking the static + // call graph + if (f == sc->func) + { error("cyclic constructor call"); + return new ErrorExp(); + } + } + } + else if (e1->op == TOKoverloadset) + { + OverExp *eo = (OverExp *)e1; + FuncDeclaration *f = NULL; + Dsymbol *s = NULL; + for (size_t i = 0; i < eo->vars->a.dim; i++) + { s = eo->vars->a.tdata()[i]; + FuncDeclaration *f2 = s->isFuncDeclaration(); + if (f2) + { + f2 = f2->overloadResolve(loc, ethis, arguments, 1); + } + else + { TemplateDeclaration *td = s->isTemplateDeclaration(); + assert(td); + f2 = td->deduceFunctionTemplate(sc, loc, targsi, ethis, arguments, 1); + } + if (f2) + { if (f) + /* Error if match in more than one overload set, + * even if one is a 'better' match than the other. + */ + ScopeDsymbol::multiplyDefined(loc, f, f2); + else + f = f2; + } + } + if (!f) + { /* No overload matches + */ + error("no overload matches for %s", s->toChars()); + return new ErrorExp(); + } + if (ethis) + e1 = new DotVarExp(loc, ethis, f); + else + e1 = new VarExp(loc, f); + goto Lagain; + } + else if (!t1) + { + error("function expected before (), not '%s'", e1->toChars()); + return new ErrorExp(); + } + else if (t1->ty != Tfunction) + { + if (t1->ty == Tdelegate) + { TypeDelegate *td = (TypeDelegate *)t1; + assert(td->next->ty == Tfunction); + tf = (TypeFunction *)(td->next); + if (sc->func && !tf->purity && !(sc->flags & SCOPEdebug)) + { + if (sc->func->setImpure()) + error("pure function '%s' cannot call impure delegate '%s'", sc->func->toChars(), e1->toChars()); + } + if (sc->func && tf->trust <= TRUSTsystem) + { + if (sc->func->setUnsafe()) + error("safe function '%s' cannot call system delegate '%s'", sc->func->toChars(), e1->toChars()); + } + goto Lcheckargs; + } + else if (t1->ty == Tpointer && ((TypePointer *)t1)->next->ty == Tfunction) + { + Expression *e = new PtrExp(loc, e1); + t1 = ((TypePointer *)t1)->next; + if (sc->func && !((TypeFunction *)t1)->purity && !(sc->flags & SCOPEdebug)) + { + if (sc->func->setImpure()) + error("pure function '%s' cannot call impure function pointer '%s'", sc->func->toChars(), e1->toChars()); + } + if (sc->func && ((TypeFunction *)t1)->trust <= TRUSTsystem) + { + if (sc->func->setUnsafe()) + error("safe function '%s' cannot call system function pointer '%s'", sc->func->toChars(), e1->toChars()); + } + e->type = t1; + e1 = e; + } + else if (e1->op == TOKtemplate) + { + TemplateExp *te = (TemplateExp *)e1; + f = te->td->deduceFunctionTemplate(sc, loc, targsi, NULL, arguments); + if (!f) + { if (tierror) + tierror->error("errors instantiating template"); // give better error message + return new ErrorExp(); + } + if (f->needThis() && hasThis(sc)) + { + // Supply an implicit 'this', as in + // this.ident + + e1 = new DotTemplateExp(loc, (new ThisExp(loc))->semantic(sc), te->td); + goto Lagain; + } + + e1 = new VarExp(loc, f); + goto Lagain; + } + else + { error("function expected before (), not %s of type %s", e1->toChars(), e1->type->toChars()); + return new ErrorExp(); + } + } + else if (e1->op == TOKvar) + { + // Do overload resolution + VarExp *ve = (VarExp *)e1; + + f = ve->var->isFuncDeclaration(); + assert(f); + + if (ve->hasOverloads) + f = f->overloadResolve(loc, NULL, arguments); + checkDeprecated(sc, f); +#if DMDV2 + checkPurity(sc, f); + checkSafety(sc, f); +#endif + f->checkNestedReference(sc, loc); + + if (f->needThis() && hasThis(sc)) + { + // Supply an implicit 'this', as in + // this.ident + + e1 = new DotVarExp(loc, new ThisExp(loc), f); + goto Lagain; + } + + accessCheck(loc, sc, NULL, f); + + ethis = NULL; + + ve->var = f; +// ve->hasOverloads = 0; + ve->type = f->type; + t1 = f->type; + } + assert(t1->ty == Tfunction); + tf = (TypeFunction *)(t1); + +Lcheckargs: + assert(tf->ty == Tfunction); + + if (!arguments) + arguments = new Expressions(); + int olderrors = global.errors; + type = functionParameters(loc, sc, tf, ethis, arguments, f); + if (olderrors != global.errors) + return new ErrorExp(); + + if (!type) + { + error("forward reference to inferred return type of function call %s", toChars()); + return new ErrorExp(); + } + + if (f && f->tintro) + { + Type *t = type; + int offset = 0; + TypeFunction *tf = (TypeFunction *)f->tintro; + + if (tf->next->isBaseOf(t, &offset) && offset) + { + type = tf->next; + return castTo(sc, t); + } + } + + return this; +} + + +#if DMDV2 +int CallExp::isLvalue() +{ +// if (type->toBasetype()->ty == Tstruct) +// return 1; + Type *tb = e1->type->toBasetype(); + if (tb->ty == Tfunction && ((TypeFunction *)tb)->isref) + return 1; // function returns a reference + return 0; +} +#endif + +Expression *CallExp::toLvalue(Scope *sc, Expression *e) +{ + if (isLvalue()) + return this; + return Expression::toLvalue(sc, e); +} + +Expression *CallExp::addDtorHook(Scope *sc) +{ + /* Only need to add dtor hook if it's a type that needs destruction. + * Use same logic as VarDeclaration::callScopeDtor() + */ + + if (e1->type && e1->type->ty == Tfunction) + { + TypeFunction *tf = (TypeFunction *)e1->type; + if (tf->isref) + return this; + } + + Type *tv = type->toBasetype(); + while (tv->ty == Tsarray) + { TypeSArray *ta = (TypeSArray *)tv; + tv = tv->nextOf()->toBasetype(); + } + if (tv->ty == Tstruct) + { TypeStruct *ts = (TypeStruct *)tv; + StructDeclaration *sd = ts->sym; + if (sd->dtor) + { /* Type needs destruction, so declare a tmp + * which the back end will recognize and call dtor on + */ + Identifier *idtmp = Lexer::uniqueId("__tmpfordtor"); + VarDeclaration *tmp = new VarDeclaration(loc, type, idtmp, new ExpInitializer(loc, this)); + tmp->storage_class |= STCctfe; + Expression *ae = new DeclarationExp(loc, tmp); + Expression *e = new CommaExp(loc, ae, new VarExp(loc, tmp)); + e = e->semantic(sc); + return e; + } + } +Lnone: + return this; +} + +void CallExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (e1->op == TOKtype) + /* Avoid parens around type to prevent forbidden cast syntax: + * (sometype)(arg1) + * This is ok since types in constructor calls + * can never depend on parens anyway + */ + e1->toCBuffer(buf, hgs); + else + expToCBuffer(buf, hgs, e1, precedence[op]); + buf->writeByte('('); + argsToCBuffer(buf, arguments, hgs); + buf->writeByte(')'); +} + + +/************************************************************/ + +AddrExp::AddrExp(Loc loc, Expression *e) + : UnaExp(loc, TOKaddress, sizeof(AddrExp), e) +{ +} + +Expression *AddrExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("AddrExp::semantic('%s')\n", toChars()); +#endif + if (!type) + { + UnaExp::semantic(sc); + if (e1->type == Type::terror) + return new ErrorExp(); + e1 = e1->toLvalue(sc, NULL); + if (e1->op == TOKerror) + return e1; + if (!e1->type) + { + error("cannot take address of %s", e1->toChars()); + return new ErrorExp(); + } + if (!e1->type->deco) + { + /* No deco means semantic() was not run on the type. + * We have to run semantic() on the symbol to get the right type: + * auto x = &bar; + * pure: int bar() { return 1;} + * otherwise the 'pure' is missing from the type assigned to x. + */ + + error("forward reference to %s", e1->toChars()); + return new ErrorExp(); + } + + type = e1->type->pointerTo(); + + // See if this should really be a delegate + if (e1->op == TOKdotvar) + { + DotVarExp *dve = (DotVarExp *)e1; + FuncDeclaration *f = dve->var->isFuncDeclaration(); + + if (f) + { + if (!dve->hasOverloads) + f->tookAddressOf++; + Expression *e = new DelegateExp(loc, dve->e1, f, dve->hasOverloads); + e = e->semantic(sc); + return e; + } + } + else if (e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e1; + + VarDeclaration *v = ve->var->isVarDeclaration(); + if (v) + { + if (!v->canTakeAddressOf()) + { error("cannot take address of %s", e1->toChars()); + return new ErrorExp(); + } + + if (sc->func && !sc->intypeof && !v->isDataseg()) + { + if (sc->func->setUnsafe()) + { + error("cannot take address of %s %s in @safe function %s", + v->isParameter() ? "parameter" : "local", + v->toChars(), + sc->func->toChars()); + } + } + } + + FuncDeclaration *f = ve->var->isFuncDeclaration(); + + if (f) + { + if (!ve->hasOverloads || + /* Because nested functions cannot be overloaded, + * mark here that we took its address because castTo() + * may not be called with an exact match. + */ + f->toParent2()->isFuncDeclaration()) + f->tookAddressOf++; + if (f->isNested()) + { + Expression *e = new DelegateExp(loc, e1, f, ve->hasOverloads); + e = e->semantic(sc); + return e; + } + if (f->needThis() && hasThis(sc)) + { + /* Should probably supply 'this' after overload resolution, + * not before. + */ + Expression *ethis = new ThisExp(loc); + Expression *e = new DelegateExp(loc, ethis, f, ve->hasOverloads); + e = e->semantic(sc); + return e; + } + } + } + return optimize(WANTvalue); + } + return this; +} + +void AddrExp::checkEscape() +{ + e1->checkEscapeRef(); +} + +/************************************************************/ + +PtrExp::PtrExp(Loc loc, Expression *e) + : UnaExp(loc, TOKstar, sizeof(PtrExp), e) +{ +// if (e->type) +// type = ((TypePointer *)e->type)->next; +} + +PtrExp::PtrExp(Loc loc, Expression *e, Type *t) + : UnaExp(loc, TOKstar, sizeof(PtrExp), e) +{ + type = t; +} + +Expression *PtrExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("PtrExp::semantic('%s')\n", toChars()); +#endif + if (!type) + { + Expression *e = op_overload(sc); + if (e) + return e; + Type *tb = e1->type->toBasetype(); + switch (tb->ty) + { + case Tpointer: + type = ((TypePointer *)tb)->next; + break; + + case Tsarray: + case Tarray: + if (!global.params.useDeprecated) + error("using * on an array is deprecated; use *(%s).ptr instead", e1->toChars()); + type = ((TypeArray *)tb)->next; + e1 = e1->castTo(sc, type->pointerTo()); + break; + + default: + error("can only * a pointer, not a '%s'", e1->type->toChars()); + case Terror: + return new ErrorExp(); + } + if (!rvalue()) + return new ErrorExp(); + } + return this; +} + +#if DMDV2 +int PtrExp::isLvalue() +{ + return 1; +} +#endif + +void PtrExp::checkEscapeRef() +{ + e1->checkEscape(); +} + +Expression *PtrExp::toLvalue(Scope *sc, Expression *e) +{ +#if 0 + tym = tybasic(e1->ET->Tty); + if (!(tyscalar(tym) || + tym == TYstruct || + tym == TYarray && e->Eoper == TOKaddr)) + synerr(EM_lvalue); // lvalue expected +#endif + return this; +} + +#if DMDV2 +Expression *PtrExp::modifiableLvalue(Scope *sc, Expression *e) +{ + //printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type->toChars()); + + if (e1->op == TOKsymoff) + { SymOffExp *se = (SymOffExp *)e1; + se->var->checkModify(loc, sc, type); + //return toLvalue(sc, e); + } + + return Expression::modifiableLvalue(sc, e); +} +#endif + +void PtrExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writeByte('*'); + expToCBuffer(buf, hgs, e1, precedence[op]); +} + +/************************************************************/ + +NegExp::NegExp(Loc loc, Expression *e) + : UnaExp(loc, TOKneg, sizeof(NegExp), e) +{ +} + +Expression *NegExp::semantic(Scope *sc) +{ Expression *e; + +#if LOGSEMANTIC + printf("NegExp::semantic('%s')\n", toChars()); +#endif + if (!type) + { + e = op_overload(sc); + if (e) + return e; + + e1->checkNoBool(); + if (!e1->isArrayOperand()) + e1->checkArithmetic(); + type = e1->type; + } + return this; +} + +/************************************************************/ + +UAddExp::UAddExp(Loc loc, Expression *e) + : UnaExp(loc, TOKuadd, sizeof(UAddExp), e) +{ +} + +Expression *UAddExp::semantic(Scope *sc) +{ Expression *e; + +#if LOGSEMANTIC + printf("UAddExp::semantic('%s')\n", toChars()); +#endif + assert(!type); + e = op_overload(sc); + if (e) + return e; + e1->checkNoBool(); + e1->checkArithmetic(); + return e1; +} + +/************************************************************/ + +ComExp::ComExp(Loc loc, Expression *e) + : UnaExp(loc, TOKtilde, sizeof(ComExp), e) +{ +} + +Expression *ComExp::semantic(Scope *sc) +{ Expression *e; + + if (!type) + { + e = op_overload(sc); + if (e) + return e; + + e1->checkNoBool(); + if (!e1->isArrayOperand()) + e1 = e1->checkIntegral(); + type = e1->type; + } + return this; +} + +/************************************************************/ + +NotExp::NotExp(Loc loc, Expression *e) + : UnaExp(loc, TOKnot, sizeof(NotExp), e) +{ +} + +Expression *NotExp::semantic(Scope *sc) +{ + if (!type) + { // Note there is no operator overload + UnaExp::semantic(sc); + e1 = resolveProperties(sc, e1); + e1 = e1->checkToBoolean(sc); + type = Type::tboolean; + } + return this; +} + +int NotExp::isBit() +{ + return TRUE; +} + + + +/************************************************************/ + +BoolExp::BoolExp(Loc loc, Expression *e, Type *t) + : UnaExp(loc, TOKtobool, sizeof(BoolExp), e) +{ + type = t; +} + +Expression *BoolExp::semantic(Scope *sc) +{ + if (!type) + { // Note there is no operator overload + UnaExp::semantic(sc); + e1 = resolveProperties(sc, e1); + e1 = e1->checkToBoolean(sc); + type = Type::tboolean; + } + return this; +} + +int BoolExp::isBit() +{ + return TRUE; +} + +/************************************************************/ + +DeleteExp::DeleteExp(Loc loc, Expression *e) + : UnaExp(loc, TOKdelete, sizeof(DeleteExp), e) +{ +} + +Expression *DeleteExp::semantic(Scope *sc) +{ + Type *tb; + + UnaExp::semantic(sc); + e1 = resolveProperties(sc, e1); + e1 = e1->modifiableLvalue(sc, NULL); + if (e1->op == TOKerror) + return e1; + type = Type::tvoid; + + tb = e1->type->toBasetype(); + switch (tb->ty) + { case Tclass: + { TypeClass *tc = (TypeClass *)tb; + ClassDeclaration *cd = tc->sym; + + if (cd->isCOMinterface()) + { /* Because COM classes are deleted by IUnknown.Release() + */ + error("cannot delete instance of COM interface %s", cd->toChars()); + } + break; + } + case Tpointer: + tb = ((TypePointer *)tb)->next->toBasetype(); + if (tb->ty == Tstruct) + { + TypeStruct *ts = (TypeStruct *)tb; + StructDeclaration *sd = ts->sym; + FuncDeclaration *f = sd->aggDelete; + FuncDeclaration *fd = sd->dtor; + + if (!f && !fd) + break; + + /* Construct: + * ea = copy e1 to a tmp to do side effects only once + * eb = call destructor + * ec = call deallocator + */ + Expression *ea = NULL; + Expression *eb = NULL; + Expression *ec = NULL; + VarDeclaration *v; + + if (fd && f) + { Identifier *id = Lexer::idPool("__tmp"); + v = new VarDeclaration(loc, e1->type, id, new ExpInitializer(loc, e1)); + v->semantic(sc); + v->parent = sc->parent; + ea = new DeclarationExp(loc, v); + ea->type = v->type; + } + + if (fd) + { Expression *e = ea ? new VarExp(loc, v) : e1; + e = new DotVarExp(0, e, fd, 0); + eb = new CallExp(loc, e); + eb = eb->semantic(sc); + } + + if (f) + { + Type *tpv = Type::tvoid->pointerTo(); + Expression *e = ea ? new VarExp(loc, v) : e1->castTo(sc, tpv); + e = new CallExp(loc, new VarExp(loc, f), e); + ec = e->semantic(sc); + } + ea = combine(ea, eb); + ea = combine(ea, ec); + assert(ea); + return ea; + } + break; + + case Tarray: + /* BUG: look for deleting arrays of structs with dtors. + */ + break; + + default: + if (e1->op == TOKindex) + { + IndexExp *ae = (IndexExp *)(e1); + Type *tb1 = ae->e1->type->toBasetype(); + if (tb1->ty == Taarray) + break; + } + error("cannot delete type %s", e1->type->toChars()); + return new ErrorExp(); + } + + if (e1->op == TOKindex) + { + IndexExp *ae = (IndexExp *)(e1); + Type *tb1 = ae->e1->type->toBasetype(); + if (tb1->ty == Taarray) + { if (!global.params.useDeprecated) + error("delete aa[key] deprecated, use aa.remove(key)"); + } + } + + return this; +} + + +Expression *DeleteExp::checkToBoolean(Scope *sc) +{ + error("delete does not give a boolean result"); + return new ErrorExp(); +} + +void DeleteExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("delete "); + expToCBuffer(buf, hgs, e1, precedence[op]); +} + +/************************************************************/ + +CastExp::CastExp(Loc loc, Expression *e, Type *t) + : UnaExp(loc, TOKcast, sizeof(CastExp), e) +{ + to = t; + this->mod = ~0; +} + +#if DMDV2 +/* For cast(const) and cast(immutable) + */ +CastExp::CastExp(Loc loc, Expression *e, unsigned mod) + : UnaExp(loc, TOKcast, sizeof(CastExp), e) +{ + to = NULL; + this->mod = mod; +} +#endif + +Expression *CastExp::syntaxCopy() +{ + return to ? new CastExp(loc, e1->syntaxCopy(), to->syntaxCopy()) + : new CastExp(loc, e1->syntaxCopy(), mod); +} + + +Expression *CastExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("CastExp::semantic('%s')\n", toChars()); +#endif + +//static int x; assert(++x < 10); + + if (type) + return this; + UnaExp::semantic(sc); + if (e1->type) // if not a tuple + { + e1 = resolveProperties(sc, e1); + + if (!to) + { + /* Handle cast(const) and cast(immutable), etc. + */ + to = e1->type->castMod(mod); + } + else + to = to->semantic(loc, sc); + + if (!to->equals(e1->type)) + { +#if 0 // attempt at fixing 6720 + if (e1->type->ty == Tvoid) + { + error("cannot cast from void to %s", to->toChars()); + return new ErrorExp(); + } +#endif + Expression *e = op_overload(sc); + if (e) + { + return e->implicitCastTo(sc, to); + } + } + + if (e1->op == TOKtemplate) + { + error("cannot cast template %s to type %s", e1->toChars(), to->toChars()); + return new ErrorExp(); + } + + Type *t1b = e1->type->toBasetype(); + Type *tob = to->toBasetype(); + + if (e1->op == TOKfunction && + (tob->ty == Tdelegate || tob->ty == Tpointer && tob->nextOf()->ty == Tfunction)) + { + FuncExp *fe = (FuncExp *)e1; + Expression *e = NULL; + if (e1->type == Type::tvoid) + { + e = fe->inferType(sc, tob); + } + else if (e1->type->ty == Tpointer && e1->type->nextOf()->ty == Tfunction && + fe->tok == TOKreserved && + tob->ty == Tdelegate) + { + if (fe->implicitConvTo(tob)) + e = fe->castTo(sc, tob); + } + if (e) + e1 = e->semantic(sc); + } + + if (tob->ty == Tstruct && + !tob->equals(t1b) + ) + { + /* Look to replace: + * cast(S)t + * with: + * S(t) + */ + + // Rewrite as to.call(e1) + Expression *e = new TypeExp(loc, to); + e = new CallExp(loc, e, e1); + e = e->trySemantic(sc); + if (e) + return e; + } + + // Struct casts are possible only when the sizes match + // Same with static array -> static array + if (tob->ty == Tstruct || t1b->ty == Tstruct || + (tob->ty == Tsarray && t1b->ty == Tsarray)) + { + size_t fromsize = t1b->size(loc); + size_t tosize = tob->size(loc); + if (fromsize != tosize) + { + error("cannot cast from %s to %s", e1->type->toChars(), to->toChars()); + return new ErrorExp(); + } + } + + // Look for casting to a vector type + if (tob->ty == Tvector && t1b->ty != Tvector) + { + return new VectorExp(loc, e1, to); + } + } + else if (!to) + { error("cannot cast tuple"); + return new ErrorExp(); + } + + if (!e1->type) + { error("cannot cast %s", e1->toChars()); + return new ErrorExp(); + } + + // Check for unsafe casts + if (sc->func && !sc->intypeof) + { // Disallow unsafe casts + Type *tob = to->toBasetype(); + Type *t1b = e1->type->toBasetype(); + + // Implicit conversions are always safe + if (t1b->implicitConvTo(tob)) + goto Lsafe; + + if (!t1b->isMutable() && tob->isMutable()) + goto Lunsafe; + + if (t1b->isShared() && !tob->isShared()) + // Cast away shared + goto Lunsafe; + + if (!tob->hasPointers()) + goto Lsafe; + + if (tob->ty == Tclass && t1b->ty == Tclass) + { + ClassDeclaration *cdfrom = t1b->isClassHandle(); + ClassDeclaration *cdto = tob->isClassHandle(); + + int offset; + if (!cdfrom->isBaseOf(cdto, &offset)) + goto Lunsafe; + + if (cdfrom->isCPPinterface() || + cdto->isCPPinterface()) + goto Lunsafe; + + goto Lsafe; + } + + if (tob->ty == Tarray && t1b->ty == Tarray) + { + Type* tobn = tob->nextOf()->toBasetype(); + Type* t1bn = t1b->nextOf()->toBasetype(); + if (!tobn->hasPointers() && MODimplicitConv(t1bn->mod, tobn->mod)) + goto Lsafe; + } + if (tob->ty == Tpointer && t1b->ty == Tpointer) + { + Type* tobn = tob->nextOf()->toBasetype(); + Type* t1bn = t1b->nextOf()->toBasetype(); + if (!tobn->hasPointers() && + tobn->ty != Tfunction && t1bn->ty != Tfunction && + tobn->size() <= t1bn->size() && + MODimplicitConv(t1bn->mod, tobn->mod)) + goto Lsafe; + } + + Lunsafe: + if (sc->func->setUnsafe()) + { error("cast from %s to %s not allowed in safe code", e1->type->toChars(), to->toChars()); + return new ErrorExp(); + } + } + +Lsafe: + Expression *e = e1->castTo(sc, to); + return e; +} + + +void CastExp::checkEscape() +{ Type *tb = type->toBasetype(); + if (tb->ty == Tarray && e1->op == TOKvar && + e1->type->toBasetype()->ty == Tsarray) + { VarExp *ve = (VarExp *)e1; + VarDeclaration *v = ve->var->isVarDeclaration(); + if (v) + { + if (!v->isDataseg() && !v->isParameter()) + error("escaping reference to local %s", v->toChars()); + } + } +} + +void CastExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("cast("); +#if DMDV1 + to->toCBuffer(buf, NULL, hgs); +#else + if (to) + to->toCBuffer(buf, NULL, hgs); + else + { + MODtoBuffer(buf, mod); + } +#endif + buf->writeByte(')'); + expToCBuffer(buf, hgs, e1, precedence[op]); +} + + +/************************************************************/ + +VectorExp::VectorExp(Loc loc, Expression *e, Type *t) + : UnaExp(loc, TOKvector, sizeof(VectorExp), e) +{ + assert(t->ty == Tvector); + to = t; + dim = ~0; +} + +Expression *VectorExp::syntaxCopy() +{ + return new VectorExp(loc, e1->syntaxCopy(), to->syntaxCopy()); +} + +Expression *VectorExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("VectorExp::semantic('%s')\n", toChars()); +#endif + + if (type) + return this; + e1 = e1->semantic(sc); + type = to->semantic(loc, sc); + if (e1->op == TOKerror || type->ty == Terror) + return e1; + Type *tb = type->toBasetype(); + assert(tb->ty == Tvector); + TypeVector *tv = (TypeVector *)tb; + Type *te = tv->elementType(); + dim = tv->size(loc) / te->size(loc); + return this; +} + +void VectorExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("cast("); + to->toCBuffer(buf, NULL, hgs); + buf->writeByte(')'); + expToCBuffer(buf, hgs, e1, precedence[op]); +} + +/************************************************************/ + +SliceExp::SliceExp(Loc loc, Expression *e1, Expression *lwr, Expression *upr) + : UnaExp(loc, TOKslice, sizeof(SliceExp), e1) +{ + this->upr = upr; + this->lwr = lwr; + lengthVar = NULL; +} + +Expression *SliceExp::syntaxCopy() +{ + Expression *lwr = NULL; + if (this->lwr) + lwr = this->lwr->syntaxCopy(); + + Expression *upr = NULL; + if (this->upr) + upr = this->upr->syntaxCopy(); + + return new SliceExp(loc, e1->syntaxCopy(), lwr, upr); +} + +Expression *SliceExp::semantic(Scope *sc) +{ Expression *e; + AggregateDeclaration *ad; + //FuncDeclaration *fd; + ScopeDsymbol *sym; + +#if LOGSEMANTIC + printf("SliceExp::semantic('%s')\n", toChars()); +#endif + if (type) + return this; + +Lagain: + UnaExp::semantic(sc); + e1 = resolveProperties(sc, e1); + + e = this; + + Type *t = e1->type->toBasetype(); + if (t->ty == Tpointer) + { + if (!lwr || !upr) + { error("need upper and lower bound to slice pointer"); + return new ErrorExp(); + } + } + else if (t->ty == Tarray) + { + } + else if (t->ty == Tsarray) + { + } + else if (t->ty == Tclass) + { + ad = ((TypeClass *)t)->sym; + goto L1; + } + else if (t->ty == Tstruct) + { + ad = ((TypeStruct *)t)->sym; + + L1: + if (search_function(ad, Id::slice)) + { + // Rewrite as e1.slice(lwr, upr) + e = new DotIdExp(loc, e1, Id::slice); + + if (lwr) + { + assert(upr); + e = new CallExp(loc, e, lwr, upr); + } + else + { assert(!upr); + e = new CallExp(loc, e); + } + e = e->semantic(sc); + return e; + } + if (ad->aliasthis) + { + e1 = new DotIdExp(e1->loc, e1, ad->aliasthis->ident); + goto Lagain; + } + goto Lerror; + } + else if (t->ty == Ttuple) + { + if (!lwr && !upr) + return e1; + if (!lwr || !upr) + { error("need upper and lower bound to slice tuple"); + goto Lerror; + } + } + else if (t == Type::terror) + goto Lerr; + else + goto Lerror; + + { + Scope *sc2 = sc; + if (t->ty == Tsarray || t->ty == Tarray || t->ty == Ttuple) + { + sym = new ArrayScopeSymbol(sc, this); + sym->loc = loc; + sym->parent = sc->scopesym; + sc2 = sc->push(sym); + } + + if (lwr) + { lwr = lwr->semantic(sc2); + lwr = resolveProperties(sc2, lwr); + lwr = lwr->implicitCastTo(sc2, Type::tsize_t); + if (lwr->type == Type::terror) + goto Lerr; + } + if (upr) + { upr = upr->semantic(sc2); + upr = resolveProperties(sc2, upr); + upr = upr->implicitCastTo(sc2, Type::tsize_t); + if (upr->type == Type::terror) + goto Lerr; + } + + if (sc2 != sc) + sc2->pop(); + } + + if (t->ty == Ttuple) + { + lwr = lwr->optimize(WANTvalue | WANTinterpret); + upr = upr->optimize(WANTvalue | WANTinterpret); + uinteger_t i1 = lwr->toUInteger(); + uinteger_t i2 = upr->toUInteger(); + + size_t length; + TupleExp *te; + TypeTuple *tup; + + if (e1->op == TOKtuple) // slicing an expression tuple + { te = (TupleExp *)e1; + length = te->exps->dim; + } + else if (e1->op == TOKtype) // slicing a type tuple + { tup = (TypeTuple *)t; + length = Parameter::dim(tup->arguments); + } + else + assert(0); + + if (i1 <= i2 && i2 <= length) + { size_t j1 = (size_t) i1; + size_t j2 = (size_t) i2; + + if (e1->op == TOKtuple) + { Expressions *exps = new Expressions; + exps->setDim(j2 - j1); + for (size_t i = 0; i < j2 - j1; i++) + { Expression *e = (*te->exps)[j1 + i]; + (*exps)[i] = e; + } + if (j1 > 0 && j2 - j1 > 0 && sc->func && (*te->exps)[0]->op == TOKdotvar) + { + Expression *einit = ((DotVarExp *)(*te->exps)[0])->e1->isTemp(); + if (einit) + ((DotVarExp *)(*exps)[0])->e1 = einit; + } + e = new TupleExp(loc, exps); + } + else + { Parameters *args = new Parameters; + args->reserve(j2 - j1); + for (size_t i = j1; i < j2; i++) + { Parameter *arg = Parameter::getNth(tup->arguments, i); + args->push(arg); + } + e = new TypeExp(e1->loc, new TypeTuple(args)); + } + e = e->semantic(sc); + } + else + { + error("string slice [%ju .. %ju] is out of bounds", i1, i2); + goto Lerr; + } + return e; + } + + type = t->nextOf()->arrayOf(); + // Allow typedef[] -> typedef[] + if (type->equals(t)) + type = e1->type; + + return e; + +Lerror: + if (e1->op == TOKerror) + return e1; + char *s; + if (t->ty == Tvoid) + s = e1->toChars(); + else + s = t->toChars(); + error("%s cannot be sliced with []", s); +Lerr: + e = new ErrorExp(); + return e; +} + +void SliceExp::checkEscape() +{ + e1->checkEscape(); +} + +void SliceExp::checkEscapeRef() +{ + e1->checkEscapeRef(); +} + +#if DMDV2 +int SliceExp::isLvalue() +{ + return 1; +} +#endif + +Expression *SliceExp::toLvalue(Scope *sc, Expression *e) +{ + return this; +} + +Expression *SliceExp::modifiableLvalue(Scope *sc, Expression *e) +{ + error("slice expression %s is not a modifiable lvalue", toChars()); + return this; +} + +int SliceExp::isBool(int result) +{ + return e1->isBool(result); +} + +void SliceExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, precedence[op]); + buf->writeByte('['); + if (upr || lwr) + { + if (lwr) + expToCBuffer(buf, hgs, lwr, PREC_assign); + else + buf->writeByte('0'); + buf->writestring(".."); + if (upr) + expToCBuffer(buf, hgs, upr, PREC_assign); + else + buf->writestring("length"); // BUG: should be array.length + } + buf->writeByte(']'); +} + +/********************** ArrayLength **************************************/ + +ArrayLengthExp::ArrayLengthExp(Loc loc, Expression *e1) + : UnaExp(loc, TOKarraylength, sizeof(ArrayLengthExp), e1) +{ +} + +Expression *ArrayLengthExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("ArrayLengthExp::semantic('%s')\n", toChars()); +#endif + if (!type) + { + UnaExp::semantic(sc); + e1 = resolveProperties(sc, e1); + + type = Type::tsize_t; + } + return this; +} + +Expression *opAssignToOp(Loc loc, enum TOK op, Expression *e1, Expression *e2) +{ Expression *e; + + switch (op) + { + case TOKaddass: e = new AddExp(loc, e1, e2); break; + case TOKminass: e = new MinExp(loc, e1, e2); break; + case TOKmulass: e = new MulExp(loc, e1, e2); break; + case TOKdivass: e = new DivExp(loc, e1, e2); break; + case TOKmodass: e = new ModExp(loc, e1, e2); break; + case TOKandass: e = new AndExp(loc, e1, e2); break; + case TOKorass: e = new OrExp (loc, e1, e2); break; + case TOKxorass: e = new XorExp(loc, e1, e2); break; + case TOKshlass: e = new ShlExp(loc, e1, e2); break; + case TOKshrass: e = new ShrExp(loc, e1, e2); break; + case TOKushrass: e = new UshrExp(loc, e1, e2); break; + default: assert(0); + } + return e; +} + +/********************* + * Rewrite: + * array.length op= e2 + * as: + * array.length = array.length op e2 + * or: + * auto tmp = &array; + * (*tmp).length = (*tmp).length op e2 + */ + +Expression *ArrayLengthExp::rewriteOpAssign(BinExp *exp) +{ Expression *e; + + assert(exp->e1->op == TOKarraylength); + ArrayLengthExp *ale = (ArrayLengthExp *)exp->e1; + if (ale->e1->op == TOKvar) + { e = opAssignToOp(exp->loc, exp->op, ale, exp->e2); + e = new AssignExp(exp->loc, ale->syntaxCopy(), e); + } + else + { + /* auto tmp = &array; + * (*tmp).length = (*tmp).length op e2 + */ + Identifier *id = Lexer::uniqueId("__arraylength"); + ExpInitializer *ei = new ExpInitializer(ale->loc, new AddrExp(ale->loc, ale->e1)); + VarDeclaration *tmp = new VarDeclaration(ale->loc, ale->e1->type->pointerTo(), id, ei); + + Expression *e1 = new ArrayLengthExp(ale->loc, new PtrExp(ale->loc, new VarExp(ale->loc, tmp))); + Expression *elvalue = e1->syntaxCopy(); + e = opAssignToOp(exp->loc, exp->op, e1, exp->e2); + e = new AssignExp(exp->loc, elvalue, e); + e = new CommaExp(exp->loc, new DeclarationExp(ale->loc, tmp), e); + } + return e; +} + +void ArrayLengthExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, PREC_primary); + buf->writestring(".length"); +} + +/*********************** ArrayExp *************************************/ + +// e1 [ i1, i2, i3, ... ] + +ArrayExp::ArrayExp(Loc loc, Expression *e1, Expressions *args) + : UnaExp(loc, TOKarray, sizeof(ArrayExp), e1) +{ + arguments = args; + lengthVar = NULL; + currentDimension = 0; +} + +Expression *ArrayExp::syntaxCopy() +{ + return new ArrayExp(loc, e1->syntaxCopy(), arraySyntaxCopy(arguments)); +} + +Expression *ArrayExp::semantic(Scope *sc) +{ Expression *e; + Type *t1; + +#if LOGSEMANTIC + printf("ArrayExp::semantic('%s')\n", toChars()); +#endif + UnaExp::semantic(sc); + e1 = resolveProperties(sc, e1); + + t1 = e1->type->toBasetype(); + if (t1->ty != Tclass && t1->ty != Tstruct) + { // Convert to IndexExp + if (arguments->dim != 1) + { error("only one index allowed to index %s", t1->toChars()); + goto Lerr; + } + e = new IndexExp(loc, e1, arguments->tdata()[0]); + return e->semantic(sc); + } + + e = op_overload(sc); + if (!e) + { error("no [] operator overload for type %s", e1->type->toChars()); + goto Lerr; + } + return e; + +Lerr: + return new ErrorExp(); +} + +#if DMDV2 +int ArrayExp::isLvalue() +{ + if (type && type->toBasetype()->ty == Tvoid) + return 0; + return 1; +} +#endif + +Expression *ArrayExp::toLvalue(Scope *sc, Expression *e) +{ + if (type && type->toBasetype()->ty == Tvoid) + error("voids have no value"); + return this; +} + + +void ArrayExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, PREC_primary); + buf->writeByte('['); + argsToCBuffer(buf, arguments, hgs); + buf->writeByte(']'); +} + +/************************* DotExp ***********************************/ + +DotExp::DotExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKdotexp, sizeof(DotExp), e1, e2) +{ +} + +Expression *DotExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("DotExp::semantic('%s')\n", toChars()); + if (type) printf("\ttype = %s\n", type->toChars()); +#endif + e1 = e1->semantic(sc); + e2 = e2->semantic(sc); + if (e2->op == TOKimport) + { + ScopeExp *se = (ScopeExp *)e2; + TemplateDeclaration *td = se->sds->isTemplateDeclaration(); + if (td) + { Expression *e = new DotTemplateExp(loc, e1, td); + e = e->semantic(sc); + return e; + } + } + if (!type) + type = e2->type; + return this; +} + +void DotExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, PREC_primary); + buf->writeByte('.'); + expToCBuffer(buf, hgs, e2, PREC_primary); +} + +/************************* CommaExp ***********************************/ + +CommaExp::CommaExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKcomma, sizeof(CommaExp), e1, e2) +{ +} + +Expression *CommaExp::semantic(Scope *sc) +{ + if (!type) + { BinExp::semanticp(sc); + e1 = e1->addDtorHook(sc); + type = e2->type; + } + return this; +} + +void CommaExp::checkEscape() +{ + e2->checkEscape(); +} + +void CommaExp::checkEscapeRef() +{ + e2->checkEscapeRef(); +} + +#if DMDV2 +int CommaExp::isLvalue() +{ + return e2->isLvalue(); +} +#endif + +Expression *CommaExp::toLvalue(Scope *sc, Expression *e) +{ + e2 = e2->toLvalue(sc, NULL); + return this; +} + +Expression *CommaExp::modifiableLvalue(Scope *sc, Expression *e) +{ + e2 = e2->modifiableLvalue(sc, e); + return this; +} + +int CommaExp::isBool(int result) +{ + return e2->isBool(result); +} + + +Expression *CommaExp::addDtorHook(Scope *sc) +{ + e2 = e2->addDtorHook(sc); + return this; +} + +/************************** IndexExp **********************************/ + +// e1 [ e2 ] + +IndexExp::IndexExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKindex, sizeof(IndexExp), e1, e2) +{ + //printf("IndexExp::IndexExp('%s')\n", toChars()); + lengthVar = NULL; + modifiable = 0; // assume it is an rvalue +} + +Expression *IndexExp::semantic(Scope *sc) +{ Expression *e; + Type *t1; + ScopeDsymbol *sym; + +#if LOGSEMANTIC + printf("IndexExp::semantic('%s')\n", toChars()); +#endif + if (type) + return this; + if (!e1->type) + e1 = e1->semantic(sc); + assert(e1->type); // semantic() should already be run on it + if (e1->op == TOKerror) + goto Lerr; + e = this; + + // Note that unlike C we do not implement the int[ptr] + + t1 = e1->type->toBasetype(); + + if (t1->ty == Tsarray || t1->ty == Tarray || t1->ty == Ttuple) + { // Create scope for 'length' variable + sym = new ArrayScopeSymbol(sc, this); + sym->loc = loc; + sym->parent = sc->scopesym; + sc = sc->push(sym); + } + + e2 = e2->semantic(sc); + e2 = resolveProperties(sc, e2); + if (e2->type == Type::terror) + goto Lerr; + if (e2->type->ty == Ttuple && ((TupleExp *)e2)->exps->dim == 1) // bug 4444 fix + e2 = ((TupleExp *)e2)->exps->tdata()[0]; + + if (t1->ty == Tsarray || t1->ty == Tarray || t1->ty == Ttuple) + sc = sc->pop(); + + switch (t1->ty) + { + case Tpointer: + case Tarray: + e2 = e2->implicitCastTo(sc, Type::tsize_t); + e->type = ((TypeNext *)t1)->next; + break; + + case Tsarray: + { + e2 = e2->implicitCastTo(sc, Type::tsize_t); + + TypeSArray *tsa = (TypeSArray *)t1; + +#if 0 // Don't do now, because it might be short-circuit evaluated + // Do compile time array bounds checking if possible + e2 = e2->optimize(WANTvalue); + if (e2->op == TOKint64) + { + dinteger_t index = e2->toInteger(); + dinteger_t length = tsa->dim->toInteger(); + if (index < 0 || index >= length) + error("array index [%lld] is outside array bounds [0 .. %lld]", + index, length); + } +#endif + e->type = t1->nextOf(); + break; + } + + case Taarray: + { TypeAArray *taa = (TypeAArray *)t1; + /* We can skip the implicit conversion if they differ only by + * constness (Bugzilla 2684, see also bug 2954b) + */ + if (!arrayTypeCompatibleWithoutCasting(e2->loc, e2->type, taa->index)) + { + e2 = e2->implicitCastTo(sc, taa->index); // type checking + } + type = taa->next; + break; + } + + case Ttuple: + { + e2 = e2->implicitCastTo(sc, Type::tsize_t); + e2 = e2->optimize(WANTvalue | WANTinterpret); + uinteger_t index = e2->toUInteger(); + size_t length; + TupleExp *te; + TypeTuple *tup; + + if (e1->op == TOKtuple) + { te = (TupleExp *)e1; + length = te->exps->dim; + } + else if (e1->op == TOKtype) + { + tup = (TypeTuple *)t1; + length = Parameter::dim(tup->arguments); + } + else + assert(0); + + if (index < length) + { + + if (e1->op == TOKtuple) + { + e = (*te->exps)[(size_t)index]; + if (sc->func && (*te->exps)[0]->op == TOKdotvar) + { + Expression *einit = ((DotVarExp *)(*te->exps)[0])->e1->isTemp(); + if (einit) + ((DotVarExp *)e)->e1 = einit; + } + } + else + e = new TypeExp(e1->loc, Parameter::getNth(tup->arguments, (size_t)index)->type); + } + else + { + error("array index [%ju] is outside array bounds [0 .. %zu]", + index, length); + e = e1; + } + break; + } + + default: + if (e1->op == TOKerror) + goto Lerr; + error("%s must be an array or pointer type, not %s", + e1->toChars(), e1->type->toChars()); + case Terror: + goto Lerr; + } + return e; + +Lerr: + return new ErrorExp(); +} + +#if DMDV2 +int IndexExp::isLvalue() +{ + return 1; +} +#endif + +Expression *IndexExp::toLvalue(Scope *sc, Expression *e) +{ +// if (type && type->toBasetype()->ty == Tvoid) +// error("voids have no value"); + return this; +} + +Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e) +{ + //printf("IndexExp::modifiableLvalue(%s)\n", toChars()); + modifiable = 1; + if (e1->op == TOKstring) + error("string literals are immutable"); + if (type && (!type->isMutable() || !type->isAssignable())) + error("%s isn't mutable", e->toChars()); + Type *t1 = e1->type->toBasetype(); + if (t1->ty == Taarray) + { TypeAArray *taa = (TypeAArray *)t1; + Type *t2b = e2->type->toBasetype(); + if (t2b->ty == Tarray && t2b->nextOf()->isMutable()) + error("associative arrays can only be assigned values with immutable keys, not %s", e2->type->toChars()); + e1 = e1->modifiableLvalue(sc, e1); + } + return toLvalue(sc, e); +} + +void IndexExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, PREC_primary); + buf->writeByte('['); + expToCBuffer(buf, hgs, e2, PREC_assign); + buf->writeByte(']'); +} + + +/************************* PostExp ***********************************/ + +PostExp::PostExp(enum TOK op, Loc loc, Expression *e) + : BinExp(loc, op, sizeof(PostExp), e, + new IntegerExp(loc, 1, Type::tint32)) +{ +} + +Expression *PostExp::semantic(Scope *sc) +{ Expression *e = this; + + if (!type) + { + BinExp::semantic(sc); + e1 = resolveProperties(sc, e1); + + e = op_overload(sc); + if (e) + return e; + + e1 = e1->modifiableLvalue(sc, e1); + + Type *t1 = e1->type->toBasetype(); + if (t1->ty == Tclass || t1->ty == Tstruct) + { /* Check for operator overloading, + * but rewrite in terms of ++e instead of e++ + */ + + /* If e1 is not trivial, take a reference to it + */ + Expression *de = NULL; + if (e1->op != TOKvar) + { + // ref v = e1; + Identifier *id = Lexer::uniqueId("__postref"); + ExpInitializer *ei = new ExpInitializer(loc, e1); + VarDeclaration *v = new VarDeclaration(loc, e1->type, id, ei); + v->storage_class |= STCref | STCforeach; + de = new DeclarationExp(loc, v); + e1 = new VarExp(e1->loc, v); + } + + /* Rewrite as: + * auto tmp = e1; ++e1; tmp + */ + Identifier *id = Lexer::uniqueId("__pitmp"); + ExpInitializer *ei = new ExpInitializer(loc, e1); + VarDeclaration *tmp = new VarDeclaration(loc, e1->type, id, ei); + Expression *ea = new DeclarationExp(loc, tmp); + + Expression *eb = e1->syntaxCopy(); + eb = new PreExp(op == TOKplusplus ? TOKpreplusplus : TOKpreminusminus, loc, eb); + + Expression *ec = new VarExp(loc, tmp); + + // Combine de,ea,eb,ec + if (de) + ea = new CommaExp(loc, de, ea); + e = new CommaExp(loc, ea, eb); + e = new CommaExp(loc, e, ec); + e = e->semantic(sc); + return e; + } + + e = this; + e1->checkScalar(); + e1->checkNoBool(); + if (e1->type->ty == Tpointer) + e = scaleFactor(sc); + else + e2 = e2->castTo(sc, e1->type); + e->type = e1->type; + } + return e; +} + +void PostExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, precedence[op]); + buf->writestring(Token::toChars(op)); +} + +/************************* PreExp ***********************************/ + +PreExp::PreExp(enum TOK op, Loc loc, Expression *e) + : UnaExp(loc, op, sizeof(PreExp), e) +{ +} + +Expression *PreExp::semantic(Scope *sc) +{ + Expression *e; + + e = op_overload(sc); + if (e) + return e; + + // Rewrite as e1+=1 or e1-=1 + if (op == TOKpreplusplus) + e = new AddAssignExp(loc, e1, new IntegerExp(loc, 1, Type::tint32)); + else + e = new MinAssignExp(loc, e1, new IntegerExp(loc, 1, Type::tint32)); + return e->semantic(sc); +} + +void PreExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(Token::toChars(op)); + expToCBuffer(buf, hgs, e1, precedence[op]); +} + +/************************************************************/ + +/* op can be TOKassign, TOKconstruct, or TOKblit */ + +AssignExp::AssignExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKassign, sizeof(AssignExp), e1, e2) +{ + ismemset = 0; +} + +Expression *AssignExp::semantic(Scope *sc) +{ + Expression *e1old = e1; + +#if LOGSEMANTIC + printf("AssignExp::semantic('%s')\n", toChars()); +#endif + //printf("e1->op = %d, '%s'\n", e1->op, Token::toChars(e1->op)); + //printf("e2->op = %d, '%s'\n", e2->op, Token::toChars(e2->op)); + + if (type) + return this; + + if (e2->op == TOKcomma) + { /* Rewrite to get rid of the comma from rvalue + */ + AssignExp *ea = new AssignExp(loc, e1, ((CommaExp *)e2)->e2); + ea->op = op; + Expression *e = new CommaExp(loc, ((CommaExp *)e2)->e1, ea); + return e->semantic(sc); + } + + /* Look for operator overloading of a[i]=value. + * Do it before semantic() otherwise the a[i] will have been + * converted to a.opIndex() already. + */ + if (e1->op == TOKarray) + { + ArrayExp *ae = (ArrayExp *)e1; + AggregateDeclaration *ad = NULL; + Identifier *id = Id::index; + + ae->e1 = ae->e1->semantic(sc); + Type *t1 = ae->e1->type->toBasetype(); + if (t1->ty == Tstruct) + { + ad = ((TypeStruct *)t1)->sym; + goto L1; + } + else if (t1->ty == Tclass) + { + ad = ((TypeClass *)t1)->sym; + L1: + // Rewrite (a[i] = value) to (a.opIndexAssign(value, i)) + if (search_function(ad, Id::indexass)) + { Expression *e = new DotIdExp(loc, ae->e1, Id::indexass); + // Deal with $ + for (size_t i = 0; i < ae->arguments->dim; i++) + { Expression *x = ae->arguments->tdata()[i]; + // Create scope for '$' variable for this dimension + ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae); + sym->loc = loc; + sym->parent = sc->scopesym; + sc = sc->push(sym); + ae->lengthVar = NULL; // Create it only if required + ae->currentDimension = i; // Dimension for $, if required + + x = x->semantic(sc); + if (!x->type) + ae->error("%s has no value", x->toChars()); + if (ae->lengthVar) + { // If $ was used, declare it now + Expression *av = new DeclarationExp(ae->loc, ae->lengthVar); + x = new CommaExp(0, av, x); + x->semantic(sc); + } + ae->arguments->tdata()[i] = x; + sc = sc->pop(); + } + Expressions *a = (Expressions *)ae->arguments->copy(); + + a->insert(0, e2); + e = new CallExp(loc, e, a); + e = e->semantic(sc); + return e; + } +#if 0 // Turned off to allow rewriting (a[i]=value) to (a.opIndex(i)=value) + else + { + // Rewrite (a[i] = value) to (a.opIndex(i, value)) + if (search_function(ad, id)) + { Expression *e = new DotIdExp(loc, ae->e1, id); + + if (1 || !global.params.useDeprecated) + { error("operator [] assignment overload with opIndex(i, value) illegal, use opIndexAssign(value, i)"); + return new ErrorExp(); + } + + e = new CallExp(loc, e, ae->arguments->tdata()[0], e2); + e = e->semantic(sc); + return e; + } + } +#endif + } + + // No opIndexAssign found yet, but there might be an alias this to try. + if (ad && ad->aliasthis) + { Expression *at = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); + at = at->semantic(sc); + Type *attype = at->type->toBasetype(); + + if (attype->ty == Tstruct) + { + ad = ((TypeStruct *)attype)->sym; + goto L1; + } + else if (attype->ty == Tclass) + { + ad = ((TypeClass *)attype)->sym; + goto L1; + } + } + } + /* Look for operator overloading of a[i..j]=value. + * Do it before semantic() otherwise the a[i..j] will have been + * converted to a.opSlice() already. + */ + if (e1->op == TOKslice) + { Type *t1; + SliceExp *ae = (SliceExp *)e1; + AggregateDeclaration *ad = NULL; + Identifier *id = Id::index; + + ae->e1 = ae->e1->semantic(sc); + ae->e1 = resolveProperties(sc, ae->e1); + t1 = ae->e1->type->toBasetype(); + if (t1->ty == Tstruct) + { + ad = ((TypeStruct *)t1)->sym; + goto L2; + } + else if (t1->ty == Tclass) + { + ad = ((TypeClass *)t1)->sym; + L2: + // Rewrite (a[i..j] = value) to (a.opSliceAssign(value, i, j)) + if (search_function(ad, Id::sliceass)) + { Expression *e = new DotIdExp(loc, ae->e1, Id::sliceass); + Expressions *a = new Expressions(); + + a->push(e2); + if (ae->lwr) + { a->push(ae->lwr); + assert(ae->upr); + a->push(ae->upr); + } + else + assert(!ae->upr); + e = new CallExp(loc, e, a); + e = e->semantic(sc); + return e; + } + } + + // No opSliceAssign found yet, but there might be an alias this to try. + if (ad && ad->aliasthis) + { Expression *at = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); + at = at->semantic(sc); + Type *attype = at->type->toBasetype(); + + if (attype->ty == Tstruct) + { + ad = ((TypeStruct *)attype)->sym; + goto L2; + } + else if (attype->ty == Tclass) + { + ad = ((TypeClass *)attype)->sym; + goto L2; + } + } + } + + { + Expression *e = BinExp::semantic(sc); + if (e->op == TOKerror) + return e; + } + + e2 = resolveProperties(sc, e2); + + /* We have f = value. + * Could mean: + * f(value) + * or: + * f() = value + */ + TemplateDeclaration *td; + Objects *targsi; + FuncDeclaration *fd; + Expression *ethis; + if (e1->op == TOKdotti) + { + DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1; + td = dti->getTempdecl(sc); + dti->ti->semanticTiargs(sc); + targsi = dti->ti->tiargs; + ethis = dti->e1; + goto L3; + } + else if (e1->op == TOKdottd) + { + DotTemplateExp *dte = (DotTemplateExp *)e1; + td = dte->td; + targsi = NULL; + ethis = dte->e1; + goto L3; + } + else if (e1->op == TOKtemplate) + { + td = ((TemplateExp *)e1)->td; + targsi = NULL; + ethis = NULL; + L3: + { + assert(td); + Expressions a; + a.push(e2); + + fd = td->deduceFunctionTemplate(sc, loc, targsi, ethis, &a, 1); + if (fd && fd->type) + goto Lsetter; + + fd = td->deduceFunctionTemplate(sc, loc, targsi, ethis, NULL, 1); + if (fd && fd->type) + goto Lgetter; + } + goto Leprop; + } + else if (e1->op == TOKdotvar && e1->type->toBasetype()->ty == Tfunction) + { + DotVarExp *dve = (DotVarExp *)e1; + fd = dve->var->isFuncDeclaration(); + ethis = dve->e1; + goto L4; + } + else if (e1->op == TOKvar && e1->type->toBasetype()->ty == Tfunction) + { + fd = ((VarExp *)e1)->var->isFuncDeclaration(); + ethis = NULL; + L4: + { + assert(fd); + FuncDeclaration *f = fd; + Expressions a; + a.push(e2); + + fd = f->overloadResolve(loc, ethis, &a, 1); + if (fd && fd->type) + goto Lsetter; + + fd = f->overloadResolve(loc, ethis, NULL, 1); + if (fd && fd->type) + goto Lgetter; + + goto Leprop; + } + + Expression *e; + TypeFunction *tf; + + Lsetter: + assert(fd->type->ty == Tfunction); + tf = (TypeFunction *)fd->type; + if (!tf->isproperty && global.params.enforcePropertySyntax) + goto Leprop; + e = new CallExp(loc, e1, e2); + return e->semantic(sc); + + Lgetter: + assert(fd->type->ty == Tfunction); + tf = (TypeFunction *)fd->type; + if (!tf->isref) + goto Leprop; + if (!tf->isproperty && global.params.enforcePropertySyntax) + goto Leprop; + e = new CallExp(loc, e1); + e = new AssignExp(loc, e, e2); + return e->semantic(sc); + + Leprop: + ::error(e1->loc, "not a property %s", e1->toChars()); + return new ErrorExp(); + } + + assert(e1->type); + + /* Rewrite tuple assignment as a tuple of assignments. + */ +Ltupleassign: + if (e1->op == TOKtuple && e2->op == TOKtuple) + { TupleExp *tup1 = (TupleExp *)e1; + TupleExp *tup2 = (TupleExp *)e2; + size_t dim = tup1->exps->dim; + if (dim != tup2->exps->dim) + { + error("mismatched tuple lengths, %d and %d", (int)dim, (int)tup2->exps->dim); + return new ErrorExp(); + } + else + { Expressions *exps = new Expressions; + exps->setDim(dim); + + for (size_t i = 0; i < dim; i++) + { Expression *ex1 = (*tup1->exps)[i]; + Expression *ex2 = (*tup2->exps)[i]; + (*exps)[i] = new AssignExp(loc, ex1, ex2); + } + Expression *e = new TupleExp(loc, exps); + e = e->semantic(sc); + return e; + } + } + + if (e1->op == TOKtuple) + { + if (TupleDeclaration *td = isAliasThisTuple(e2)) + { + assert(e1->type->ty == Ttuple); + TypeTuple *tt = (TypeTuple *)e1->type; + + Identifier *id = Lexer::uniqueId("__tup"); + VarDeclaration *v = new VarDeclaration(e2->loc, NULL, id, new ExpInitializer(e2->loc, e2)); + v->storage_class = STCctfe | STCref | STCforeach; + Expression *ve = new VarExp(e2->loc, v); + ve->type = e2->type; + + Expressions *iexps = new Expressions(); + iexps->push(ve); + + for (size_t u = 0; u < iexps->dim ; u++) + { + Lexpand: + Expression *e = iexps->tdata()[u]; + + Parameter *arg = Parameter::getNth(tt->arguments, u); + //printf("[%d] iexps->dim = %d, ", u, iexps->dim); + //printf("e = (%s %s, %s), ", Token::tochars[e->op], e->toChars(), e->type->toChars()); + //printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars()); + + if (!e->type->implicitConvTo(arg->type)) + { + // expand initializer to tuple + if (expandAliasThisTuples(iexps, u) != -1) + goto Lexpand; + + goto Lnomatch; + } + } + iexps->tdata()[0] = new CommaExp(loc, new DeclarationExp(e2->loc, v), iexps->tdata()[0]); + e2 = new TupleExp(e2->loc, iexps); + e2 = e2->semantic(sc); + goto Ltupleassign; + + Lnomatch: + ; + } + } + + // Determine if this is an initialization of a reference + int refinit = 0; + if (op == TOKconstruct && e1->op == TOKvar) + { VarExp *ve = (VarExp *)e1; + VarDeclaration *v = ve->var->isVarDeclaration(); + if (v->storage_class & (STCout | STCref)) + refinit = 1; + } + + Type *t1 = e1->type->toBasetype(); + + if (t1->ty == Tdelegate || (t1->ty == Tpointer && t1->nextOf()->ty == Tfunction) + && e2->op == TOKfunction) + { + FuncExp *fe = (FuncExp *)e2; + if (e2->type == Type::tvoid) + { + e2 = fe->inferType(sc, t1); + } + else if (e2->type->ty == Tpointer && e2->type->nextOf()->ty == Tfunction && + fe->tok == TOKreserved && + t1->ty == Tdelegate) + { + if (fe->implicitConvTo(t1)) + e2 = fe->castTo(sc, t1); + } + if (!e2) + { error("cannot infer function literal type from %s", t1->toChars()); + e2 = new ErrorExp(); + } + } + + /* If it is an assignment from a 'foreign' type, + * check for operator overloading. + */ + if (t1->ty == Tstruct) + { + StructDeclaration *sd = ((TypeStruct *)t1)->sym; + if (op == TOKassign) + { + /* See if we need to set ctorinit, i.e. track + * assignments to fields. An assignment to a field counts even + * if done through an opAssign overload. + */ + if (e1->op == TOKdotvar) + { DotVarExp *dve = (DotVarExp *)e1; + VarDeclaration *v = dve->var->isVarDeclaration(); + if (v && v->storage_class & STCnodefaultctor) + modifyFieldVar(loc, sc, v, dve->e1); + } + + Expression *e = op_overload(sc); + if (e && e1->op == TOKindex && + ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) + { + // Deal with AAs (Bugzilla 2451) + // Rewrite as: + // e1 = (typeof(aa.value) tmp = void, tmp = e2, tmp); + Type * aaValueType = ((TypeAArray *)((IndexExp*)e1)->e1->type->toBasetype())->next; + Identifier *id = Lexer::uniqueId("__aatmp"); + VarDeclaration *v = new VarDeclaration(loc, aaValueType, + id, new VoidInitializer(0)); + v->storage_class |= STCctfe; + v->semantic(sc); + v->parent = sc->parent; + + Expression *de = new DeclarationExp(loc, v); + VarExp *ve = new VarExp(loc, v); + + AssignExp *ae = new AssignExp(loc, ve, e2); + e = ae->op_overload(sc); + e2 = new CommaExp(loc, new CommaExp(loc, de, e), ve); + e2 = e2->semantic(sc); + } + else if (e) + return e; + } + else if (op == TOKconstruct && !refinit) + { Type *t2 = e2->type->toBasetype(); + if (t2->ty == Tstruct && + sd == ((TypeStruct *)t2)->sym && + sd->cpctor) + { /* We have a copy constructor for this + */ + // Scan past commma's + Expression *ec = NULL; + while (e2->op == TOKcomma) + { CommaExp *ecomma = (CommaExp *)e2; + e2 = ecomma->e2; + if (ec) + ec = new CommaExp(ecomma->loc, ec, ecomma->e1); + else + ec = ecomma->e1; + } + if (e2->op == TOKquestion) + { /* Write as: + * a ? e1 = b : e1 = c; + */ + CondExp *econd = (CondExp *)e2; + AssignExp *ea1 = new AssignExp(econd->e1->loc, e1, econd->e1); + ea1->op = op; + AssignExp *ea2 = new AssignExp(econd->e1->loc, e1, econd->e2); + ea2->op = op; + Expression *e = new CondExp(loc, econd->econd, ea1, ea2); + if (ec) + e = new CommaExp(loc, ec, e); + return e->semantic(sc); + } + else if (e2->op == TOKvar || + e2->op == TOKdotvar || + e2->op == TOKstar || + e2->op == TOKthis || + e2->op == TOKindex) + { /* Write as: + * e1.cpctor(e2); + */ + if (!e2->type->implicitConvTo(e1->type)) + error("conversion error from %s to %s", e2->type->toChars(), e1->type->toChars()); + + Expression *e = new DotVarExp(loc, e1, sd->cpctor, 0); + e = new CallExp(loc, e, e2); + if (ec) + e = new CommaExp(loc, ec, e); + return e->semantic(sc); + } + else if (e2->op == TOKcall) + { + /* The struct value returned from the function is transferred + * so should not call the destructor on it. + */ + valueNoDtor(e2); + } + } + } + } + else if (t1->ty == Tclass) + { // Disallow assignment operator overloads for same type + if (!e2->implicitConvTo(e1->type)) + { + Expression *e = op_overload(sc); + if (e) + return e; + } + } + + if (t1->ty == Tsarray && !refinit) + { + if (e1->op == TOKindex && + ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) + { + // Assignment to an AA of fixed-length arrays. + // Convert T[n][U] = T[] into T[n][U] = T[n] + e2 = e2->implicitCastTo(sc, e1->type); + if (e2->type == Type::terror) + return e2; + } + else + { + Type *t2 = e2->type->toBasetype(); + // Convert e2 to e2[], unless e2-> e1[0] + if (t2->ty == Tsarray && !t2->implicitConvTo(t1->nextOf())) + { + e2 = new SliceExp(e2->loc, e2, NULL, NULL); + e2 = e2->semantic(sc); + } + + // Convert e1 to e1[] + Expression *e = new SliceExp(e1->loc, e1, NULL, NULL); + e1 = e->semantic(sc); + t1 = e1->type->toBasetype(); + } + } + + if (!e2->rvalue()) + return new ErrorExp(); + + if (e1->op == TOKarraylength) + { + // e1 is not an lvalue, but we let code generator handle it + ArrayLengthExp *ale = (ArrayLengthExp *)e1; + + ale->e1 = ale->e1->modifiableLvalue(sc, e1); + } + else if (e1->op == TOKslice) + { + Type *tn = e1->type->nextOf(); + if (op == TOKassign && tn && (!tn->isMutable() || !tn->isAssignable())) + { error("slice %s is not mutable", e1->toChars()); + return new ErrorExp(); + } + } + else + { // Try to do a decent error message with the expression + // before it got constant folded + if (e1->op != TOKvar) + e1 = e1->optimize(WANTvalue); + + if (op != TOKconstruct) + e1 = e1->modifiableLvalue(sc, e1old); + } + + Type *t2 = e2->type->toBasetype(); +#if 0 + if (t1->ty == Tvector && t2->ty != Tvector && + e2->implicitConvTo(((TypeVector *)t1)->basetype->nextOf()) + ) + { // memset + ismemset = 1; // make it easy for back end to tell what this is + e2 = e2->implicitCastTo(sc, ((TypeVector *)t1)->basetype->nextOf()); + } + else +#endif + if (e1->op == TOKslice && + t1->nextOf() && + e2->implicitConvTo(t1->nextOf()) + ) + { // memset + ismemset = 1; // make it easy for back end to tell what this is + e2 = e2->implicitCastTo(sc, t1->nextOf()); + } + else if (t1->ty == Tsarray) + { + /* Should have already converted e1 => e1[] + * unless it is an AA + */ + if (!(e1->op == TOKindex && t2->ty == Tsarray && + ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)) + { + assert(op == TOKconstruct); + } + //error("cannot assign to static array %s", e1->toChars()); + } + else if (e1->op == TOKslice && t2->ty == Tarray && + t2->nextOf()->implicitConvTo(t1->nextOf())) + { + e2 = e2->implicitCastTo(sc, e1->type->constOf()); + } + else + { + e2 = e2->implicitCastTo(sc, e1->type); + } + + /* Look for array operations + */ + if (e1->op == TOKslice && !ismemset && + (e2->op == TOKadd || e2->op == TOKmin || + e2->op == TOKmul || e2->op == TOKdiv || + e2->op == TOKmod || e2->op == TOKxor || + e2->op == TOKand || e2->op == TOKor || +#if DMDV2 + e2->op == TOKpow || +#endif + e2->op == TOKtilde || e2->op == TOKneg)) + { + type = e1->type; + return arrayOp(sc); + } + + if (e1->op == TOKvar && + (((VarExp *)e1)->var->storage_class & STCscope) && + op == TOKassign) + { + error("cannot rebind scope variables"); + } + + type = e1->type; + assert(type); + return this; +} + +Expression *AssignExp::checkToBoolean(Scope *sc) +{ + // Things like: + // if (a = b) ... + // are usually mistakes. + + error("assignment cannot be used as a condition, perhaps == was meant?"); + return new ErrorExp(); +} + +/************************************************************/ + +ConstructExp::ConstructExp(Loc loc, Expression *e1, Expression *e2) + : AssignExp(loc, e1, e2) +{ + op = TOKconstruct; +} + +/************************************************************/ + +AddAssignExp::AddAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKaddass, sizeof(AddAssignExp), e1, e2) +{ +} + +Expression *AddAssignExp::semantic(Scope *sc) +{ Expression *e; + + if (type) + return this; + + e = op_overload(sc); + if (e) + return e; + + Type *tb1 = e1->type->toBasetype(); + Type *tb2 = e2->type->toBasetype(); + + if (e1->op == TOKarraylength) + { + e = ArrayLengthExp::rewriteOpAssign(this); + e = e->semantic(sc); + return e; + } + + if (e1->op == TOKslice) + { + e = typeCombine(sc); + if (e->op == TOKerror) + return e; + type = e1->type; + return arrayOp(sc); + } + else + { + e1 = e1->modifiableLvalue(sc, e1); + } + + if ((tb1->ty == Tarray || tb1->ty == Tsarray) && + (tb2->ty == Tarray || tb2->ty == Tsarray) && + tb1->nextOf()->equals(tb2->nextOf()) + ) + { + type = e1->type; + typeCombine(sc); + e = this; + } + else + { + e1->checkScalar(); + e1->checkNoBool(); + if (tb1->ty == Tpointer && tb2->isintegral()) + e = scaleFactor(sc); + else if (tb1->ty == Tbool) + { +#if 0 + // Need to rethink this + if (e1->op != TOKvar) + { // Rewrite e1+=e2 to (v=&e1),*v=*v+e2 + VarDeclaration *v; + Expression *ea; + Expression *ex; + + Identifier *id = Lexer::uniqueId("__name"); + + v = new VarDeclaration(loc, tb1->pointerTo(), id, NULL); + v->semantic(sc); + if (!sc->insert(v)) + assert(0); + v->parent = sc->func; + + ea = new AddrExp(loc, e1); + ea = new AssignExp(loc, new VarExp(loc, v), ea); + + ex = new VarExp(loc, v); + ex = new PtrExp(loc, ex); + e = new AddExp(loc, ex, e2); + e = new CastExp(loc, e, e1->type); + e = new AssignExp(loc, ex->syntaxCopy(), e); + + e = new CommaExp(loc, ea, e); + } + else +#endif + { // Rewrite e1+=e2 to e1=e1+e2 + // BUG: doesn't account for side effects in e1 + // BUG: other assignment operators for bits aren't handled at all + e = new AddExp(loc, e1, e2); + e = new CastExp(loc, e, e1->type); + e = new AssignExp(loc, e1->syntaxCopy(), e); + } + e = e->semantic(sc); + } + else + { + type = e1->type; + typeCombine(sc); + e1->checkArithmetic(); + e2->checkArithmetic(); + checkComplexAddAssign(); + if (type->isreal() || type->isimaginary()) + { + assert(global.errors || e2->type->isfloating()); + e2 = e2->castTo(sc, e1->type); + } + e = this; + } + } + return e; +} + +/************************************************************/ + +MinAssignExp::MinAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKminass, sizeof(MinAssignExp), e1, e2) +{ +} + +Expression *MinAssignExp::semantic(Scope *sc) +{ Expression *e; + + if (type) + return this; + + e = op_overload(sc); + if (e) + return e; + + if (e1->op == TOKarraylength) + { + e = ArrayLengthExp::rewriteOpAssign(this); + e = e->semantic(sc); + return e; + } + + if (e1->op == TOKslice) + { // T[] -= ... + e = typeCombine(sc); + if (e->op == TOKerror) + return e; + type = e1->type; + return arrayOp(sc); + } + + e1 = e1->modifiableLvalue(sc, e1); + e1->checkScalar(); + e1->checkNoBool(); + if (e1->type->ty == Tpointer && e2->type->isintegral()) + e = scaleFactor(sc); + else + { + e1 = e1->checkArithmetic(); + e2 = e2->checkArithmetic(); + checkComplexAddAssign(); + type = e1->type; + typeCombine(sc); + if (type->isreal() || type->isimaginary()) + { + assert(e2->type->isfloating()); + e2 = e2->castTo(sc, e1->type); + } + e = this; + } + return e; +} + +/************************************************************/ + +CatAssignExp::CatAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKcatass, sizeof(CatAssignExp), e1, e2) +{ +} + +Expression *CatAssignExp::semantic(Scope *sc) +{ Expression *e; + + //printf("CatAssignExp::semantic() %s\n", toChars()); + e = op_overload(sc); + if (e) + return e; + + if (e1->op == TOKslice) + { SliceExp *se = (SliceExp *)e1; + + if (se->e1->type->toBasetype()->ty == Tsarray) + { error("cannot append to static array %s", se->e1->type->toChars()); + return new ErrorExp(); + } + } + + e1 = e1->modifiableLvalue(sc, e1); + + Type *tb1 = e1->type->toBasetype(); + Type *tb2 = e2->type->toBasetype(); + + if (!e2->rvalue()) + return new ErrorExp(); + + Type *tb1next = tb1->nextOf(); + + if ((tb1->ty == Tarray) && + (tb2->ty == Tarray || tb2->ty == Tsarray) && + (e2->implicitConvTo(e1->type) +#if DMDV2 + || tb2->nextOf()->implicitConvTo(tb1next) +#endif + ) + ) + { // Append array + e2 = e2->castTo(sc, e1->type); + type = e1->type; + e = this; + } + else if ((tb1->ty == Tarray) && + e2->implicitConvTo(tb1next) + ) + { // Append element + e2 = e2->castTo(sc, tb1next); + type = e1->type; + e = this; + } + else if (tb1->ty == Tarray && + (tb1next->ty == Tchar || tb1next->ty == Twchar) && + e2->type->ty != tb1next->ty && + e2->implicitConvTo(Type::tdchar) + ) + { // Append dchar to char[] or wchar[] + e2 = e2->castTo(sc, Type::tdchar); + type = e1->type; + e = this; + + /* Do not allow appending wchar to char[] because if wchar happens + * to be a surrogate pair, nothing good can result. + */ + } + else + { + if (tb1 != Type::terror && tb2 != Type::terror) + error("cannot append type %s to type %s", tb2->toChars(), tb1->toChars()); + e = new ErrorExp(); + } + return e; +} + +/************************************************************/ + +MulAssignExp::MulAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKmulass, sizeof(MulAssignExp), e1, e2) +{ +} + +Expression *MulAssignExp::semantic(Scope *sc) +{ Expression *e; + + e = op_overload(sc); + if (e) + return e; + +#if DMDV2 + if (e1->op == TOKarraylength) + { + e = ArrayLengthExp::rewriteOpAssign(this); + e = e->semantic(sc); + return e; + } +#endif + + if (e1->op == TOKslice) + { // T[] *= ... + e = typeCombine(sc); + if (e->op == TOKerror) + return e; + return arrayOp(sc); + } + + e1 = e1->modifiableLvalue(sc, e1); + e1->checkScalar(); + e1->checkNoBool(); + type = e1->type; + typeCombine(sc); + e1->checkArithmetic(); + e2->checkArithmetic(); + checkComplexMulAssign(); + if (e2->type->isfloating()) + { + Type *t1 = e1->type; + Type *t2 = e2->type; + if (t1->isreal()) + { + if (t2->isimaginary() || t2->iscomplex()) + { + e2 = e2->castTo(sc, t1); + } + } + else if (t1->isimaginary()) + { + if (t2->isimaginary() || t2->iscomplex()) + { + switch (t1->ty) + { + case Timaginary32: t2 = Type::tfloat32; break; + case Timaginary64: t2 = Type::tfloat64; break; + case Timaginary80: t2 = Type::tfloat80; break; + default: + assert(0); + } + e2 = e2->castTo(sc, t2); + } + } + } + else if (type->toBasetype()->ty == Tvector && + ((TypeVector *)type->toBasetype())->elementType()->size(loc) != 2) + { // Only short[8] and ushort[8] work with multiply + return incompatibleTypes(); + } + return this; +} + +/************************************************************/ + +DivAssignExp::DivAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKdivass, sizeof(DivAssignExp), e1, e2) +{ +} + +Expression *DivAssignExp::semantic(Scope *sc) +{ Expression *e; + + e = op_overload(sc); + if (e) + return e; + +#if DMDV2 + if (e1->op == TOKarraylength) + { + e = ArrayLengthExp::rewriteOpAssign(this); + e = e->semantic(sc); + return e; + } +#endif + + if (e1->op == TOKslice) + { // T[] /= ... + e = typeCombine(sc); + if (e->op == TOKerror) + return e; + type = e1->type; + return arrayOp(sc); + } + + e1 = e1->modifiableLvalue(sc, e1); + e1->checkScalar(); + e1->checkNoBool(); + type = e1->type; + typeCombine(sc); + e1->checkArithmetic(); + e2->checkArithmetic(); + checkComplexMulAssign(); + if (e2->type->isimaginary()) + { + Type *t1 = e1->type; + if (t1->isreal()) + { // x/iv = i(-x/v) + // Therefore, the result is 0 + e2 = new CommaExp(loc, e2, new RealExp(loc, 0, t1)); + e2->type = t1; + e = new AssignExp(loc, e1, e2); + e->type = t1; + return e; + } + else if (t1->isimaginary()) + { Type *t2; + + switch (t1->ty) + { + case Timaginary32: t2 = Type::tfloat32; break; + case Timaginary64: t2 = Type::tfloat64; break; + case Timaginary80: t2 = Type::tfloat80; break; + default: + assert(0); + } + e2 = e2->castTo(sc, t2); + Expression *e = new AssignExp(loc, e1, e2); + e->type = t1; + return e; + } + } + else if (type->toBasetype()->ty == Tvector && !e1->type->isfloating()) + return incompatibleTypes(); + return this; +} + +/************************************************************/ + +ModAssignExp::ModAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKmodass, sizeof(ModAssignExp), e1, e2) +{ +} + +Expression *ModAssignExp::semantic(Scope *sc) +{ + if (!type) + { + Expression *e = op_overload(sc); + if (e) + return e; + + checkComplexMulAssign(); + return commonSemanticAssign(sc); + } + return this; +} + +/************************************************************/ + +ShlAssignExp::ShlAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKshlass, sizeof(ShlAssignExp), e1, e2) +{ +} + +Expression *ShlAssignExp::semantic(Scope *sc) +{ Expression *e; + + //printf("ShlAssignExp::semantic()\n"); + + e = op_overload(sc); + if (e) + return e; + + if (e1->op == TOKarraylength) + { + e = ArrayLengthExp::rewriteOpAssign(this); + e = e->semantic(sc); + return e; + } + + e1 = e1->modifiableLvalue(sc, e1); + e1->checkScalar(); + e1->checkNoBool(); + type = e1->type; + if (e1->type->toBasetype()->ty == Tvector || e2->type->toBasetype()->ty == Tvector) + return incompatibleTypes(); + typeCombine(sc); + e1->checkIntegral(); + e2 = e2->checkIntegral(); + e2 = e2->castTo(sc, Type::tshiftcnt); + return this; +} + +/************************************************************/ + +ShrAssignExp::ShrAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKshrass, sizeof(ShrAssignExp), e1, e2) +{ +} + +Expression *ShrAssignExp::semantic(Scope *sc) +{ Expression *e; + + e = op_overload(sc); + if (e) + return e; + + if (e1->op == TOKarraylength) + { + e = ArrayLengthExp::rewriteOpAssign(this); + e = e->semantic(sc); + return e; + } + + e1 = e1->modifiableLvalue(sc, e1); + e1->checkScalar(); + e1->checkNoBool(); + type = e1->type; + if (e1->type->toBasetype()->ty == Tvector || e2->type->toBasetype()->ty == Tvector) + return incompatibleTypes(); + typeCombine(sc); + e1->checkIntegral(); + e2 = e2->checkIntegral(); + e2 = e2->castTo(sc, Type::tshiftcnt); + return this; +} + +/************************************************************/ + +UshrAssignExp::UshrAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKushrass, sizeof(UshrAssignExp), e1, e2) +{ +} + +Expression *UshrAssignExp::semantic(Scope *sc) +{ Expression *e; + + e = op_overload(sc); + if (e) + return e; + + if (e1->op == TOKarraylength) + { + e = ArrayLengthExp::rewriteOpAssign(this); + e = e->semantic(sc); + return e; + } + + e1 = e1->modifiableLvalue(sc, e1); + e1->checkScalar(); + e1->checkNoBool(); + type = e1->type; + if (e1->type->toBasetype()->ty == Tvector || e2->type->toBasetype()->ty == Tvector) + return incompatibleTypes(); + typeCombine(sc); + e1->checkIntegral(); + e2 = e2->checkIntegral(); + e2 = e2->castTo(sc, Type::tshiftcnt); + return this; +} + +/************************************************************/ + +AndAssignExp::AndAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKandass, sizeof(AndAssignExp), e1, e2) +{ +} + +Expression *AndAssignExp::semantic(Scope *sc) +{ + return commonSemanticAssignIntegral(sc); +} + +/************************************************************/ + +OrAssignExp::OrAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKorass, sizeof(OrAssignExp), e1, e2) +{ +} + +Expression *OrAssignExp::semantic(Scope *sc) +{ + return commonSemanticAssignIntegral(sc); +} + +/************************************************************/ + +XorAssignExp::XorAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKxorass, sizeof(XorAssignExp), e1, e2) +{ +} + +Expression *XorAssignExp::semantic(Scope *sc) +{ + return commonSemanticAssignIntegral(sc); +} + +/***************** PowAssignExp *******************************************/ + +PowAssignExp::PowAssignExp(Loc loc, Expression *e1, Expression *e2) + : BinAssignExp(loc, TOKpowass, sizeof(PowAssignExp), e1, e2) +{ +} + +Expression *PowAssignExp::semantic(Scope *sc) +{ + Expression *e; + + if (type) + return this; + + e = op_overload(sc); + if (e) + return e; + + assert(e1->type && e2->type); + if (e1->op == TOKslice) + { // T[] ^^= ... + e = typeCombine(sc); + if (e->op == TOKerror) + return e; + + // Check element types are arithmetic + Type *tb1 = e1->type->nextOf()->toBasetype(); + Type *tb2 = e2->type->toBasetype(); + if (tb2->ty == Tarray || tb2->ty == Tsarray) + tb2 = tb2->nextOf()->toBasetype(); + + if ( (tb1->isintegral() || tb1->isfloating()) && + (tb2->isintegral() || tb2->isfloating())) + { + type = e1->type; + return arrayOp(sc); + } + } + else + { + e1 = e1->modifiableLvalue(sc, e1); + } + + if ( (e1->type->isintegral() || e1->type->isfloating()) && + (e2->type->isintegral() || e2->type->isfloating())) + { + if (e1->op == TOKvar) + { // Rewrite: e1 = e1 ^^ e2 + e = new PowExp(loc, e1->syntaxCopy(), e2); + e = new AssignExp(loc, e1, e); + } + else + { // Rewrite: ref tmp = e1; tmp = tmp ^^ e2 + Identifier *id = Lexer::uniqueId("__powtmp"); + VarDeclaration *v = new VarDeclaration(e1->loc, e1->type, id, new ExpInitializer(loc, e1)); + v->storage_class |= STCref | STCforeach; + Expression *de = new DeclarationExp(e1->loc, v); + VarExp *ve = new VarExp(e1->loc, v); + e = new PowExp(loc, ve, e2); + e = new AssignExp(loc, new VarExp(e1->loc, v), e); + e = new CommaExp(loc, de, e); + } + e = e->semantic(sc); + if (e->type->toBasetype()->ty == Tvector) + return incompatibleTypes(); + return e; + } + return incompatibleTypes(); +} + + +/************************* AddExp *****************************/ + +AddExp::AddExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKadd, sizeof(AddExp), e1, e2) +{ +} + +Expression *AddExp::semantic(Scope *sc) +{ Expression *e; + +#if LOGSEMANTIC + printf("AddExp::semantic('%s')\n", toChars()); +#endif + if (!type) + { + BinExp::semanticp(sc); + + e = op_overload(sc); + if (e) + return e; + + Type *tb1 = e1->type->toBasetype(); + Type *tb2 = e2->type->toBasetype(); + + if ((tb1->ty == Tarray || tb1->ty == Tsarray) && + (tb2->ty == Tarray || tb2->ty == Tsarray) && + tb1->nextOf()->equals(tb2->nextOf()) + ) + { + type = e1->type; + e = this; + } + else if (tb1->ty == Tpointer && e2->type->isintegral() || + tb2->ty == Tpointer && e1->type->isintegral()) + e = scaleFactor(sc); + else if (tb1->ty == Tpointer && tb2->ty == Tpointer) + { + return incompatibleTypes(); + } + else + { + typeCombine(sc); + Type *tb1 = e1->type->toBasetype(); + if (tb1->ty == Tvector && !tb1->isscalar()) + { + return incompatibleTypes(); + } + if ((tb1->isreal() && e2->type->isimaginary()) || + (tb1->isimaginary() && e2->type->isreal())) + { + switch (type->toBasetype()->ty) + { + case Tfloat32: + case Timaginary32: + type = Type::tcomplex32; + break; + + case Tfloat64: + case Timaginary64: + type = Type::tcomplex64; + break; + + case Tfloat80: + case Timaginary80: + type = Type::tcomplex80; + break; + + default: + assert(0); + } + } + e = this; + } + return e; + } + return this; +} + +/************************************************************/ + +MinExp::MinExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKmin, sizeof(MinExp), e1, e2) +{ +} + +Expression *MinExp::semantic(Scope *sc) +{ Expression *e; + +#if LOGSEMANTIC + printf("MinExp::semantic('%s')\n", toChars()); +#endif + if (type) + return this; + + BinExp::semanticp(sc); + + e = op_overload(sc); + if (e) + return e; + + e = this; + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + if (t1->ty == Tpointer) + { + if (t2->ty == Tpointer) + { // Need to divide the result by the stride + // Replace (ptr - ptr) with (ptr - ptr) / stride + d_int64 stride; + Expression *e; + + typeCombine(sc); // make sure pointer types are compatible + type = Type::tptrdiff_t; + stride = t2->nextOf()->size(); + if (stride == 0) + { + e = new IntegerExp(loc, 0, Type::tptrdiff_t); + } + else + { + e = new DivExp(loc, this, new IntegerExp(0, stride, Type::tptrdiff_t)); + e->type = Type::tptrdiff_t; + } + return e; + } + else if (t2->isintegral()) + e = scaleFactor(sc); + else + { error("can't subtract %s from pointer", t2->toChars()); + return new ErrorExp(); + } + } + else if (t2->ty == Tpointer) + { + type = e2->type; + error("can't subtract pointer from %s", e1->type->toChars()); + return new ErrorExp(); + } + else + { + typeCombine(sc); + t1 = e1->type->toBasetype(); + t2 = e2->type->toBasetype(); + if (t1->ty == Tvector && !t1->isscalar()) + { + return incompatibleTypes(); + } + if ((t1->isreal() && t2->isimaginary()) || + (t1->isimaginary() && t2->isreal())) + { + switch (type->ty) + { + case Tfloat32: + case Timaginary32: + type = Type::tcomplex32; + break; + + case Tfloat64: + case Timaginary64: + type = Type::tcomplex64; + break; + + case Tfloat80: + case Timaginary80: + type = Type::tcomplex80; + break; + + default: + assert(0); + } + } + } + return e; +} + +/************************* CatExp *****************************/ + +CatExp::CatExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKcat, sizeof(CatExp), e1, e2) +{ +} + +Expression *CatExp::semantic(Scope *sc) +{ Expression *e; + + //printf("CatExp::semantic() %s\n", toChars()); + if (!type) + { + BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + + Type *tb1 = e1->type->toBasetype(); + Type *tb2 = e2->type->toBasetype(); + + + /* BUG: Should handle things like: + * char c; + * c ~ ' ' + * ' ' ~ c; + */ + +#if 0 + e1->type->print(); + e2->type->print(); +#endif + Type *tb1next = tb1->nextOf(); + Type *tb2next = tb2->nextOf(); + + if ((tb1->ty == Tsarray || tb1->ty == Tarray) && + e2->implicitConvTo(tb1next) >= MATCHconvert) + { + e2 = e2->implicitCastTo(sc, tb1next); + type = tb1next->arrayOf(); + if (tb2->ty == Tarray) + { // Make e2 into [e2] + e2 = new ArrayLiteralExp(e2->loc, e2); + e2->type = type; + } + return this; + } + else if ((tb2->ty == Tsarray || tb2->ty == Tarray) && + e1->implicitConvTo(tb2next) >= MATCHconvert) + { + e1 = e1->implicitCastTo(sc, tb2next); + type = tb2next->arrayOf(); + if (tb1->ty == Tarray) + { // Make e1 into [e1] + e1 = new ArrayLiteralExp(e1->loc, e1); + e1->type = type; + } + return this; + } + + if ((tb1->ty == Tsarray || tb1->ty == Tarray) && + (tb2->ty == Tsarray || tb2->ty == Tarray) && + (tb1next->mod || tb2next->mod) && + (tb1next->mod != tb2next->mod) + ) + { + Type *t1 = tb1next->mutableOf()->constOf()->arrayOf(); + Type *t2 = tb2next->mutableOf()->constOf()->arrayOf(); + if (e1->op == TOKstring && !((StringExp *)e1)->committed) + e1->type = t1; + else + e1 = e1->castTo(sc, t1); + if (e2->op == TOKstring && !((StringExp *)e2)->committed) + e2->type = t2; + else + e2 = e2->castTo(sc, t2); + } + + typeCombine(sc); + type = type->toHeadMutable(); + + Type *tb = type->toBasetype(); + if (tb->ty == Tsarray) + type = tb->nextOf()->arrayOf(); + if (type->ty == Tarray && tb1next && tb2next && + tb1next->mod != tb2next->mod) + { + type = type->nextOf()->toHeadMutable()->arrayOf(); + } +#if 0 + e1->type->print(); + e2->type->print(); + type->print(); + print(); +#endif + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + if (e1->op == TOKstring && e2->op == TOKstring) + e = optimize(WANTvalue); + else if ((t1->ty == Tarray || t1->ty == Tsarray) && + (t2->ty == Tarray || t2->ty == Tsarray)) + { + e = this; + } + else + { + //printf("(%s) ~ (%s)\n", e1->toChars(), e2->toChars()); + incompatibleTypes(); + return new ErrorExp(); + } + e->type = e->type->semantic(loc, sc); + return e; + } + return this; +} + +/************************************************************/ + +MulExp::MulExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKmul, sizeof(MulExp), e1, e2) +{ +} + +Expression *MulExp::semantic(Scope *sc) +{ Expression *e; + +#if 0 + printf("MulExp::semantic() %s\n", toChars()); +#endif + if (type) + { + return this; + } + + BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + + typeCombine(sc); + if (!e1->isArrayOperand()) + e1->checkArithmetic(); + if (!e2->isArrayOperand()) + e2->checkArithmetic(); + if (type->isfloating()) + { Type *t1 = e1->type; + Type *t2 = e2->type; + + if (t1->isreal()) + { + type = t2; + } + else if (t2->isreal()) + { + type = t1; + } + else if (t1->isimaginary()) + { + if (t2->isimaginary()) + { Expression *e; + + switch (t1->toBasetype()->ty) + { + case Timaginary32: type = Type::tfloat32; break; + case Timaginary64: type = Type::tfloat64; break; + case Timaginary80: type = Type::tfloat80; break; + default: assert(0); + } + + // iy * iv = -yv + e1->type = type; + e2->type = type; + e = new NegExp(loc, this); + e = e->semantic(sc); + return e; + } + else + type = t2; // t2 is complex + } + else if (t2->isimaginary()) + { + type = t1; // t1 is complex + } + } + else if (type->toBasetype()->ty == Tvector && + ((TypeVector *)type->toBasetype())->elementType()->size(loc) != 2) + { // Only short[8] and ushort[8] work with multiply + return incompatibleTypes(); + } + return this; +} + +/************************************************************/ + +DivExp::DivExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKdiv, sizeof(DivExp), e1, e2) +{ +} + +Expression *DivExp::semantic(Scope *sc) +{ Expression *e; + + if (type) + return this; + + BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + + typeCombine(sc); + if (!e1->isArrayOperand()) + e1->checkArithmetic(); + if (!e2->isArrayOperand()) + e2->checkArithmetic(); + if (type->isfloating()) + { Type *t1 = e1->type; + Type *t2 = e2->type; + + if (t1->isreal()) + { + type = t2; + if (t2->isimaginary()) + { Expression *e; + + // x/iv = i(-x/v) + e2->type = t1; + e = new NegExp(loc, this); + e = e->semantic(sc); + return e; + } + } + else if (t2->isreal()) + { + type = t1; + } + else if (t1->isimaginary()) + { + if (t2->isimaginary()) + { + switch (t1->toBasetype()->ty) + { + case Timaginary32: type = Type::tfloat32; break; + case Timaginary64: type = Type::tfloat64; break; + case Timaginary80: type = Type::tfloat80; break; + default: assert(0); + } + } + else + type = t2; // t2 is complex + } + else if (t2->isimaginary()) + { + type = t1; // t1 is complex + } + } + else if (type->toBasetype()->ty == Tvector) + { incompatibleTypes(); + return new ErrorExp(); + } + return this; +} + +/************************************************************/ + +ModExp::ModExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKmod, sizeof(ModExp), e1, e2) +{ +} + +Expression *ModExp::semantic(Scope *sc) +{ Expression *e; + + if (type) + return this; + + BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + + typeCombine(sc); + if (!e1->isArrayOperand()) + e1->checkArithmetic(); + if (!e2->isArrayOperand()) + e2->checkArithmetic(); + if (type->toBasetype()->ty == Tvector) + { incompatibleTypes(); + return new ErrorExp(); + } + if (type->isfloating()) + { type = e1->type; + if (e2->type->iscomplex()) + { error("cannot perform modulo complex arithmetic"); + return new ErrorExp(); + } + } + return this; +} + +/************************************************************/ + +PowExp::PowExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKpow, sizeof(PowExp), e1, e2) +{ +} + +Expression *PowExp::semantic(Scope *sc) +{ Expression *e; + + if (type) + return this; + + //printf("PowExp::semantic() %s\n", toChars()); + BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + + assert(e1->type && e2->type); + typeCombine(sc); + + if (e1->op == TOKslice) + { + // Check element types are arithmetic + Type *tb1 = e1->type->nextOf()->toBasetype(); + Type *tb2 = e2->type->toBasetype(); + if (tb2->ty == Tarray || tb2->ty == Tsarray) + tb2 = tb2->nextOf()->toBasetype(); + + if ( (tb1->isintegral() || tb1->isfloating()) && + (tb2->isintegral() || tb2->isfloating())) + { + type = e1->type; + return this; + } + } + + if ( (e1->type->isintegral() || e1->type->isfloating()) && + (e2->type->isintegral() || e2->type->isfloating())) + { + // For built-in numeric types, there are several cases. + // TODO: backend support, especially for e1 ^^ 2. + + bool wantSqrt = false; + + // First, attempt to fold the expression. + e = optimize(WANTvalue); + if (e->op != TOKpow) + { + e = e->semantic(sc); + return e; + } + + // Determine if we're raising to an integer power. + sinteger_t intpow = 0; + if (e2->op == TOKint64 && ((sinteger_t)e2->toInteger() == 2 || (sinteger_t)e2->toInteger() == 3)) + intpow = e2->toInteger(); + else if (e2->op == TOKfloat64 && (e2->toReal() == (sinteger_t)(e2->toReal()))) + intpow = (sinteger_t)(e2->toReal()); + + // Deal with x^^2, x^^3 immediately, since they are of practical importance. + if (intpow == 2 || intpow == 3) + { + // Replace x^^2 with (tmp = x, tmp*tmp) + // Replace x^^3 with (tmp = x, tmp*tmp*tmp) + Identifier *idtmp = Lexer::uniqueId("__powtmp"); + VarDeclaration *tmp = new VarDeclaration(loc, e1->type->toBasetype(), idtmp, new ExpInitializer(0, e1)); + tmp->storage_class = STCctfe; + Expression *ve = new VarExp(loc, tmp); + Expression *ae = new DeclarationExp(loc, tmp); + /* Note that we're reusing ve. This should be ok. + */ + Expression *me = new MulExp(loc, ve, ve); + if (intpow == 3) + me = new MulExp(loc, me, ve); + e = new CommaExp(loc, ae, me); + e = e->semantic(sc); + return e; + } + + static int importMathChecked = 0; + if (!importMathChecked) + { + importMathChecked = 1; + for (size_t i = 0; i < Module::amodules.dim; i++) + { Module *mi = Module::amodules.tdata()[i]; + //printf("\t[%d] %s\n", i, mi->toChars()); + if (mi->ident == Id::math && + mi->parent->ident == Id::std && + !mi->parent->parent) + goto L1; + } + error("must import std.math to use ^^ operator"); + return new ErrorExp(); + + L1: ; + } + + e = new IdentifierExp(loc, Id::empty); + e = new DotIdExp(loc, e, Id::std); + e = new DotIdExp(loc, e, Id::math); + if (e2->op == TOKfloat64 && e2->toReal() == 0.5) + { // Replace e1 ^^ 0.5 with .std.math.sqrt(x) + e = new CallExp(loc, new DotIdExp(loc, e, Id::_sqrt), e1); + } + else + { + // Replace e1 ^^ e2 with .std.math.pow(e1, e2) + e = new CallExp(loc, new DotIdExp(loc, e, Id::_pow), e1, e2); + } + e = e->semantic(sc); + return e; + } + return incompatibleTypes(); +} + +/************************************************************/ + +ShlExp::ShlExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKshl, sizeof(ShlExp), e1, e2) +{ +} + +Expression *ShlExp::semantic(Scope *sc) +{ Expression *e; + + //printf("ShlExp::semantic(), type = %p\n", type); + if (!type) + { BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + e1 = e1->checkIntegral(); + e2 = e2->checkIntegral(); + if (e1->type->toBasetype()->ty == Tvector || + e2->type->toBasetype()->ty == Tvector) + return incompatibleTypes(); + e1 = e1->integralPromotions(sc); + e2 = e2->castTo(sc, Type::tshiftcnt); + type = e1->type; + } + return this; +} + +/************************************************************/ + +ShrExp::ShrExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKshr, sizeof(ShrExp), e1, e2) +{ +} + +Expression *ShrExp::semantic(Scope *sc) +{ Expression *e; + + if (!type) + { BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + e1 = e1->checkIntegral(); + e2 = e2->checkIntegral(); + if (e1->type->toBasetype()->ty == Tvector || + e2->type->toBasetype()->ty == Tvector) + return incompatibleTypes(); + e1 = e1->integralPromotions(sc); + e2 = e2->castTo(sc, Type::tshiftcnt); + type = e1->type; + } + return this; +} + +/************************************************************/ + +UshrExp::UshrExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKushr, sizeof(UshrExp), e1, e2) +{ +} + +Expression *UshrExp::semantic(Scope *sc) +{ Expression *e; + + if (!type) + { BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + e1 = e1->checkIntegral(); + e2 = e2->checkIntegral(); + if (e1->type->toBasetype()->ty == Tvector || + e2->type->toBasetype()->ty == Tvector) + return incompatibleTypes(); + e1 = e1->integralPromotions(sc); + e2 = e2->castTo(sc, Type::tshiftcnt); + type = e1->type; + } + return this; +} + +/************************************************************/ + +AndExp::AndExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKand, sizeof(AndExp), e1, e2) +{ +} + +Expression *AndExp::semantic(Scope *sc) +{ Expression *e; + + if (!type) + { BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + if (e1->type->toBasetype()->ty == Tbool && + e2->type->toBasetype()->ty == Tbool) + { + type = e1->type; + e = this; + } + else + { + typeCombine(sc); + if (!e1->isArrayOperand()) + e1->checkIntegral(); + if (!e2->isArrayOperand()) + e2->checkIntegral(); + } + } + return this; +} + +/************************************************************/ + +OrExp::OrExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKor, sizeof(OrExp), e1, e2) +{ +} + +Expression *OrExp::semantic(Scope *sc) +{ Expression *e; + + if (!type) + { BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + if (e1->type->toBasetype()->ty == Tbool && + e2->type->toBasetype()->ty == Tbool) + { + type = e1->type; + e = this; + } + else + { + typeCombine(sc); + if (!e1->isArrayOperand()) + e1->checkIntegral(); + if (!e2->isArrayOperand()) + e2->checkIntegral(); + } + } + return this; +} + +/************************************************************/ + +XorExp::XorExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKxor, sizeof(XorExp), e1, e2) +{ +} + +Expression *XorExp::semantic(Scope *sc) +{ Expression *e; + + if (!type) + { BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + if (e1->type->toBasetype()->ty == Tbool && + e2->type->toBasetype()->ty == Tbool) + { + type = e1->type; + e = this; + } + else + { + typeCombine(sc); + if (!e1->isArrayOperand()) + e1->checkIntegral(); + if (!e2->isArrayOperand()) + e2->checkIntegral(); + } + } + return this; +} + + +/************************************************************/ + +OrOrExp::OrOrExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKoror, sizeof(OrOrExp), e1, e2) +{ +} + +Expression *OrOrExp::semantic(Scope *sc) +{ + unsigned cs1; + + // same as for AndAnd + e1 = e1->semantic(sc); + e1 = resolveProperties(sc, e1); + e1 = e1->checkToPointer(); + e1 = e1->checkToBoolean(sc); + cs1 = sc->callSuper; + + if (sc->flags & SCOPEstaticif) + { + /* If in static if, don't evaluate e2 if we don't have to. + */ + e1 = e1->optimize(WANTflags); + if (e1->isBool(TRUE)) + { + return new IntegerExp(loc, 1, Type::tboolean); + } + } + + e2 = e2->semantic(sc); + sc->mergeCallSuper(loc, cs1); + e2 = resolveProperties(sc, e2); + e2 = e2->checkToPointer(); + + if (e2->type->ty == Tvoid) + type = Type::tvoid; + else + { + e2 = e2->checkToBoolean(sc); + type = Type::tboolean; + } + if (e2->op == TOKtype || e2->op == TOKimport) + { error("%s is not an expression", e2->toChars()); + return new ErrorExp(); + } + if (e1->op == TOKerror) + return e1; + if (e2->op == TOKerror) + return e2; + return this; +} + +Expression *OrOrExp::checkToBoolean(Scope *sc) +{ + e2 = e2->checkToBoolean(sc); + return this; +} + +int OrOrExp::isBit() +{ + return TRUE; +} + + +/************************************************************/ + +AndAndExp::AndAndExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKandand, sizeof(AndAndExp), e1, e2) +{ +} + +Expression *AndAndExp::semantic(Scope *sc) +{ + unsigned cs1; + + // same as for OrOr + e1 = e1->semantic(sc); + e1 = resolveProperties(sc, e1); + e1 = e1->checkToPointer(); + e1 = e1->checkToBoolean(sc); + cs1 = sc->callSuper; + + if (sc->flags & SCOPEstaticif) + { + /* If in static if, don't evaluate e2 if we don't have to. + */ + e1 = e1->optimize(WANTflags); + if (e1->isBool(FALSE)) + { + return new IntegerExp(loc, 0, Type::tboolean); + } + } + + e2 = e2->semantic(sc); + sc->mergeCallSuper(loc, cs1); + e2 = resolveProperties(sc, e2); + e2 = e2->checkToPointer(); + + if (e2->type->ty == Tvoid) + type = Type::tvoid; + else + { + e2 = e2->checkToBoolean(sc); + type = Type::tboolean; + } + if (e2->op == TOKtype || e2->op == TOKimport) + { error("%s is not an expression", e2->toChars()); + return new ErrorExp(); + } + if (e1->op == TOKerror) + return e1; + if (e2->op == TOKerror) + return e2; + return this; +} + +Expression *AndAndExp::checkToBoolean(Scope *sc) +{ + e2 = e2->checkToBoolean(sc); + return this; +} + +int AndAndExp::isBit() +{ + return TRUE; +} + + +/************************************************************/ + +InExp::InExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKin, sizeof(InExp), e1, e2) +{ +} + +Expression *InExp::semantic(Scope *sc) +{ Expression *e; + + if (type) + return this; + + BinExp::semanticp(sc); + e = op_overload(sc); + if (e) + return e; + + //type = Type::tboolean; + Type *t2b = e2->type->toBasetype(); + switch (t2b->ty) + { + case Taarray: + { + TypeAArray *ta = (TypeAArray *)t2b; + +#if DMDV2 + // Special handling for array keys + if (!arrayTypeCompatible(e1->loc, e1->type, ta->index)) +#endif + { + // Convert key to type of key + e1 = e1->implicitCastTo(sc, ta->index); + } + + // Return type is pointer to value + type = ta->nextOf()->pointerTo(); + break; + } + + default: + error("rvalue of in expression must be an associative array, not %s", e2->type->toChars()); + case Terror: + return new ErrorExp(); + } + return this; +} + +int InExp::isBit() +{ + return FALSE; +} + + +/************************************************************/ + +/* This deletes the key e1 from the associative array e2 + */ + +RemoveExp::RemoveExp(Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, TOKremove, sizeof(RemoveExp), e1, e2) +{ + type = Type::tboolean; +} + +void RemoveExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, e1, PREC_primary); + buf->writestring(".remove("); + expToCBuffer(buf, hgs, e2, PREC_assign); + buf->writestring(")"); +} + +/************************************************************/ + +CmpExp::CmpExp(enum TOK op, Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, op, sizeof(CmpExp), e1, e2) +{ +} + +Expression *CmpExp::semantic(Scope *sc) +{ Expression *e; + +#if LOGSEMANTIC + printf("CmpExp::semantic('%s')\n", toChars()); +#endif + if (type) + return this; + + BinExp::semanticp(sc); + + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + if (t1->ty == Tclass && e2->op == TOKnull || + t2->ty == Tclass && e1->op == TOKnull) + { + error("do not use null when comparing class types"); + return new ErrorExp(); + } + + e = op_overload(sc); + if (e) + { + if (!e->type->isscalar() && e->type->equals(e1->type)) + { + error("recursive opCmp expansion"); + e = new ErrorExp(); + } + else if (e->op == TOKcall) + { e = new CmpExp(op, loc, e, new IntegerExp(loc, 0, Type::tint32)); + e = e->semantic(sc); + } + return e; + } + + /* Disallow comparing T[]==T and T==T[] + */ + if (e1->op == TOKslice && t1->ty == Tarray && e2->implicitConvTo(t1->nextOf()) || + e2->op == TOKslice && t2->ty == Tarray && e1->implicitConvTo(t2->nextOf())) + { + incompatibleTypes(); + return new ErrorExp(); + } + + Expression *eb1 = e1; + Expression *eb2 = e2; + + e = typeCombine(sc); + if (e->op == TOKerror) + return e; + +#if 0 + // For integer comparisons, ensure the combined type can hold both arguments. + if (type && type->isintegral() && (op == TOKlt || op == TOKle || + op == TOKgt || op == TOKge)) + { + IntRange trange = IntRange::fromType(type); + + Expression *errorexp = 0; + if (!trange.contains(eb1->getIntRange())) + errorexp = eb1; + if (!trange.contains(eb2->getIntRange())) + errorexp = eb2; + + if (errorexp) + { + error("implicit conversion of '%s' to '%s' is unsafe in '(%s) %s (%s)'", + errorexp->toChars(), type->toChars(), eb1->toChars(), Token::toChars(op), eb2->toChars()); + return new ErrorExp(); + } + } +#endif + + type = Type::tboolean; + + // Special handling for array comparisons + t1 = e1->type->toBasetype(); + t2 = e2->type->toBasetype(); + if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) && + (t2->ty == Tarray || t2->ty == Tsarray || t2->ty == Tpointer)) + { + if (t1->nextOf()->implicitConvTo(t2->nextOf()) < MATCHconst && + t2->nextOf()->implicitConvTo(t1->nextOf()) < MATCHconst && + (t1->nextOf()->ty != Tvoid && t2->nextOf()->ty != Tvoid)) + error("array comparison type mismatch, %s vs %s", t1->nextOf()->toChars(), t2->nextOf()->toChars()); + e = this; + } + else if (t1->ty == Tstruct || t2->ty == Tstruct || + (t1->ty == Tclass && t2->ty == Tclass)) + { + if (t2->ty == Tstruct) + error("need member function opCmp() for %s %s to compare", t2->toDsymbol(sc)->kind(), t2->toChars()); + else + error("need member function opCmp() for %s %s to compare", t1->toDsymbol(sc)->kind(), t1->toChars()); + e = new ErrorExp(); + } +#if 1 + else if (t1->iscomplex() || t2->iscomplex()) + { + error("compare not defined for complex operands"); + e = new ErrorExp(); + } +#endif + else if (t1->ty == Tvector) + return incompatibleTypes(); + else + { if (!e1->rvalue() || !e2->rvalue()) + return new ErrorExp(); + e = this; + } + //printf("CmpExp: %s, type = %s\n", e->toChars(), e->type->toChars()); + return e; +} + +int CmpExp::isBit() +{ + return TRUE; +} + + +/************************************************************/ + +EqualExp::EqualExp(enum TOK op, Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, op, sizeof(EqualExp), e1, e2) +{ + assert(op == TOKequal || op == TOKnotequal); +} + +int needDirectEq(Type *t1, Type *t2) +{ + assert(t1->ty == Tarray || t1->ty == Tsarray); + assert(t2->ty == Tarray || t2->ty == Tsarray); + + Type *t1n = t1->nextOf()->toBasetype(); + Type *t2n = t2->nextOf()->toBasetype(); + + if (((t1n->ty == Tchar || t1n->ty == Twchar || t1n->ty == Tdchar) && + (t2n->ty == Tchar || t2n->ty == Twchar || t2n->ty == Tdchar)) || + (t1n->ty == Tvoid || t2n->ty == Tvoid)) + { + return FALSE; + } + + if (t1n->constOf() != t2n->constOf()) + return TRUE; + + Type *t = t1n; + while (t->toBasetype()->nextOf()) + t = t->nextOf()->toBasetype(); + if (t->ty != Tstruct) + return FALSE; + + return ((TypeStruct *)t)->sym->xeq == StructDeclaration::xerreq; +} + +Expression *EqualExp::semantic(Scope *sc) +{ Expression *e; + + //printf("EqualExp::semantic('%s')\n", toChars()); + if (type) + return this; + + BinExp::semanticp(sc); + + /* Before checking for operator overloading, check to see if we're + * comparing the addresses of two statics. If so, we can just see + * if they are the same symbol. + */ + if (e1->op == TOKaddress && e2->op == TOKaddress) + { AddrExp *ae1 = (AddrExp *)e1; + AddrExp *ae2 = (AddrExp *)e2; + + if (ae1->e1->op == TOKvar && ae2->e1->op == TOKvar) + { VarExp *ve1 = (VarExp *)ae1->e1; + VarExp *ve2 = (VarExp *)ae2->e1; + + if (ve1->var == ve2->var /*|| ve1->var->toSymbol() == ve2->var->toSymbol()*/) + { + // They are the same, result is 'true' for ==, 'false' for != + e = new IntegerExp(loc, (op == TOKequal), Type::tboolean); + return e; + } + } + } + + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + if (t1->ty == Tclass && e2->op == TOKnull || + t2->ty == Tclass && e1->op == TOKnull) + { + error("use '%s' instead of '%s' when comparing with null", + Token::toChars(op == TOKequal ? TOKidentity : TOKnotidentity), + Token::toChars(op)); + return new ErrorExp(); + } + + if ((t1->ty == Tarray || t1->ty == Tsarray) && + (t2->ty == Tarray || t2->ty == Tsarray)) + { + if (needDirectEq(t1, t2)) + { /* Rewrite as: + * _ArrayEq(e1, e2) + */ + Expression *eq = new IdentifierExp(loc, Id::_ArrayEq); + Expressions *args = new Expressions(); + args->push(e1); + args->push(e2); + e = new CallExp(loc, eq, args); + if (op == TOKnotequal) + e = new NotExp(loc, e); + e = e->trySemantic(sc); // for better error message + if (!e) + { error("cannot compare %s and %s", t1->toChars(), t2->toChars()); + return new ErrorExp(); + } + return e; + } + } + + //if (e2->op != TOKnull) + { + e = op_overload(sc); + if (e) + { + if (e->op == TOKcall && op == TOKnotequal) + { + e = new NotExp(e->loc, e); + e = e->semantic(sc); + } + return e; + } + } + + /* Disallow comparing T[]==T and T==T[] + */ + if (e1->op == TOKslice && t1->ty == Tarray && e2->implicitConvTo(t1->nextOf()) || + e2->op == TOKslice && t2->ty == Tarray && e1->implicitConvTo(t2->nextOf())) + { + incompatibleTypes(); + return new ErrorExp(); + } + + e = typeCombine(sc); + if (e->op == TOKerror) + return e; + + type = Type::tboolean; + + // Special handling for array comparisons + if (!arrayTypeCompatible(loc, e1->type, e2->type)) + { + if (e1->type != e2->type && e1->type->isfloating() && e2->type->isfloating()) + { + // Cast both to complex + e1 = e1->castTo(sc, Type::tcomplex80); + e2 = e2->castTo(sc, Type::tcomplex80); + } + } + + if (e1->type->toBasetype()->ty == Tvector) + return incompatibleTypes(); + + return e; +} + +int EqualExp::isBit() +{ + return TRUE; +} + + + +/************************************************************/ + +IdentityExp::IdentityExp(enum TOK op, Loc loc, Expression *e1, Expression *e2) + : BinExp(loc, op, sizeof(IdentityExp), e1, e2) +{ +} + +Expression *IdentityExp::semantic(Scope *sc) +{ + if (type) + return this; + + BinExp::semanticp(sc); + type = Type::tboolean; + + Expression *e = typeCombine(sc); + if (e->op == TOKerror) + return e; + + if (e1->type != e2->type && e1->type->isfloating() && e2->type->isfloating()) + { + // Cast both to complex + e1 = e1->castTo(sc, Type::tcomplex80); + e2 = e2->castTo(sc, Type::tcomplex80); + } + + if (e1->type->toBasetype()->ty == Tvector) + return incompatibleTypes(); + + return this; +} + +int IdentityExp::isBit() +{ + return TRUE; +} + + +/****************************************************************/ + +CondExp::CondExp(Loc loc, Expression *econd, Expression *e1, Expression *e2) + : BinExp(loc, TOKquestion, sizeof(CondExp), e1, e2) +{ + this->econd = econd; +} + +Expression *CondExp::syntaxCopy() +{ + return new CondExp(loc, econd->syntaxCopy(), e1->syntaxCopy(), e2->syntaxCopy()); +} + + +Expression *CondExp::semantic(Scope *sc) +{ Type *t1; + Type *t2; + unsigned cs0; + unsigned cs1; + +#if LOGSEMANTIC + printf("CondExp::semantic('%s')\n", toChars()); +#endif + if (type) + return this; + + econd = econd->semantic(sc); + econd = resolveProperties(sc, econd); + econd = econd->checkToPointer(); + econd = econd->checkToBoolean(sc); + +#if 0 /* this cannot work right because the types of e1 and e2 + * both contribute to the type of the result. + */ + if (sc->flags & SCOPEstaticif) + { + /* If in static if, don't evaluate what we don't have to. + */ + econd = econd->optimize(WANTflags); + if (econd->isBool(TRUE)) + { + e1 = e1->semantic(sc); + e1 = resolveProperties(sc, e1); + return e1; + } + else if (econd->isBool(FALSE)) + { + e2 = e2->semantic(sc); + e2 = resolveProperties(sc, e2); + return e2; + } + } +#endif + + + cs0 = sc->callSuper; + e1 = e1->semantic(sc); + e1 = resolveProperties(sc, e1); + cs1 = sc->callSuper; + sc->callSuper = cs0; + e2 = e2->semantic(sc); + e2 = resolveProperties(sc, e2); + sc->mergeCallSuper(loc, cs1); + + + // If either operand is void, the result is void + t1 = e1->type; + t2 = e2->type; + if (t1->ty == Tvoid || t2->ty == Tvoid) + type = Type::tvoid; + else if (t1 == t2) + type = t1; + else + { + typeCombine(sc); + switch (e1->type->toBasetype()->ty) + { + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: + e2 = e2->castTo(sc, e1->type); + break; + } + switch (e2->type->toBasetype()->ty) + { + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: + e1 = e1->castTo(sc, e2->type); + break; + } + if (type->toBasetype()->ty == Tarray) + { + e1 = e1->castTo(sc, type); + e2 = e2->castTo(sc, type); + } + } +#if 0 + printf("res: %s\n", type->toChars()); + printf("e1 : %s\n", e1->type->toChars()); + printf("e2 : %s\n", e2->type->toChars()); +#endif + return this; +} + +#if DMDV2 +int CondExp::isLvalue() +{ + return e1->isLvalue() && e2->isLvalue(); +} +#endif + +Expression *CondExp::toLvalue(Scope *sc, Expression *ex) +{ + PtrExp *e; + + // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2) + e = new PtrExp(loc, this, type); + + e1 = e1->addressOf(sc); + e2 = e2->addressOf(sc); + + typeCombine(sc); + + type = e2->type; + return e; +} + +Expression *CondExp::modifiableLvalue(Scope *sc, Expression *e) +{ + //error("conditional expression %s is not a modifiable lvalue", toChars()); + e1 = e1->modifiableLvalue(sc, e1); + e2 = e2->modifiableLvalue(sc, e1); + return toLvalue(sc, this); +} + +void CondExp::checkEscape() +{ + e1->checkEscape(); + e2->checkEscape(); +} + +void CondExp::checkEscapeRef() +{ + e1->checkEscapeRef(); + e2->checkEscapeRef(); +} + + +Expression *CondExp::checkToBoolean(Scope *sc) +{ + e1 = e1->checkToBoolean(sc); + e2 = e2->checkToBoolean(sc); + return this; +} + + +void CondExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + expToCBuffer(buf, hgs, econd, PREC_oror); + buf->writestring(" ? "); + expToCBuffer(buf, hgs, e1, PREC_expr); + buf->writestring(" : "); + expToCBuffer(buf, hgs, e2, PREC_cond); +} + + +/****************************************************************/ + +DefaultInitExp::DefaultInitExp(Loc loc, enum TOK subop, int size) + : Expression(loc, TOKdefault, size) +{ + this->subop = subop; +} + +void DefaultInitExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(Token::toChars(subop)); +} + +/****************************************************************/ + +FileInitExp::FileInitExp(Loc loc) + : DefaultInitExp(loc, TOKfile, sizeof(FileInitExp)) +{ +} + +Expression *FileInitExp::semantic(Scope *sc) +{ + //printf("FileInitExp::semantic()\n"); + type = Type::tchar->invariantOf()->arrayOf(); + return this; +} + +Expression *FileInitExp::resolveLoc(Loc loc, Scope *sc) +{ + //printf("FileInitExp::resolve() %s\n", toChars()); + const char *s = loc.filename ? loc.filename : sc->module->ident->toChars(); + Expression *e = new StringExp(loc, (char *)s); + e = e->semantic(sc); + e = e->castTo(sc, type); + return e; +} + +/****************************************************************/ + +LineInitExp::LineInitExp(Loc loc) + : DefaultInitExp(loc, TOKline, sizeof(LineInitExp)) +{ +} + +Expression *LineInitExp::semantic(Scope *sc) +{ + type = Type::tint32; + return this; +} + +Expression *LineInitExp::resolveLoc(Loc loc, Scope *sc) +{ + Expression *e = new IntegerExp(loc, loc.linnum, Type::tint32); + e = e->castTo(sc, type); + return e; +} + + diff --git a/expression.h b/expression.h new file mode 100644 index 00000000..5febe4c9 --- /dev/null +++ b/expression.h @@ -0,0 +1,1697 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_EXPRESSION_H +#define DMD_EXPRESSION_H + +#include "mars.h" +#include "identifier.h" +#include "lexer.h" +#include "arraytypes.h" +#include "intrange.h" + +struct Type; +struct Scope; +struct TupleDeclaration; +struct VarDeclaration; +struct FuncDeclaration; +struct FuncLiteralDeclaration; +struct Declaration; +struct CtorDeclaration; +struct NewDeclaration; +struct Dsymbol; +struct Import; +struct Module; +struct ScopeDsymbol; +struct InlineCostState; +struct InlineDoState; +struct InlineScanState; +struct Expression; +struct Declaration; +struct AggregateDeclaration; +struct StructDeclaration; +struct TemplateInstance; +struct TemplateDeclaration; +struct ClassDeclaration; +struct HdrGenState; +struct BinExp; +struct InterState; +struct Symbol; // back end symbol +struct OverloadSet; +struct Initializer; +struct StringExp; + +enum TOK; + +// Back end +struct IRState; +struct dt_t; + +#ifdef IN_GCC +union tree_node; typedef union tree_node elem; +#else +struct elem; +#endif + +void initPrecedence(); + +typedef int (*apply_fp_t)(Expression *, void *); + +Expression *resolveProperties(Scope *sc, Expression *e); +void accessCheck(Loc loc, Scope *sc, Expression *e, Declaration *d); +Expression *build_overload(Loc loc, Scope *sc, Expression *ethis, Expression *earg, Dsymbol *d); +Dsymbol *search_function(ScopeDsymbol *ad, Identifier *funcid); +void argExpTypesToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs); +void argsToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs); +void expandTuples(Expressions *exps); +TupleDeclaration *isAliasThisTuple(Expression *e); +int expandAliasThisTuples(Expressions *exps, int starti = 0); +FuncDeclaration *hasThis(Scope *sc); +Expression *fromConstInitializer(int result, Expression *e); +int arrayExpressionCanThrow(Expressions *exps, bool mustNotThrow); +TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s); +void valueNoDtor(Expression *e); +void modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1); + + +/* Interpreter: what form of return value expression is required? + */ +enum CtfeGoal +{ ctfeNeedRvalue, // Must return an Rvalue + ctfeNeedLvalue, // Must return an Lvalue + ctfeNeedAnyValue, // Can return either an Rvalue or an Lvalue + ctfeNeedLvalueRef,// Must return a reference to an Lvalue (for ref types) + ctfeNeedNothing // The return value is not required +}; + +struct Expression : Object +{ + Loc loc; // file location + enum TOK op; // handy to minimize use of dynamic_cast + Type *type; // !=NULL means that semantic() has been run + unsigned char size; // # of bytes in Expression so we can copy() it + unsigned char parens; // if this is a parenthesized expression + + Expression(Loc loc, enum TOK op, int size); + Expression *copy(); + virtual Expression *syntaxCopy(); + virtual int apply(apply_fp_t fp, void *param); + virtual Expression *semantic(Scope *sc); + Expression *trySemantic(Scope *sc); + + int dyncast() { return DYNCAST_EXPRESSION; } // kludge for template.isExpression() + + void print(); + char *toChars(); + virtual void dump(int indent); + void error(const char *format, ...); + void warning(const char *format, ...); + virtual int rvalue(); + + static Expression *combine(Expression *e1, Expression *e2); + static Expressions *arraySyntaxCopy(Expressions *exps); + + virtual dinteger_t toInteger(); + virtual uinteger_t toUInteger(); + virtual real_t toReal(); + virtual real_t toImaginary(); + virtual complex_t toComplex(); + virtual StringExp *toString(); + virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + virtual void toMangleBuffer(OutBuffer *buf); + virtual int isLvalue(); + virtual Expression *toLvalue(Scope *sc, Expression *e); + virtual Expression *modifiableLvalue(Scope *sc, Expression *e); + virtual Expression *implicitCastTo(Scope *sc, Type *t); + virtual MATCH implicitConvTo(Type *t); + virtual IntRange getIntRange(); + virtual Expression *castTo(Scope *sc, Type *t); + virtual void checkEscape(); + virtual void checkEscapeRef(); + virtual Expression *resolveLoc(Loc loc, Scope *sc); + void checkScalar(); + void checkNoBool(); + Expression *checkIntegral(); + Expression *checkArithmetic(); + void checkDeprecated(Scope *sc, Dsymbol *s); + void checkPurity(Scope *sc, FuncDeclaration *f); + void checkPurity(Scope *sc, VarDeclaration *v, Expression *e1); + void checkSafety(Scope *sc, FuncDeclaration *f); + virtual Expression *checkToBoolean(Scope *sc); + virtual Expression *addDtorHook(Scope *sc); + Expression *checkToPointer(); + Expression *addressOf(Scope *sc); + Expression *deref(); + Expression *integralPromotions(Scope *sc); + Expression *isTemp(); + + Expression *toDelegate(Scope *sc, Type *t); + + virtual Expression *optimize(int result); + #define WANTflags 1 + #define WANTvalue 2 + // A compile-time result is required. Give an error if not possible + #define WANTinterpret 4 + // Same as WANTvalue, but also expand variables as far as possible + #define WANTexpand 8 + + virtual Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + + virtual int isConst(); + virtual int isBool(int result); + virtual int isBit(); + bool hasSideEffect(); + void discardValue(); + void useValue(); + int canThrow(bool mustNotThrow); + + virtual int inlineCost3(InlineCostState *ics); + virtual Expression *doInline(InlineDoState *ids); + virtual Expression *inlineScan(InlineScanState *iss); + Expression *inlineCopy(Scope *sc); + + // For operator overloading + virtual int isCommutative(); + virtual Identifier *opId(); + virtual Identifier *opId_r(); + + // For array ops + virtual void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + virtual Expression *buildArrayLoop(Parameters *fparams); + int isArrayOperand(); + + // Back end + virtual elem *toElem(IRState *irs); + elem *toElemDtor(IRState *irs); + virtual dt_t **toDt(dt_t **pdt); +}; + +struct IntegerExp : Expression +{ + dinteger_t value; + + IntegerExp(Loc loc, dinteger_t value, Type *type); + IntegerExp(dinteger_t value); + int equals(Object *o); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + char *toChars(); + void dump(int indent); + IntRange getIntRange(); + dinteger_t toInteger(); + real_t toReal(); + real_t toImaginary(); + complex_t toComplex(); + int isConst(); + int isBool(int result); + MATCH implicitConvTo(Type *t); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toMangleBuffer(OutBuffer *buf); + Expression *toLvalue(Scope *sc, Expression *e); + elem *toElem(IRState *irs); + dt_t **toDt(dt_t **pdt); +}; + +struct ErrorExp : IntegerExp +{ + ErrorExp(); + + Expression *implicitCastTo(Scope *sc, Type *t); + Expression *castTo(Scope *sc, Type *t); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Expression *toLvalue(Scope *sc, Expression *e); +}; + +struct RealExp : Expression +{ + real_t value; + + RealExp(Loc loc, real_t value, Type *type); + int equals(Object *o); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + char *toChars(); + dinteger_t toInteger(); + uinteger_t toUInteger(); + real_t toReal(); + real_t toImaginary(); + complex_t toComplex(); + Expression *castTo(Scope *sc, Type *t); + int isConst(); + int isBool(int result); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toMangleBuffer(OutBuffer *buf); + elem *toElem(IRState *irs); + dt_t **toDt(dt_t **pdt); +}; + +struct ComplexExp : Expression +{ + complex_t value; + + ComplexExp(Loc loc, complex_t value, Type *type); + int equals(Object *o); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + char *toChars(); + dinteger_t toInteger(); + uinteger_t toUInteger(); + real_t toReal(); + real_t toImaginary(); + complex_t toComplex(); + Expression *castTo(Scope *sc, Type *t); + int isConst(); + int isBool(int result); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toMangleBuffer(OutBuffer *buf); + OutBuffer hexp; + elem *toElem(IRState *irs); + dt_t **toDt(dt_t **pdt); +}; + +struct IdentifierExp : Expression +{ + Identifier *ident; + Declaration *var; + + IdentifierExp(Loc loc, Identifier *ident); + IdentifierExp(Loc loc, Declaration *var); + Expression *semantic(Scope *sc); + char *toChars(); + void dump(int indent); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); +}; + +struct DollarExp : IdentifierExp +{ + DollarExp(Loc loc); +}; + +struct DsymbolExp : Expression +{ + Dsymbol *s; + int hasOverloads; + + DsymbolExp(Loc loc, Dsymbol *s, int hasOverloads = 0); + Expression *semantic(Scope *sc); + char *toChars(); + void dump(int indent); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); +}; + +struct ThisExp : Expression +{ + Declaration *var; + + ThisExp(Loc loc); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + int isBool(int result); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + + int inlineCost3(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + //Expression *inlineScan(InlineScanState *iss); + + elem *toElem(IRState *irs); +}; + +struct SuperExp : ThisExp +{ + SuperExp(Loc loc); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + Expression *doInline(InlineDoState *ids); + //Expression *inlineScan(InlineScanState *iss); +}; + +struct NullExp : Expression +{ + unsigned char committed; // !=0 if type is committed + + NullExp(Loc loc, Type *t = NULL); + int equals(Object *o); + Expression *semantic(Scope *sc); + int isBool(int result); + int isConst(); + StringExp *toString(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toMangleBuffer(OutBuffer *buf); + MATCH implicitConvTo(Type *t); + Expression *castTo(Scope *sc, Type *t); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + elem *toElem(IRState *irs); + dt_t **toDt(dt_t **pdt); +}; + +struct StringExp : Expression +{ + void *string; // char, wchar, or dchar data + size_t len; // number of chars, wchars, or dchars + unsigned char sz; // 1: char, 2: wchar, 4: dchar + unsigned char committed; // !=0 if type is committed + unsigned char postfix; // 'c', 'w', 'd' + bool ownedByCtfe; // true = created in CTFE + + StringExp(Loc loc, char *s); + StringExp(Loc loc, void *s, size_t len); + StringExp(Loc loc, void *s, size_t len, unsigned char postfix); + //Expression *syntaxCopy(); + int equals(Object *o); + char *toChars(); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + size_t length(); + StringExp *toString(); + StringExp *toUTF8(Scope *sc); + Expression *implicitCastTo(Scope *sc, Type *t); + MATCH implicitConvTo(Type *t); + Expression *castTo(Scope *sc, Type *t); + int compare(Object *obj); + int isBool(int result); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + Expression *modifiableLvalue(Scope *sc, Expression *e); + unsigned charAt(size_t i); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toMangleBuffer(OutBuffer *buf); + elem *toElem(IRState *irs); + dt_t **toDt(dt_t **pdt); +}; + +// Tuple + +struct TupleExp : Expression +{ + Expressions *exps; + + TupleExp(Loc loc, Expressions *exps); + TupleExp(Loc loc, TupleDeclaration *tup); + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + int equals(Object *o); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void checkEscape(); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + Expression *castTo(Scope *sc, Type *t); + elem *toElem(IRState *irs); + + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); +}; + +struct ArrayLiteralExp : Expression +{ + Expressions *elements; + bool ownedByCtfe; // true = created in CTFE + + ArrayLiteralExp(Loc loc, Expressions *elements); + ArrayLiteralExp(Loc loc, Expression *e); + + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *semantic(Scope *sc); + int isBool(int result); + elem *toElem(IRState *irs); + StringExp *toString(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toMangleBuffer(OutBuffer *buf); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + MATCH implicitConvTo(Type *t); + Expression *castTo(Scope *sc, Type *t); + dt_t **toDt(dt_t **pdt); + + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); +}; + +struct AssocArrayLiteralExp : Expression +{ + Expressions *keys; + Expressions *values; + bool ownedByCtfe; // true = created in CTFE + + AssocArrayLiteralExp(Loc loc, Expressions *keys, Expressions *values); + + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *semantic(Scope *sc); + int isBool(int result); + elem *toElem(IRState *irs); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toMangleBuffer(OutBuffer *buf); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + MATCH implicitConvTo(Type *t); + Expression *castTo(Scope *sc, Type *t); + + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); +}; + +struct StructLiteralExp : Expression +{ + StructDeclaration *sd; // which aggregate this is for + Expressions *elements; // parallels sd->fields[] with + // NULL entries for fields to skip + Type *stype; // final type of result (can be different from sd's type) + + Symbol *sym; // back end symbol to initialize with literal + size_t soffset; // offset from start of s + int fillHoles; // fill alignment 'holes' with zero + bool ownedByCtfe; // true = created in CTFE + + StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions *elements, Type *stype = NULL); + + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *semantic(Scope *sc); + Expression *getField(Type *type, unsigned offset); + int getFieldIndex(Type *type, unsigned offset); + elem *toElem(IRState *irs); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toMangleBuffer(OutBuffer *buf); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + dt_t **toDt(dt_t **pdt); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + MATCH implicitConvTo(Type *t); + + int inlineCost3(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); +}; + +Expression *typeDotIdExp(Loc loc, Type *type, Identifier *ident); + +struct TypeExp : Expression +{ + TypeExp(Loc loc, Type *type); + Expression *syntaxCopy(); + Expression *semantic(Scope *sc); + int rvalue(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Expression *optimize(int result); + elem *toElem(IRState *irs); +}; + +struct ScopeExp : Expression +{ + ScopeDsymbol *sds; + + ScopeExp(Loc loc, ScopeDsymbol *sds); + Expression *syntaxCopy(); + Expression *semantic(Scope *sc); + elem *toElem(IRState *irs); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct TemplateExp : Expression +{ + TemplateDeclaration *td; + + TemplateExp(Loc loc, TemplateDeclaration *td); + int rvalue(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct NewExp : Expression +{ + /* thisexp.new(newargs) newtype(arguments) + */ + Expression *thisexp; // if !NULL, 'this' for class being allocated + Expressions *newargs; // Array of Expression's to call new operator + Type *newtype; + Expressions *arguments; // Array of Expression's + + CtorDeclaration *member; // constructor function + NewDeclaration *allocator; // allocator function + int onstack; // allocate on stack + + NewExp(Loc loc, Expression *thisexp, Expressions *newargs, + Type *newtype, Expressions *arguments); + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + Expression *optimize(int result); + elem *toElem(IRState *irs); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + //int inlineCost3(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + //Expression *inlineScan(InlineScanState *iss); +}; + +struct NewAnonClassExp : Expression +{ + /* thisexp.new(newargs) class baseclasses { } (arguments) + */ + Expression *thisexp; // if !NULL, 'this' for class being allocated + Expressions *newargs; // Array of Expression's to call new operator + ClassDeclaration *cd; // class being instantiated + Expressions *arguments; // Array of Expression's to call class constructor + + NewAnonClassExp(Loc loc, Expression *thisexp, Expressions *newargs, + ClassDeclaration *cd, Expressions *arguments); + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +#if DMDV2 +struct SymbolExp : Expression +{ + Declaration *var; + int hasOverloads; + + SymbolExp(Loc loc, enum TOK op, int size, Declaration *var, int hasOverloads); + + elem *toElem(IRState *irs); +}; +#endif + +// Offset from symbol + +struct SymOffExp : SymbolExp +{ + unsigned offset; + + SymOffExp(Loc loc, Declaration *var, unsigned offset, int hasOverloads = 0); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void checkEscape(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + int isConst(); + int isBool(int result); + Expression *doInline(InlineDoState *ids); + MATCH implicitConvTo(Type *t); + Expression *castTo(Scope *sc, Type *t); + + dt_t **toDt(dt_t **pdt); +}; + +// Variable + +struct VarExp : SymbolExp +{ + VarExp(Loc loc, Declaration *var, int hasOverloads = 0); + int equals(Object *o); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void dump(int indent); + char *toChars(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void checkEscape(); + void checkEscapeRef(); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + Expression *modifiableLvalue(Scope *sc, Expression *e); + dt_t **toDt(dt_t **pdt); + + int inlineCost3(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + //Expression *inlineScan(InlineScanState *iss); +}; + +#if DMDV2 +// Overload Set + +struct OverExp : Expression +{ + OverloadSet *vars; + + OverExp(OverloadSet *s); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); +}; +#endif + +// Function/Delegate literal + +struct FuncExp : Expression +{ + FuncLiteralDeclaration *fd; + TemplateDeclaration *td; + enum TOK tok; + Type *tded; + Scope *scope; + + FuncExp(Loc loc, FuncLiteralDeclaration *fd, TemplateDeclaration *td = NULL); + Expression *syntaxCopy(); + Expression *semantic(Scope *sc); + Expression *semantic(Scope *sc, Expressions *arguments); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + MATCH implicitConvTo(Type *t); + Expression *castTo(Scope *sc, Type *t); + Expression *inferType(Scope *sc, Type *t); + void setType(Type *t); + char *toChars(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + elem *toElem(IRState *irs); + dt_t **toDt(dt_t **pdt); + + int inlineCost3(InlineCostState *ics); + //Expression *doInline(InlineDoState *ids); + //Expression *inlineScan(InlineScanState *iss); +}; + +// Declaration of a symbol + +struct DeclarationExp : Expression +{ + Dsymbol *declaration; + + DeclarationExp(Loc loc, Dsymbol *declaration); + Expression *syntaxCopy(); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + elem *toElem(IRState *irs); + + int inlineCost3(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); +}; + +struct TypeidExp : Expression +{ + Object *obj; + + TypeidExp(Loc loc, Object *obj); + Expression *syntaxCopy(); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +#if DMDV2 +struct TraitsExp : Expression +{ + Identifier *ident; + Objects *args; + + TraitsExp(Loc loc, Identifier *ident, Objects *args); + Expression *syntaxCopy(); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; +#endif + +struct HaltExp : Expression +{ + HaltExp(Loc loc); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + elem *toElem(IRState *irs); +}; + +struct IsExp : Expression +{ + /* is(targ id tok tspec) + * is(targ id == tok2) + */ + Type *targ; + Identifier *id; // can be NULL + enum TOK tok; // ':' or '==' + Type *tspec; // can be NULL + enum TOK tok2; // 'struct', 'union', 'typedef', etc. + TemplateParameters *parameters; + + IsExp(Loc loc, Type *targ, Identifier *id, enum TOK tok, Type *tspec, + enum TOK tok2, TemplateParameters *parameters); + Expression *syntaxCopy(); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +/****************************************************************/ + +struct UnaExp : Expression +{ + Expression *e1; + + UnaExp(Loc loc, enum TOK op, int size, Expression *e1); + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Expression *optimize(int result); + void dump(int indent); + Expression *interpretCommon(InterState *istate, CtfeGoal goal, + Expression *(*fp)(Type *, Expression *)); + Expression *resolveLoc(Loc loc, Scope *sc); + + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); + + virtual Expression *op_overload(Scope *sc); +}; + +struct BinExp : Expression +{ + Expression *e1; + Expression *e2; + + BinExp(Loc loc, enum TOK op, int size, Expression *e1, Expression *e2); + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *semantic(Scope *sc); + Expression *semanticp(Scope *sc); + void checkComplexMulAssign(); + void checkComplexAddAssign(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Expression *scaleFactor(Scope *sc); + Expression *typeCombine(Scope *sc); + Expression *optimize(int result); + int isunsigned(); + Expression *incompatibleTypes(); + void dump(int indent); + Expression *interpretCommon(InterState *istate, CtfeGoal goal, + Expression *(*fp)(Type *, Expression *, Expression *)); + Expression *interpretCommon2(InterState *istate, CtfeGoal goal, + Expression *(*fp)(TOK, Type *, Expression *, Expression *)); + Expression *interpretAssignCommon(InterState *istate, CtfeGoal goal, + Expression *(*fp)(Type *, Expression *, Expression *), int post = 0); + Expression *arrayOp(Scope *sc); + + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); + + Expression *op_overload(Scope *sc); + Expression *compare_overload(Scope *sc, Identifier *id); + + elem *toElemBin(IRState *irs, int op); +}; + +struct BinAssignExp : BinExp +{ + BinAssignExp(Loc loc, enum TOK op, int size, Expression *e1, Expression *e2) + : BinExp(loc, op, size, e1, e2) + { + } + + Expression *commonSemanticAssign(Scope *sc); + Expression *commonSemanticAssignIntegral(Scope *sc); + + Expression *op_overload(Scope *sc); + + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *ex); + Expression *modifiableLvalue(Scope *sc, Expression *e); +}; + +/****************************************************************/ + +struct CompileExp : UnaExp +{ + CompileExp(Loc loc, Expression *e); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct FileExp : UnaExp +{ + FileExp(Loc loc, Expression *e); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct AssertExp : UnaExp +{ + Expression *msg; + + AssertExp(Loc loc, Expression *e, Expression *msg = NULL); + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); + + elem *toElem(IRState *irs); +}; + +struct DotIdExp : UnaExp +{ + Identifier *ident; + + DotIdExp(Loc loc, Expression *e, Identifier *ident); + Expression *semantic(Scope *sc); + Expression *semantic(Scope *sc, int flag); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void dump(int i); +}; + +struct DotTemplateExp : UnaExp +{ + TemplateDeclaration *td; + + DotTemplateExp(Loc loc, Expression *e, TemplateDeclaration *td); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct DotVarExp : UnaExp +{ + Declaration *var; + int hasOverloads; + + DotVarExp(Loc loc, Expression *e, Declaration *var, int hasOverloads = 0); + Expression *semantic(Scope *sc); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + Expression *modifiableLvalue(Scope *sc, Expression *e); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void dump(int indent); + elem *toElem(IRState *irs); +}; + +struct DotTemplateInstanceExp : UnaExp +{ + TemplateInstance *ti; + + DotTemplateInstanceExp(Loc loc, Expression *e, Identifier *name, Objects *tiargs); + Expression *syntaxCopy(); + TemplateDeclaration *getTempdecl(Scope *sc); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void dump(int indent); +}; + +struct DelegateExp : UnaExp +{ + FuncDeclaration *func; + int hasOverloads; + + DelegateExp(Loc loc, Expression *e, FuncDeclaration *func, int hasOverloads = 0); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + MATCH implicitConvTo(Type *t); + Expression *castTo(Scope *sc, Type *t); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void dump(int indent); + + int inlineCost3(InlineCostState *ics); + elem *toElem(IRState *irs); +}; + +struct DotTypeExp : UnaExp +{ + Dsymbol *sym; // symbol that represents a type + + DotTypeExp(Loc loc, Expression *e, Dsymbol *sym); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + elem *toElem(IRState *irs); +}; + +struct CallExp : UnaExp +{ + Expressions *arguments; // function arguments + FuncDeclaration *f; // symbol to call + + CallExp(Loc loc, Expression *e, Expressions *exps); + CallExp(Loc loc, Expression *e); + CallExp(Loc loc, Expression *e, Expression *earg1); + CallExp(Loc loc, Expression *e, Expression *earg1, Expression *earg2); + + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *resolveUFCS(Scope *sc); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void dump(int indent); + elem *toElem(IRState *irs); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + Expression *addDtorHook(Scope *sc); + MATCH implicitConvTo(Type *t); + + int inlineCost3(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); +}; + +struct AddrExp : UnaExp +{ + AddrExp(Loc loc, Expression *e); + Expression *semantic(Scope *sc); + void checkEscape(); + elem *toElem(IRState *irs); + MATCH implicitConvTo(Type *t); + Expression *castTo(Scope *sc, Type *t); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); +}; + +struct PtrExp : UnaExp +{ + PtrExp(Loc loc, Expression *e); + PtrExp(Loc loc, Expression *e, Type *t); + Expression *semantic(Scope *sc); + int isLvalue(); + void checkEscapeRef(); + Expression *toLvalue(Scope *sc, Expression *e); + Expression *modifiableLvalue(Scope *sc, Expression *e); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + elem *toElem(IRState *irs); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + + // For operator overloading + Identifier *opId(); +}; + +struct NegExp : UnaExp +{ + NegExp(Loc loc, Expression *e); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); + + // For operator overloading + Identifier *opId(); + + elem *toElem(IRState *irs); +}; + +struct UAddExp : UnaExp +{ + UAddExp(Loc loc, Expression *e); + Expression *semantic(Scope *sc); + + // For operator overloading + Identifier *opId(); +}; + +struct ComExp : UnaExp +{ + ComExp(Loc loc, Expression *e); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); + + // For operator overloading + Identifier *opId(); + + elem *toElem(IRState *irs); +}; + +struct NotExp : UnaExp +{ + NotExp(Loc loc, Expression *e); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + int isBit(); + elem *toElem(IRState *irs); +}; + +struct BoolExp : UnaExp +{ + BoolExp(Loc loc, Expression *e, Type *type); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + int isBit(); + elem *toElem(IRState *irs); +}; + +struct DeleteExp : UnaExp +{ + DeleteExp(Loc loc, Expression *e); + Expression *semantic(Scope *sc); + Expression *checkToBoolean(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + elem *toElem(IRState *irs); +}; + +struct CastExp : UnaExp +{ + // Possible to cast to one type while painting to another type + Type *to; // type to cast to + unsigned mod; // MODxxxxx + + CastExp(Loc loc, Expression *e, Type *t); + CastExp(Loc loc, Expression *e, unsigned mod); + Expression *syntaxCopy(); + Expression *semantic(Scope *sc); + MATCH implicitConvTo(Type *t); + IntRange getIntRange(); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void checkEscape(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + elem *toElem(IRState *irs); + + // For operator overloading + Identifier *opId(); + Expression *op_overload(Scope *sc); +}; + +struct VectorExp : UnaExp +{ + Type *to; + unsigned dim; // number of elements in the vector + + VectorExp(Loc loc, Expression *e, Type *t); + Expression *syntaxCopy(); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + elem *toElem(IRState *irs); +}; + +struct SliceExp : UnaExp +{ + Expression *upr; // NULL if implicit 0 + Expression *lwr; // NULL if implicit [length - 1] + VarDeclaration *lengthVar; + + SliceExp(Loc loc, Expression *e1, Expression *lwr, Expression *upr); + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *semantic(Scope *sc); + void checkEscape(); + void checkEscapeRef(); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + Expression *modifiableLvalue(Scope *sc, Expression *e); + int isBool(int result); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void dump(int indent); + elem *toElem(IRState *irs); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); +}; + +struct ArrayLengthExp : UnaExp +{ + ArrayLengthExp(Loc loc, Expression *e1); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + elem *toElem(IRState *irs); + + static Expression *rewriteOpAssign(BinExp *exp); +}; + +// e1[a0,a1,a2,a3,...] + +struct ArrayExp : UnaExp +{ + Expressions *arguments; // Array of Expression's + size_t currentDimension; // for opDollar + VarDeclaration *lengthVar; + + ArrayExp(Loc loc, Expression *e1, Expressions *arguments); + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *semantic(Scope *sc); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + // For operator overloading + Identifier *opId(); + Expression *op_overload(Scope *sc); + + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); +}; + +/****************************************************************/ + +struct DotExp : BinExp +{ + DotExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct CommaExp : BinExp +{ + CommaExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + void checkEscape(); + void checkEscapeRef(); + IntRange getIntRange(); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + Expression *modifiableLvalue(Scope *sc, Expression *e); + int isBool(int result); + MATCH implicitConvTo(Type *t); + Expression *addDtorHook(Scope *sc); + Expression *castTo(Scope *sc, Type *t); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + elem *toElem(IRState *irs); +}; + +struct IndexExp : BinExp +{ + VarDeclaration *lengthVar; + int modifiable; + + IndexExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + Expression *modifiableLvalue(Scope *sc, Expression *e); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + Expression *doInline(InlineDoState *ids); + + elem *toElem(IRState *irs); +}; + +/* For both i++ and i-- + */ +struct PostExp : BinExp +{ + PostExp(enum TOK op, Loc loc, Expression *e); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Identifier *opId(); // For operator overloading + elem *toElem(IRState *irs); +}; + +/* For both ++i and --i + */ +struct PreExp : UnaExp +{ + PreExp(enum TOK op, Loc loc, Expression *e); + Expression *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct AssignExp : BinExp +{ int ismemset; // !=0 if setting the contents of an array + + AssignExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *checkToBoolean(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + Identifier *opId(); // For operator overloading + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + elem *toElem(IRState *irs); +}; + +struct ConstructExp : AssignExp +{ + ConstructExp(Loc loc, Expression *e1, Expression *e2); +}; + +#define ASSIGNEXP(op) \ +struct op##AssignExp : BinAssignExp \ +{ \ + op##AssignExp(Loc loc, Expression *e1, Expression *e2); \ + Expression *semantic(Scope *sc); \ + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); \ + X(void buildArrayIdent(OutBuffer *buf, Expressions *arguments);) \ + X(Expression *buildArrayLoop(Parameters *fparams);) \ + \ + Identifier *opId(); /* For operator overloading */ \ + \ + elem *toElem(IRState *irs); \ +}; + +#define X(a) a +ASSIGNEXP(Add) +ASSIGNEXP(Min) +ASSIGNEXP(Mul) +ASSIGNEXP(Div) +ASSIGNEXP(Mod) +ASSIGNEXP(And) +ASSIGNEXP(Or) +ASSIGNEXP(Xor) +#if DMDV2 +ASSIGNEXP(Pow) +#endif +#undef X + +#define X(a) + +ASSIGNEXP(Shl) +ASSIGNEXP(Shr) +ASSIGNEXP(Ushr) +ASSIGNEXP(Cat) + +#undef X +#undef ASSIGNEXP + +struct AddExp : BinExp +{ + AddExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); + + // For operator overloading + int isCommutative(); + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct MinExp : BinExp +{ + MinExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); + + // For operator overloading + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct CatExp : BinExp +{ + CatExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + + // For operator overloading + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct MulExp : BinExp +{ + MulExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); + + // For operator overloading + int isCommutative(); + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct DivExp : BinExp +{ + DivExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); + + // For operator overloading + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct ModExp : BinExp +{ + ModExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); + + // For operator overloading + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +#if DMDV2 +struct PowExp : BinExp +{ + PowExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + + // For operator overloading + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; +#endif + +struct ShlExp : BinExp +{ + ShlExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + IntRange getIntRange(); + + // For operator overloading + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct ShrExp : BinExp +{ + ShrExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + IntRange getIntRange(); + + // For operator overloading + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct UshrExp : BinExp +{ + UshrExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + IntRange getIntRange(); + + // For operator overloading + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct AndExp : BinExp +{ + AndExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); + + // For operator overloading + int isCommutative(); + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct OrExp : BinExp +{ + OrExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + MATCH implicitConvTo(Type *t); + IntRange getIntRange(); + + // For operator overloading + int isCommutative(); + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct XorExp : BinExp +{ + XorExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void buildArrayIdent(OutBuffer *buf, Expressions *arguments); + Expression *buildArrayLoop(Parameters *fparams); + MATCH implicitConvTo(Type *t); + IntRange getIntRange(); + + // For operator overloading + int isCommutative(); + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct OrOrExp : BinExp +{ + OrOrExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *checkToBoolean(Scope *sc); + int isBit(); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + elem *toElem(IRState *irs); +}; + +struct AndAndExp : BinExp +{ + AndAndExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *checkToBoolean(Scope *sc); + int isBit(); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + elem *toElem(IRState *irs); +}; + +struct CmpExp : BinExp +{ + CmpExp(enum TOK op, Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + int isBit(); + + // For operator overloading + int isCommutative(); + Identifier *opId(); + Expression *op_overload(Scope *sc); + + elem *toElem(IRState *irs); +}; + +struct InExp : BinExp +{ + InExp(Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + int isBit(); + + // For operator overloading + Identifier *opId(); + Identifier *opId_r(); + + elem *toElem(IRState *irs); +}; + +struct RemoveExp : BinExp +{ + RemoveExp(Loc loc, Expression *e1, Expression *e2); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + elem *toElem(IRState *irs); +}; + +// == and != + +struct EqualExp : BinExp +{ + EqualExp(enum TOK op, Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + int isBit(); + + // For operator overloading + int isCommutative(); + Identifier *opId(); + Expression *op_overload(Scope *sc); + + elem *toElem(IRState *irs); +}; + +// === and !=== + +struct IdentityExp : BinExp +{ + IdentityExp(enum TOK op, Loc loc, Expression *e1, Expression *e2); + Expression *semantic(Scope *sc); + int isBit(); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + elem *toElem(IRState *irs); +}; + +/****************************************************************/ + +struct CondExp : BinExp +{ + Expression *econd; + + CondExp(Loc loc, Expression *econd, Expression *e1, Expression *e2); + Expression *syntaxCopy(); + int apply(apply_fp_t fp, void *param); + Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void checkEscape(); + void checkEscapeRef(); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + Expression *modifiableLvalue(Scope *sc, Expression *e); + Expression *checkToBoolean(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + MATCH implicitConvTo(Type *t); + Expression *castTo(Scope *sc, Type *t); + + Expression *doInline(InlineDoState *ids); + Expression *inlineScan(InlineScanState *iss); + + elem *toElem(IRState *irs); +}; + +#if DMDV2 +/****************************************************************/ + +struct DefaultInitExp : Expression +{ + enum TOK subop; // which of the derived classes this is + + DefaultInitExp(Loc loc, enum TOK subop, int size); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct FileInitExp : DefaultInitExp +{ + FileInitExp(Loc loc); + Expression *semantic(Scope *sc); + Expression *resolveLoc(Loc loc, Scope *sc); +}; + +struct LineInitExp : DefaultInitExp +{ + LineInitExp(Loc loc); + Expression *semantic(Scope *sc); + Expression *resolveLoc(Loc loc, Scope *sc); +}; +#endif + +/****************************************************************/ + +/* Special values used by the interpreter + */ +#define EXP_CANT_INTERPRET ((Expression *)1) +#define EXP_CONTINUE_INTERPRET ((Expression *)2) +#define EXP_BREAK_INTERPRET ((Expression *)3) +#define EXP_GOTO_INTERPRET ((Expression *)4) +#define EXP_VOID_INTERPRET ((Expression *)5) + +Expression *expType(Type *type, Expression *e); + +Expression *Neg(Type *type, Expression *e1); +Expression *Com(Type *type, Expression *e1); +Expression *Not(Type *type, Expression *e1); +Expression *Bool(Type *type, Expression *e1); +Expression *Cast(Type *type, Type *to, Expression *e1); +Expression *ArrayLength(Type *type, Expression *e1); +Expression *Ptr(Type *type, Expression *e1); + +Expression *Add(Type *type, Expression *e1, Expression *e2); +Expression *Min(Type *type, Expression *e1, Expression *e2); +Expression *Mul(Type *type, Expression *e1, Expression *e2); +Expression *Div(Type *type, Expression *e1, Expression *e2); +Expression *Mod(Type *type, Expression *e1, Expression *e2); +Expression *Pow(Type *type, Expression *e1, Expression *e2); +Expression *Shl(Type *type, Expression *e1, Expression *e2); +Expression *Shr(Type *type, Expression *e1, Expression *e2); +Expression *Ushr(Type *type, Expression *e1, Expression *e2); +Expression *And(Type *type, Expression *e1, Expression *e2); +Expression *Or(Type *type, Expression *e1, Expression *e2); +Expression *Xor(Type *type, Expression *e1, Expression *e2); +Expression *Index(Type *type, Expression *e1, Expression *e2); +Expression *Cat(Type *type, Expression *e1, Expression *e2); + +Expression *Equal(enum TOK op, Type *type, Expression *e1, Expression *e2); +Expression *Cmp(enum TOK op, Type *type, Expression *e1, Expression *e2); +Expression *Identity(enum TOK op, Type *type, Expression *e1, Expression *e2); + +Expression *Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr); + +// Const-folding functions used by CTFE + +void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, int firstIndex); +void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, int firstIndex); +void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, int firstIndex); + + +#endif /* DMD_EXPRESSION_H */ diff --git a/func.c b/func.c new file mode 100644 index 00000000..39ba3aba --- /dev/null +++ b/func.c @@ -0,0 +1,4069 @@ +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "mars.h" +#include "init.h" +#include "declaration.h" +#include "attrib.h" +#include "expression.h" +#include "scope.h" +#include "mtype.h" +#include "aggregate.h" +#include "identifier.h" +#include "id.h" +#include "module.h" +#include "statement.h" +#include "template.h" +#include "hdrgen.h" + +#ifdef IN_GCC +#include "d-dmd-gcc.h" +#endif + +/********************************* FuncDeclaration ****************************/ + +FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type) + : Declaration(id) +{ + //printf("FuncDeclaration(id = '%s', type = %p)\n", id->toChars(), type); + //printf("storage_class = x%x\n", storage_class); + this->storage_class = storage_class; + this->type = type; + if (type) + this->storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR); + this->loc = loc; + this->endloc = endloc; + fthrows = NULL; + frequire = NULL; + fdrequire = NULL; + fdensure = NULL; + outId = NULL; + vresult = NULL; + returnLabel = NULL; + fensure = NULL; + fbody = NULL; + localsymtab = NULL; + vthis = NULL; + v_arguments = NULL; +#if IN_GCC + v_argptr = NULL; +#endif + v_argsave = NULL; + parameters = NULL; + labtab = NULL; + overnext = NULL; + vtblIndex = -1; + hasReturnExp = 0; + naked = 0; + inlineStatusExp = ILSuninitialized; + inlineStatusStmt = ILSuninitialized; + inlineNest = 0; + isArrayOp = 0; + semanticRun = PASSinit; + semantic3Errors = 0; +#if DMDV1 + nestedFrameRef = 0; +#endif + fes = NULL; + introducing = 0; + tintro = NULL; + /* The type given for "infer the return type" is a TypeFunction with + * NULL for the return type. + */ + inferRetType = (type && type->nextOf() == NULL); + storage_class2 = 0; + hasReturnExp = 0; + nrvo_can = 1; + nrvo_var = NULL; + shidden = NULL; +#if DMDV2 + builtin = BUILTINunknown; + tookAddressOf = 0; + flags = 0; +#endif +} + +Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s) +{ + FuncDeclaration *f; + + //printf("FuncDeclaration::syntaxCopy('%s')\n", toChars()); + if (s) + f = (FuncDeclaration *)s; + else + f = new FuncDeclaration(loc, endloc, ident, storage_class, type->syntaxCopy()); + f->outId = outId; + f->frequire = frequire ? frequire->syntaxCopy() : NULL; + f->fensure = fensure ? fensure->syntaxCopy() : NULL; + f->fbody = fbody ? fbody->syntaxCopy() : NULL; + assert(!fthrows); // deprecated + return f; +} + + +// Do the semantic analysis on the external interface to the function. + +void FuncDeclaration::semantic(Scope *sc) +{ TypeFunction *f; + AggregateDeclaration *ad; + StructDeclaration *sd; + ClassDeclaration *cd; + InterfaceDeclaration *id; + Dsymbol *pd; + bool doesoverride; + +#if 0 + printf("FuncDeclaration::semantic(sc = %p, this = %p, '%s', linkage = %d)\n", sc, this, toPrettyChars(), sc->linkage); + if (isFuncLiteralDeclaration()) + printf("\tFuncLiteralDeclaration()\n"); + printf("sc->parent = %s, parent = %s\n", sc->parent->toChars(), parent ? parent->toChars() : ""); + printf("type: %p, %s\n", type, type->toChars()); +#endif + + if (semanticRun != PASSinit && isFuncLiteralDeclaration()) + { + /* Member functions that have return types that are + * forward references can have semantic() run more than + * once on them. + * See test\interface2.d, test20 + */ + return; + } + + parent = sc->parent; + Dsymbol *parent = toParent(); + + if (semanticRun >= PASSsemanticdone) + { + if (!parent->isClassDeclaration()) + { + return; + } + // need to re-run semantic() in order to set the class's vtbl[] + } + else + { + assert(semanticRun <= PASSsemantic); + semanticRun = PASSsemantic; + } + + unsigned dprogress_save = Module::dprogress; + + foverrides.setDim(0); // reset in case semantic() is being retried for this function + + storage_class |= sc->stc & ~STCref; + ad = isThis(); + if (ad) + storage_class |= ad->storage_class & (STC_TYPECTOR | STCsynchronized); + + //printf("function storage_class = x%llx, sc->stc = x%llx, %x\n", storage_class, sc->stc, Declaration::isFinal()); + + if (!originalType) + originalType = type; + if (!type->deco) + { + sc = sc->push(); + sc->stc |= storage_class & STCdisable; // forward to function type + TypeFunction *tf = (TypeFunction *)type; + if (tf->isref) sc->stc |= STCref; + if (tf->isnothrow) sc->stc |= STCnothrow; + if (tf->isproperty) sc->stc |= STCproperty; + if (tf->purity == PUREfwdref) sc->stc |= STCpure; + if (tf->trust == TRUSTsafe) sc->stc |= STCsafe; + if (tf->trust == TRUSTsystem) sc->stc |= STCsystem; + if (tf->trust == TRUSTtrusted) sc->stc |= STCtrusted; + + if (isCtorDeclaration()) + sc->flags |= SCOPEctor; + type = type->semantic(loc, sc); + sc = sc->pop(); + + /* Apply const, immutable and shared storage class + * to the function type + */ + StorageClass stc = storage_class; + if (type->isImmutable()) + stc |= STCimmutable; + if (type->isConst()) + stc |= STCconst; + if (type->isShared() || storage_class & STCsynchronized) + stc |= STCshared; + if (type->isWild()) + stc |= STCwild; + switch (stc & STC_TYPECTOR) + { + case STCimmutable: + case STCimmutable | STCconst: + case STCimmutable | STCconst | STCshared: + case STCimmutable | STCshared: + case STCimmutable | STCwild: + case STCimmutable | STCconst | STCwild: + case STCimmutable | STCconst | STCshared | STCwild: + case STCimmutable | STCshared | STCwild: + // Don't use toInvariant(), as that will do a merge() + type = type->makeInvariant(); + goto Lmerge; + + case STCconst: + case STCconst | STCwild: + type = type->makeConst(); + goto Lmerge; + + case STCshared | STCconst: + case STCshared | STCconst | STCwild: + type = type->makeSharedConst(); + goto Lmerge; + + case STCshared: + type = type->makeShared(); + goto Lmerge; + + case STCwild: + type = type->makeWild(); + goto Lmerge; + + case STCshared | STCwild: + type = type->makeSharedWild(); + goto Lmerge; + + Lmerge: + if (!(type->ty == Tfunction && !type->nextOf())) + /* Can't do merge if return type is not known yet + */ + type->deco = type->merge()->deco; + break; + + case 0: + break; + + default: + assert(0); + } + } + storage_class &= ~STCref; + if (type->ty != Tfunction) + { + error("%s must be a function instead of %s", toChars(), type->toChars()); + return; + } + f = (TypeFunction *)(type); + size_t nparams = Parameter::dim(f->parameters); + + linkage = sc->linkage; + protection = sc->protection; + + /* Purity and safety can be inferred for some functions by examining + * the function body. + */ + if (fbody && + (isFuncLiteralDeclaration() || parent->isTemplateInstance())) + { + if (f->purity == PUREimpure) // purity not specified + flags |= FUNCFLAGpurityInprocess; + + if (f->trust == TRUSTdefault) + flags |= FUNCFLAGsafetyInprocess; + + if (!f->isnothrow) + flags |= FUNCFLAGnothrowInprocess; + } + + if (storage_class & STCscope) + error("functions cannot be scope"); + + if (isAbstract() && !isVirtual()) + error("non-virtual functions cannot be abstract"); + + if (isOverride() && !isVirtual()) + error("cannot override a non-virtual function"); + + if ((f->isConst() || f->isImmutable()) && !isThis()) + error("without 'this' cannot be const/immutable"); + + if (isAbstract() && isFinal()) + error("cannot be both final and abstract"); +#if 0 + if (isAbstract() && fbody) + error("abstract functions cannot have bodies"); +#endif + +#if 0 + if (isStaticConstructor() || isStaticDestructor()) + { + if (!isStatic() || type->nextOf()->ty != Tvoid) + error("static constructors / destructors must be static void"); + if (f->arguments && f->arguments->dim) + error("static constructors / destructors must have empty parameter list"); + // BUG: check for invalid storage classes + } +#endif + +#ifdef IN_GCC + { + AggregateDeclaration *ad = parent->isAggregateDeclaration(); + if (ad) + ad->methods.push(this); + } +#endif + sd = parent->isStructDeclaration(); + if (sd) + { + if (isCtorDeclaration()) + { + goto Ldone; + } +#if 0 + // Verify no constructors, destructors, etc. + if (isCtorDeclaration() + //||isDtorDeclaration() + //|| isInvariantDeclaration() + //|| isUnitTestDeclaration() + ) + { + error("special member functions not allowed for %ss", sd->kind()); + } + + if (!sd->inv) + sd->inv = isInvariantDeclaration(); + + if (!sd->aggNew) + sd->aggNew = isNewDeclaration(); + + if (isDelete()) + { + if (sd->aggDelete) + error("multiple delete's for struct %s", sd->toChars()); + sd->aggDelete = (DeleteDeclaration *)(this); + } +#endif + } + + id = parent->isInterfaceDeclaration(); + if (id) + { + storage_class |= STCabstract; + + if (isCtorDeclaration() || +#if DMDV2 + isPostBlitDeclaration() || +#endif + isDtorDeclaration() || + isInvariantDeclaration() || + isUnitTestDeclaration() || isNewDeclaration() || isDelete()) + error("constructors, destructors, postblits, invariants, unittests, new and delete functions are not allowed in interface %s", id->toChars()); + if (fbody && isVirtual()) + error("function body is not abstract in interface %s", id->toChars()); + } + + /* Contracts can only appear without a body when they are virtual interface functions + */ + if (!fbody && (fensure || frequire) && !(id && isVirtual())) + error("in and out contracts require function body"); + + /* Template member functions aren't virtual: + * interface TestInterface { void tpl(T)(); } + * and so won't work in interfaces + */ + if ((pd = toParent()) != NULL && + pd->isTemplateInstance() && + (pd = toParent2()) != NULL && + (id = pd->isInterfaceDeclaration()) != NULL) + { + error("template member functions are not allowed in interface %s", id->toChars()); + } + + cd = parent->isClassDeclaration(); + if (cd) + { int vi; + CtorDeclaration *ctor; + DtorDeclaration *dtor; + InvariantDeclaration *inv; + + if (isCtorDeclaration()) + { +// ctor = (CtorDeclaration *)this; +// if (!cd->ctor) +// cd->ctor = ctor; + goto Ldone; + } + +#if 0 + dtor = isDtorDeclaration(); + if (dtor) + { + if (cd->dtor) + error("multiple destructors for class %s", cd->toChars()); + cd->dtor = dtor; + } + + inv = isInvariantDeclaration(); + if (inv) + { + cd->inv = inv; + } + + if (isNewDeclaration()) + { + if (!cd->aggNew) + cd->aggNew = (NewDeclaration *)(this); + } + + if (isDelete()) + { + if (cd->aggDelete) + error("multiple delete's for class %s", cd->toChars()); + cd->aggDelete = (DeleteDeclaration *)(this); + } +#endif + + if (storage_class & STCabstract) + cd->isabstract = 1; + + // if static function, do not put in vtbl[] + if (!isVirtual()) + { + //printf("\tnot virtual\n"); + goto Ldone; + } + + /* Find index of existing function in base class's vtbl[] to override + * (the index will be the same as in cd's current vtbl[]) + */ + vi = cd->baseClass ? findVtblIndex((Dsymbols*)&cd->baseClass->vtbl, cd->baseClass->vtbl.dim) + : -1; + + doesoverride = FALSE; + switch (vi) + { + case -1: + /* Didn't find one, so + * This is an 'introducing' function which gets a new + * slot in the vtbl[]. + */ + + // Verify this doesn't override previous final function + if (cd->baseClass) + { Dsymbol *s = cd->baseClass->search(loc, ident, 0); + if (s) + { + FuncDeclaration *f = s->isFuncDeclaration(); + f = f->overloadExactMatch(type); + if (f && f->isFinal() && f->prot() != PROTprivate) + error("cannot override final function %s", f->toPrettyChars()); + } + } + + if (isFinal()) + { + // Don't check here, as it may override an interface function + //if (isOverride()) + //error("is marked as override, but does not override any function"); + cd->vtblFinal.push(this); + } + else + { + // Append to end of vtbl[] + //printf("\tintroducing function\n"); + introducing = 1; + vi = cd->vtbl.dim; + cd->vtbl.push(this); + vtblIndex = vi; + } + break; + + case -2: // can't determine because of fwd refs + cd->sizeok = 2; // can't finish due to forward reference + Module::dprogress = dprogress_save; + return; + + default: + { FuncDeclaration *fdv = (FuncDeclaration *)cd->baseClass->vtbl[vi]; + // This function is covariant with fdv + if (fdv->isFinal()) + error("cannot override final function %s", fdv->toPrettyChars()); + + doesoverride = TRUE; +#if DMDV2 + if (!isOverride()) + warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv->toPrettyChars()); +#endif + + FuncDeclaration *fdc = ((Dsymbol *)cd->vtbl.data[vi])->isFuncDeclaration(); + if (fdc->toParent() == parent) + { + // If both are mixins, then error. + // If either is not, the one that is not overrides the other. + + if (this->parent->isClassDeclaration() && fdc->parent->isClassDeclaration()) + error("multiple overrides of same function"); + + // if (this is mixin) && (fdc is not mixin) then fdc overrides + else if (!this->parent->isClassDeclaration() && fdc->parent->isClassDeclaration()) + break; + + else if (!this->parent->isClassDeclaration() // if both are mixins then error +#if !BREAKABI + && !isDtorDeclaration() +#endif +#if DMDV2 + && !isPostBlitDeclaration() +#endif + ) + error("multiple overrides of same function"); + } + cd->vtbl[vi] = this; + vtblIndex = vi; + + /* Remember which functions this overrides + */ + foverrides.push(fdv); + + /* This works by whenever this function is called, + * it actually returns tintro, which gets dynamically + * cast to type. But we know that tintro is a base + * of type, so we could optimize it by not doing a + * dynamic cast, but just subtracting the isBaseOf() + * offset if the value is != null. + */ + + if (fdv->tintro) + tintro = fdv->tintro; + else if (!type->equals(fdv->type)) + { + /* Only need to have a tintro if the vptr + * offsets differ + */ + int offset; + if (fdv->type->nextOf()->isBaseOf(type->nextOf(), &offset)) + { + tintro = fdv->type; + } + } + break; + } + } + + /* Go through all the interface bases. + * If this function is covariant with any members of those interface + * functions, set the tintro. + */ + for (int i = 0; i < cd->interfaces_dim; i++) + { + BaseClass *b = cd->interfaces[i]; + vi = findVtblIndex((Dsymbols *)&b->base->vtbl, b->base->vtbl.dim); + switch (vi) + { + case -1: + break; + + case -2: + cd->sizeok = 2; // can't finish due to forward reference + Module::dprogress = dprogress_save; + return; + + default: + { FuncDeclaration *fdv = (FuncDeclaration *)b->base->vtbl.tdata()[vi]; + Type *ti = NULL; + + /* Remember which functions this overrides + */ + foverrides.push(fdv); + +#if DMDV2 + /* Should we really require 'override' when implementing + * an interface function? + */ + //if (!isOverride()) + //warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv->toPrettyChars()); +#endif + + if (fdv->tintro) + ti = fdv->tintro; + else if (!type->equals(fdv->type)) + { + /* Only need to have a tintro if the vptr + * offsets differ + */ + unsigned errors = global.errors; + global.gag++; // suppress printing of error messages + int offset; + int baseOf = fdv->type->nextOf()->isBaseOf(type->nextOf(), &offset); + global.gag--; // suppress printing of error messages + if (errors != global.errors) + { + // any error in isBaseOf() is a forward reference error, so we bail out + global.errors = errors; + cd->sizeok = 2; // can't finish due to forward reference + Module::dprogress = dprogress_save; + return; + } + if (baseOf) + { + ti = fdv->type; + } + } + if (ti) + { + if (tintro && !tintro->equals(ti)) + { + error("incompatible covariant types %s and %s", tintro->toChars(), ti->toChars()); + } + tintro = ti; + } + goto L2; + } + } + } + + if (!doesoverride && isOverride()) + { + error("does not override any function"); + } + + L2: ; + + /* Go through all the interface bases. + * Disallow overriding any final functions in the interface(s). + */ + for (int i = 0; i < cd->interfaces_dim; i++) + { + BaseClass *b = cd->interfaces[i]; + if (b->base) + { + Dsymbol *s = search_function(b->base, ident); + if (s) + { + FuncDeclaration *f = s->isFuncDeclaration(); + if (f) + { + f = f->overloadExactMatch(type); + if (f && f->isFinal() && f->prot() != PROTprivate) + error("cannot override final function %s.%s", b->base->toChars(), f->toPrettyChars()); + } + } + } + } + } + else if (isOverride() && !parent->isTemplateInstance()) + error("override only applies to class member functions"); + + /* Do not allow template instances to add virtual functions + * to a class. + */ + if (isVirtual()) + { + TemplateInstance *ti = parent->isTemplateInstance(); + if (ti) + { + // Take care of nested templates + while (1) + { + TemplateInstance *ti2 = ti->tempdecl->parent->isTemplateInstance(); + if (!ti2) + break; + ti = ti2; + } + + // If it's a member template + ClassDeclaration *cd = ti->tempdecl->isClassMember(); + if (cd) + { + error("cannot use template to add virtual function to class '%s'", cd->toChars()); + } + } + } + + if (isMain()) + { + // Check parameters to see if they are either () or (char[][] args) + switch (nparams) + { + case 0: + break; + + case 1: + { + Parameter *arg0 = Parameter::getNth(f->parameters, 0); + if (arg0->type->ty != Tarray || + arg0->type->nextOf()->ty != Tarray || + arg0->type->nextOf()->nextOf()->ty != Tchar || + arg0->storageClass & (STCout | STCref | STClazy)) + goto Lmainerr; + break; + } + + default: + goto Lmainerr; + } + + if (!f->nextOf()) + error("must return int or void"); + else if (f->nextOf()->ty != Tint32 && f->nextOf()->ty != Tvoid) + error("must return int or void, not %s", f->nextOf()->toChars()); + if (f->varargs) + { + Lmainerr: + error("parameters must be main() or main(string[] args)"); + } + } + + if (ident == Id::assign && (sd || cd)) + { // Disallow identity assignment operator. + + // opAssign(...) + if (nparams == 0) + { if (f->varargs == 1) + goto Lassignerr; + } + else + { + Parameter *arg0 = Parameter::getNth(f->parameters, 0); + Type *t0 = arg0->type->toBasetype(); + Type *tb = sd ? sd->type : cd->type; + if (arg0->type->implicitConvTo(tb) || + (sd && t0->ty == Tpointer && t0->nextOf()->implicitConvTo(tb)) + ) + { + if (nparams == 1) + goto Lassignerr; + Parameter *arg1 = Parameter::getNth(f->parameters, 1); + if (arg1->defaultArg) + goto Lassignerr; + } + } + } + + if (isVirtual() && semanticRun != PASSsemanticdone) + { + /* Rewrite contracts as nested functions, then call them. + * Doing it as nested functions means that overriding functions + * can call them. + */ + if (frequire) + { /* in { ... } + * becomes: + * void __require() { ... } + * __require(); + */ + Loc loc = frequire->loc; + TypeFunction *tf = new TypeFunction(NULL, Type::tvoid, 0, LINKd); + FuncDeclaration *fd = new FuncDeclaration(loc, loc, + Id::require, STCundefined, tf); + fd->fbody = frequire; + Statement *s1 = new ExpStatement(loc, fd); + Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), (Expressions *)NULL); + Statement *s2 = new ExpStatement(loc, e); + frequire = new CompoundStatement(loc, s1, s2); + fdrequire = fd; + } + + if (!outId && f->nextOf() && f->nextOf()->toBasetype()->ty != Tvoid) + outId = Id::result; // provide a default + + if (fensure) + { /* out (result) { ... } + * becomes: + * tret __ensure(ref tret result) { ... } + * __ensure(result); + */ + Loc loc = fensure->loc; + Parameters *arguments = new Parameters(); + Parameter *a = NULL; + if (outId) + { a = new Parameter(STCref | STCconst, f->nextOf(), outId, NULL); + arguments->push(a); + } + TypeFunction *tf = new TypeFunction(arguments, Type::tvoid, 0, LINKd); + FuncDeclaration *fd = new FuncDeclaration(loc, loc, + Id::ensure, STCundefined, tf); + fd->fbody = fensure; + Statement *s1 = new ExpStatement(loc, fd); + Expression *eresult = NULL; + if (outId) + eresult = new IdentifierExp(loc, outId); + Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), eresult); + Statement *s2 = new ExpStatement(loc, e); + fensure = new CompoundStatement(loc, s1, s2); + fdensure = fd; + } + } + +Ldone: + Module::dprogress++; + semanticRun = PASSsemanticdone; + + /* Save scope for possible later use (if we need the + * function internals) + */ + scope = new Scope(*sc); + scope->setNoFree(); + return; + +Lassignerr: + if (sd) + { + sd->hasIdentityAssign = 1; // don't need to generate it + goto Ldone; + } + error("identity assignment operator overload is illegal"); +} + +void FuncDeclaration::semantic2(Scope *sc) +{ +} + +// Do the semantic analysis on the internals of the function. + +void FuncDeclaration::semantic3(Scope *sc) +{ TypeFunction *f; + VarDeclaration *argptr = NULL; + VarDeclaration *_arguments = NULL; + int nerrors = global.errors; + + if (!parent) + { + if (global.errors) + return; + //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", kind(), toChars(), sc); + assert(0); + } + //printf("FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent->toChars(), toChars(), sc, loc.toChars()); + //fflush(stdout); + //printf("storage class = x%x %x\n", sc->stc, storage_class); + //{ static int x; if (++x == 2) *(char*)0=0; } + //printf("\tlinkage = %d\n", sc->linkage); + + //printf(" sc->incontract = %d\n", sc->incontract); + if (semanticRun >= PASSsemantic3) + return; + semanticRun = PASSsemantic3; + semantic3Errors = 0; + + if (!type || type->ty != Tfunction) + return; + f = (TypeFunction *)(type); + +#if 0 + // Check the 'throws' clause + if (fthrows) + { + for (int i = 0; i < fthrows->dim; i++) + { + Type *t = fthrows->tdata()[i]; + + t = t->semantic(loc, sc); + if (!t->isClassHandle()) + error("can only throw classes, not %s", t->toChars()); + } + } +#endif + + if (frequire) + { + for (int i = 0; i < foverrides.dim; i++) + { + FuncDeclaration *fdv = foverrides.tdata()[i]; + + if (fdv->fbody && !fdv->frequire) + { + error("cannot have an in contract when overriden function %s does not have an in contract", fdv->toPrettyChars()); + break; + } + } + } + + frequire = mergeFrequire(frequire); + fensure = mergeFensure(fensure); + + if (fbody || frequire || fensure) + { + /* Symbol table into which we place parameters and nested functions, + * solely to diagnose name collisions. + */ + localsymtab = new DsymbolTable(); + + // Establish function scope + ScopeDsymbol *ss = new ScopeDsymbol(); + ss->parent = sc->scopesym; + Scope *sc2 = sc->push(ss); + sc2->func = this; + sc2->parent = this; + sc2->callSuper = 0; + sc2->sbreak = NULL; + sc2->scontinue = NULL; + sc2->sw = NULL; + sc2->fes = fes; + sc2->linkage = LINKd; + sc2->stc &= ~(STCauto | STCscope | STCstatic | STCabstract | + STCdeprecated | STCoverride | + STC_TYPECTOR | STCfinal | STCtls | STCgshared | STCref | + STCproperty | STCsafe | STCtrusted | STCsystem); + sc2->protection = PROTpublic; + sc2->explicitProtection = 0; + sc2->structalign = 8; + sc2->incontract = 0; + sc2->tf = NULL; + sc2->noctor = 0; + + // Declare 'this' + AggregateDeclaration *ad = isThis(); + if (ad) + { + if (isFuncLiteralDeclaration() && isNested() && !sc->intypeof) + { + error("function literals cannot be class members"); + return; + } + else + assert(!isNested() || sc->intypeof); // can't be both member and nested + } + vthis = declareThis(sc2, ad); + + // Declare hidden variable _arguments[] and _argptr + if (f->varargs == 1) + { +#if TARGET_NET + varArgs(sc2, f, argptr, _arguments); +#else + Type *t; + + if (global.params.is64bit) + { // Declare save area for varargs registers + Type *t = new TypeIdentifier(loc, Id::va_argsave_t); + t = t->semantic(loc, sc); + if (t == Type::terror) + { + error("must import core.vararg to use variadic functions"); + return; + } + else + { + v_argsave = new VarDeclaration(loc, t, Id::va_argsave, NULL); + v_argsave->semantic(sc2); + sc2->insert(v_argsave); + v_argsave->parent = this; + } + } + + if (f->linkage == LINKd) + { // Declare _arguments[] +#if BREAKABI + v_arguments = new VarDeclaration(0, Type::typeinfotypelist->type, Id::_arguments_typeinfo, NULL); + v_arguments->storage_class = STCparameter; + v_arguments->semantic(sc2); + sc2->insert(v_arguments); + v_arguments->parent = this; + + //t = Type::typeinfo->type->constOf()->arrayOf(); + t = Type::typeinfo->type->arrayOf(); + _arguments = new VarDeclaration(0, t, Id::_arguments, NULL); + _arguments->semantic(sc2); + sc2->insert(_arguments); + _arguments->parent = this; +#else + t = Type::typeinfo->type->arrayOf(); + v_arguments = new VarDeclaration(0, t, Id::_arguments, NULL); + v_arguments->storage_class = STCparameter | STCin; + v_arguments->semantic(sc2); + sc2->insert(v_arguments); + v_arguments->parent = this; +#endif + } + if (f->linkage == LINKd || (f->parameters && Parameter::dim(f->parameters))) + { // Declare _argptr +#if IN_GCC + t = d_gcc_builtin_va_list_d_type; +#else + t = Type::tvoid->pointerTo(); +#endif + argptr = new VarDeclaration(0, t, Id::_argptr, NULL); + argptr->semantic(sc2); + sc2->insert(argptr); + argptr->parent = this; + } +#endif + } + +#if 0 + // Propagate storage class from tuple parameters to their element-parameters. + if (f->parameters) + { + for (size_t i = 0; i < f->parameters->dim; i++) + { Parameter *arg = f->parameters->tdata()[i]; + + //printf("[%d] arg->type->ty = %d %s\n", i, arg->type->ty, arg->type->toChars()); + if (arg->type->ty == Ttuple) + { TypeTuple *t = (TypeTuple *)arg->type; + size_t dim = Parameter::dim(t->arguments); + for (size_t j = 0; j < dim; j++) + { Parameter *narg = Parameter::getNth(t->arguments, j); + narg->storageClass = arg->storageClass; + } + } + } + } +#endif + + /* Declare all the function parameters as variables + * and install them in parameters[] + */ + size_t nparams = Parameter::dim(f->parameters); + if (nparams) + { /* parameters[] has all the tuples removed, as the back end + * doesn't know about tuples + */ + parameters = new VarDeclarations(); + parameters->reserve(nparams); + for (size_t i = 0; i < nparams; i++) + { + Parameter *arg = Parameter::getNth(f->parameters, i); + Identifier *id = arg->ident; + if (!id) + { + /* Generate identifier for un-named parameter, + * because we need it later on. + */ + arg->ident = id = Identifier::generateId("_param_", i); + } + Type *vtype = arg->type; + //if (isPure()) + //vtype = vtype->addMod(MODconst); + VarDeclaration *v = new VarDeclaration(loc, vtype, id, NULL); + //printf("declaring parameter %s of type %s\n", v->toChars(), v->type->toChars()); + v->storage_class |= STCparameter; + if (f->varargs == 2 && i + 1 == nparams) + v->storage_class |= STCvariadic; + v->storage_class |= arg->storageClass & (STCin | STCout | STCref | STClazy | STCfinal | STC_TYPECTOR | STCnodtor); + v->semantic(sc2); + if (!sc2->insert(v)) + error("parameter %s.%s is already defined", toChars(), v->toChars()); + else + parameters->push(v); + localsymtab->insert(v); + v->parent = this; + } + } + + // Declare the tuple symbols and put them in the symbol table, + // but not in parameters[]. + if (f->parameters) + { + for (size_t i = 0; i < f->parameters->dim; i++) + { Parameter *arg = f->parameters->tdata()[i]; + + if (!arg->ident) + continue; // never used, so ignore + if (arg->type->ty == Ttuple) + { TypeTuple *t = (TypeTuple *)arg->type; + size_t dim = Parameter::dim(t->arguments); + Objects *exps = new Objects(); + exps->setDim(dim); + for (size_t j = 0; j < dim; j++) + { Parameter *narg = Parameter::getNth(t->arguments, j); + assert(narg->ident); + VarDeclaration *v = sc2->search(0, narg->ident, NULL)->isVarDeclaration(); + assert(v); + Expression *e = new VarExp(v->loc, v); + exps->tdata()[j] = e; + } + assert(arg->ident); + TupleDeclaration *v = new TupleDeclaration(loc, arg->ident, exps); + //printf("declaring tuple %s\n", v->toChars()); + v->isexp = 1; + if (!sc2->insert(v)) + error("parameter %s.%s is already defined", toChars(), v->toChars()); + localsymtab->insert(v); + v->parent = this; + } + } + } + + /* Do the semantic analysis on the [in] preconditions and + * [out] postconditions. + */ + sc2->incontract++; + + if (frequire) + { /* frequire is composed of the [in] contracts + */ + // BUG: need to error if accessing out parameters + // BUG: need to treat parameters as const + // BUG: need to disallow returns and throws + // BUG: verify that all in and ref parameters are read + frequire = frequire->semantic(sc2); + labtab = NULL; // so body can't refer to labels + } + + if (fensure || addPostInvariant()) + { /* fensure is composed of the [out] contracts + */ + if (!type->nextOf()) // if return type is inferred + { /* This case: + * auto fp = function() out { } body { }; + * Can fix by doing semantic() onf fbody first. + */ + error("post conditions are not supported if the return type is inferred"); + return; + } + + ScopeDsymbol *sym = new ScopeDsymbol(); + sym->parent = sc2->scopesym; + sc2 = sc2->push(sym); + + assert(type->nextOf()); + if (type->nextOf()->ty == Tvoid) + { + if (outId) + error("void functions have no result"); + } + else + { + if (!outId) + outId = Id::result; // provide a default + } + + if (outId) + { // Declare result variable + Loc loc = this->loc; + + if (fensure) + loc = fensure->loc; + + VarDeclaration *v = new VarDeclaration(loc, type->nextOf(), outId, NULL); + v->noscope = 1; + v->storage_class |= STCresult; +#if DMDV2 + if (!isVirtual()) + v->storage_class |= STCconst; + if (f->isref) + { + v->storage_class |= STCref | STCforeach; + } +#endif + sc2->incontract--; + v->semantic(sc2); + sc2->incontract++; + if (!sc2->insert(v)) + error("out result %s is already defined", v->toChars()); + v->parent = this; + vresult = v; + + // vresult gets initialized with the function return value + // in ReturnStatement::semantic() + } + + // BUG: need to treat parameters as const + // BUG: need to disallow returns and throws + if (fensure) + { fensure = fensure->semantic(sc2); + labtab = NULL; // so body can't refer to labels + } + + if (!global.params.useOut) + { fensure = NULL; // discard + vresult = NULL; + } + + // Postcondition invariant + if (addPostInvariant()) + { + Expression *e = NULL; + if (isCtorDeclaration()) + { + // Call invariant directly only if it exists + InvariantDeclaration *inv = ad->inv; + ClassDeclaration *cd = ad->isClassDeclaration(); + + while (!inv && cd) + { + cd = cd->baseClass; + if (!cd) + break; + inv = cd->inv; + } + if (inv) + { + e = new DsymbolExp(0, inv); + e = new CallExp(0, e); + e = e->semantic(sc2); + } + } + else + { // Call invariant virtually + Expression *v = new ThisExp(0); + v->type = vthis->type; +#if STRUCTTHISREF + if (ad->isStructDeclaration()) + v = v->addressOf(sc); +#endif + e = new AssertExp(0, v); + } + if (e) + { + ExpStatement *s = new ExpStatement(0, e); + if (fensure) + fensure = new CompoundStatement(0, s, fensure); + else + fensure = s; + } + } + + if (fensure) + { returnLabel = new LabelDsymbol(Id::returnLabel); + LabelStatement *ls = new LabelStatement(0, Id::returnLabel, fensure); + returnLabel->statement = ls; + } + sc2 = sc2->pop(); + } + + sc2->incontract--; + + if (fbody) + { AggregateDeclaration *ad = isAggregateMember(); + + /* If this is a class constructor + */ + if (ad && isCtorDeclaration()) + { + for (size_t i = 0; i < ad->fields.dim; i++) + { VarDeclaration *v = ad->fields[i]; + + v->ctorinit = 0; + } + } + + if (inferRetType || f->retStyle() != RETstack) + nrvo_can = 0; + + fbody = fbody->semantic(sc2); + if (!fbody) + fbody = new CompoundStatement(0, new Statements()); + + if (inferRetType) + { // If no return type inferred yet, then infer a void + if (!type->nextOf()) + { + ((TypeFunction *)type)->next = Type::tvoid; + //type = type->semantic(loc, sc); // Removed with 6902 + } + f = (TypeFunction *)type; + } + + if (isStaticCtorDeclaration()) + { /* It's a static constructor. Ensure that all + * ctor consts were initialized. + */ + + Dsymbol *p = toParent(); + ScopeDsymbol *pd = p->isScopeDsymbol(); + if (!pd) + { + error("static constructor can only be member of struct/class/module, not %s %s", p->kind(), p->toChars()); + } + else + { + for (size_t i = 0; i < pd->members->dim; i++) + { Dsymbol *s = pd->members->tdata()[i]; + + s->checkCtorConstInit(); + } + } + } + + if (isCtorDeclaration() && ad) + { + //printf("callSuper = x%x\n", sc2->callSuper); + + ClassDeclaration *cd = ad->isClassDeclaration(); + + // Verify that all the ctorinit fields got initialized + if (!(sc2->callSuper & CSXthis_ctor)) + { + for (size_t i = 0; i < ad->fields.dim; i++) + { VarDeclaration *v = ad->fields[i]; + + if (v->ctorinit == 0) + { + /* Current bugs in the flow analysis: + * 1. union members should not produce error messages even if + * not assigned to + * 2. structs should recognize delegating opAssign calls as well + * as delegating calls to other constructors + */ + if (v->isCtorinit() && !v->type->isMutable() && cd) + error("missing initializer for final field %s", v->toChars()); + else if (v->storage_class & STCnodefaultctor) + error("field %s must be initialized in constructor", v->toChars()); + } + } + } + + if (cd && + !(sc2->callSuper & CSXany_ctor) && + cd->baseClass && cd->baseClass->ctor) + { + sc2->callSuper = 0; + + // Insert implicit super() at start of fbody + Expression *e1 = new SuperExp(0); + Expression *e = new CallExp(0, e1); + + e = e->trySemantic(sc2); + if (!e) + error("no match for implicit super() call in constructor"); + else + { + Statement *s = new ExpStatement(0, e); + fbody = new CompoundStatement(0, s, fbody); + } + } + } + else if (fes) + { // For foreach(){} body, append a return 0; + Expression *e = new IntegerExp(0); + Statement *s = new ReturnStatement(0, e); + fbody = new CompoundStatement(0, fbody, s); + assert(!returnLabel); + } + else if (!hasReturnExp && type->nextOf()->ty != Tvoid) + error("has no return statement, but is expected to return a value of type %s", type->nextOf()->toChars()); + else if (hasReturnExp & 8) // if inline asm + { + flags &= ~FUNCFLAGnothrowInprocess; + } + else + { +#if DMDV2 + // Check for errors related to 'nothrow'. + int nothrowErrors = global.errors; + int blockexit = fbody ? fbody->blockExit(f->isnothrow) : BEfallthru; + if (f->isnothrow && (global.errors != nothrowErrors) ) + error("'%s' is nothrow yet may throw", toChars()); + if (flags & FUNCFLAGnothrowInprocess) + { + flags &= ~FUNCFLAGnothrowInprocess; + if (!(blockexit & BEthrow)) + f->isnothrow = TRUE; + } + + int offend = blockexit & BEfallthru; +#endif + if (type->nextOf()->ty == Tvoid) + { + if (offend && isMain()) + { // Add a return 0; statement + Statement *s = new ReturnStatement(0, new IntegerExp(0)); + fbody = new CompoundStatement(0, fbody, s); + } + } + else + { + if (offend) + { Expression *e; +#if DMDV1 + warning(loc, "no return exp; or assert(0); at end of function"); +#else + error("no return exp; or assert(0); at end of function"); +#endif + if (global.params.useAssert && + !global.params.useInline) + { /* Add an assert(0, msg); where the missing return + * should be. + */ + e = new AssertExp( + endloc, + new IntegerExp(0), + new StringExp(loc, (char *)"missing return expression") + ); + } + else + e = new HaltExp(endloc); + e = new CommaExp(0, e, type->nextOf()->defaultInit()); + e = e->semantic(sc2); + Statement *s = new ExpStatement(0, e); + fbody = new CompoundStatement(0, fbody, s); + } + } + } + } + + { + Statements *a = new Statements(); + + // Merge in initialization of 'out' parameters + if (parameters) + { for (size_t i = 0; i < parameters->dim; i++) + { + VarDeclaration *v = parameters->tdata()[i]; + if (v->storage_class & STCout) + { + assert(v->init); + ExpInitializer *ie = v->init->isExpInitializer(); + assert(ie); + if (ie->exp->op == TOKconstruct) + ie->exp->op = TOKassign; // construction occured in parameter processing + a->push(new ExpStatement(0, ie->exp)); + } + } + } + + if (argptr) + { // Initialize _argptr +#if IN_GCC + // Handled in FuncDeclaration::toObjFile + v_argptr = argptr; + v_argptr->init = new VoidInitializer(loc); +#else + Type *t = argptr->type; + if (global.params.is64bit) + { // Initialize _argptr to point to v_argsave + Expression *e1 = new VarExp(0, argptr); + Expression *e = new SymOffExp(0, v_argsave, 6*8 + 8*16); + e->type = argptr->type; + e = new AssignExp(0, e1, e); + e = e->semantic(sc); + a->push(new ExpStatement(0, e)); + } + else + { // Initialize _argptr to point past non-variadic arg + VarDeclaration *p; + unsigned offset = 0; + + Expression *e1 = new VarExp(0, argptr); + // Find the last non-ref parameter + if (parameters && parameters->dim) + { + int lastNonref = parameters->dim -1; + p = parameters->tdata()[lastNonref]; + /* The trouble with out and ref parameters is that taking + * the address of it doesn't work, because later processing + * adds in an extra level of indirection. So we skip over them. + */ + while (p->storage_class & (STCout | STCref)) + { + --lastNonref; + offset += PTRSIZE; + if (lastNonref < 0) + { + p = v_arguments; + break; + } + p = parameters->tdata()[lastNonref]; + } + } + else + p = v_arguments; // last parameter is _arguments[] + if (p->storage_class & STClazy) + // If the last parameter is lazy, it's the size of a delegate + offset += PTRSIZE * 2; + else + offset += p->type->size(); + offset = (offset + PTRSIZE - 1) & ~(PTRSIZE - 1); // assume stack aligns on pointer size + Expression *e = new SymOffExp(0, p, offset); + e->type = Type::tvoidptr; + //e = e->semantic(sc); + e = new AssignExp(0, e1, e); + e->type = t; + a->push(new ExpStatement(0, e)); + p->isargptr = TRUE; + } +#endif + } + + if (_arguments) + { + /* Advance to elements[] member of TypeInfo_Tuple with: + * _arguments = v_arguments.elements; + */ + Expression *e = new VarExp(0, v_arguments); + e = new DotIdExp(0, e, Id::elements); + Expression *e1 = new VarExp(0, _arguments); + e = new ConstructExp(0, e1, e); + e = e->semantic(sc2); + a->push(new ExpStatement(0, e)); + } + + // Merge contracts together with body into one compound statement + + if (frequire && global.params.useIn) + { frequire->incontract = 1; + a->push(frequire); + } + + // Precondition invariant + if (addPreInvariant()) + { + Expression *e = NULL; + if (isDtorDeclaration()) + { + // Call invariant directly only if it exists + InvariantDeclaration *inv = ad->inv; + ClassDeclaration *cd = ad->isClassDeclaration(); + + while (!inv && cd) + { + cd = cd->baseClass; + if (!cd) + break; + inv = cd->inv; + } + if (inv) + { + e = new DsymbolExp(0, inv); + e = new CallExp(0, e); + e = e->semantic(sc2); + } + } + else + { // Call invariant virtually + Expression *v = new ThisExp(0); + v->type = vthis->type; +#if STRUCTTHISREF + if (ad->isStructDeclaration()) + v = v->addressOf(sc); +#endif + Expression *se = new StringExp(0, (char *)"null this"); + se = se->semantic(sc); + se->type = Type::tchar->arrayOf(); + e = new AssertExp(loc, v, se); + } + if (e) + { + ExpStatement *s = new ExpStatement(0, e); + a->push(s); + } + } + + if (fbody) + a->push(fbody); + + if (fensure) + { + a->push(returnLabel->statement); + + if (type->nextOf()->ty != Tvoid) + { + // Create: return vresult; + assert(vresult); + Expression *e = new VarExp(0, vresult); + if (tintro) + { e = e->implicitCastTo(sc, tintro->nextOf()); + e = e->semantic(sc); + } + ReturnStatement *s = new ReturnStatement(0, e); + a->push(s); + } + } + + fbody = new CompoundStatement(0, a); +#if DMDV2 + /* Append destructor calls for parameters as finally blocks. + */ + if (parameters) + { for (size_t i = 0; i < parameters->dim; i++) + { + VarDeclaration *v = parameters->tdata()[i]; + + if (v->storage_class & (STCref | STCout)) + continue; + + /* Don't do this for static arrays, since static + * arrays are called by reference. Remove this + * when we change them to call by value. + */ + if (v->type->toBasetype()->ty == Tsarray) + continue; + + if (v->noscope) + continue; + + Expression *e = v->edtor; + if (e) + { Statement *s = new ExpStatement(0, e); + s = s->semantic(sc2); + if (fbody->blockExit(f->isnothrow) == BEfallthru) + fbody = new CompoundStatement(0, fbody, s); + else + fbody = new TryFinallyStatement(0, fbody, s); + } + } + } +#endif + +#if 1 + if (isSynchronized()) + { /* Wrap the entire function body in a synchronized statement + */ + AggregateDeclaration *ad = isThis(); + ClassDeclaration *cd = ad ? ad->isClassDeclaration() : parent->isClassDeclaration(); + + if (cd) + { +#if TARGET_WINDOS + if (/*config.flags2 & CFG2seh &&*/ // always on for WINDOS + !isStatic() && !fbody->usesEH()) + { + /* The back end uses the "jmonitor" hack for syncing; + * no need to do the sync at this level. + */ + } + else +#endif + { + Expression *vsync; + if (isStatic()) + { // The monitor is in the ClassInfo + vsync = new DotIdExp(loc, new DsymbolExp(loc, cd), Id::classinfo); + } + else + { // 'this' is the monitor + vsync = new VarExp(loc, vthis); + } + fbody = new PeelStatement(fbody); // don't redo semantic() + fbody = new SynchronizedStatement(loc, vsync, fbody); + fbody = fbody->semantic(sc2); + } + } + else + { + error("synchronized function %s must be a member of a class", toChars()); + } + } +#endif + } + + sc2->callSuper = 0; + sc2->pop(); + } + + /* If function survived being marked as impure, then it is pure + */ + if (flags & FUNCFLAGpurityInprocess) + { + flags &= ~FUNCFLAGpurityInprocess; + f->purity = PUREfwdref; + } + + if (flags & FUNCFLAGsafetyInprocess) + { + flags &= ~FUNCFLAGsafetyInprocess; + f->trust = TRUSTsafe; + } + + // Do semantic type AFTER pure/nothrow inference. + if (inferRetType) + { + type = type->semantic(loc, sc); + } + + if (global.gag && global.errors != nerrors) + semanticRun = PASSsemanticdone; // Ensure errors get reported again + else + { + semanticRun = PASSsemantic3done; + semantic3Errors = global.errors - nerrors; + } + //printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent->toChars(), toChars(), sc, loc.toChars()); + //fflush(stdout); +} + +void FuncDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + //printf("FuncDeclaration::toCBuffer() '%s'\n", toChars()); + + StorageClassDeclaration::stcToCBuffer(buf, storage_class); + type->toCBuffer(buf, ident, hgs); + bodyToCBuffer(buf, hgs); +} + +VarDeclaration *FuncDeclaration::declareThis(Scope *sc, AggregateDeclaration *ad) +{ + if (ad) + { VarDeclaration *v; + + { + assert(ad->handle); + Type *thandle = ad->handle; +#if STRUCTTHISREF + thandle = thandle->addMod(type->mod); + thandle = thandle->addStorageClass(storage_class); + //if (isPure()) + //thandle = thandle->addMod(MODconst); +#else + if (storage_class & STCconst || type->isConst()) + { + assert(0); // BUG: shared not handled + if (thandle->ty == Tclass) + thandle = thandle->constOf(); + else + { assert(thandle->ty == Tpointer); + thandle = thandle->nextOf()->constOf()->pointerTo(); + } + } + else if (storage_class & STCimmutable || type->isImmutable()) + { + if (thandle->ty == Tclass) + thandle = thandle->invariantOf(); + else + { assert(thandle->ty == Tpointer); + thandle = thandle->nextOf()->invariantOf()->pointerTo(); + } + } + else if (storage_class & STCshared || type->isShared()) + { + assert(0); // not implemented + } +#endif + v = new ThisDeclaration(loc, thandle); + //v = new ThisDeclaration(loc, isCtorDeclaration() ? ad->handle : thandle); + v->storage_class |= STCparameter; +#if STRUCTTHISREF + if (thandle->ty == Tstruct) + v->storage_class |= STCref; +#endif + v->semantic(sc); + if (!sc->insert(v)) + assert(0); + v->parent = this; + return v; + } + } + else if (isNested()) + { + /* The 'this' for a nested function is the link to the + * enclosing function's stack frame. + * Note that nested functions and member functions are disjoint. + */ + VarDeclaration *v = new ThisDeclaration(loc, Type::tvoid->pointerTo()); + v->storage_class |= STCparameter; + v->semantic(sc); + if (!sc->insert(v)) + assert(0); + v->parent = this; + return v; + } + + return NULL; +} + +int FuncDeclaration::equals(Object *o) +{ + if (this == o) + return TRUE; + + Dsymbol *s = isDsymbol(o); + if (s) + { + FuncDeclaration *fd = s->isFuncDeclaration(); + if (fd) + { + return toParent()->equals(fd->toParent()) && + ident->equals(fd->ident) && type->equals(fd->type); + } + } + return FALSE; +} + +void FuncDeclaration::bodyToCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (fbody && + (!hgs->hdrgen || hgs->tpltMember || canInline(1,1,1)) + ) + { buf->writenl(); + + // in{} + if (frequire) + { buf->writestring("in"); + buf->writenl(); + frequire->toCBuffer(buf, hgs); + } + + // out{} + if (fensure) + { buf->writestring("out"); + if (outId) + { buf->writebyte('('); + buf->writestring(outId->toChars()); + buf->writebyte(')'); + } + buf->writenl(); + fensure->toCBuffer(buf, hgs); + } + + if (frequire || fensure) + { buf->writestring("body"); + buf->writenl(); + } + + buf->writebyte('{'); + buf->writenl(); + fbody->toCBuffer(buf, hgs); + buf->writebyte('}'); + buf->writenl(); + } + else + { buf->writeByte(';'); + buf->writenl(); + } +} + +/**************************************************** + * Merge into this function the 'in' contracts of all it overrides. + * 'in's are OR'd together, i.e. only one of them needs to pass. + */ + +Statement *FuncDeclaration::mergeFrequire(Statement *sf) +{ + /* If a base function and its override both have an IN contract, then + * only one of them needs to succeed. This is done by generating: + * + * void derived.in() { + * try { + * base.in(); + * } + * catch () { + * ... body of derived.in() ... + * } + * } + * + * So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid. + * If base.in() throws, then derived.in()'s body is executed. + */ + + /* Implementing this is done by having the overriding function call + * nested functions (the fdrequire functions) nested inside the overridden + * function. This requires that the stack layout of the calling function's + * parameters and 'this' pointer be in the same place (as the nested + * function refers to them). + * This is easy for the parameters, as they are all on the stack in the same + * place by definition, since it's an overriding function. The problem is + * getting the 'this' pointer in the same place, since it is a local variable. + * We did some hacks in the code generator to make this happen: + * 1. always generate exception handler frame, or at least leave space for it + * in the frame (Windows 32 SEH only) + * 2. always generate an EBP style frame + * 3. since 'this' is passed in a register that is subsequently copied into + * a stack local, allocate that local immediately following the exception + * handler block, so it is always at the same offset from EBP. + */ + for (int i = 0; i < foverrides.dim; i++) + { + FuncDeclaration *fdv = foverrides.tdata()[i]; + + /* The semantic pass on the contracts of the overridden functions must + * be completed before code generation occurs (bug 3602). + */ + if (fdv->fdrequire && fdv->fdrequire->semanticRun != PASSsemantic3done) + { + assert(fdv->scope); + Scope *sc = fdv->scope->push(); + sc->stc &= ~STCoverride; + fdv->semantic3(sc); + sc->pop(); + } + + sf = fdv->mergeFrequire(sf); + if (sf && fdv->fdrequire) + { + //printf("fdv->frequire: %s\n", fdv->frequire->toChars()); + /* Make the call: + * try { __require(); } + * catch { frequire; } + */ + Expression *eresult = NULL; + Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdrequire, 0), eresult); + Statement *s2 = new ExpStatement(loc, e); + + Catch *c = new Catch(loc, NULL, NULL, sf); + c->internalCatch = true; + Catches *catches = new Catches(); + catches->push(c); + sf = new TryCatchStatement(loc, s2, catches); + } + else + return NULL; + } + return sf; +} + +/**************************************************** + * Merge into this function the 'out' contracts of all it overrides. + * 'out's are AND'd together, i.e. all of them need to pass. + */ + +Statement *FuncDeclaration::mergeFensure(Statement *sf) +{ + /* Same comments as for mergeFrequire(), except that we take care + * of generating a consistent reference to the 'result' local by + * explicitly passing 'result' to the nested function as a reference + * argument. + * This won't work for the 'this' parameter as it would require changing + * the semantic code for the nested function so that it looks on the parameter + * list for the 'this' pointer, something that would need an unknown amount + * of tweaking of various parts of the compiler that I'd rather leave alone. + */ + for (int i = 0; i < foverrides.dim; i++) + { + FuncDeclaration *fdv = foverrides.tdata()[i]; + + /* The semantic pass on the contracts of the overridden functions must + * be completed before code generation occurs (bug 3602 and 5230). + */ + if (fdv->fdensure && fdv->fdensure->semanticRun != PASSsemantic3done) + { + assert(fdv->scope); + Scope *sc = fdv->scope->push(); + sc->stc &= ~STCoverride; + fdv->semantic3(sc); + sc->pop(); + } + + sf = fdv->mergeFensure(sf); + if (fdv->fdensure) + { + //printf("fdv->fensure: %s\n", fdv->fensure->toChars()); + // Make the call: __ensure(result) + Expression *eresult = NULL; + if (outId) + eresult = new IdentifierExp(loc, outId); + Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, 0), eresult); + Statement *s2 = new ExpStatement(loc, e); + + if (sf) + { + sf = new CompoundStatement(fensure->loc, s2, sf); + } + else + sf = s2; + } + } + return sf; +} + +/**************************************************** + * Determine if 'this' overrides fd. + * Return !=0 if it does. + */ + +int FuncDeclaration::overrides(FuncDeclaration *fd) +{ int result = 0; + + if (fd->ident == ident) + { + int cov = type->covariant(fd->type); + if (cov) + { ClassDeclaration *cd1 = toParent()->isClassDeclaration(); + ClassDeclaration *cd2 = fd->toParent()->isClassDeclaration(); + + if (cd1 && cd2 && cd2->isBaseOf(cd1, NULL)) + result = 1; + } + } + return result; +} + +/************************************************* + * Find index of function in vtbl[0..dim] that + * this function overrides. + * Prefer an exact match to a covariant one. + * Returns: + * -1 didn't find one + * -2 can't determine because of forward references + */ + +int FuncDeclaration::findVtblIndex(Dsymbols *vtbl, int dim) +{ + FuncDeclaration *mismatch = NULL; + int bestvi = -1; + for (int vi = 0; vi < dim; vi++) + { + FuncDeclaration *fdv = vtbl->tdata()[vi]->isFuncDeclaration(); + if (fdv && fdv->ident == ident) + { + if (type->equals(fdv->type)) // if exact match + return vi; // no need to look further + + int cov = type->covariant(fdv->type); + //printf("\tbaseclass cov = %d\n", cov); + switch (cov) + { + case 0: // types are distinct + break; + + case 1: + bestvi = vi; // covariant, but not identical + break; // keep looking for an exact match + + case 2: + mismatch = fdv; // overrides, but is not covariant + break; // keep looking for an exact match + + case 3: + return -2; // forward references + + default: + assert(0); + } + } + } + if (bestvi == -1 && mismatch) + { + //type->print(); + //mismatch->type->print(); + //printf("%s %s\n", type->deco, mismatch->type->deco); + error("of type %s overrides but is not covariant with %s of type %s", + type->toChars(), mismatch->toPrettyChars(), mismatch->type->toChars()); + } + return bestvi; +} + +/**************************************************** + * Overload this FuncDeclaration with the new one f. + * Return !=0 if successful; i.e. no conflict. + */ + +int FuncDeclaration::overloadInsert(Dsymbol *s) +{ + FuncDeclaration *f; + AliasDeclaration *a; + + //printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s->toChars(), toChars()); + a = s->isAliasDeclaration(); + if (a) + { + if (overnext) + return overnext->overloadInsert(a); + if (!a->aliassym && a->type->ty != Tident && a->type->ty != Tinstance) + { + //printf("\ta = '%s'\n", a->type->toChars()); + return FALSE; + } + overnext = a; + //printf("\ttrue: no conflict\n"); + return TRUE; + } + f = s->isFuncDeclaration(); + if (!f) + return FALSE; + +#if 0 + /* Disable this check because: + * const void foo(); + * semantic() isn't run yet on foo(), so the const hasn't been + * applied yet. + */ + if (type) + { printf("type = %s\n", type->toChars()); + printf("f->type = %s\n", f->type->toChars()); + } + if (type && f->type && // can be NULL for overloaded constructors + f->type->covariant(type) && + f->type->mod == type->mod && + !isFuncAliasDeclaration()) + { + //printf("\tfalse: conflict %s\n", kind()); + return FALSE; + } +#endif + + if (overnext) + return overnext->overloadInsert(f); + overnext = f; + //printf("\ttrue: no conflict\n"); + return TRUE; +} + +/******************************************** + * Find function in overload list that exactly matches t. + */ + +/*************************************************** + * Visit each overloaded function in turn, and call + * (*fp)(param, f) on it. + * Exit when no more, or (*fp)(param, f) returns 1. + * Returns: + * 0 continue + * 1 done + */ + +int overloadApply(FuncDeclaration *fstart, + int (*fp)(void *, FuncDeclaration *), + void *param) +{ + FuncDeclaration *f; + Declaration *d; + Declaration *next; + + for (d = fstart; d; d = next) + { FuncAliasDeclaration *fa = d->isFuncAliasDeclaration(); + + if (fa) + { + if (overloadApply(fa->funcalias, fp, param)) + return 1; + next = fa->overnext; + } + else + { + AliasDeclaration *a = d->isAliasDeclaration(); + + if (a) + { + Dsymbol *s = a->toAlias(); + next = s->isDeclaration(); + if (next == a) + break; + if (next == fstart) + break; + } + else + { + f = d->isFuncDeclaration(); + if (!f) + { d->error("is aliased to a function"); + break; // BUG: should print error message? + } + if ((*fp)(param, f)) + return 1; + + next = f->overnext; + } + } + } + return 0; +} + +/******************************************** + * If there are no overloads of function f, return that function, + * otherwise return NULL. + */ + +static int fpunique(void *param, FuncDeclaration *f) +{ FuncDeclaration **pf = (FuncDeclaration **)param; + + if (*pf) + { *pf = NULL; + return 1; // ambiguous, done + } + else + { *pf = f; + return 0; + } +} + +FuncDeclaration *FuncDeclaration::isUnique() +{ FuncDeclaration *result = NULL; + + overloadApply(this, &fpunique, &result); + return result; +} + +/******************************************** + * Find function in overload list that exactly matches t. + */ + +struct Param1 +{ + Type *t; // type to match + FuncDeclaration *f; // return value +}; + +int fp1(void *param, FuncDeclaration *f) +{ Param1 *p = (Param1 *)param; + Type *t = p->t; + + if (t->equals(f->type)) + { p->f = f; + return 1; + } + +#if DMDV2 + /* Allow covariant matches, as long as the return type + * is just a const conversion. + * This allows things like pure functions to match with an impure function type. + */ + if (t->ty == Tfunction) + { TypeFunction *tf = (TypeFunction *)f->type; + if (tf->covariant(t) == 1 && + tf->nextOf()->implicitConvTo(t->nextOf()) >= MATCHconst) + { + p->f = f; + return 1; + } + } +#endif + return 0; +} + +FuncDeclaration *FuncDeclaration::overloadExactMatch(Type *t) +{ + Param1 p; + p.t = t; + p.f = NULL; + overloadApply(this, &fp1, &p); + return p.f; +} + + +/******************************************** + * Decide which function matches the arguments best. + */ + +struct Param2 +{ + Match *m; +#if DMDV2 + Expression *ethis; + int property; // 0: unintialized + // 1: seen @property + // 2: not @property +#endif + Expressions *arguments; +}; + +int fp2(void *param, FuncDeclaration *f) +{ Param2 *p = (Param2 *)param; + Match *m = p->m; + Expressions *arguments = p->arguments; + MATCH match; + + if (f != m->lastf) // skip duplicates + { + m->anyf = f; + TypeFunction *tf = (TypeFunction *)f->type; + + int property = (tf->isproperty) ? 1 : 2; + if (p->property == 0) + p->property = property; + else if (p->property != property) + error(f->loc, "cannot overload both property and non-property functions"); + + /* For constructors, don't worry about the right type of ethis. It's a problem + * anyway, because the constructor attribute may not match the ethis attribute, + * but we don't care because the attribute on the ethis doesn't matter until + * after it's constructed. + */ + match = (MATCH) tf->callMatch(f->needThis() && !f->isCtorDeclaration() ? p->ethis : NULL, arguments); + //printf("test1: match = %d\n", match); + if (match != MATCHnomatch) + { + if (match > m->last) + goto LfIsBetter; + + if (match < m->last) + goto LlastIsBetter; + + /* See if one of the matches overrides the other. + */ + if (m->lastf->overrides(f)) + goto LlastIsBetter; + else if (f->overrides(m->lastf)) + goto LfIsBetter; + +#if DMDV2 + /* Try to disambiguate using template-style partial ordering rules. + * In essence, if f() and g() are ambiguous, if f() can call g(), + * but g() cannot call f(), then pick f(). + * This is because f() is "more specialized." + */ + { + MATCH c1 = f->leastAsSpecialized(m->lastf); + MATCH c2 = m->lastf->leastAsSpecialized(f); + //printf("c1 = %d, c2 = %d\n", c1, c2); + if (c1 > c2) + goto LfIsBetter; + if (c1 < c2) + goto LlastIsBetter; + } +#endif + Lambiguous: + m->nextf = f; + m->count++; + return 0; + + LfIsBetter: + m->last = match; + m->lastf = f; + m->count = 1; + return 0; + + LlastIsBetter: + return 0; + } + } + return 0; +} + + +void overloadResolveX(Match *m, FuncDeclaration *fstart, + Expression *ethis, Expressions *arguments) +{ + Param2 p; + p.m = m; + p.ethis = ethis; + p.property = 0; + p.arguments = arguments; + overloadApply(fstart, &fp2, &p); +} + + +FuncDeclaration *FuncDeclaration::overloadResolve(Loc loc, Expression *ethis, Expressions *arguments, int flags) +{ + TypeFunction *tf; + Match m; + +#if 0 +printf("FuncDeclaration::overloadResolve('%s')\n", toChars()); +if (arguments) +{ int i; + + for (i = 0; i < arguments->dim; i++) + { Expression *arg; + + arg = arguments->tdata()[i]; + assert(arg->type); + printf("\t%s: ", arg->toChars()); + arg->type->print(); + } +} +#endif + + memset(&m, 0, sizeof(m)); + m.last = MATCHnomatch; + overloadResolveX(&m, this, ethis, arguments); + + if (m.count == 1) // exactly one match + { + return m.lastf; + } + else + { + OutBuffer buf; + + buf.writeByte('('); + if (arguments) + { + HdrGenState hgs; + + argExpTypesToCBuffer(&buf, arguments, &hgs); + buf.writeByte(')'); + if (ethis) + ethis->type->modToBuffer(&buf); + } + else + buf.writeByte(')'); + + if (m.last == MATCHnomatch) + { + if (flags & 1) // if do not print error messages + return NULL; // no match + + tf = (TypeFunction *)type; + + OutBuffer buf2; + tf->modToBuffer(&buf2); + + //printf("tf = %s, args = %s\n", tf->deco, arguments->tdata()[0]->type->deco); + error(loc, "%s%s is not callable using argument types %s", + Parameter::argsTypesToChars(tf->parameters, tf->varargs), + buf2.toChars(), + buf.toChars()); + return m.anyf; // as long as it's not a FuncAliasDeclaration + } + else + { +#if 1 + TypeFunction *t1 = (TypeFunction *)m.lastf->type; + TypeFunction *t2 = (TypeFunction *)m.nextf->type; + + error(loc, "called with argument types:\n\t(%s)\nmatches both:\n\t%s%s\nand:\n\t%s%s", + buf.toChars(), + m.lastf->toPrettyChars(), Parameter::argsTypesToChars(t1->parameters, t1->varargs), + m.nextf->toPrettyChars(), Parameter::argsTypesToChars(t2->parameters, t2->varargs)); +#else + error(loc, "overloads %s and %s both match argument list for %s", + m.lastf->type->toChars(), + m.nextf->type->toChars(), + m.lastf->toChars()); +#endif + return m.lastf; + } + } +} + +/************************************* + * Determine partial specialization order of 'this' vs g. + * This is very similar to TemplateDeclaration::leastAsSpecialized(). + * Returns: + * match 'this' is at least as specialized as g + * 0 g is more specialized than 'this' + */ + +#if DMDV2 +MATCH FuncDeclaration::leastAsSpecialized(FuncDeclaration *g) +{ +#define LOG_LEASTAS 0 + +#if LOG_LEASTAS + printf("%s.leastAsSpecialized(%s)\n", toChars(), g->toChars()); + printf("%s, %s\n", type->toChars(), g->type->toChars()); +#endif + + /* This works by calling g() with f()'s parameters, and + * if that is possible, then f() is at least as specialized + * as g() is. + */ + + TypeFunction *tf = (TypeFunction *)type; + TypeFunction *tg = (TypeFunction *)g->type; + size_t nfparams = Parameter::dim(tf->parameters); + size_t ngparams = Parameter::dim(tg->parameters); + MATCH match = MATCHexact; + + /* If both functions have a 'this' pointer, and the mods are not + * the same and g's is not const, then this is less specialized. + */ + if (needThis() && g->needThis()) + { + if (tf->mod != tg->mod) + { + if (MODimplicitConv(tf->mod, tg->mod)) + match = MATCHconst; + else + return MATCHnomatch; + } + } + + /* Create a dummy array of arguments out of the parameters to f() + */ + Expressions args; + args.setDim(nfparams); + for (int u = 0; u < nfparams; u++) + { + Parameter *p = Parameter::getNth(tf->parameters, u); + Expression *e; + if (p->storageClass & (STCref | STCout)) + { + e = new IdentifierExp(0, p->ident); + e->type = p->type; + } + else + e = p->type->defaultInit(); + args.tdata()[u] = e; + } + + MATCH m = (MATCH) tg->callMatch(NULL, &args, 1); + if (m) + { + /* A variadic parameter list is less specialized than a + * non-variadic one. + */ + if (tf->varargs && !tg->varargs) + goto L1; // less specialized + +#if LOG_LEASTAS + printf(" matches %d, so is least as specialized\n", m); +#endif + return m; + } + L1: +#if LOG_LEASTAS + printf(" doesn't match, so is not as specialized\n"); +#endif + return MATCHnomatch; +} + +/******************************************* + * Given a symbol that could be either a FuncDeclaration or + * a function template, resolve it to a function symbol. + * sc instantiation scope + * loc instantiation location + * targsi initial list of template arguments + * ethis if !NULL, the 'this' pointer argument + * fargs arguments to function + * flags 1: do not issue error message on no match, just return NULL + */ + +FuncDeclaration *resolveFuncCall(Scope *sc, Loc loc, Dsymbol *s, + Objects *tiargs, + Expression *ethis, + Expressions *arguments, + int flags) +{ + if (!s) + return NULL; // no match + FuncDeclaration *f = s->isFuncDeclaration(); + if (f) + f = f->overloadResolve(loc, ethis, arguments); + else + { TemplateDeclaration *td = s->isTemplateDeclaration(); + assert(td); + f = td->deduceFunctionTemplate(sc, loc, tiargs, NULL, arguments, flags); + } + return f; +} +#endif + +/******************************** + * Labels are in a separate scope, one per function. + */ + +LabelDsymbol *FuncDeclaration::searchLabel(Identifier *ident) +{ Dsymbol *s; + + if (!labtab) + labtab = new DsymbolTable(); // guess we need one + + s = labtab->lookup(ident); + if (!s) + { + s = new LabelDsymbol(ident); + labtab->insert(s); + } + return (LabelDsymbol *)s; +} + +/**************************************** + * If non-static member function that has a 'this' pointer, + * return the aggregate it is a member of. + * Otherwise, return NULL. + */ + +AggregateDeclaration *FuncDeclaration::isThis() +{ AggregateDeclaration *ad; + + //printf("+FuncDeclaration::isThis() '%s'\n", toChars()); + ad = NULL; + if ((storage_class & STCstatic) == 0) + { + ad = isMember2(); + } + //printf("-FuncDeclaration::isThis() %p\n", ad); + return ad; +} + +AggregateDeclaration *FuncDeclaration::isMember2() +{ AggregateDeclaration *ad; + + //printf("+FuncDeclaration::isMember2() '%s'\n", toChars()); + ad = NULL; + for (Dsymbol *s = this; s; s = s->parent) + { +//printf("\ts = '%s', parent = '%s', kind = %s\n", s->toChars(), s->parent->toChars(), s->parent->kind()); + ad = s->isMember(); + if (ad) +{ + break; +} + if (!s->parent || + (!s->parent->isTemplateInstance())) +{ + break; +} + } + //printf("-FuncDeclaration::isMember2() %p\n", ad); + return ad; +} + +/***************************************** + * Determine lexical level difference from 'this' to nested function 'fd'. + * Error if this cannot call fd. + * Returns: + * 0 same level + * -1 increase nesting by 1 (fd is nested within 'this') + * >0 decrease nesting by number + */ + +int FuncDeclaration::getLevel(Loc loc, Scope *sc, FuncDeclaration *fd) +{ int level; + Dsymbol *s; + Dsymbol *fdparent; + + //printf("FuncDeclaration::getLevel(fd = '%s')\n", fd->toChars()); + fdparent = fd->toParent2(); + if (fdparent == this) + return -1; + s = this; + level = 0; + while (fd != s && fdparent != s->toParent2()) + { + //printf("\ts = %s, '%s'\n", s->kind(), s->toChars()); + FuncDeclaration *thisfd = s->isFuncDeclaration(); + if (thisfd) + { if (!thisfd->isNested() && !thisfd->vthis) + goto Lerr; + } + else + { + AggregateDeclaration *thiscd = s->isAggregateDeclaration(); + if (thiscd) + { if (!thiscd->isNested()) + goto Lerr; + } + else + goto Lerr; + } + + s = s->toParent2(); + assert(s); + level++; + } + return level; + +Lerr: + // Don't give error if in template constraint + if (!((sc->flags & SCOPEstaticif) && parent->isTemplateDeclaration())) + error(loc, "cannot access frame of function %s", fd->toPrettyChars()); + return 1; +} + +void FuncDeclaration::appendExp(Expression *e) +{ Statement *s; + + s = new ExpStatement(0, e); + appendState(s); +} + +void FuncDeclaration::appendState(Statement *s) +{ + if (!fbody) + fbody = s; + else + { + CompoundStatement *cs = fbody->isCompoundStatement(); + if (cs) + { + if (!cs->statements) + fbody = s; + else + cs->statements->push(s); + } + else + fbody = new CompoundStatement(0, fbody, s); + } +} + +const char *FuncDeclaration::toPrettyChars() +{ + if (isMain()) + return "D main"; + else + return Dsymbol::toPrettyChars(); +} + +int FuncDeclaration::isMain() +{ + return ident == Id::main && + linkage != LINKc && !isMember() && !isNested(); +} + +int FuncDeclaration::isWinMain() +{ + //printf("FuncDeclaration::isWinMain() %s\n", toChars()); +#if 0 + int x = ident == Id::WinMain && + linkage != LINKc && !isMember(); + printf("%s\n", x ? "yes" : "no"); + return x; +#else + return ident == Id::WinMain && + linkage != LINKc && !isMember(); +#endif +} + +int FuncDeclaration::isDllMain() +{ + return ident == Id::DllMain && + linkage != LINKc && !isMember(); +} + +int FuncDeclaration::isExport() +{ + return protection == PROTexport; +} + +int FuncDeclaration::isImportedSymbol() +{ + //printf("isImportedSymbol()\n"); + //printf("protection = %d\n", protection); + return (protection == PROTexport) && !fbody; +} + +// Determine if function goes into virtual function pointer table + +int FuncDeclaration::isVirtual() +{ + Dsymbol *p = toParent(); +#if 0 + printf("FuncDeclaration::isVirtual(%s)\n", toChars()); + printf("isMember:%p isStatic:%d private:%d ctor:%d !Dlinkage:%d\n", isMember(), isStatic(), protection == PROTprivate, isCtorDeclaration(), linkage != LINKd); + printf("result is %d\n", + isMember() && + !(isStatic() || protection == PROTprivate || protection == PROTpackage) && + p->isClassDeclaration() && + !(p->isInterfaceDeclaration() && isFinal())); +#endif + return isMember() && + !(isStatic() || protection == PROTprivate || protection == PROTpackage) && + p->isClassDeclaration() && + !(p->isInterfaceDeclaration() && isFinal()); +} + +// Determine if a function is pedantically virtual + +int FuncDeclaration::isVirtualMethod() +{ + //printf("FuncDeclaration::isVirtualMethod() %s\n", toChars()); + if (!isVirtual()) + return 0; + // If it's a final method, and does not override anything, then it is not virtual + if (isFinal() && foverrides.dim == 0) + { + return 0; + } + return 1; +} + +int FuncDeclaration::isFinal() +{ + ClassDeclaration *cd; +#if 0 + printf("FuncDeclaration::isFinal(%s), %x\n", toChars(), Declaration::isFinal()); + printf("%p %d %d %d\n", isMember(), isStatic(), Declaration::isFinal(), ((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal)); + printf("result is %d\n", + isMember() && + (Declaration::isFinal() || + ((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal))); + if (cd) + printf("\tmember of %s\n", cd->toChars()); +#if 0 + !(isStatic() || protection == PROTprivate || protection == PROTpackage) && + (cd = toParent()->isClassDeclaration()) != NULL && + cd->storage_class & STCfinal); +#endif +#endif + return isMember() && + (Declaration::isFinal() || + ((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal)); +} + +int FuncDeclaration::isAbstract() +{ + return storage_class & STCabstract; +} + +int FuncDeclaration::isCodeseg() +{ + return TRUE; // functions are always in the code segment +} + +int FuncDeclaration::isOverloadable() +{ + return 1; // functions can be overloaded +} + +enum PURE FuncDeclaration::isPure() +{ + //printf("FuncDeclaration::isPure() '%s'\n", toChars()); + assert(type->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)type; + if (flags & FUNCFLAGpurityInprocess) + setImpure(); + if (tf->purity == PUREfwdref) + tf->purityLevel(); + enum PURE purity = tf->purity; + if (purity > PUREweak && needThis()) + { // The attribute of the 'this' reference affects purity strength + if (type->mod & (MODimmutable | MODwild)) + ; + else if (type->mod & MODconst && purity >= PUREconst) + purity = PUREconst; + else + purity = PUREweak; + } + tf->purity = purity; + // ^ This rely on the current situation that every FuncDeclaration has a + // unique TypeFunction. + return purity; +} + +enum PURE FuncDeclaration::isPureBypassingInference() +{ + if (flags & FUNCFLAGpurityInprocess) + return PUREfwdref; + else + return isPure(); +} + +/************************************** + * The function is doing something impure, + * so mark it as impure. + * If there's a purity error, return TRUE. + */ +bool FuncDeclaration::setImpure() +{ + if (flags & FUNCFLAGpurityInprocess) + { + flags &= ~FUNCFLAGpurityInprocess; + } + else if (isPure()) + return TRUE; + return FALSE; +} + +int FuncDeclaration::isSafe() +{ + assert(type->ty == Tfunction); + if (flags & FUNCFLAGsafetyInprocess) + setUnsafe(); + return ((TypeFunction *)type)->trust == TRUSTsafe; +} + +int FuncDeclaration::isTrusted() +{ + assert(type->ty == Tfunction); + if (flags & FUNCFLAGsafetyInprocess) + setUnsafe(); + return ((TypeFunction *)type)->trust == TRUSTtrusted; +} + +/************************************** + * The function is doing something unsave, + * so mark it as unsafe. + * If there's a safe error, return TRUE. + */ +bool FuncDeclaration::setUnsafe() +{ + if (flags & FUNCFLAGsafetyInprocess) + { + flags &= ~FUNCFLAGsafetyInprocess; + ((TypeFunction *)type)->trust = TRUSTsystem; + } + else if (isSafe()) + return TRUE; + return FALSE; +} + +// Determine if function needs +// a static frame pointer to its lexically enclosing function + +int FuncDeclaration::isNested() +{ + //if (!toParent()) + //printf("FuncDeclaration::isNested('%s') parent=%p\n", toChars(), parent); + //printf("\ttoParent2() = '%s'\n", toParent2()->toChars()); + return ((storage_class & STCstatic) == 0) && + (toParent2()->isFuncDeclaration() != NULL); +} + +int FuncDeclaration::needThis() +{ + //printf("FuncDeclaration::needThis() '%s'\n", toChars()); + int i = isThis() != NULL; + //printf("\t%d\n", i); + if (!i && isFuncAliasDeclaration()) + i = ((FuncAliasDeclaration *)this)->funcalias->needThis(); + return i; +} + +int FuncDeclaration::addPreInvariant() +{ + AggregateDeclaration *ad = isThis(); + return (ad && + //ad->isClassDeclaration() && + global.params.useInvariants && + (protection == PROTprotected || protection == PROTpublic || protection == PROTexport) && + !naked && + ident != Id::cpctor); +} + +int FuncDeclaration::addPostInvariant() +{ + AggregateDeclaration *ad = isThis(); + return (ad && + ad->inv && + //ad->isClassDeclaration() && + global.params.useInvariants && + (protection == PROTprotected || protection == PROTpublic || protection == PROTexport) && + !naked && + ident != Id::cpctor); +} + +/********************************** + * Generate a FuncDeclaration for a runtime library function. + */ + +FuncDeclaration *FuncDeclaration::genCfunc(Type *treturn, const char *name) +{ + return genCfunc(treturn, Lexer::idPool(name)); +} + +FuncDeclaration *FuncDeclaration::genCfunc(Type *treturn, Identifier *id) +{ + FuncDeclaration *fd; + TypeFunction *tf; + Dsymbol *s; + static DsymbolTable *st = NULL; + + //printf("genCfunc(name = '%s')\n", id->toChars()); + //printf("treturn\n\t"); treturn->print(); + + // See if already in table + if (!st) + st = new DsymbolTable(); + s = st->lookup(id); + if (s) + { + fd = s->isFuncDeclaration(); + assert(fd); + assert(fd->type->nextOf()->equals(treturn)); + } + else + { + tf = new TypeFunction(NULL, treturn, 0, LINKc); + fd = new FuncDeclaration(0, 0, id, STCstatic, tf); + fd->protection = PROTpublic; + fd->linkage = LINKc; + + st->insert(fd); + } + return fd; +} + +const char *FuncDeclaration::kind() +{ + return "function"; +} + +void FuncDeclaration::checkNestedReference(Scope *sc, Loc loc) +{ + //printf("FuncDeclaration::checkNestedReference() %s\n", toChars()); + if (parent && parent != sc->parent && this->isNested() && + this->ident != Id::require && this->ident != Id::ensure) + { + // The function that this function is in + FuncDeclaration *fdv = toParent()->isFuncDeclaration(); + // The current function + FuncDeclaration *fdthis = sc->parent->isFuncDeclaration(); + + //printf("this = %s in [%s]\n", this->toChars(), this->loc.toChars()); + //printf("fdv = %s in [%s]\n", fdv->toChars(), fdv->loc.toChars()); + //printf("fdthis = %s in [%s]\n", fdthis->toChars(), fdthis->loc.toChars()); + + if (fdv && fdthis && fdv != fdthis) + { + int lv = fdthis->getLevel(loc, sc, fdv); + if (lv == -1) + return; // OK + if (lv == 0) + return; // OK + + // BUG: may need to walk up outer scopes like Declaration::checkNestedReference() does + + // function literal has reference to enclosing scope is delegate + if (FuncLiteralDeclaration *fld = fdthis->isFuncLiteralDeclaration()) + fld->tok = TOKdelegate; + } + } +} + +/******************************* + * Look at all the variables in this function that are referenced + * by nested functions, and determine if a closure needs to be + * created for them. + */ + +#if DMDV2 +int FuncDeclaration::needsClosure() +{ + /* Need a closure for all the closureVars[] if any of the + * closureVars[] are accessed by a + * function that escapes the scope of this function. + * We take the conservative approach and decide that any function that: + * 1) is a virtual function + * 2) has its address taken + * 3) has a parent that escapes + * -or- + * 4) this function returns a local struct/class + * + * Note that since a non-virtual function can be called by + * a virtual one, if that non-virtual function accesses a closure + * var, the closure still has to be taken. Hence, we check for isThis() + * instead of isVirtual(). (thanks to David Friedman) + */ + + //printf("FuncDeclaration::needsClosure() %s\n", toChars()); + for (int i = 0; i < closureVars.dim; i++) + { VarDeclaration *v = closureVars.tdata()[i]; + assert(v->isVarDeclaration()); + //printf("\tv = %s\n", v->toChars()); + + for (int j = 0; j < v->nestedrefs.dim; j++) + { FuncDeclaration *f = v->nestedrefs.tdata()[j]; + assert(f != this); + + //printf("\t\tf = %s, %d, %p, %d\n", f->toChars(), f->isVirtual(), f->isThis(), f->tookAddressOf); + if (f->isThis() || f->tookAddressOf) + goto Lyes; // assume f escapes this function's scope + + // Look to see if any parents of f that are below this escape + for (Dsymbol *s = f->parent; s && s != this; s = s->parent) + { + f = s->isFuncDeclaration(); + if (f && (f->isThis() || f->tookAddressOf)) + goto Lyes; + } + } + } + + /* Look for case (4) + */ + if (closureVars.dim) + { + assert(type->ty == Tfunction); + Type *tret = ((TypeFunction *)type)->next; + assert(tret); + tret = tret->toBasetype(); + if (tret->ty == Tclass || tret->ty == Tstruct) + { Dsymbol *st = tret->toDsymbol(NULL); + for (Dsymbol *s = st->parent; s; s = s->parent) + { + if (s == this) + goto Lyes; + } + } + } + + return 0; + +Lyes: + //printf("\tneeds closure\n"); + return 1; +} +#endif + +/*********************************************** + * Determine if function's variables are referenced by a function + * nested within it. + */ + +int FuncDeclaration::hasNestedFrameRefs() +{ +#if DMDV2 + if (closureVars.dim) +#else + if (nestedFrameRef) +#endif + return 1; + + /* If a virtual method has contracts, assume its variables are referenced + * by those contracts, even if they aren't. Because they might be referenced + * by the overridden or overriding function's contracts. + * This can happen because frequire and fensure are implemented as nested functions, + * and they can be called directly by an overriding function and the overriding function's + * context had better match, or Bugzilla 7337 will bite. + */ + if ((fdrequire || fdensure) && isVirtualMethod()) + return 1; + + if (foverrides.dim && isVirtualMethod()) + { + for (size_t i = 0; i < foverrides.dim; i++) + { + FuncDeclaration *fdv = foverrides.tdata()[i]; + if (fdv->hasNestedFrameRefs()) + return 1; + } + } + + return 0; +} + +/********************************************* + * Return the function's parameter list, and whether + * it is variadic or not. + */ + +Parameters *FuncDeclaration::getParameters(int *pvarargs) +{ Parameters *fparameters; + int fvarargs; + + if (type) + { + assert(type->ty == Tfunction); + TypeFunction *fdtype = (TypeFunction *)type; + fparameters = fdtype->parameters; + fvarargs = fdtype->varargs; + } + if (pvarargs) + *pvarargs = fvarargs; + return fparameters; +} + + +/****************************** FuncAliasDeclaration ************************/ + +// Used as a way to import a set of functions from another scope into this one. + +FuncAliasDeclaration::FuncAliasDeclaration(FuncDeclaration *funcalias) + : FuncDeclaration(funcalias->loc, funcalias->endloc, funcalias->ident, + funcalias->storage_class, funcalias->type) +{ + assert(funcalias != this); + this->funcalias = funcalias; +} + +const char *FuncAliasDeclaration::kind() +{ + return "function alias"; +} + + +/****************************** FuncLiteralDeclaration ************************/ + +FuncLiteralDeclaration::FuncLiteralDeclaration(Loc loc, Loc endloc, Type *type, + enum TOK tok, ForeachStatement *fes) + : FuncDeclaration(loc, endloc, NULL, STCundefined, type) +{ + const char *id; + + if (fes) + id = "__foreachbody"; + else if (tok == TOKreserved) + id = "__lambda"; + else if (tok == TOKdelegate) + id = "__dgliteral"; + else + id = "__funcliteral"; + this->ident = Lexer::uniqueId(id); + this->tok = tok; + this->fes = fes; + //printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this->ident->toChars(), type->toChars()); +} + +Dsymbol *FuncLiteralDeclaration::syntaxCopy(Dsymbol *s) +{ + FuncLiteralDeclaration *f; + + //printf("FuncLiteralDeclaration::syntaxCopy('%s')\n", toChars()); + if (s) + f = (FuncLiteralDeclaration *)s; + else + { f = new FuncLiteralDeclaration(loc, endloc, type->syntaxCopy(), tok, fes); + f->ident = ident; // keep old identifier + } + FuncDeclaration::syntaxCopy(f); + return f; +} + +int FuncLiteralDeclaration::isNested() +{ + //printf("FuncLiteralDeclaration::isNested() '%s'\n", toChars()); + return (tok != TOKfunction); +} + +int FuncLiteralDeclaration::isVirtual() +{ + return FALSE; +} + +const char *FuncLiteralDeclaration::kind() +{ + // GCC requires the (char*) casts + return (tok != TOKfunction) ? (char*)"delegate" : (char*)"function"; +} + +void FuncLiteralDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(kind()); + buf->writeByte(' '); + type->toCBuffer(buf, NULL, hgs); + bodyToCBuffer(buf, hgs); +} + + +/********************************* CtorDeclaration ****************************/ + +CtorDeclaration::CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *type) + : FuncDeclaration(loc, endloc, Id::ctor, stc, type) +{ + //printf("CtorDeclaration(loc = %s) %s\n", loc.toChars(), toChars()); +} + +Dsymbol *CtorDeclaration::syntaxCopy(Dsymbol *s) +{ + CtorDeclaration *f = new CtorDeclaration(loc, endloc, storage_class, type->syntaxCopy()); + + f->outId = outId; + f->frequire = frequire ? frequire->syntaxCopy() : NULL; + f->fensure = fensure ? fensure->syntaxCopy() : NULL; + f->fbody = fbody ? fbody->syntaxCopy() : NULL; + assert(!fthrows); // deprecated + + return f; +} + + +void CtorDeclaration::semantic(Scope *sc) +{ + //printf("CtorDeclaration::semantic() %s\n", toChars()); + TypeFunction *tf = (TypeFunction *)type; + assert(tf && tf->ty == Tfunction); + + sc = sc->push(); + sc->stc &= ~STCstatic; // not a static constructor + sc->flags |= SCOPEctor; + + parent = sc->parent; + Dsymbol *parent = toParent2(); + Type *tret; + AggregateDeclaration *ad = parent->isAggregateDeclaration(); + if (!ad || parent->isUnionDeclaration()) + { + error("constructors are only for class or struct definitions"); + tret = Type::tvoid; + } + else + { tret = ad->handle; + assert(tret); + tret = tret->addStorageClass(storage_class | sc->stc); + tret = tret->addMod(type->mod); + } + tf->next = tret; + type = type->semantic(loc, sc); + +#if STRUCTTHISREF + if (ad && ad->isStructDeclaration()) + { if (!originalType) + originalType = type->syntaxCopy(); + ((TypeFunction *)type)->isref = 1; + } +#endif + if (!originalType) + originalType = type; + + // Append: + // return this; + // to the function body + if (fbody && semanticRun < PASSsemantic) + { + Expression *e = new ThisExp(loc); + if (parent->isClassDeclaration()) + e->type = tret; + Statement *s = new ReturnStatement(loc, e); + fbody = new CompoundStatement(loc, fbody, s); + } + + FuncDeclaration::semantic(sc); + + sc->pop(); + + // See if it's the default constructor + if (ad && tf->varargs == 0 && Parameter::dim(tf->parameters) == 0) + { + StructDeclaration *sd = ad->isStructDeclaration(); + if (sd) + { + if (fbody || !(storage_class & STCdisable)) + { error("default constructor for structs only allowed with @disable and no body"); + storage_class |= STCdisable; + fbody = NULL; + } + sd->noDefaultCtor = TRUE; + } + else + ad->defaultCtor = this; + } +} + +const char *CtorDeclaration::kind() +{ + return "constructor"; +} + +char *CtorDeclaration::toChars() +{ + return (char *)"this"; +} + +int CtorDeclaration::isVirtual() +{ + return FALSE; +} + +int CtorDeclaration::addPreInvariant() +{ + return FALSE; +} + +int CtorDeclaration::addPostInvariant() +{ + return (isThis() && vthis && global.params.useInvariants); +} + + +/********************************* PostBlitDeclaration ****************************/ + +#if DMDV2 +PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc) + : FuncDeclaration(loc, endloc, Id::_postblit, stc, NULL) +{ +} + +PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, Identifier *id) + : FuncDeclaration(loc, endloc, id, STCundefined, NULL) +{ +} + +Dsymbol *PostBlitDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(!s); + PostBlitDeclaration *dd = new PostBlitDeclaration(loc, endloc, ident); + return FuncDeclaration::syntaxCopy(dd); +} + + +void PostBlitDeclaration::semantic(Scope *sc) +{ + //printf("PostBlitDeclaration::semantic() %s\n", toChars()); + //printf("ident: %s, %s, %p, %p\n", ident->toChars(), Id::dtor->toChars(), ident, Id::dtor); + //printf("stc = x%llx\n", sc->stc); + parent = sc->parent; + Dsymbol *parent = toParent(); + StructDeclaration *ad = parent->isStructDeclaration(); + if (!ad) + { + error("post blits are only for struct/union definitions, not %s %s", parent->kind(), parent->toChars()); + } + else if (ident == Id::_postblit && semanticRun < PASSsemantic) + ad->postblits.push(this); + + if (!type) + type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd, storage_class); + + sc = sc->push(); + sc->stc &= ~STCstatic; // not static + sc->linkage = LINKd; + + FuncDeclaration::semantic(sc); + + sc->pop(); +} + +int PostBlitDeclaration::overloadInsert(Dsymbol *s) +{ + return FALSE; // cannot overload postblits +} + +int PostBlitDeclaration::addPreInvariant() +{ + return FALSE; +} + +int PostBlitDeclaration::addPostInvariant() +{ + return (isThis() && vthis && global.params.useInvariants); +} + +int PostBlitDeclaration::isVirtual() +{ + return FALSE; +} + +void PostBlitDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("this(this)"); + bodyToCBuffer(buf, hgs); +} +#endif + +/********************************* DtorDeclaration ****************************/ + +DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc) + : FuncDeclaration(loc, endloc, Id::dtor, STCundefined, NULL) +{ +} + +DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc, Identifier *id) + : FuncDeclaration(loc, endloc, id, STCundefined, NULL) +{ +} + +Dsymbol *DtorDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(!s); + DtorDeclaration *dd = new DtorDeclaration(loc, endloc, ident); + return FuncDeclaration::syntaxCopy(dd); +} + + +void DtorDeclaration::semantic(Scope *sc) +{ + //printf("DtorDeclaration::semantic() %s\n", toChars()); + //printf("ident: %s, %s, %p, %p\n", ident->toChars(), Id::dtor->toChars(), ident, Id::dtor); + parent = sc->parent; + Dsymbol *parent = toParent(); + AggregateDeclaration *ad = parent->isAggregateDeclaration(); + if (!ad) + { + error("destructors are only for class/struct/union definitions, not %s %s", parent->kind(), parent->toChars()); + } + else if (ident == Id::dtor && semanticRun < PASSsemantic) + ad->dtors.push(this); + + if (!type) + type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); + + sc = sc->push(); + sc->stc &= ~STCstatic; // not a static destructor + sc->linkage = LINKd; + + FuncDeclaration::semantic(sc); + + sc->pop(); +} + +int DtorDeclaration::overloadInsert(Dsymbol *s) +{ + return FALSE; // cannot overload destructors +} + +int DtorDeclaration::addPreInvariant() +{ + return (isThis() && vthis && global.params.useInvariants); +} + +int DtorDeclaration::addPostInvariant() +{ + return FALSE; +} + +const char *DtorDeclaration::kind() +{ + return "destructor"; +} + +char *DtorDeclaration::toChars() +{ + return (char *)"~this"; +} + +int DtorDeclaration::isVirtual() +{ + /* This should be FALSE so that dtor's don't get put into the vtbl[], + * but doing so will require recompiling everything. + */ +#if BREAKABI + return FALSE; +#else + return FuncDeclaration::isVirtual(); +#endif +} + +void DtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("~this()"); + bodyToCBuffer(buf, hgs); +} + +/********************************* StaticCtorDeclaration ****************************/ + +StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc) + : FuncDeclaration(loc, endloc, + Identifier::generateId("_staticCtor"), STCstatic, NULL) +{ +} + +StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc, const char *name) + : FuncDeclaration(loc, endloc, + Identifier::generateId(name), STCstatic, NULL) +{ +} + +Dsymbol *StaticCtorDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(!s); + StaticCtorDeclaration *scd = new StaticCtorDeclaration(loc, endloc); + return FuncDeclaration::syntaxCopy(scd); +} + + +void StaticCtorDeclaration::semantic(Scope *sc) +{ + //printf("StaticCtorDeclaration::semantic()\n"); + + if (!type) + type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); + + /* If the static ctor appears within a template instantiation, + * it could get called multiple times by the module constructors + * for different modules. Thus, protect it with a gate. + */ + if (inTemplateInstance() && semanticRun < PASSsemantic) + { + /* Add this prefix to the function: + * static int gate; + * if (++gate != 1) return; + * Note that this is not thread safe; should not have threads + * during static construction. + */ + Identifier *id = Lexer::idPool("__gate"); + VarDeclaration *v = new VarDeclaration(0, Type::tint32, id, NULL); + v->storage_class = isSharedStaticCtorDeclaration() ? STCstatic : STCtls; + Statements *sa = new Statements(); + Statement *s = new ExpStatement(0, v); + sa->push(s); + Expression *e = new IdentifierExp(0, id); + e = new AddAssignExp(0, e, new IntegerExp(1)); + e = new EqualExp(TOKnotequal, 0, e, new IntegerExp(1)); + s = new IfStatement(0, NULL, e, new ReturnStatement(0, NULL), NULL); + sa->push(s); + if (fbody) + sa->push(fbody); + fbody = new CompoundStatement(0, sa); + } + + FuncDeclaration::semantic(sc); + + // We're going to need ModuleInfo + Module *m = getModule(); + if (!m) + m = sc->module; + if (m) + { m->needmoduleinfo = 1; + //printf("module1 %s needs moduleinfo\n", m->toChars()); +#ifdef IN_GCC + m->strictlyneedmoduleinfo = 1; +#endif + } +} + +AggregateDeclaration *StaticCtorDeclaration::isThis() +{ + return NULL; +} + +int StaticCtorDeclaration::isVirtual() +{ + return FALSE; +} + +bool StaticCtorDeclaration::hasStaticCtorOrDtor() +{ + return TRUE; +} + +int StaticCtorDeclaration::addPreInvariant() +{ + return FALSE; +} + +int StaticCtorDeclaration::addPostInvariant() +{ + return FALSE; +} + +void StaticCtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (hgs->hdrgen) + { buf->writestring("static this();"); + buf->writenl(); + return; + } + buf->writestring("static this()"); + bodyToCBuffer(buf, hgs); +} + +/********************************* SharedStaticCtorDeclaration ****************************/ + +SharedStaticCtorDeclaration::SharedStaticCtorDeclaration(Loc loc, Loc endloc) + : StaticCtorDeclaration(loc, endloc, "_sharedStaticCtor") +{ +} + +Dsymbol *SharedStaticCtorDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(!s); + SharedStaticCtorDeclaration *scd = new SharedStaticCtorDeclaration(loc, endloc); + return FuncDeclaration::syntaxCopy(scd); +} + +void SharedStaticCtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("shared "); + StaticCtorDeclaration::toCBuffer(buf, hgs); +} + +/********************************* StaticDtorDeclaration ****************************/ + +StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc) + : FuncDeclaration(loc, endloc, + Identifier::generateId("_staticDtor"), STCstatic, NULL) +{ + vgate = NULL; +} + +StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc, const char *name) + : FuncDeclaration(loc, endloc, + Identifier::generateId(name), STCstatic, NULL) +{ + vgate = NULL; +} + +Dsymbol *StaticDtorDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(!s); + StaticDtorDeclaration *sdd = new StaticDtorDeclaration(loc, endloc); + return FuncDeclaration::syntaxCopy(sdd); +} + + +void StaticDtorDeclaration::semantic(Scope *sc) +{ + ClassDeclaration *cd = sc->scopesym->isClassDeclaration(); + + if (!type) + type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); + + /* If the static ctor appears within a template instantiation, + * it could get called multiple times by the module constructors + * for different modules. Thus, protect it with a gate. + */ + if (inTemplateInstance() && semanticRun < PASSsemantic) + { + /* Add this prefix to the function: + * static int gate; + * if (--gate != 0) return; + * Increment gate during constructor execution. + * Note that this is not thread safe; should not have threads + * during static destruction. + */ + Identifier *id = Lexer::idPool("__gate"); + VarDeclaration *v = new VarDeclaration(0, Type::tint32, id, NULL); + v->storage_class = isSharedStaticDtorDeclaration() ? STCstatic : STCtls; + Statements *sa = new Statements(); + Statement *s = new ExpStatement(0, v); + sa->push(s); + Expression *e = new IdentifierExp(0, id); + e = new AddAssignExp(0, e, new IntegerExp(-1)); + e = new EqualExp(TOKnotequal, 0, e, new IntegerExp(0)); + s = new IfStatement(0, NULL, e, new ReturnStatement(0, NULL), NULL); + sa->push(s); + if (fbody) + sa->push(fbody); + fbody = new CompoundStatement(0, sa); + vgate = v; + } + + FuncDeclaration::semantic(sc); + + // We're going to need ModuleInfo + Module *m = getModule(); + if (!m) + m = sc->module; + if (m) + { m->needmoduleinfo = 1; + //printf("module2 %s needs moduleinfo\n", m->toChars()); +#ifdef IN_GCC + m->strictlyneedmoduleinfo = 1; +#endif + } +} + +AggregateDeclaration *StaticDtorDeclaration::isThis() +{ + return NULL; +} + +int StaticDtorDeclaration::isVirtual() +{ + return FALSE; +} + +bool StaticDtorDeclaration::hasStaticCtorOrDtor() +{ + return TRUE; +} + +int StaticDtorDeclaration::addPreInvariant() +{ + return FALSE; +} + +int StaticDtorDeclaration::addPostInvariant() +{ + return FALSE; +} + +void StaticDtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (hgs->hdrgen) + return; + buf->writestring("static ~this()"); + bodyToCBuffer(buf, hgs); +} + +/********************************* SharedStaticDtorDeclaration ****************************/ + +SharedStaticDtorDeclaration::SharedStaticDtorDeclaration(Loc loc, Loc endloc) + : StaticDtorDeclaration(loc, endloc, "_sharedStaticDtor") +{ +} + +Dsymbol *SharedStaticDtorDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(!s); + SharedStaticDtorDeclaration *sdd = new SharedStaticDtorDeclaration(loc, endloc); + return FuncDeclaration::syntaxCopy(sdd); +} + +void SharedStaticDtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (!hgs->hdrgen) + { + buf->writestring("shared "); + StaticDtorDeclaration::toCBuffer(buf, hgs); + } +} + + +/********************************* InvariantDeclaration ****************************/ + +InvariantDeclaration::InvariantDeclaration(Loc loc, Loc endloc) + : FuncDeclaration(loc, endloc, Id::classInvariant, STCundefined, NULL) +{ +} + +Dsymbol *InvariantDeclaration::syntaxCopy(Dsymbol *s) +{ + InvariantDeclaration *id; + + assert(!s); + id = new InvariantDeclaration(loc, endloc); + FuncDeclaration::syntaxCopy(id); + return id; +} + + +void InvariantDeclaration::semantic(Scope *sc) +{ + parent = sc->parent; + Dsymbol *parent = toParent(); + AggregateDeclaration *ad = parent->isAggregateDeclaration(); + if (!ad) + { + error("invariants are only for struct/union/class definitions"); + return; + } + else if (ad->inv && ad->inv != this && semanticRun < PASSsemantic) + { + error("more than one invariant for %s", ad->toChars()); + } + ad->inv = this; + if (!type) + type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); + + sc = sc->push(); + sc->stc &= ~STCstatic; // not a static invariant + sc->stc |= STCconst; // invariant() is always const + sc->incontract++; + sc->linkage = LINKd; + + FuncDeclaration::semantic(sc); + + sc->pop(); +} + +int InvariantDeclaration::isVirtual() +{ + return FALSE; +} + +int InvariantDeclaration::addPreInvariant() +{ + return FALSE; +} + +int InvariantDeclaration::addPostInvariant() +{ + return FALSE; +} + +void InvariantDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (hgs->hdrgen) + return; + buf->writestring("invariant"); + bodyToCBuffer(buf, hgs); +} + + +/********************************* UnitTestDeclaration ****************************/ + +/******************************* + * Generate unique unittest function Id so we can have multiple + * instances per module. + */ + +static Identifier *unitTestId() +{ + return Lexer::uniqueId("__unittest"); +} + +UnitTestDeclaration::UnitTestDeclaration(Loc loc, Loc endloc) + : FuncDeclaration(loc, endloc, unitTestId(), STCundefined, NULL) +{ +} + +Dsymbol *UnitTestDeclaration::syntaxCopy(Dsymbol *s) +{ + UnitTestDeclaration *utd; + + assert(!s); + utd = new UnitTestDeclaration(loc, endloc); + return FuncDeclaration::syntaxCopy(utd); +} + + +void UnitTestDeclaration::semantic(Scope *sc) +{ + if (global.params.useUnitTests) + { + if (!type) + type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); + Scope *sc2 = sc->push(); + // It makes no sense for unit tests to be pure or nothrow. + sc2->stc &= ~(STCnothrow | STCpure); + sc2->linkage = LINKd; + FuncDeclaration::semantic(sc2); + sc2->pop(); + } + +#if 0 + // We're going to need ModuleInfo even if the unit tests are not + // compiled in, because other modules may import this module and refer + // to this ModuleInfo. + // (This doesn't make sense to me?) + Module *m = getModule(); + if (!m) + m = sc->module; + if (m) + { + //printf("module3 %s needs moduleinfo\n", m->toChars()); + m->needmoduleinfo = 1; + } +#endif +} + +AggregateDeclaration *UnitTestDeclaration::isThis() +{ + return NULL; +} + +int UnitTestDeclaration::isVirtual() +{ + return FALSE; +} + +int UnitTestDeclaration::addPreInvariant() +{ + return FALSE; +} + +int UnitTestDeclaration::addPostInvariant() +{ + return FALSE; +} + +void UnitTestDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (hgs->hdrgen) + return; + buf->writestring("unittest"); + bodyToCBuffer(buf, hgs); +} + +/********************************* NewDeclaration ****************************/ + +NewDeclaration::NewDeclaration(Loc loc, Loc endloc, Parameters *arguments, int varargs) + : FuncDeclaration(loc, endloc, Id::classNew, STCstatic, NULL) +{ + this->arguments = arguments; + this->varargs = varargs; +} + +Dsymbol *NewDeclaration::syntaxCopy(Dsymbol *s) +{ + NewDeclaration *f; + + f = new NewDeclaration(loc, endloc, NULL, varargs); + + FuncDeclaration::syntaxCopy(f); + + f->arguments = Parameter::arraySyntaxCopy(arguments); + + return f; +} + + +void NewDeclaration::semantic(Scope *sc) +{ + //printf("NewDeclaration::semantic()\n"); + + parent = sc->parent; + Dsymbol *parent = toParent(); + ClassDeclaration *cd = parent->isClassDeclaration(); + if (!cd && !parent->isStructDeclaration()) + { + error("new allocators only are for class or struct definitions"); + } + Type *tret = Type::tvoid->pointerTo(); + if (!type) + type = new TypeFunction(arguments, tret, varargs, LINKd); + + type = type->semantic(loc, sc); + assert(type->ty == Tfunction); + + // Check that there is at least one argument of type size_t + TypeFunction *tf = (TypeFunction *)type; + if (Parameter::dim(tf->parameters) < 1) + { + error("at least one argument of type size_t expected"); + } + else + { + Parameter *a = Parameter::getNth(tf->parameters, 0); + if (!a->type->equals(Type::tsize_t)) + error("first argument must be type size_t, not %s", a->type->toChars()); + } + + FuncDeclaration::semantic(sc); +} + +const char *NewDeclaration::kind() +{ + return "allocator"; +} + +int NewDeclaration::isVirtual() +{ + return FALSE; +} + +int NewDeclaration::addPreInvariant() +{ + return FALSE; +} + +int NewDeclaration::addPostInvariant() +{ + return FALSE; +} + +void NewDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("new"); + Parameter::argsToCBuffer(buf, hgs, arguments, varargs); + bodyToCBuffer(buf, hgs); +} + + +/********************************* DeleteDeclaration ****************************/ + +DeleteDeclaration::DeleteDeclaration(Loc loc, Loc endloc, Parameters *arguments) + : FuncDeclaration(loc, endloc, Id::classDelete, STCstatic, NULL) +{ + this->arguments = arguments; +} + +Dsymbol *DeleteDeclaration::syntaxCopy(Dsymbol *s) +{ + DeleteDeclaration *f; + + f = new DeleteDeclaration(loc, endloc, NULL); + + FuncDeclaration::syntaxCopy(f); + + f->arguments = Parameter::arraySyntaxCopy(arguments); + + return f; +} + + +void DeleteDeclaration::semantic(Scope *sc) +{ + //printf("DeleteDeclaration::semantic()\n"); + + parent = sc->parent; + Dsymbol *parent = toParent(); + ClassDeclaration *cd = parent->isClassDeclaration(); + if (!cd && !parent->isStructDeclaration()) + { + error("new allocators only are for class or struct definitions"); + } + if (!type) + type = new TypeFunction(arguments, Type::tvoid, 0, LINKd); + + type = type->semantic(loc, sc); + assert(type->ty == Tfunction); + + // Check that there is only one argument of type void* + TypeFunction *tf = (TypeFunction *)type; + if (Parameter::dim(tf->parameters) != 1) + { + error("one argument of type void* expected"); + } + else + { + Parameter *a = Parameter::getNth(tf->parameters, 0); + if (!a->type->equals(Type::tvoid->pointerTo())) + error("one argument of type void* expected, not %s", a->type->toChars()); + } + + FuncDeclaration::semantic(sc); +} + +const char *DeleteDeclaration::kind() +{ + return "deallocator"; +} + +int DeleteDeclaration::isDelete() +{ + return TRUE; +} + +int DeleteDeclaration::isVirtual() +{ + return FALSE; +} + +int DeleteDeclaration::addPreInvariant() +{ + return FALSE; +} + +int DeleteDeclaration::addPostInvariant() +{ + return FALSE; +} + +void DeleteDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("delete"); + Parameter::argsToCBuffer(buf, hgs, arguments, 0); + bodyToCBuffer(buf, hgs); +} + + + + diff --git a/glue.c b/glue.c new file mode 100644 index 00000000..9c223223 --- /dev/null +++ b/glue.c @@ -0,0 +1,1234 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com + +#include +#include +#include +#include + +#if __sun&&__SVR4 +#include +#endif + +#include "mars.h" +#include "module.h" +#include "mtype.h" +#include "declaration.h" +#include "statement.h" +#include "enum.h" +#include "aggregate.h" +#include "init.h" +#include "attrib.h" +#include "id.h" +#include "import.h" +#include "template.h" +#include "lib.h" + +#include "rmem.h" +#include "cc.h" +#include "global.h" +#include "oper.h" +#include "code.h" +#include "type.h" +#include "dt.h" +#include "cgcv.h" +#include "outbuf.h" +#include "irstate.h" + +struct Environment; + +Environment *benv; + +void slist_add(Symbol *s); +void slist_reset(); +void clearStringTab(); + +#define STATICCTOR 0 + +typedef ArrayBase symbols; + +elem *eictor; +symbol *ictorlocalgot; +symbols sctors; +StaticDtorDeclarations ectorgates; +symbols sdtors; +symbols stests; + +symbols ssharedctors; +SharedStaticDtorDeclarations esharedctorgates; +symbols sshareddtors; + +int dtorcount; +int shareddtorcount; + +char *lastmname; + +/************************************** + * Append s to list of object files to generate later. + */ + +Dsymbols obj_symbols_towrite; + +void obj_append(Dsymbol *s) +{ + obj_symbols_towrite.push(s); +} + +void obj_write_deferred(Library *library) +{ + for (size_t i = 0; i < obj_symbols_towrite.dim; i++) + { Dsymbol *s = obj_symbols_towrite.tdata()[i]; + Module *m = s->getModule(); + + char *mname; + if (m) + { mname = m->srcfile->toChars(); + lastmname = mname; + } + else + { + //mname = s->ident->toChars(); + mname = lastmname; + assert(mname); + } + + obj_start(mname); + + static int count; + count++; // sequence for generating names + + /* Create a module that's a doppelganger of m, with just + * enough to be able to create the moduleinfo. + */ + OutBuffer idbuf; + idbuf.printf("%s.%d", m ? m->ident->toChars() : mname, count); + char *idstr = idbuf.toChars(); + idbuf.data = NULL; + Identifier *id = new Identifier(idstr, TOKidentifier); + + Module *md = new Module(mname, id, 0, 0); + md->members = new Dsymbols(); + md->members->push(s); // its only 'member' is s + if (m) + { + md->doppelganger = 1; // identify this module as doppelganger + md->md = m->md; + md->aimports.push(m); // it only 'imports' m + md->massert = m->massert; + md->munittest = m->munittest; + md->marray = m->marray; + } + + md->genobjfile(0); + + /* Set object file name to be source name with sequence number, + * as mangled symbol names get way too long. + */ + char *fname = FileName::removeExt(mname); + OutBuffer namebuf; + unsigned hash = 0; + for (char *p = s->toChars(); *p; p++) + hash += *p; + namebuf.printf("%s_%x_%x.%s", fname, count, hash, global.obj_ext); + namebuf.writeByte(0); + mem.free(fname); + fname = (char *)namebuf.extractData(); + + //printf("writing '%s'\n", fname); + File *objfile = new File(fname); + obj_end(library, objfile); + } + obj_symbols_towrite.dim = 0; +} + +/*********************************************** + * Generate function that calls array of functions and gates. + */ + +symbol *callFuncsAndGates(Module *m, symbols *sctors, StaticDtorDeclarations *ectorgates, + const char *id) +{ + symbol *sctor = NULL; + + if ((sctors && sctors->dim) || + (ectorgates && ectorgates->dim)) + { + static type *t; + if (!t) + { + /* t will be the type of the functions generated: + * extern (C) void func(); + */ + t = type_alloc(TYnfunc); + t->Tflags |= TFprototype | TFfixed; + t->Tmangle = mTYman_c; + t->Tnext = tsvoid; + tsvoid->Tcount++; + } + + localgot = NULL; + sctor = m->toSymbolX(id, SCglobal, t, "FZv"); + cstate.CSpsymtab = &sctor->Sfunc->Flocsym; + elem *ector = NULL; + + if (ectorgates) + { + for (size_t i = 0; i < ectorgates->dim; i++) + { StaticDtorDeclaration *f = (*ectorgates)[i]; + + Symbol *s = f->vgate->toSymbol(); + elem *e = el_var(s); + e = el_bin(OPaddass, TYint, e, el_long(TYint, 1)); + ector = el_combine(ector, e); + } + } + + if (sctors) + { + for (size_t i = 0; i < sctors->dim; i++) + { symbol *s = (*sctors)[i]; + elem *e = el_una(OPucall, TYvoid, el_var(s)); + ector = el_combine(ector, e); + } + } + + block *b = block_calloc(); + b->BC = BCret; + b->Belem = ector; + sctor->Sfunc->Fstartline.Sfilename = m->arg; + sctor->Sfunc->Fstartblock = b; + writefunc(sctor); + } + return sctor; +} + +/************************************** + * Prepare for generating obj file. + */ + +Outbuffer objbuf; + +void obj_start(char *srcfile) +{ + //printf("obj_start()\n"); + + rtlsym_reset(); + slist_reset(); + clearStringTab(); + + obj_init(&objbuf, srcfile, NULL); + + el_reset(); +#if TX86 + cg87_reset(); +#endif + out_reset(); +} + +void obj_end(Library *library, File *objfile) +{ + obj_term(); + + if (library) + { + // Transfer image to library + library->addObject(objfile->name->toChars(), objbuf.buf, objbuf.p - objbuf.buf); + objbuf.buf = NULL; + } + else + { + // Transfer image to file + objfile->setbuffer(objbuf.buf, objbuf.p - objbuf.buf); + objbuf.buf = NULL; + + char *p = FileName::path(objfile->name->toChars()); + FileName::ensurePathExists(p); + //mem.free(p); + + //printf("write obj %s\n", objfile->name->toChars()); + objfile->writev(); + } + objbuf.pend = NULL; + objbuf.p = NULL; + objbuf.len = 0; + objbuf.inc = 0; +} + +/************************************** + * Generate .obj file for Module. + */ + +void Module::genobjfile(int multiobj) +{ + //EEcontext *ee = env->getEEcontext(); + + //printf("Module::genobjfile(multiobj = %d) %s\n", multiobj, toChars()); + + lastmname = srcfile->toChars(); + + obj_initfile(lastmname, NULL, toPrettyChars()); + + eictor = NULL; + ictorlocalgot = NULL; + sctors.setDim(0); + ectorgates.setDim(0); + sdtors.setDim(0); + ssharedctors.setDim(0); + esharedctorgates.setDim(0); + sshareddtors.setDim(0); + stests.setDim(0); + dtorcount = 0; + shareddtorcount = 0; + + if (doppelganger) + { + /* Generate a reference to the moduleinfo, so the module constructors + * and destructors get linked in. + */ + Module *m = aimports.tdata()[0]; + assert(m); + if (m->sictor || m->sctor || m->sdtor || m->ssharedctor || m->sshareddtor) + { + Symbol *s = m->toSymbol(); + //objextern(s); + //if (!s->Sxtrnnum) objextdef(s->Sident); + if (!s->Sxtrnnum) + { + //printf("%s\n", s->Sident); +#if 0 /* This should work, but causes optlink to fail in common/newlib.asm */ + objextdef(s->Sident); +#else +#if ELFOBJ || MACHOBJ + int nbytes = reftoident(DATA, Offset(DATA), s, 0, I64 ? (CFoff | CFoffset64) : CFoff); +#else + int nbytes = reftoident(DATA, Doffset, s, 0, CFoff); + Doffset += nbytes; +#endif +#endif + } + } + } + + if (global.params.cov) + { + /* Create coverage identifier: + * private uint[numlines] __coverage; + */ + cov = symbol_calloc("__coverage"); + cov->Stype = type_fake(TYint); + cov->Stype->Tmangle = mTYman_c; + cov->Stype->Tcount++; + cov->Sclass = SCstatic; + cov->Sfl = FLdata; +#if ELFOBJ || MACHOBJ + cov->Sseg = UDATA; +#endif + dtnzeros(&cov->Sdt, 4 * numlines); + outdata(cov); + slist_add(cov); + + covb = (unsigned *)calloc((numlines + 32) / 32, sizeof(*covb)); + } + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *member = members->tdata()[i]; + member->toObjFile(multiobj); + } + + if (global.params.cov) + { + /* Generate + * bit[numlines] __bcoverage; + */ + Symbol *bcov = symbol_calloc("__bcoverage"); + bcov->Stype = type_fake(TYuint); + bcov->Stype->Tcount++; + bcov->Sclass = SCstatic; + bcov->Sfl = FLdata; +#if ELFOBJ || MACHOBJ + bcov->Sseg = DATA; +#endif + dtnbytes(&bcov->Sdt, (numlines + 32) / 32 * sizeof(*covb), (char *)covb); + outdata(bcov); + + free(covb); + covb = NULL; + + /* Generate: + * _d_cover_register(uint[] __coverage, BitArray __bcoverage, string filename); + * and prepend it to the static constructor. + */ + + /* t will be the type of the functions generated: + * extern (C) void func(); + */ + type *t = type_alloc(TYnfunc); + t->Tflags |= TFprototype | TFfixed; + t->Tmangle = mTYman_c; + t->Tnext = tsvoid; + tsvoid->Tcount++; + + sictor = toSymbolX("__modictor", SCglobal, t, "FZv"); + cstate.CSpsymtab = &sictor->Sfunc->Flocsym; + localgot = ictorlocalgot; + elem *e; + + e = el_params(el_pair(TYdarray, el_long(TYsize_t, numlines), el_ptr(cov)), + el_pair(TYdarray, el_long(TYsize_t, numlines), el_ptr(bcov)), + toEfilename(), + NULL); + e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_DCOVER]), e); + eictor = el_combine(e, eictor); + ictorlocalgot = localgot; + } + + // If coverage / static constructor / destructor / unittest calls + if (eictor || sctors.dim || ectorgates.dim || sdtors.dim || + ssharedctors.dim || esharedctorgates.dim || sshareddtors.dim || stests.dim) + { + if (eictor) + { + localgot = ictorlocalgot; + + block *b = block_calloc(); + b->BC = BCret; + b->Belem = eictor; + sictor->Sfunc->Fstartline.Sfilename = arg; + sictor->Sfunc->Fstartblock = b; + writefunc(sictor); + } + + sctor = callFuncsAndGates(this, &sctors, &ectorgates, "__modctor"); + sdtor = callFuncsAndGates(this, &sdtors, NULL, "__moddtor"); + +#if DMDV2 + ssharedctor = callFuncsAndGates(this, &ssharedctors, (StaticDtorDeclarations *)&esharedctorgates, "__modsharedctor"); + sshareddtor = callFuncsAndGates(this, &sshareddtors, NULL, "__modshareddtor"); +#endif + stest = callFuncsAndGates(this, &stests, NULL, "__modtest"); + + if (doppelganger) + genmoduleinfo(); + } + + if (doppelganger) + { + obj_termfile(); + return; + } + + if (global.params.multiobj) + { /* This is necessary because the main .obj for this module is written + * first, but determining whether marray or massert or munittest are needed is done + * possibly later in the doppelganger modules. + * Another way to fix it is do the main one last. + */ + toModuleAssert(); + toModuleUnittest(); + toModuleArray(); + } + +#if 1 + // Always generate module info, because of templates and -cov + if (1 || needModuleInfo()) + genmoduleinfo(); +#endif + + // If module assert + for (int i = 0; i < 3; i++) + { + Symbol *ma; + unsigned rt; + unsigned bc; + switch (i) + { + case 0: ma = marray; rt = RTLSYM_DARRAY; bc = BCexit; break; + case 1: ma = massert; rt = RTLSYM_DASSERTM; bc = BCexit; break; + case 2: ma = munittest; rt = RTLSYM_DUNITTESTM; bc = BCret; break; + default: assert(0); + } + + if (ma) + { + elem *elinnum; + + localgot = NULL; + + // Call dassert(filename, line) + // Get sole parameter, linnum + { + Symbol *sp = symbol_calloc("linnum"); + sp->Stype = type_fake(TYint); + sp->Stype->Tcount++; + sp->Sclass = SCfastpar; + sp->Spreg = I64 ? DI : AX; + sp->Sflags &= ~SFLspill; + sp->Sfl = FLpara; // FLauto? + cstate.CSpsymtab = &ma->Sfunc->Flocsym; + symbol_add(sp); + + elinnum = el_var(sp); + } + + elem *efilename = el_ptr(toSymbol()); + + elem *e = el_var(rtlsym[rt]); + e = el_bin(OPcall, TYvoid, e, el_param(elinnum, efilename)); + + block *b = block_calloc(); + b->BC = bc; + b->Belem = e; + ma->Sfunc->Fstartline.Sfilename = arg; + ma->Sfunc->Fstartblock = b; + ma->Sclass = SCglobal; + ma->Sfl = 0; + ma->Sflags |= rtlsym[rt]->Sflags & SFLexit; + writefunc(ma); + } + } + + obj_termfile(); +} + + +/* ================================================================== */ + +void FuncDeclaration::toObjFile(int multiobj) +{ + FuncDeclaration *func = this; + ClassDeclaration *cd = func->parent->isClassDeclaration(); + int reverse; + int has_arguments; + + //printf("FuncDeclaration::toObjFile(%p, %s.%s)\n", func, parent->toChars(), func->toChars()); + //if (type) printf("type = %s\n", func->type->toChars()); +#if 0 + //printf("line = %d\n",func->getWhere() / LINEINC); + EEcontext *ee = env->getEEcontext(); + if (ee->EEcompile == 2) + { + if (ee->EElinnum < (func->getWhere() / LINEINC) || + ee->EElinnum > (func->endwhere / LINEINC) + ) + return; // don't compile this function + ee->EEfunc = func->toSymbol(); + } +#endif + + if (semanticRun >= PASSobj) // if toObjFile() already run + return; + + // If errors occurred compiling it, such as bugzilla 6118 + if (type && type->ty == Tfunction && ((TypeFunction *)type)->next->ty == Terror) + return; + + if (!func->fbody) + { + return; + } + if (func->isUnitTestDeclaration() && !global.params.useUnitTests) + return; + + if (multiobj && !isStaticDtorDeclaration() && !isStaticCtorDeclaration()) + { obj_append(this); + return; + } + + assert(semanticRun == PASSsemantic3done); + semanticRun = PASSobj; + + if (global.params.verbose) + printf("function %s\n",func->toChars()); + + Symbol *s = func->toSymbol(); + func_t *f = s->Sfunc; + +#if TARGET_WINDOS + /* This is done so that the 'this' pointer on the stack is the same + * distance away from the function parameters, so that an overriding + * function can call the nested fdensure or fdrequire of its overridden function + * and the stack offsets are the same. + */ + if (isVirtual() && (fensure || frequire)) + f->Fflags3 |= Ffakeeh; +#endif + +#if TARGET_OSX + s->Sclass = SCcomdat; +#else + s->Sclass = SCglobal; +#endif + for (Dsymbol *p = parent; p; p = p->parent) + { + if (p->isTemplateInstance()) + { + s->Sclass = SCcomdat; + break; + } + } + + /* Vector operations should be comdat's + */ + if (isArrayOp) + s->Sclass = SCcomdat; + + if (isNested()) + { +// if (!(config.flags3 & CFG3pic)) +// s->Sclass = SCstatic; + f->Fflags3 |= Fnested; + } + else + { + const char *libname = (global.params.symdebug) + ? global.params.debuglibname + : global.params.defaultlibname; + + // Pull in RTL startup code + if (func->isMain()) + { objextdef("_main"); +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + obj_ehsections(); // initialize exception handling sections +#endif +#if TARGET_WINDOS + objextdef("__acrtused_con"); +#endif + obj_includelib(libname); + s->Sclass = SCglobal; + } + else if (strcmp(s->Sident, "main") == 0 && linkage == LINKc) + { +#if TARGET_WINDOS + objextdef("__acrtused_con"); // bring in C startup code + obj_includelib("snn.lib"); // bring in C runtime library +#endif + s->Sclass = SCglobal; + } + else if (func->isWinMain()) + { + objextdef("__acrtused"); + obj_includelib(libname); + s->Sclass = SCglobal; + } + + // Pull in RTL startup code + else if (func->isDllMain()) + { + objextdef("__acrtused_dll"); + obj_includelib(libname); + s->Sclass = SCglobal; + } + } + + cstate.CSpsymtab = &f->Flocsym; + + // Find module m for this function + Module *m = NULL; + for (Dsymbol *p = parent; p; p = p->parent) + { + m = p->isModule(); + if (m) + break; + } + + IRState irs(m, func); + Dsymbols deferToObj; // write these to OBJ file later + irs.deferToObj = &deferToObj; + + TypeFunction *tf; + enum RET retmethod; + symbol *shidden = NULL; + Symbol *sthis = NULL; + tym_t tyf; + + tyf = tybasic(s->Stype->Tty); + //printf("linkage = %d, tyf = x%x\n", linkage, tyf); + reverse = tyrevfunc(s->Stype->Tty); + + assert(func->type->ty == Tfunction); + tf = (TypeFunction *)(func->type); + has_arguments = (tf->linkage == LINKd) && (tf->varargs == 1); + retmethod = tf->retStyle(); + if (retmethod == RETstack) + { + // If function returns a struct, put a pointer to that + // as the first argument + ::type *thidden = tf->next->pointerTo()->toCtype(); + char hiddenparam[5+4+1]; + static int hiddenparami; // how many we've generated so far + + sprintf(hiddenparam,"__HID%d",++hiddenparami); + shidden = symbol_name(hiddenparam,SCparameter,thidden); + shidden->Sflags |= SFLtrue | SFLfree; +#if DMDV1 + if (func->nrvo_can && func->nrvo_var && func->nrvo_var->nestedref) +#else + if (func->nrvo_can && func->nrvo_var && func->nrvo_var->nestedrefs.dim) +#endif + type_setcv(&shidden->Stype, shidden->Stype->Tty | mTYvolatile); + irs.shidden = shidden; + this->shidden = shidden; + } + + if (vthis) + { + assert(!vthis->csym); + sthis = vthis->toSymbol(); + irs.sthis = sthis; + if (!(f->Fflags3 & Fnested)) + f->Fflags3 |= Fmember; + } + + Symbol **params; + unsigned pi; + + // Estimate number of parameters, pi + pi = (v_arguments != NULL); + if (parameters) + pi += parameters->dim; + // Allow extra 2 for sthis and shidden + params = (Symbol **)alloca((pi + 2) * sizeof(Symbol *)); + + // Get the actual number of parameters, pi, and fill in the params[] + pi = 0; + if (v_arguments) + { + params[pi] = v_arguments->toSymbol(); + pi += 1; + } + if (parameters) + { + for (size_t i = 0; i < parameters->dim; i++) + { VarDeclaration *v = parameters->tdata()[i]; + if (v->csym) + { + error("compiler error, parameter '%s', bugzilla 2962?", v->toChars()); + assert(0); + } + params[pi + i] = v->toSymbol(); + } + pi += parameters->dim; + } + + if (reverse) + { // Reverse params[] entries + for (size_t i = 0; i < pi/2; i++) + { + Symbol *sptmp = params[i]; + params[i] = params[pi - 1 - i]; + params[pi - 1 - i] = sptmp; + } + } + + if (shidden) + { +#if 0 + // shidden becomes last parameter + params[pi] = shidden; +#else + // shidden becomes first parameter + memmove(params + 1, params, pi * sizeof(params[0])); + params[0] = shidden; +#endif + pi++; + } + + + if (sthis) + { +#if 0 + // sthis becomes last parameter + params[pi] = sthis; +#else + // sthis becomes first parameter + memmove(params + 1, params, pi * sizeof(params[0])); + params[0] = sthis; +#endif + pi++; + } + + if ((global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isSolaris) && + linkage != LINKd && shidden && sthis) + { + /* swap shidden and sthis + */ + Symbol *sp = params[0]; + params[0] = params[1]; + params[1] = sp; + } + + for (size_t i = 0; i < pi; i++) + { Symbol *sp = params[i]; + sp->Sclass = SCparameter; + sp->Sflags &= ~SFLspill; + sp->Sfl = FLpara; + symbol_add(sp); + } + + // Determine register assignments + if (pi) + { + if (global.params.is64bit) + { + // Order of assignment of pointer or integer parameters + static const unsigned char argregs[6] = { DI,SI,DX,CX,R8,R9 }; + int r = 0; + int xmmcnt = XMM0; + + for (size_t i = 0; i < pi; i++) + { Symbol *sp = params[i]; + tym_t ty = tybasic(sp->Stype->Tty); + // BUG: doesn't work for structs + if (r < sizeof(argregs)/sizeof(argregs[0])) + { + if (type_jparam(sp->Stype)) + { + sp->Sclass = SCfastpar; + sp->Spreg = argregs[r]; + sp->Sfl = FLauto; + ++r; + } + } + if (xmmcnt <= XMM7) + { + if (tyxmmreg(ty)) + { + sp->Sclass = SCfastpar; + sp->Spreg = xmmcnt; + sp->Sfl = FLauto; + ++xmmcnt; + } + } + } + } + else + { + // First parameter goes in register + Symbol *sp = params[0]; + if ((tyf == TYjfunc || tyf == TYmfunc) && + type_jparam(sp->Stype)) + { sp->Sclass = SCfastpar; + sp->Spreg = (tyf == TYjfunc) ? AX : CX; + sp->Sfl = FLauto; + //printf("'%s' is SCfastpar\n",sp->Sident); + } + } + } + + if (func->fbody) + { block *b; + Blockx bx; + Statement *sbody; + + localgot = NULL; + + sbody = func->fbody; + memset(&bx,0,sizeof(bx)); + bx.startblock = block_calloc(); + bx.curblock = bx.startblock; + bx.funcsym = s; + bx.scope_index = -1; + bx.classdec = cd; + bx.member = func; + bx.module = getModule(); + irs.blx = &bx; +#if DMDV2 + buildClosure(&irs); +#endif + +#if 0 + if (func->isSynchronized()) + { + if (cd) + { elem *esync; + if (func->isStatic()) + { // monitor is in ClassInfo + esync = el_ptr(cd->toSymbol()); + } + else + { // 'this' is the monitor + esync = el_var(sthis); + } + + if (func->isStatic() || sbody->usesEH() || + !(config.flags2 & CFG2seh)) + { // BUG: what if frequire or fensure uses EH? + + sbody = new SynchronizedStatement(func->loc, esync, sbody); + } + else + { +#if TARGET_WINDOS + if (config.flags2 & CFG2seh) + { + /* The "jmonitor" uses an optimized exception handling frame + * which is a little shorter than the more general EH frame. + * It isn't strictly necessary. + */ + s->Sfunc->Fflags3 |= Fjmonitor; + } +#endif + el_free(esync); + } + } + else + { + error("synchronized function %s must be a member of a class", func->toChars()); + } + } +#elif TARGET_WINDOS + if (func->isSynchronized() && cd && config.flags2 & CFG2seh && + !func->isStatic() && !sbody->usesEH()) + { + /* The "jmonitor" hack uses an optimized exception handling frame + * which is a little shorter than the more general EH frame. + */ + s->Sfunc->Fflags3 |= Fjmonitor; + } +#endif + + sbody->toIR(&irs); + bx.curblock->BC = BCret; + + f->Fstartblock = bx.startblock; +// einit = el_combine(einit,bx.init); + + if (isCtorDeclaration()) + { + assert(sthis); + for (b = f->Fstartblock; b; b = b->Bnext) + { + if (b->BC == BCret) + { + b->BC = BCretexp; + b->Belem = el_combine(b->Belem, el_var(sthis)); + } + } + } + } + + // If static constructor +#if DMDV2 + if (isSharedStaticCtorDeclaration()) // must come first because it derives from StaticCtorDeclaration + { + ssharedctors.push(s); + } + else +#endif + if (isStaticCtorDeclaration()) + { + sctors.push(s); + } + + // If static destructor +#if DMDV2 + if (isSharedStaticDtorDeclaration()) // must come first because it derives from StaticDtorDeclaration + { + SharedStaticDtorDeclaration *f = isSharedStaticDtorDeclaration(); + assert(f); + if (f->vgate) + { /* Increment destructor's vgate at construction time + */ + esharedctorgates.push(f); + } + + sshareddtors.shift(s); + } + else +#endif + if (isStaticDtorDeclaration()) + { + StaticDtorDeclaration *f = isStaticDtorDeclaration(); + assert(f); + if (f->vgate) + { /* Increment destructor's vgate at construction time + */ + ectorgates.push(f); + } + + sdtors.shift(s); + } + + // If unit test + if (isUnitTestDeclaration()) + { + stests.push(s); + } + + if (global.errors) + return; + + writefunc(s); + if (isExport()) + obj_export(s, Poffset); + + for (size_t i = 0; i < irs.deferToObj->dim; i++) + { + Dsymbol *s = irs.deferToObj->tdata()[i]; + s->toObjFile(0); + } + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + // A hack to get a pointer to this function put in the .dtors segment + if (ident && memcmp(ident->toChars(), "_STD", 4) == 0) + obj_staticdtor(s); +#endif +#if DMDV2 + if (irs.startaddress) + { + printf("Setting start address\n"); + obj_startaddress(irs.startaddress); + } +#endif +} + +/* ================================================================== */ + +/***************************** + * Return back end type corresponding to D front end type. + */ + +unsigned Type::totym() +{ unsigned t; + + switch (ty) + { + case Tvoid: t = TYvoid; break; + case Tint8: t = TYschar; break; + case Tuns8: t = TYuchar; break; + case Tint16: t = TYshort; break; + case Tuns16: t = TYushort; break; + case Tint32: t = TYint; break; + case Tuns32: t = TYuint; break; + case Tint64: t = TYllong; break; + case Tuns64: t = TYullong; break; + case Tfloat32: t = TYfloat; break; + case Tfloat64: t = TYdouble; break; + case Tfloat80: t = TYldouble; break; + case Timaginary32: t = TYifloat; break; + case Timaginary64: t = TYidouble; break; + case Timaginary80: t = TYildouble; break; + case Tcomplex32: t = TYcfloat; break; + case Tcomplex64: t = TYcdouble; break; + case Tcomplex80: t = TYcldouble; break; + case Tbool: t = TYbool; break; + case Tchar: t = TYchar; break; +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + case Twchar: t = TYwchar_t; break; + case Tdchar: t = TYdchar; break; +#else + case Twchar: t = TYwchar_t; break; + case Tdchar: + t = (global.params.symdebug == 1) ? TYdchar : TYulong; + break; +#endif + + case Taarray: t = TYaarray; break; + case Tclass: + case Treference: + case Tpointer: t = TYnptr; break; + case Tdelegate: t = TYdelegate; break; + case Tarray: t = TYdarray; break; +#if SARRAYVALUE + case Tsarray: t = TYstruct; break; +#else + case Tsarray: t = TYarray; break; +#endif + case Tstruct: t = TYstruct; break; + + case Tenum: + case Ttypedef: + t = toBasetype()->totym(); + break; + + case Tident: + case Ttypeof: +#ifdef DEBUG + printf("ty = %d, '%s'\n", ty, toChars()); +#endif + error(0, "forward reference of %s", toChars()); + t = TYint; + break; + + case Tnull: + t = TYnptr; + break; + + case Tvector: + { TypeVector *tv = (TypeVector *)this; + TypeBasic *tb = tv->elementType(); + switch (tb->ty) + { case Tvoid: + case Tint8: t = TYschar16; break; + case Tuns8: t = TYuchar16; break; + case Tint16: t = TYshort8; break; + case Tuns16: t = TYushort8; break; + case Tint32: t = TYlong4; break; + case Tuns32: t = TYulong4; break; + case Tint64: t = TYllong2; break; + case Tuns64: t = TYullong2; break; + case Tfloat32: t = TYfloat4; break; + case Tfloat64: t = TYdouble2; break; + default: + assert(0); + break; + } + break; + } + + default: +#ifdef DEBUG + printf("ty = %d, '%s'\n", ty, toChars()); + halt(); +#endif + assert(0); + } + +#if DMDV2 + // Add modifiers + switch (mod) + { + case 0: + break; + case MODconst: + case MODwild: + t |= mTYconst; + break; + case MODimmutable: + t |= mTYimmutable; + break; + case MODshared: + t |= mTYshared; + break; + case MODshared | MODwild: + case MODshared | MODconst: + t |= mTYshared | mTYconst; + break; + default: + assert(0); + } +#endif + + return t; +} + +unsigned TypeFunction::totym() +{ + tym_t tyf; + + //printf("TypeFunction::totym(), linkage = %d\n", linkage); + switch (linkage) + { + case LINKwindows: + tyf = (varargs == 1) ? TYnfunc : TYnsfunc; + break; + + case LINKpascal: + tyf = (varargs == 1) ? TYnfunc : TYnpfunc; + break; + + case LINKc: + tyf = TYnfunc; +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (I32 && retStyle() == RETstack) + tyf = TYhfunc; +#endif + break; + + case LINKd: + tyf = (varargs == 1) ? TYnfunc : TYjfunc; + break; + + case LINKcpp: + tyf = TYnfunc; + break; + + default: + printf("linkage = %d\n", linkage); + assert(0); + } +#if DMDV2 + if (isnothrow) + tyf |= mTYnothrow; +#endif + return tyf; +} + +/************************************** + */ + +Symbol *Type::toSymbol() +{ + assert(0); + return NULL; +} + +Symbol *TypeClass::toSymbol() +{ + return sym->toSymbol(); +} + +/************************************* + * Generate symbol in data segment for critical section. + */ + +Symbol *Module::gencritsec() +{ + Symbol *s; + type *t; + + t = Type::tint32->toCtype(); + s = symbol_name("critsec", SCstatic, t); + s->Sfl = FLdata; + /* Must match D_CRITICAL_SECTION in phobos/internal/critical.c + */ + dtnzeros(&s->Sdt, PTRSIZE + (I64 ? os_critsecsize64() : os_critsecsize32())); +#if ELFOBJ || MACHOBJ // Burton + s->Sseg = DATA; +#endif + outdata(s); + return s; +} + +/************************************** + * Generate elem that is a pointer to the module file name. + */ + +elem *Module::toEfilename() +{ elem *efilename; + + if (!sfilename) + { + dt_t *dt = NULL; + char *id; + int len; + + id = srcfile->toChars(); + len = strlen(id); + dtsize_t(&dt, len); + dtabytes(&dt,TYnptr, 0, len + 1, id); + + sfilename = symbol_generate(SCstatic,type_fake(TYdarray)); + sfilename->Sdt = dt; + sfilename->Sfl = FLdata; +#if ELFOBJ + sfilename->Sseg = CDATA; +#endif +#if MACHOBJ + // Because of PIC and CDATA being in the _TEXT segment, cannot + // have pointers in CDATA + sfilename->Sseg = DATA; +#endif + outdata(sfilename); + } + + efilename = el_var(sfilename); + return efilename; +} + + diff --git a/gpl.txt b/gpl.txt new file mode 100644 index 00000000..43cd72c3 --- /dev/null +++ b/gpl.txt @@ -0,0 +1,248 @@ + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work based +on the Program" means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this General + Public License. + + d) You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + + 7. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program 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 1, or (at your option) + any later version. + + This program 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 this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/hdrgen.c b/hdrgen.c new file mode 100644 index 00000000..78dd4001 --- /dev/null +++ b/hdrgen.c @@ -0,0 +1,100 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// Initial header generation implementation by Dave Fladebo +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +// Routines to emit header files + +#define PRETTY_PRINT +#define TEST_EMIT_ALL 0 // For Testing + +#define LOG 0 + +#include +#include +#include +#if __DMC__ +#include +#endif + +#include "rmem.h" + +#include "id.h" +#include "init.h" + +#include "attrib.h" +#include "cond.h" +#include "enum.h" +#include "import.h" +#include "module.h" +#include "mtype.h" +#include "scope.h" +#include "staticassert.h" +#include "template.h" +#include "utf.h" +#include "version.h" + +#include "declaration.h" +#include "aggregate.h" +#include "expression.h" +#include "statement.h" +#include "mtype.h" +#include "hdrgen.h" + +void argsToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs); + +void Module::genhdrfile() +{ + OutBuffer hdrbufr; + + hdrbufr.printf("// D import file generated from '%s'", srcfile->toChars()); + hdrbufr.writenl(); + + HdrGenState hgs; + memset(&hgs, 0, sizeof(hgs)); + hgs.hdrgen = 1; + + toCBuffer(&hdrbufr, &hgs); + + // Transfer image to file + hdrfile->setbuffer(hdrbufr.data, hdrbufr.offset); + hdrbufr.data = NULL; + + char *pt = FileName::path(hdrfile->toChars()); + if (*pt) + FileName::ensurePathExists(pt); + mem.free(pt); + hdrfile->writev(); +} + + +void Module::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (md) + { + buf->writestring("module "); + buf->writestring(md->toChars()); + buf->writebyte(';'); + buf->writenl(); + } + + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + + s->toHBuffer(buf, hgs); + } +} + + +void Dsymbol::toHBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + toCBuffer(buf, hgs); +} + + +/*************************************/ diff --git a/hdrgen.h b/hdrgen.h new file mode 100644 index 00000000..79cfb732 --- /dev/null +++ b/hdrgen.h @@ -0,0 +1,34 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// initial header generation implementation by Dave Fladebo +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +struct HdrGenState +{ + int hdrgen; // 1 if generating header file + int ddoc; // 1 if generating Ddoc file + int console; // 1 if writing to console + int tpltMember; + int inCallExp; + int inPtrExp; + int inSlcExp; + int inDotExp; + int inBinExp; + int inArrExp; + int emitInst; + struct + { + int init; + int decl; + } FLinit; + + HdrGenState() { memset(this, 0, sizeof(HdrGenState)); } +}; + + diff --git a/iasm.c b/iasm.c new file mode 100644 index 00000000..6973bf7f --- /dev/null +++ b/iasm.c @@ -0,0 +1,4851 @@ + +/* + * Copyright (c) 1992-1999 by Symantec + * Copyright (c) 1999-2011 by Digital Mars + * All Rights Reserved + * http://www.digitalmars.com + * http://www.dsource.org/projects/dmd/browser/branches/dmd-1.x/src/iasm.c + * http://www.dsource.org/projects/dmd/browser/trunk/src/iasm.c + * Written by Mike Cote, John Micco and Walter Bright + * D version by Walter Bright + * + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +// Inline assembler for the D programming language compiler + +#include +#include +#include +#include +#include +#include +#include +#if __DMC__ +#undef setjmp +#include +#endif + + +// D compiler +#include "mars.h" +#include "lexer.h" +#include "mtype.h" +#include "statement.h" +#include "id.h" +#include "declaration.h" +#include "scope.h" +#include "init.h" +#include "enum.h" +#include "module.h" + +// C/C++ compiler +#define SCOPE_H 1 // avoid conflicts with D's Scope +#include "cc.h" +#include "token.h" +#include "global.h" +#include "el.h" +#include "type.h" +#include "oper.h" +#include "code.h" +#include "iasm.h" +#include "xmm.h" + +// I32 isn't set correctly yet because this is the front end, and I32 +// is a backend flag +#undef I16 +#undef I32 +#undef I64 +#define I16 0 +#define I32 (global.params.is64bit == 0) +#define I64 (global.params.is64bit == 1) + +//#define EXTRA_DEBUG 1 + +#undef ADDFWAIT +#define ADDFWAIT() 0 + +// Error numbers +enum ASMERRMSGS +{ + EM_bad_float_op, + EM_bad_addr_mode, + EM_align, + EM_opcode_exp, + EM_prefix, + EM_eol, + EM_bad_operand, + EM_bad_integral_operand, + EM_ident_exp, + EM_not_struct, + EM_nops_expected, + EM_bad_op, + EM_const_init, + EM_undefined, + EM_pointer, + EM_colon, + EM_rbra, + EM_rpar, + EM_ptr_exp, + EM_num, + EM_float, + EM_char, + EM_label_expected, + EM_uplevel, + EM_type_as_operand, + EM_invalid_64bit_opcode, +}; + +const char *asmerrmsgs[] = +{ + "unknown operand for floating point instruction", + "bad addr mode", + "align %d must be a power of 2", + "opcode expected, not %s", + "prefix", + "end of instruction", + "bad operand", + "bad integral operand", + "identifier expected", + "not struct", + "%u operands found for %s instead of the expected %u", + "bad type/size of operands '%s'", + "constant initializer expected", + "undefined identifier '%s'", + "pointer", + "colon", + "] expected instead of '%s'", + ") expected instead of '%s'", + "ptr expected", + "integer expected", + "floating point expected", + "character is truncated", + "label expected", + "uplevel nested reference to variable %s", + "cannot use type %s as an operand", + "opcode %s is unavailable in 64bit mode" +}; + +// Additional tokens for the inline assembler +typedef enum +{ + ASMTKlocalsize = TOKMAX + 1, + ASMTKdword, + ASMTKeven, + ASMTKfar, + ASMTKnaked, + ASMTKnear, + ASMTKptr, + ASMTKqword, + ASMTKseg, + ASMTKword, + ASMTKmax = ASMTKword-(TOKMAX+1)+1 +} ASMTK; + +static const char *apszAsmtk[ASMTKmax] = { + "__LOCAL_SIZE", + "dword", + "even", + "far", + "naked", + "near", + "ptr", + "qword", + "seg", + "word", +}; + +struct ASM_STATE +{ + unsigned char ucItype; // Instruction type +#define ITprefix 0x10 // special prefix +#define ITjump 0x20 // jump instructions CALL, Jxx and LOOPxx +#define ITimmed 0x30 // value of an immediate operand controls + // code generation +#define ITopt 0x40 // not all operands are required +#define ITshift 0x50 // rotate and shift instructions +#define ITfloat 0x60 // floating point coprocessor instructions +#define ITdata 0x70 // DB, DW, DD, DQ, DT pseudo-ops +#define ITaddr 0x80 // DA (define addresss) pseudo-op +#define ITMASK 0xF0 +#define ITSIZE 0x0F // mask for size + + Loc loc; + unsigned char bInit; + LabelDsymbol *psDollar; + Dsymbol *psLocalsize; + jmp_buf env; + unsigned char bReturnax; + AsmStatement *statement; + Scope *sc; +}; + +ASM_STATE asmstate; + +static Token *asmtok; +static enum TOK tok_value; +//char debuga = 1; + +// From ptrntab.c +const char *asm_opstr(OP *pop); +OP *asm_op_lookup(const char *s); +void init_optab(); + +static unsigned char asm_TKlbra_seen = FALSE; + +typedef struct +{ + char regstr[6]; + unsigned char val; + opflag_t ty; +} REG; + +static REG regFp = { "ST", 0, _st }; + +static REG aregFp[] = { + { "ST(0)", 0, _sti }, + { "ST(1)", 1, _sti }, + { "ST(2)", 2, _sti }, + { "ST(3)", 3, _sti }, + { "ST(4)", 4, _sti }, + { "ST(5)", 5, _sti }, + { "ST(6)", 6, _sti }, + { "ST(7)", 7, _sti } +}; +#define _AL 0 +#define _AH 4 +#define _AX 0 +#define _EAX 0 +#define _BL 3 +#define _BH 7 +#define _BX 3 +#define _EBX 3 +#define _CL 1 +#define _CH 5 +#define _CX 1 +#define _ECX 1 +#define _DL 2 +#define _DH 6 +#define _DX 2 +#define _EDX 2 +#define _BP 5 +#define _EBP 5 +#define _SP 4 +#define _ESP 4 +#define _DI 7 +#define _EDI 7 +#define _SI 6 +#define _ESI 6 +#define _ES 0 +#define _CS 1 +#define _SS 2 +#define _DS 3 +#define _GS 5 +#define _FS 4 + +static REG regtab[] = +{ +"AL", _AL, _r8 | _al, +"AH", _AH, _r8, +"AX", _AX, _r16 | _ax, +"EAX", _EAX, _r32 | _eax, +"BL", _BL, _r8, +"BH", _BH, _r8, +"BX", _BX, _r16, +"EBX", _EBX, _r32, +"CL", _CL, _r8 | _cl, +"CH", _CH, _r8, +"CX", _CX, _r16, +"ECX", _ECX, _r32, +"DL", _DL, _r8, +"DH", _DH, _r8, +"DX", _DX, _r16 | _dx, +"EDX", _EDX, _r32, +"BP", _BP, _r16, +"EBP", _EBP, _r32, +"SP", _SP, _r16, +"ESP", _ESP, _r32, +"DI", _DI, _r16, +"EDI", _EDI, _r32, +"SI", _SI, _r16, +"ESI", _ESI, _r32, +"ES", _ES, _seg | _es, +"CS", _CS, _seg | _cs, +"SS", _SS, _seg | _ss , +"DS", _DS, _seg | _ds, +"GS", _GS, _seg | _gs, +"FS", _FS, _seg | _fs, +"CR0", 0, _special | _crn, +"CR2", 2, _special | _crn, +"CR3", 3, _special | _crn, +"CR4", 4, _special | _crn, +"DR0", 0, _special | _drn, +"DR1", 1, _special | _drn, +"DR2", 2, _special | _drn, +"DR3", 3, _special | _drn, +"DR4", 4, _special | _drn, +"DR5", 5, _special | _drn, +"DR6", 6, _special | _drn, +"DR7", 7, _special | _drn, +"TR3", 3, _special | _trn, +"TR4", 4, _special | _trn, +"TR5", 5, _special | _trn, +"TR6", 6, _special | _trn, +"TR7", 7, _special | _trn, +"MM0", 0, _mm, +"MM1", 1, _mm, +"MM2", 2, _mm, +"MM3", 3, _mm, +"MM4", 4, _mm, +"MM5", 5, _mm, +"MM6", 6, _mm, +"MM7", 7, _mm, +"XMM0", 0, _xmm | _xmm0, +"XMM1", 1, _xmm, +"XMM2", 2, _xmm, +"XMM3", 3, _xmm, +"XMM4", 4, _xmm, +"XMM5", 5, _xmm, +"XMM6", 6, _xmm, +"XMM7", 7, _xmm, +}; + +// 64 bit only registers +#define _RAX 0 +#define _RBX 3 +#define _RCX 1 +#define _RDX 2 +#define _RSI 6 +#define _RDI 7 +#define _RBP 5 +#define _RSP 4 +#define _R8 8 +#define _R9 9 +#define _R10 10 +#define _R11 11 +#define _R12 12 +#define _R13 13 +#define _R14 14 +#define _R15 15 + +#define _R8D 8 +#define _R9D 9 +#define _R10D 10 +#define _R11D 11 +#define _R12D 12 +#define _R13D 13 +#define _R14D 14 +#define _R15D 15 + +#define _R8W 8 +#define _R9W 9 +#define _R10W 10 +#define _R11W 11 +#define _R12W 12 +#define _R13W 13 +#define _R14W 13 +#define _R15W 15 + +#define _SIL 6 +#define _DIL 7 +#define _BPL 5 +#define _SPL 4 +#define _R8B 8 +#define _R9B 9 +#define _R10B 10 +#define _R11B 11 +#define _R12B 12 +#define _R13B 13 +#define _R14B 14 +#define _R15B 15 + +static REG regtab64[] = +{ +"RAX", _RAX, _r64 | _rax, +"RBX", _RBX, _r64, +"RCX", _RCX, _r64, +"RDX", _RDX, _r64, +"RSI", _RSI, _r64, +"RDI", _RDI, _r64, +"RBP", _RBP, _r64, +"RSP", _RSP, _r64, +"R8", _R8, _r64, +"R9", _R9, _r64, +"R10", _R10, _r64, +"R11", _R11, _r64, +"R12", _R12, _r64, +"R13", _R13, _r64, +"R14", _R14, _r64, +"R15", _R15, _r64, + +"R8D", _R8D, _r32, +"R9D", _R9D, _r32, +"R10D", _R10D, _r32, +"R11D", _R11D, _r32, +"R12D", _R12D, _r32, +"R13D", _R13D, _r32, +"R14D", _R14D, _r32, +"R15D", _R15D, _r32, + +"R8W", _R8W, _r16, +"R9W", _R9W, _r16, +"R10W", _R10W, _r16, +"R11W", _R11W, _r16, +"R12W", _R12W, _r16, +"R13W", _R13W, _r16, +"R14W", _R14W, _r16, +"R15W", _R15W, _r16, + +"SIL", _SIL, _r8, +"DIL", _DIL, _r8, +"BPL", _BPL, _r8, +"SPL", _SPL, _r8, +"R8B", _R8B, _r8, +"R9B", _R9B, _r8, +"R10B", _R10B, _r8, +"R11B", _R11B, _r8, +"R12B", _R12B, _r8, +"R13B", _R13B, _r8, +"R14B", _R14B, _r8, +"R15B", _R15B, _r8, + +"XMM8", 8, _xmm, +"XMM9", 9, _xmm, +"XMM10", 10, _xmm, +"XMM11", 11, _xmm, +"XMM12", 12, _xmm, +"XMM13", 13, _xmm, +"XMM14", 14, _xmm, +"XMM15", 15, _xmm, + +"YMM0", 0, _ymm, +"YMM1", 1, _ymm, +"YMM2", 2, _ymm, +"YMM3", 3, _ymm, +"YMM4", 4, _ymm, +"YMM5", 5, _ymm, +"YMM6", 6, _ymm, +"YMM7", 7, _ymm, +"YMM8", 8, _ymm, +"YMM9", 9, _ymm, +"YMM10", 10, _ymm, +"YMM11", 11, _ymm, +"YMM12", 12, _ymm, +"YMM13", 13, _ymm, +"YMM14", 14, _ymm, +"YMM15", 15, _ymm, +}; + +typedef enum { + ASM_JUMPTYPE_UNSPECIFIED, + ASM_JUMPTYPE_SHORT, + ASM_JUMPTYPE_NEAR, + ASM_JUMPTYPE_FAR +} ASM_JUMPTYPE; // ajt + +typedef struct opnd +{ + REG *base; // if plain register + REG *pregDisp1; // if [register1] + REG *pregDisp2; + REG *segreg; // if segment override + char indirect; // if had a '*' or '->' + char bOffset; // if 'offset' keyword + char bSeg; // if 'segment' keyword + char bPtr; // if 'ptr' keyword + unsigned uchMultiplier; // register multiplier; valid values are 0,1,2,4,8 + opflag_t usFlags; + Dsymbol *s; + targ_llong disp; + long double real; + Type *ptype; + ASM_JUMPTYPE ajt; +} OPND; + +// +// Exported functions called from the compiler +// +int asm_state(int iFlags); +void iasm_term(); + +// +// Local functions defined and only used here +// +STATIC OPND *asm_add_exp(); +STATIC OPND *opnd_calloc(); +STATIC void opnd_free(OPND *popnd); +STATIC OPND *asm_and_exp(); +STATIC OPND *asm_cond_exp(); +STATIC opflag_t asm_determine_operand_flags(OPND *popnd); +code *asm_genloc(Loc loc, code *c); +int asm_getnum(); + +STATIC void asmerr(const char *, ...); + +#if __clang__ +STATIC void asmerr(int, ...) __attribute__((analyzer_noreturn)); +#else +STATIC void asmerr(int, ...); +#if __DMC__ +#pragma SC noreturn(asmerr) +#endif +#endif + +STATIC OPND *asm_equal_exp(); +STATIC OPND *asm_inc_or_exp(); +STATIC OPND *asm_log_and_exp(); +STATIC OPND *asm_log_or_exp(); +STATIC char asm_length_type_size(OPND *popnd); +STATIC void asm_token(); +STATIC void asm_token_trans(Token *tok); +STATIC unsigned char asm_match_flags(opflag_t usOp , opflag_t usTable ); +STATIC unsigned char asm_match_float_flags(opflag_t usOp, opflag_t usTable); +STATIC void asm_make_modrm_byte( +#ifdef DEBUG + unsigned char *puchOpcode, unsigned *pusIdx, +#endif + code *pc, + unsigned usFlags, + OPND *popnd, OPND *popnd2); +STATIC regm_t asm_modify_regs(PTRNTAB ptb, OPND *popnd1, OPND *popnd2); +STATIC void asm_output_flags(opflag_t usFlags); +STATIC void asm_output_popnd(OPND *popnd); +STATIC unsigned asm_type_size(Type * ptype); +STATIC opflag_t asm_float_type_size(Type * ptype, opflag_t *pusFloat); +STATIC OPND *asm_mul_exp(); +STATIC OPND *asm_br_exp(); +STATIC OPND *asm_primary_exp(); +STATIC OPND *asm_prim_post(OPND *); +STATIC OPND *asm_rel_exp(); +STATIC OPND *asm_shift_exp(); +STATIC OPND *asm_una_exp(); +STATIC OPND *asm_xor_exp(); +STATIC void *link_alloc(size_t, void *); +STATIC void asm_chktok(enum TOK toknum, unsigned errnum); +STATIC code *asm_db_parse(OP *pop); +STATIC code *asm_da_parse(OP *pop); + +unsigned compute_hashkey(char *); + + +/******************************* + */ + +STATIC OPND *opnd_calloc() +{ OPND *o; + + o = new OPND(); + memset(o, 0, sizeof(*o)); + return o; +} + +/******************************* + */ + +STATIC void opnd_free(OPND *o) +{ + if (o) + { + delete o; + } +} + +/******************************* + */ + +STATIC void asm_chktok(enum TOK toknum,unsigned errnum) +{ + if (tok_value == toknum) + asm_token(); // scan past token + else + /* When we run out of tokens, asmtok is NULL. + * But when this happens when a ';' was hit. + */ + asmerr(errnum, asmtok ? asmtok->toChars() : ";"); +} + + +/******************************* + */ + +STATIC PTRNTAB asm_classify(OP *pop, OPND *popnd1, OPND *popnd2, + OPND *popnd3, OPND *popnd4, unsigned *pusNumops) +{ + unsigned usNumops; + unsigned usActual; + PTRNTAB ptbRet = { NULL }; + opflag_t opflags1 = 0 ; + opflag_t opflags2 = 0; + opflag_t opflags3 = 0; + opflag_t opflags4 = 0; + char bFake = FALSE; + char bInvalid64bit = FALSE; + + unsigned char bMatch1, bMatch2, bMatch3, bMatch4, bRetry = FALSE; + + // How many arguments are there? the parser is strictly left to right + // so this should work. + + if (!popnd1) + usNumops = 0; + else + { + popnd1->usFlags = opflags1 = asm_determine_operand_flags(popnd1); + if (!popnd2) + usNumops = 1; + else + { + popnd2->usFlags = opflags2 = asm_determine_operand_flags(popnd2); + if (!popnd3) + usNumops = 2; + else + { + popnd3->usFlags = opflags3 = asm_determine_operand_flags(popnd3); + if (!popnd4) + usNumops = 3; + else + { + popnd4->usFlags = opflags4 = asm_determine_operand_flags(popnd4); + usNumops = 4; + } + } + } + } + + // Now check to insure that the number of operands is correct + usActual = (pop->usNumops & ITSIZE); + if (usActual != usNumops && asmstate.ucItype != ITopt && + asmstate.ucItype != ITfloat) + { +PARAM_ERROR: + asmerr(EM_nops_expected, usNumops, asm_opstr(pop), usActual); + } + if (usActual < usNumops) + *pusNumops = usActual; + else + *pusNumops = usNumops; +// +// The number of arguments matches, now check to find the opcode +// in the associated opcode table +// +RETRY: + //printf("usActual = %d\n", usActual); + switch (usActual) + { + case 0: + if (I64 && (pop->ptb.pptb0->usFlags & _i64_bit)) + asmerr( EM_invalid_64bit_opcode, asm_opstr(pop)); // illegal opcode in 64bit mode + + ptbRet = pop->ptb; + + goto RETURN_IT; + + case 1: + { //printf("opflags1 = "); asm_output_flags(opflags1); printf("\n"); + PTRNTAB1 *table1; + for (table1 = pop->ptb.pptb1; table1->usOpcode != ASM_END; + table1++) + { + //printf("table = "); asm_output_flags(table1->usOp1); printf("\n"); + bMatch1 = asm_match_flags(opflags1, table1->usOp1); + //printf("bMatch1 = x%x\n", bMatch1); + if (bMatch1) + { if (table1->usOpcode == 0x68 && + !I16 && + table1->usOp1 == _imm16 + ) + // Don't match PUSH imm16 in 32 bit code + continue; + + // Check if match is invalid in 64bit mode + if (I64 && (table1->usFlags & _i64_bit)) + { + bInvalid64bit = TRUE; + continue; + } + + break; + } + if ((asmstate.ucItype == ITimmed) && + asm_match_flags(opflags1, + CONSTRUCT_FLAGS(_8 | _16 | _32, _imm, _normal, + 0)) && + popnd1->disp == table1->usFlags) + break; + if ((asmstate.ucItype == ITopt || + asmstate.ucItype == ITfloat) && + !usNumops && + !table1->usOp1) + { + if (usNumops > 1) + goto PARAM_ERROR; + break; + } + } + if (table1->usOpcode == ASM_END) + { +#ifdef DEBUG + if (debuga) + { printf("\t%s\t", asm_opstr(pop)); + if (popnd1) + asm_output_popnd(popnd1); + if (popnd2) { + printf(","); + asm_output_popnd(popnd2); + } + if (popnd3) { + printf(","); + asm_output_popnd(popnd3); + } + printf("\n"); + + printf("OPCODE mism = "); + if (popnd1) + asm_output_flags(popnd1->usFlags); + else + printf("NONE"); + printf("\n"); + } +#endif +TYPE_SIZE_ERROR: + if (popnd1 && ASM_GET_aopty(popnd1->usFlags) != _reg) + { + opflags1 = popnd1->usFlags |= _anysize; + if (asmstate.ucItype == ITjump) + { + if (bRetry && popnd1->s && !popnd1->s->isLabel()) + { + asmerr(EM_label_expected, popnd1->s->toChars()); + } + + popnd1->usFlags |= CONSTRUCT_FLAGS(0, 0, 0, + _fanysize); + } + } + if (popnd2 && ASM_GET_aopty(popnd2->usFlags) != _reg) { + opflags2 = popnd2->usFlags |= (_anysize); + if (asmstate.ucItype == ITjump) + popnd2->usFlags |= CONSTRUCT_FLAGS(0, 0, 0, + _fanysize); + } + if (popnd3 && ASM_GET_aopty(popnd3->usFlags) != _reg) { + opflags3 = popnd3->usFlags |= (_anysize); + if (asmstate.ucItype == ITjump) + popnd3->usFlags |= CONSTRUCT_FLAGS(0, 0, 0, + _fanysize); + } + if (bRetry) + { + if(bInvalid64bit) + asmerr("operand for '%s' invalid in 64bit mode", asm_opstr(pop)); + else + asmerr(EM_bad_op, asm_opstr(pop)); // illegal type/size of operands + } + bRetry = TRUE; + goto RETRY; + + } + ptbRet.pptb1 = table1; + goto RETURN_IT; + } + case 2: + { //printf("opflags1 = "); asm_output_flags(opflags1); printf(" "); + //printf("opflags2 = "); asm_output_flags(opflags2); printf("\n"); + PTRNTAB2 *table2; + for (table2 = pop->ptb.pptb2; + table2->usOpcode != ASM_END; + table2++) + { + //printf("table1 = "); asm_output_flags(table2->usOp1); printf(" "); + //printf("table2 = "); asm_output_flags(table2->usOp2); printf("\n"); + if (I64 && (table2->usFlags & _i64_bit)) + asmerr( EM_invalid_64bit_opcode, asm_opstr(pop)); + + bMatch1 = asm_match_flags(opflags1, table2->usOp1); + bMatch2 = asm_match_flags(opflags2, table2->usOp2); + //printf("match1 = %d, match2 = %d\n",bMatch1,bMatch2); + if (bMatch1 && bMatch2) { + + //printf("match\n"); + + /* If they both match and the first op in the table is not AL + * or size of 8 and the second is immediate 8, + * then check to see if the constant + * is a signed 8 bit constant. If so, then do not match, otherwise match + */ + if (!bRetry && + !((ASM_GET_uSizemask(table2->usOp1) & _8) || + (ASM_GET_uRegmask(table2->usOp1) & _al)) && + (ASM_GET_aopty(table2->usOp2) == _imm) && + (ASM_GET_uSizemask(table2->usOp2) & _8)) + { + + if (popnd2->disp <= SCHAR_MAX) + break; + else + bFake = TRUE; + } + else + break; + } + if (asmstate.ucItype == ITopt || + asmstate.ucItype == ITfloat) + { + switch (usNumops) + { + case 0: + if (!table2->usOp1) + goto Lfound2; + break; + case 1: + if (bMatch1 && !table2->usOp2) + goto Lfound2; + break; + case 2: + break; + default: + goto PARAM_ERROR; + } + } +#if 0 + if (asmstate.ucItype == ITshift && + !table2->usOp2 && + bMatch1 && popnd2->disp == 1 && + asm_match_flags(opflags2, + CONSTRUCT_FLAGS(_8|_16|_32, _imm,_normal,0)) + ) + break; +#endif + } + Lfound2: + if (table2->usOpcode == ASM_END) + { +#ifdef DEBUG + if (debuga) + { printf("\t%s\t", asm_opstr(pop)); + if (popnd1) + asm_output_popnd(popnd1); + if (popnd2) { + printf(","); + asm_output_popnd(popnd2); + } + if (popnd3) { + printf(","); + asm_output_popnd(popnd3); + } + printf("\n"); + + printf("OPCODE mismatch = "); + if (popnd1) + asm_output_flags(popnd1->usFlags); + else + printf("NONE"); + printf( " Op2 = "); + if (popnd2) + asm_output_flags(popnd2->usFlags); + else + printf("NONE"); + printf("\n"); + } +#endif + goto TYPE_SIZE_ERROR; + } + ptbRet.pptb2 = table2; + goto RETURN_IT; + } + case 3: + { + PTRNTAB3 *table3; + for (table3 = pop->ptb.pptb3; + table3->usOpcode != ASM_END; + table3++) + { + bMatch1 = asm_match_flags(opflags1, table3->usOp1); + bMatch2 = asm_match_flags(opflags2, table3->usOp2); + bMatch3 = asm_match_flags(opflags3, table3->usOp3); + if (bMatch1 && bMatch2 && bMatch3) + goto Lfound3; + if (asmstate.ucItype == ITopt) + { + switch (usNumops) + { + case 0: + if (!table3->usOp1) + goto Lfound3; + break; + case 1: + if (bMatch1 && !table3->usOp2) + goto Lfound3; + break; + case 2: + if (bMatch1 && bMatch2 && !table3->usOp3) + goto Lfound3; + break; + case 3: + break; + default: + goto PARAM_ERROR; + } + } + } + Lfound3: + if (table3->usOpcode == ASM_END) + { +#ifdef DEBUG + if (debuga) + { printf("\t%s\t", asm_opstr(pop)); + if (popnd1) + asm_output_popnd(popnd1); + if (popnd2) { + printf(","); + asm_output_popnd(popnd2); + } + if (popnd3) { + printf(","); + asm_output_popnd(popnd3); + } + printf("\n"); + + printf("OPCODE mismatch = "); + if (popnd1) + asm_output_flags(popnd1->usFlags); + else + printf("NONE"); + printf( " Op2 = "); + if (popnd2) + asm_output_flags(popnd2->usFlags); + else + printf("NONE"); + if (popnd3) + asm_output_flags(popnd3->usFlags); + printf("\n"); + } +#endif + goto TYPE_SIZE_ERROR; + } + ptbRet.pptb3 = table3; + goto RETURN_IT; + } + case 4: + { + PTRNTAB4 *table4; + for (table4 = pop->ptb.pptb4; + table4->usOpcode != ASM_END; + table4++) + { + bMatch1 = asm_match_flags(opflags1, table4->usOp1); + bMatch2 = asm_match_flags(opflags2, table4->usOp2); + bMatch3 = asm_match_flags(opflags3, table4->usOp3); + bMatch4 = asm_match_flags(opflags4, table4->usOp4); + if (bMatch1 && bMatch2 && bMatch3 && bMatch4) + goto Lfound4; + if (asmstate.ucItype == ITopt) + { + switch (usNumops) + { + case 0: + if (!table4->usOp1) + goto Lfound3; + break; + case 1: + if (bMatch1 && !table4->usOp2) + goto Lfound3; + break; + case 2: + if (bMatch1 && bMatch2 && !table4->usOp3) + goto Lfound3; + break; + case 3: + if (bMatch1 && bMatch2 && bMatch3 && !table4->usOp4) + goto Lfound3; + break; + case 4: + break; + default: + goto PARAM_ERROR; + } + } + } + Lfound4: + if (table4->usOpcode == ASM_END) + { +#ifdef DEBUG + if (debuga) + { printf("\t%s\t", asm_opstr(pop)); + if (popnd1) + asm_output_popnd(popnd1); + if (popnd2) { + printf(","); + asm_output_popnd(popnd2); + } + if (popnd3) { + printf(","); + asm_output_popnd(popnd3); + } + if (popnd4) { + printf(","); + asm_output_popnd(popnd4); + } + printf("\n"); + + printf("OPCODE mismatch = "); + if (popnd1) + asm_output_flags(popnd1->usFlags); + else + printf("NONE"); + printf( " Op2 = "); + if (popnd2) + asm_output_flags(popnd2->usFlags); + else + printf("NONE"); + printf( " Op3 = "); + if (popnd3) + asm_output_flags(popnd3->usFlags); + else + printf("NONE"); + printf( " Op4 = "); + if (popnd4) + asm_output_flags(popnd4->usFlags); + else + printf("NONE"); + printf("\n"); + } +#endif + goto TYPE_SIZE_ERROR; + } + ptbRet.pptb4 = table4; + goto RETURN_IT; + } + } +RETURN_IT: + if (bRetry && !bFake) + { + asmerr(EM_bad_op, asm_opstr(pop)); + } + return ptbRet; +} + +/******************************* + */ + +STATIC opflag_t asm_determine_float_flags(OPND *popnd) +{ + //printf("asm_determine_float_flags()\n"); + + opflag_t us, usFloat; + + // Insure that if it is a register, that it is not a normal processor + // register. + + if (popnd->base && + !popnd->s && !popnd->disp && !popnd->real + && !(popnd->base->ty & (_r8 | _r16 | _r32))) + { + return popnd->base->ty; + } + if (popnd->pregDisp1 && !popnd->base) + { + us = asm_float_type_size(popnd->ptype, &usFloat); + //printf("us = x%x, usFloat = x%x\n", us, usFloat); + if (popnd->pregDisp1->ty & (_r32 | _r64)) + return(CONSTRUCT_FLAGS(us, _m, _addr32, usFloat)); + else + if (popnd->pregDisp1->ty & _r16) + return(CONSTRUCT_FLAGS(us, _m, _addr16, usFloat)); + } + else if (popnd->s != 0) + { + us = asm_float_type_size(popnd->ptype, &usFloat); + return CONSTRUCT_FLAGS(us, _m, _normal, usFloat); + } + + if (popnd->segreg) + { + us = asm_float_type_size(popnd->ptype, &usFloat); + if (I16) + return(CONSTRUCT_FLAGS(us, _m, _addr16, usFloat)); + else + return(CONSTRUCT_FLAGS(us, _m, _addr32, usFloat)); + } + +#if 0 + if (popnd->real) + { + switch (popnd->ptype->ty) + { + case Tfloat32: + popnd->s = fconst(popnd->real); + return(CONSTRUCT_FLAGS(_32, _m, _normal, 0)); + + case Tfloat64: + popnd->s = dconst(popnd->real); + return(CONSTRUCT_FLAGS(0, _m, _normal, _f64)); + + case Tfloat80: + popnd->s = ldconst(popnd->real); + return(CONSTRUCT_FLAGS(0, _m, _normal, _f80)); + } + } +#endif + + asmerr(EM_bad_float_op); // unknown operand for floating point instruction + return 0; +} + +/******************************* + */ + +STATIC opflag_t asm_determine_operand_flags(OPND *popnd) +{ + Dsymbol *ps; + int ty; + opflag_t us; + opflag_t sz; + ASM_OPERAND_TYPE opty; + ASM_MODIFIERS amod; + + // If specified 'offset' or 'segment' but no symbol + if ((popnd->bOffset || popnd->bSeg) && !popnd->s) + asmerr(EM_bad_addr_mode); // illegal addressing mode + + if (asmstate.ucItype == ITfloat) + return asm_determine_float_flags(popnd); + + // If just a register + if (popnd->base && !popnd->s && !popnd->disp && !popnd->real) + return popnd->base->ty; +#if DEBUG + if (debuga) + printf("popnd->base = %s\n, popnd->pregDisp1 = %p\n", popnd->base ? popnd->base->regstr : "NONE", popnd->pregDisp1); +#endif + ps = popnd->s; + Declaration *ds = ps ? ps->isDeclaration() : NULL; + if (ds && ds->storage_class & STClazy) + sz = _anysize; + else + sz = asm_type_size((ds && ds->storage_class & (STCout | STCref)) ? popnd->ptype->pointerTo() : popnd->ptype); + if (popnd->pregDisp1 && !popnd->base) + { + if (ps && ps->isLabel() && sz == _anysize) + sz = I16 ? _16 : _32; + return (popnd->pregDisp1->ty & (_r32 | _r64)) + ? CONSTRUCT_FLAGS(sz, _m, _addr32, 0) + : CONSTRUCT_FLAGS(sz, _m, _addr16, 0); + } + else if (ps) + { + if (popnd->bOffset || popnd->bSeg || ps == asmstate.psLocalsize) + return I16 + ? CONSTRUCT_FLAGS(_16, _imm, _normal, 0) + : CONSTRUCT_FLAGS(_32, _imm, _normal, 0); + + if (ps->isLabel()) + { + switch (popnd->ajt) + { + case ASM_JUMPTYPE_UNSPECIFIED: + if (ps == asmstate.psDollar) + { + if (popnd->disp >= CHAR_MIN && + popnd->disp <= CHAR_MAX) + us = CONSTRUCT_FLAGS(_8, _rel, _flbl,0); + else + if (popnd->disp >= SHRT_MIN && + popnd->disp <= SHRT_MAX && !I64) + us = CONSTRUCT_FLAGS(_16, _rel, _flbl,0); + else + us = CONSTRUCT_FLAGS(_32, _rel, _flbl,0); + } + else if (asmstate.ucItype != ITjump) + { if (sz == _8) + { us = CONSTRUCT_FLAGS(_8,_rel,_flbl,0); + break; + } + goto case_near; + } + else + us = I16 + ? CONSTRUCT_FLAGS(_8|_16, _rel, _flbl,0) + : CONSTRUCT_FLAGS(_8|_32, _rel, _flbl,0); + break; + + case ASM_JUMPTYPE_NEAR: + case_near: + us = I16 + ? CONSTRUCT_FLAGS(_16, _rel, _flbl, 0) + : CONSTRUCT_FLAGS(_32, _rel, _flbl, 0); + break; + case ASM_JUMPTYPE_SHORT: + us = CONSTRUCT_FLAGS(_8, _rel, _flbl, 0); + break; + case ASM_JUMPTYPE_FAR: + us = I16 + ? CONSTRUCT_FLAGS(_32, _rel, _flbl, 0) + : CONSTRUCT_FLAGS(_48, _rel, _flbl, 0); + break; + default: + assert(0); + } + return us; + } + if (!popnd->ptype) + return CONSTRUCT_FLAGS(sz, _m, _normal, 0); + ty = popnd->ptype->ty; + if (ty == Tpointer && popnd->ptype->nextOf()->ty == Tfunction && + !ps->isVarDeclaration()) + { +#if 1 + return CONSTRUCT_FLAGS(_32, _m, _fn16, 0); +#else + ty = popnd->ptype->Tnext->Tty; + if (tyfarfunc(tybasic(ty))) { + return I32 + ? CONSTRUCT_FLAGS(_48, _mnoi, _fn32, 0) + : CONSTRUCT_FLAGS(_32, _mnoi, _fn32, 0); + } + else { + return I32 + ? CONSTRUCT_FLAGS(_32, _m, _fn16, 0) + : CONSTRUCT_FLAGS(_16, _m, _fn16, 0); + } +#endif + } + else if (ty == Tfunction) + { +#if 1 + return CONSTRUCT_FLAGS(_32, _rel, _fn16, 0); +#else + if (tyfarfunc(tybasic(ty))) + return I32 + ? CONSTRUCT_FLAGS(_48, _p, _fn32, 0) + : CONSTRUCT_FLAGS(_32, _p, _fn32, 0); + else + return I32 + ? CONSTRUCT_FLAGS(_32, _rel, _fn16, 0) + : CONSTRUCT_FLAGS(_16, _rel, _fn16, 0); +#endif + } + else if (asmstate.ucItype == ITjump) + { amod = _normal; + goto L1; + } + else + return CONSTRUCT_FLAGS(sz, _m, _normal, 0); + } + if (popnd->segreg /*|| popnd->bPtr*/) + { + amod = I16 ? _addr16 : _addr32; + if (asmstate.ucItype == ITjump) + { + L1: + opty = _m; + if (I16) + { if (sz == _32) + opty = _mnoi; + } + else + { if (sz == _48) + opty = _mnoi; + } + us = CONSTRUCT_FLAGS(sz,opty,amod,0); + } + else + us = CONSTRUCT_FLAGS(sz, +// _rel, amod, 0); + _m, amod, 0); + } + + else if (popnd->ptype) + us = CONSTRUCT_FLAGS(sz, _imm, _normal, 0); + + else if (popnd->disp >= CHAR_MIN && popnd->disp <= UCHAR_MAX) + us = CONSTRUCT_FLAGS( _8 | _16 | _32 | _64, _imm, _normal, 0); + else if (popnd->disp >= SHRT_MIN && popnd->disp <= USHRT_MAX) + us = CONSTRUCT_FLAGS( _16 | _32 | _64, _imm, _normal, 0); + else if (popnd->disp >= INT_MIN && popnd->disp <= UINT_MAX) + us = CONSTRUCT_FLAGS( _32 | _64, _imm, _normal, 0); + else + us = CONSTRUCT_FLAGS( _64, _imm, _normal, 0); + return us; +} + +/****************************** + * Convert assembly instruction into a code, and append + * it to the code generated for this block. + */ + +STATIC code *asm_emit(Loc loc, + unsigned usNumops, PTRNTAB ptb, + OP *pop, + OPND *popnd1, OPND *popnd2, OPND *popnd3, OPND *popnd4) +{ +#ifdef DEBUG + unsigned char auchOpcode[16]; + unsigned usIdx = 0; + #define emit(op) (auchOpcode[usIdx++] = op) +#else + #define emit(op) ((void)(op)) +#endif + Identifier *id; +// unsigned us; + unsigned char *puc; + unsigned usDefaultseg; + code *pc = NULL; + OPND *popndTmp; + ASM_OPERAND_TYPE aoptyTmp; + unsigned uSizemaskTmp; + REG *pregSegment; + code *pcPrefix = NULL; + //ASM_OPERAND_TYPE aopty1 = _reg , aopty2 = 0, aopty3 = 0; + ASM_MODIFIERS amod1 = _normal, amod2 = _normal; + unsigned uSizemaskTable1 =0, uSizemaskTable2 =0, + uSizemaskTable3 =0; + ASM_OPERAND_TYPE aoptyTable1 = _reg, aoptyTable2 = _reg, aoptyTable3 = _reg; + ASM_MODIFIERS amodTable1 = _normal, + amodTable2 = _normal; + unsigned uRegmaskTable1 = 0, uRegmaskTable2 =0; + + pc = code_calloc(); + pc->Iflags |= CFpsw; // assume we want to keep the flags + if (popnd1) + { + //aopty1 = ASM_GET_aopty(popnd1->usFlags); + amod1 = ASM_GET_amod(popnd1->usFlags); + + uSizemaskTable1 = ASM_GET_uSizemask(ptb.pptb1->usOp1); + aoptyTable1 = ASM_GET_aopty(ptb.pptb1->usOp1); + amodTable1 = ASM_GET_amod(ptb.pptb1->usOp1); + uRegmaskTable1 = ASM_GET_uRegmask(ptb.pptb1->usOp1); + + } + if (popnd2) + { +#if 0 + printf("\nasm_emit:\nop: "); + asm_output_flags(popnd2->usFlags); + printf("\ntb: "); + asm_output_flags(ptb.pptb2->usOp2); + printf("\n"); +#endif + //aopty2 = ASM_GET_aopty(popnd2->usFlags); + amod2 = ASM_GET_amod(popnd2->usFlags); + + uSizemaskTable2 = ASM_GET_uSizemask(ptb.pptb2->usOp2); + aoptyTable2 = ASM_GET_aopty(ptb.pptb2->usOp2); + amodTable2 = ASM_GET_amod(ptb.pptb2->usOp2); + uRegmaskTable2 = ASM_GET_uRegmask(ptb.pptb2->usOp2); + } + if (popnd3) + { + //aopty3 = ASM_GET_aopty(popnd3->usFlags); + + uSizemaskTable3 = ASM_GET_uSizemask(ptb.pptb3->usOp3); + aoptyTable3 = ASM_GET_aopty(ptb.pptb3->usOp3); + } + + asmstate.statement->regs |= asm_modify_regs(ptb, popnd1, popnd2); + + if (I16 && ptb.pptb0->usFlags & _I386) + { + switch (usNumops) + { + case 0: + break; + case 1: + if (popnd1 && popnd1->s) + { +L386_WARNING: + id = popnd1->s->ident; +L386_WARNING2: + if (config.target_cpu < TARGET_80386) + { // Reference to %s caused a 386 instruction to be generated + //warerr(WM_386_op, id->toChars()); + } + } + break; + case 2: + case 3: // The third operand is always an _imm + if (popnd1 && popnd1->s) + goto L386_WARNING; + if (popnd2 && popnd2->s) + { + id = popnd2->s->ident; + goto L386_WARNING2; + } + break; + } + } + + if (ptb.pptb0->usFlags & _64_bit && !I64) + error(asmstate.loc, "use -m64 to compile 64 bit instructions"); + + if (I64 && (ptb.pptb0->usFlags & _64_bit)) + { + emit(REX | REX_W); + pc->Irex |= REX_W; + } + + switch (usNumops) + { + case 0: + if (((I32 | I64) && (ptb.pptb0->usFlags & _16_bit)) || + (I16 && (ptb.pptb0->usFlags & _32_bit))) + { + emit(0x66); + pc->Iflags |= CFopsize; + } + break; + + // vex adds 4 operand instructions, but already provides + // encoded operation size + case 4: + break; + + // 3 and 2 are the same because the third operand is always + // an immediate and does not affect operation size + case 3: + case 2: + if ((I32 && + (amod2 == _addr16 || + (uSizemaskTable2 & _16 && aoptyTable2 == _rel) || + (uSizemaskTable2 & _32 && aoptyTable2 == _mnoi) || + (ptb.pptb2->usFlags & _16_bit_addr) + ) + ) || + (I16 && + (amod2 == _addr32 || + (uSizemaskTable2 & _32 && aoptyTable2 == _rel) || + (uSizemaskTable2 & _48 && aoptyTable2 == _mnoi) || + (ptb.pptb2->usFlags & _32_bit_addr))) + ) + { + emit(0x67); + pc->Iflags |= CFaddrsize; + if (I32) + amod2 = _addr16; + else + amod2 = _addr32; + popnd2->usFlags &= ~CONSTRUCT_FLAGS(0,0,7,0); + popnd2->usFlags |= CONSTRUCT_FLAGS(0,0,amod2,0); + } + + + /* Fall through, operand 1 controls the opsize, but the + address size can be in either operand 1 or operand 2, + hence the extra checking the flags tested for SHOULD + be mutex on operand 1 and operand 2 because there is + only one MOD R/M byte + */ + + case 1: + if ((I32 && + (amod1 == _addr16 || + (uSizemaskTable1 & _16 && aoptyTable1 == _rel) || + (uSizemaskTable1 & _32 && aoptyTable1 == _mnoi) || + (ptb.pptb1->usFlags & _16_bit_addr))) || + (I16 && + (amod1 == _addr32 || + (uSizemaskTable1 & _32 && aoptyTable1 == _rel) || + (uSizemaskTable1 & _48 && aoptyTable1 == _mnoi) || + (ptb.pptb1->usFlags & _32_bit_addr)))) + { + emit(0x67); // address size prefix + pc->Iflags |= CFaddrsize; + if (I32) + amod1 = _addr16; + else + amod1 = _addr32; + popnd1->usFlags &= ~CONSTRUCT_FLAGS(0,0,7,0); + popnd1->usFlags |= CONSTRUCT_FLAGS(0,0,amod1,0); + } + + // If the size of the operand is unknown, assume that it is + // the default size + if (((I64 || I32) && (ptb.pptb0->usFlags & _16_bit)) || + (I16 && (ptb.pptb0->usFlags & _32_bit))) + { + //if (asmstate.ucItype != ITjump) + { emit(0x66); + pc->Iflags |= CFopsize; + } + } + if (((pregSegment = (popndTmp = popnd1)->segreg) != NULL) || + ((popndTmp = popnd2) != NULL && + (pregSegment = popndTmp->segreg) != NULL) + ) + { + if ((popndTmp->pregDisp1 && + popndTmp->pregDisp1->val == _BP) || + popndTmp->pregDisp2 && + popndTmp->pregDisp2->val == _BP) + usDefaultseg = _SS; + else + usDefaultseg = _DS; + if (pregSegment->val != usDefaultseg) + switch (pregSegment->val) { + case _CS: + emit(0x2e); + pc->Iflags |= CFcs; + break; + case _SS: + emit(0x36); + pc->Iflags |= CFss; + break; + case _DS: + emit(0x3e); + pc->Iflags |= CFds; + break; + case _ES: + emit(0x26); + pc->Iflags |= CFes; + break; + case _FS: + emit(0x64); + pc->Iflags |= CFfs; + break; + case _GS: + emit(0x65); + pc->Iflags |= CFgs; + break; + default: + assert(0); + } + } + break; + } + unsigned usOpcode = ptb.pptb0->usOpcode; + + pc->Iop = usOpcode; + if (pc->Ivex.pfx == 0xC4) + { +#ifdef DEBUG + unsigned oIdx = usIdx; +#endif + // vvvv + switch (pc->Ivex.vvvv) + { + case VEX_NOO: + pc->Ivex.vvvv = 0xF; // not used + + if ((aoptyTable1 == _m || aoptyTable1 == _rm) && + aoptyTable2 == _reg) + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd1, popnd2); + else + if (usNumops == 2 || usNumops == 3 && aoptyTable3 == _imm) + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd2, popnd1); + else + assert(!usNumops); // no operands + + if (usNumops == 3) + { + popndTmp = popnd3; + aoptyTmp = ASM_GET_aopty(ptb.pptb3->usOp3); + uSizemaskTmp = ASM_GET_uSizemask(ptb.pptb3->usOp3); + assert(aoptyTmp == _imm); + } + break; + + case VEX_NDD: + pc->Ivex.vvvv = ~popnd1->base->val; + + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd2, NULL); + + if (usNumops == 3) + { + popndTmp = popnd3; + aoptyTmp = ASM_GET_aopty(ptb.pptb3->usOp3); + uSizemaskTmp = ASM_GET_uSizemask(ptb.pptb3->usOp3); + assert(aoptyTmp == _imm); + } + break; + + case VEX_DDS: + assert(usNumops == 3); + pc->Ivex.vvvv = ~popnd2->base->val; + + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd3, popnd1); + break; + + case VEX_NDS: + pc->Ivex.vvvv = ~popnd2->base->val; + + if (aoptyTable1 == _m || aoptyTable1 == _rm) + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd1, popnd3); + else + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd3, popnd1); + + if (usNumops == 4) + { + popndTmp = popnd4; + aoptyTmp = ASM_GET_aopty(ptb.pptb4->usOp4); + uSizemaskTmp = ASM_GET_uSizemask(ptb.pptb4->usOp4); + assert(aoptyTmp == _imm); + } + break; + + default: + assert(0); + } + + // REX + // REX_W is solely taken from WO/W1/WIG + // pc->Ivex.w = !!(pc->Irex & REX_W); + pc->Ivex.b = !(pc->Irex & REX_B); + pc->Ivex.x = !(pc->Irex & REX_X); + pc->Ivex.r = !(pc->Irex & REX_R); + + /* Check if a 3-byte vex is needed. + */ + if (pc->Ivex.w || !pc->Ivex.x || !pc->Ivex.b || pc->Ivex.mmmm > 0x1) + { +#ifdef DEBUG + memmove(&auchOpcode[oIdx+3], &auchOpcode[oIdx], usIdx-oIdx); + usIdx = oIdx; +#endif + emit(0xC4); + emit(VEX3_B1(pc->Ivex)); + emit(VEX3_B2(pc->Ivex)); + pc->Iflags |= CFvex3; + } + else + { +#ifdef DEBUG + memmove(&auchOpcode[oIdx+2], &auchOpcode[oIdx], usIdx-oIdx); + usIdx = oIdx; +#endif + emit(0xC5); + emit(VEX2_B1(pc->Ivex)); + } + pc->Iflags |= CFvex; + emit(pc->Ivex.op); + if (popndTmp) + goto L1; + goto L2; + } + else if ((usOpcode & 0xFFFD00) == 0x0F3800) // SSSE3, SSE4 + { emit(0xFF); + emit(0xFD); + emit(0x00); + goto L3; + } + + switch (usOpcode & 0xFF0000) + { + case 0: + break; + + case 0x660000: + usOpcode &= 0xFFFF; + goto L3; + + case 0xF20000: // REPNE + case 0xF30000: // REP/REPE + // BUG: What if there's an address size prefix or segment + // override prefix? Must the REP be adjacent to the rest + // of the opcode? + usOpcode &= 0xFFFF; + goto L3; + + case 0x0F0000: // an AMD instruction + puc = ((unsigned char *) &usOpcode); + if (puc[1] != 0x0F) // if not AMD instruction 0x0F0F + goto L4; + emit(puc[2]); + emit(puc[1]); + emit(puc[0]); + pc->Iop >>= 8; + pc->IEVint2 = puc[0]; + pc->IFL2 = FLconst; + goto L3; + + default: + puc = ((unsigned char *) &usOpcode); + L4: + emit(puc[2]); + emit(puc[1]); + emit(puc[0]); + pc->Iop >>= 8; + pc->Irm = puc[0]; + goto L3; + } + if (usOpcode & 0xff00) + { + puc = ((unsigned char *) &(usOpcode)); + emit(puc[1]); + emit(puc[0]); + pc->Iop = puc[1]; + if (pc->Iop == 0x0f) + pc->Iop = 0x0F00 | puc[0]; + else + { + if (usOpcode == 0xDFE0) // FSTSW AX + { pc->Irm = puc[0]; + goto L2; + } + if (asmstate.ucItype == ITfloat) + pc->Irm = puc[0]; + else + { pc->IEVint2 = puc[0]; + pc->IFL2 = FLconst; + } + } + } + else + { + emit(usOpcode); + } + L3: ; + + // If CALL, Jxx or LOOPx to a symbolic location + if (/*asmstate.ucItype == ITjump &&*/ + popnd1 && popnd1->s && popnd1->s->isLabel()) + { Dsymbol *s; + + s = popnd1->s; + if (s == asmstate.psDollar) + { + pc->IFL2 = FLconst; + if (uSizemaskTable1 & (_8 | _16)) + pc->IEVint2 = popnd1->disp; + else if (uSizemaskTable1 & _32) + pc->IEVpointer2 = (targ_size_t) popnd1->disp; + } + else + { LabelDsymbol *label; + + label = s->isLabel(); + if (label) + { if ((pc->Iop & ~0x0F) == 0x70) + pc->Iflags |= CFjmp16; + if (usNumops == 1) + { pc->IFL2 = FLblock; + pc->IEVlsym2 = label; + } + else + { pc->IFL1 = FLblock; + pc->IEVlsym1 = label; + } + } + } + } + + switch (usNumops) + { + case 0: + break; + case 1: + if (((aoptyTable1 == _reg || aoptyTable1 == _float) && + amodTable1 == _normal && (uRegmaskTable1 & _rplus_r))) + { + unsigned reg = popnd1->base->val; + if (reg & 8) + { reg &= 7; + pc->Irex |= REX_B; + assert(I64); + } + if (asmstate.ucItype == ITfloat) + pc->Irm += reg; + else + pc->Iop += reg; +#ifdef DEBUG + auchOpcode[usIdx-1] += reg; +#endif + } + else + { asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd1, NULL); + } + popndTmp = popnd1; + aoptyTmp = aoptyTable1; + uSizemaskTmp = uSizemaskTable1; +L1: + if (aoptyTmp == _imm) + { + Declaration *d = popndTmp->s ? popndTmp->s->isDeclaration() + : NULL; + if (popndTmp->bSeg) + { + + if (!(d && d->isDataseg())) + asmerr(EM_bad_addr_mode); // illegal addressing mode + } + switch (uSizemaskTmp) + { + case _8: + case _16: + case _32: + case _64: + if (popndTmp->s == asmstate.psLocalsize) + { + pc->IFL2 = FLlocalsize; + pc->IEVdsym2 = NULL; + pc->Iflags |= CFoff; + pc->IEVoffset2 = popndTmp->disp; + } + else if (d) + { +#if 0 + if ((pc->IFL2 = d->Sfl) == 0) +#endif + pc->IFL2 = FLdsymbol; + pc->Iflags &= ~(CFseg | CFoff); + if (popndTmp->bSeg) + pc->Iflags |= CFseg; + else + pc->Iflags |= CFoff; + pc->IEVoffset2 = popndTmp->disp; + pc->IEVdsym2 = d; + } + else + { + pc->IEVllong2 = popndTmp->disp; + pc->IFL2 = FLconst; + } + break; + } + } + + break; + case 2: +// +// If there are two immediate operands then +// + if (aoptyTable1 == _imm && + aoptyTable2 == _imm) + { + pc->IEVint1 = popnd1->disp; + pc->IFL1 = FLconst; + pc->IEVint2 = popnd2->disp; + pc->IFL2 = FLconst; + break; + } + if (aoptyTable2 == _m || + aoptyTable2 == _rel || + // If not MMX register (_mm) or XMM register (_xmm) + (amodTable1 == _rspecial && !(uRegmaskTable1 & (0x08 | 0x10)) && !uSizemaskTable1) || + aoptyTable2 == _rm || + (popnd1->usFlags == _r32 && popnd2->usFlags == _xmm) || + (popnd1->usFlags == _r32 && popnd2->usFlags == _mm)) + { +#if 0 +printf("test4 %d,%d,%d,%d\n", +(aoptyTable2 == _m), +(aoptyTable2 == _rel), +(amodTable1 == _rspecial && !(uRegmaskTable1 & (0x08 | 0x10))), +(aoptyTable2 == _rm) +); +printf("usOpcode = %x\n", usOpcode); +#endif + if (ptb.pptb0->usOpcode == 0x0F7E || // MOVD _rm32,_mm + ptb.pptb0->usOpcode == 0x660F7E // MOVD _rm32,_xmm + ) + { + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd1, popnd2); + } + else + { + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd2, popnd1); + } + popndTmp = popnd1; + aoptyTmp = aoptyTable1; + uSizemaskTmp = uSizemaskTable1; + } + else + { + if (((aoptyTable1 == _reg || aoptyTable1 == _float) && + amodTable1 == _normal && + (uRegmaskTable1 & _rplus_r))) + { + unsigned reg = popnd1->base->val; + if (reg & 8) + { reg &= 7; + pc->Irex |= REX_B; + assert(I64); + } + if (asmstate.ucItype == ITfloat) + pc->Irm += reg; + else + pc->Iop += reg; +#ifdef DEBUG + auchOpcode[usIdx-1] += reg; +#endif + } + else + if (((aoptyTable2 == _reg || aoptyTable2 == _float) && + amodTable2 == _normal && + (uRegmaskTable2 & _rplus_r))) + { + unsigned reg = popnd2->base->val; + if (reg & 8) + { reg &= 7; + pc->Irex |= REX_B; + assert(I64); + } + if (asmstate.ucItype == ITfloat) + pc->Irm += reg; + else + pc->Iop += reg; +#ifdef DEBUG + auchOpcode[usIdx-1] += reg; +#endif + } + else if (ptb.pptb0->usOpcode == 0xF30FD6 || + ptb.pptb0->usOpcode == 0x0F12 || + ptb.pptb0->usOpcode == 0x0F16 || + ptb.pptb0->usOpcode == 0x660F50 || + ptb.pptb0->usOpcode == 0x0F50 || + ptb.pptb0->usOpcode == 0x660FD7 || + ptb.pptb0->usOpcode == MOVDQ2Q || + ptb.pptb0->usOpcode == 0x0FD7) + { + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd2, popnd1); + } + else + { + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd1, popnd2); + + } + if (aoptyTable1 == _imm) + { + popndTmp = popnd1; + aoptyTmp = aoptyTable1; + uSizemaskTmp = uSizemaskTable1; + } + else + { + popndTmp = popnd2; + aoptyTmp = aoptyTable2; + uSizemaskTmp = uSizemaskTable2; + } + } + goto L1; + + case 3: + if (aoptyTable2 == _m || aoptyTable2 == _rm || + usOpcode == 0x0FC5 || // pextrw _r32, _mm, _imm8 + usOpcode == 0x660FC5 || // pextrw _r32, _xmm, _imm8 + usOpcode == 0x660F3A20 || // pinsrb _xmm, _r32/m8, _imm8 + usOpcode == 0x660F3A22 // pinsrd _xmm, _rm32, _imm8 + ) + { + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd2, popnd1); + popndTmp = popnd3; + aoptyTmp = aoptyTable3; + uSizemaskTmp = uSizemaskTable3; + } + else { + + if (((aoptyTable1 == _reg || aoptyTable1 == _float) && + amodTable1 == _normal && + (uRegmaskTable1 &_rplus_r))) + { + unsigned reg = popnd1->base->val; + if (reg & 8) + { reg &= 7; + pc->Irex |= REX_B; + assert(I64); + } + if (asmstate.ucItype == ITfloat) + pc->Irm += reg; + else + pc->Iop += reg; +#ifdef DEBUG + auchOpcode[usIdx-1] += reg; +#endif + } + else + if (((aoptyTable2 == _reg || aoptyTable2 == _float) && + amodTable2 == _normal && + (uRegmaskTable2 &_rplus_r))) + { + unsigned reg = popnd1->base->val; + if (reg & 8) + { reg &= 7; + pc->Irex |= REX_B; + assert(I64); + } + if (asmstate.ucItype == ITfloat) + pc->Irm += reg; + else + pc->Iop += reg; +#ifdef DEBUG + auchOpcode[usIdx-1] += reg; +#endif + } + else + asm_make_modrm_byte( +#ifdef DEBUG + auchOpcode, &usIdx, +#endif + pc, + ptb.pptb1->usFlags, + popnd1, popnd2); + + popndTmp = popnd3; + aoptyTmp = aoptyTable3; + uSizemaskTmp = uSizemaskTable3; + + } + goto L1; + } +L2: + + if ((pc->Iop & ~7) == 0xD8 && + ADDFWAIT() && + !(ptb.pptb0->usFlags & _nfwait)) + pc->Iflags |= CFwait; + else if ((ptb.pptb0->usFlags & _fwait) && + config.target_cpu >= TARGET_80386) + pc->Iflags |= CFwait; + +#ifdef DEBUG + if (debuga) + { unsigned u; + + for (u = 0; u < usIdx; u++) + printf(" %02X", auchOpcode[u]); + + printf("\t%s\t", asm_opstr(pop)); + if (popnd1) + asm_output_popnd(popnd1); + if (popnd2) { + printf(","); + asm_output_popnd(popnd2); + } + if (popnd3) { + printf(","); + asm_output_popnd(popnd3); + } + printf("\n"); + } +#endif + pc = cat(pcPrefix, pc); + pc = asm_genloc(loc, pc); + return pc; +} + +/******************************* + * Prepend line number to c. + */ + +code *asm_genloc(Loc loc, code *c) +{ + if (global.params.symdebug) + { code *pcLin; + Srcpos srcpos; + + memset(&srcpos, 0, sizeof(srcpos)); + srcpos.Slinnum = loc.linnum; + srcpos.Sfilename = (char *)loc.filename; + pcLin = genlinnum(NULL, srcpos); + c = cat(pcLin, c); + } + return c; +} + + +/******************************* + */ + +STATIC void asmerr(int errnum, ...) +{ const char *format; + + const char *p = asmstate.loc.toChars(); + if (*p) + printf("%s: ", p); + + format = asmerrmsgs[errnum]; + va_list ap; + va_start(ap, errnum); + vprintf(format, ap); + va_end(ap); + + printf("\n"); + fflush(stdout); + longjmp(asmstate.env,1); +} + +/******************************* + */ + +STATIC void asmerr(const char *format, ...) +{ + const char *p = asmstate.loc.toChars(); + if (*p) + printf("%s: ", p); + + va_list ap; + va_start(ap, format); + vprintf(format, ap); + va_end(ap); + + printf("\n"); + fflush(stdout); + + longjmp(asmstate.env,1); +} + +/******************************* + */ + +STATIC opflag_t asm_float_type_size(Type *ptype, opflag_t *pusFloat) +{ + *pusFloat = 0; + + //printf("asm_float_type_size('%s')\n", ptype->toChars()); + if (ptype && ptype->isscalar()) + { + int sz = (int)ptype->size(); + if (sz == REALSIZE) + { *pusFloat = _f80; + return 0; + } + switch (sz) + { + case 2: + return _16; + case 4: + return _32; + case 8: + *pusFloat = _f64; + return 0; + case 10: + *pusFloat = _f80; + return 0; + default: + break; + } + } + *pusFloat = _fanysize; + return _anysize; +} + +/******************************* + */ + +STATIC int asm_isint(OPND *o) +{ + if (!o || o->base || o->s) + return 0; + //return o->disp != 0; + return 1; +} + +STATIC int asm_isNonZeroInt(OPND *o) +{ + if (!o || o->base || o->s) + return 0; + return o->disp != 0; +} + +/******************************* + */ + +STATIC int asm_is_fpreg(char *szReg) +{ +#if 1 + return(szReg[2] == '\0' && szReg[0] == 'S' && + szReg[1] == 'T'); +#else + return(szReg[2] == '\0' && (szReg[0] == 's' || szReg[0] == 'S') && + (szReg[1] == 't' || szReg[1] == 'T')); +#endif +} + +/******************************* + * Merge operands o1 and o2 into a single operand. + */ + +STATIC OPND *asm_merge_opnds(OPND *o1, OPND *o2) +{ +#ifdef DEBUG + const char *psz; +#endif +#ifdef DEBUG + if (debuga) + { printf("asm_merge_opnds(o1 = "); + if (o1) asm_output_popnd(o1); + printf(", o2 = "); + if (o2) asm_output_popnd(o2); + printf(")\n"); + } +#endif + if (!o1) + return o2; + if (!o2) + return o1; +#ifdef EXTRA_DEBUG + printf("Combining Operands: mult1 = %d, mult2 = %d", + o1->uchMultiplier, o2->uchMultiplier); +#endif + /* combine the OPND's disp field */ + if (o2->segreg) { + if (o1->segreg) { +#ifdef DEBUG + psz = "o1->segment && o2->segreg"; +#endif + goto ILLEGAL_ADDRESS_ERROR; + } + else + o1->segreg = o2->segreg; + } + + // combine the OPND's symbol field + if (o1->s && o2->s) + { +#ifdef DEBUG + psz = "o1->s && os->s"; +#endif +ILLEGAL_ADDRESS_ERROR: +#ifdef DEBUG + printf("Invalid addr because /%s/\n", psz); +#endif + + asmerr(EM_bad_addr_mode); // illegal addressing mode + } + else if (o2->s) + o1->s = o2->s; + else if (o1->s && o1->s->isTupleDeclaration()) + { TupleDeclaration *tup = o1->s->isTupleDeclaration(); + + size_t index = o2->disp; + if (index >= tup->objects->dim) + error(asmstate.loc, "tuple index %u exceeds length %u", index, tup->objects->dim); + else + { + Object *o = tup->objects->tdata()[index]; + if (o->dyncast() == DYNCAST_DSYMBOL) + { o1->s = (Dsymbol *)o; + return o1; + } + else if (o->dyncast() == DYNCAST_EXPRESSION) + { Expression *e = (Expression *)o; + if (e->op == TOKvar) + { o1->s = ((VarExp *)e)->var; + return o1; + } + else if (e->op == TOKfunction) + { o1->s = ((FuncExp *)e)->fd; + return o1; + } + } + error(asmstate.loc, "invalid asm operand %s", o1->s->toChars()); + } + } + + if (o1->disp && o2->disp) + o1->disp += o2->disp; + else if (o2->disp) + o1->disp = o2->disp; + + /* combine the OPND's base field */ + if (o1->base != NULL && o2->base != NULL) { +#ifdef DEBUG + psz = "o1->base != NULL && o2->base != NULL"; +#endif + goto ILLEGAL_ADDRESS_ERROR; + } + else if (o2->base) + o1->base = o2->base; + + /* Combine the displacement register fields */ + if (o2->pregDisp1) + { + if (o1->pregDisp2) + { +#ifdef DEBUG + psz = "o2->pregDisp1 && o1->pregDisp2"; +#endif + goto ILLEGAL_ADDRESS_ERROR; + } + else if (o1->pregDisp1) + { + if (o1->uchMultiplier || + (o2->pregDisp1->val == _ESP && + (o2->pregDisp1->ty & _r32) && + !o2->uchMultiplier)) + { + o1->pregDisp2 = o1->pregDisp1; + o1->pregDisp1 = o2->pregDisp1; + } + else + o1->pregDisp2 = o2->pregDisp1; + } + else + o1->pregDisp1 = o2->pregDisp1; + } + if (o2->pregDisp2) { + if (o1->pregDisp2) { +#ifdef DEBUG + psz = "o1->pregDisp2 && o2->pregDisp2"; +#endif + goto ILLEGAL_ADDRESS_ERROR; + } + else + o1->pregDisp2 = o2->pregDisp2; + } + if (o2->uchMultiplier) + { + if (o1->uchMultiplier) + { +#ifdef DEBUG + psz = "o1->uchMultiplier && o2->uchMultiplier"; +#endif + goto ILLEGAL_ADDRESS_ERROR; + } + else + o1->uchMultiplier = o2->uchMultiplier; + } + if (o2->ptype && !o1->ptype) + o1->ptype = o2->ptype; + if (o2->bOffset) + o1->bOffset = o2->bOffset; + if (o2->bSeg) + o1->bSeg = o2->bSeg; + + if (o2->ajt && !o1->ajt) + o1->ajt = o2->ajt; + + opnd_free(o2); +#ifdef EXTRA_DEBUG + printf("Result = %d\n", + o1->uchMultiplier); +#endif +#ifdef DEBUG + if (debuga) + { printf("Merged result = /"); + asm_output_popnd(o1); + printf("/\n"); + } +#endif + return o1; +} + +/*************************************** + */ + +STATIC void asm_merge_symbol(OPND *o1, Dsymbol *s) +{ + VarDeclaration *v; + EnumMember *em; + + //printf("asm_merge_symbol(s = %s %s)\n", s->kind(), s->toChars()); + s = s->toAlias(); + //printf("s = %s %s\n", s->kind(), s->toChars()); + if (s->isLabel()) + { + o1->s = s; + return; + } + + v = s->isVarDeclaration(); + if (v) + { + if (v->isParameter()) + asmstate.statement->refparam = TRUE; + + v->checkNestedReference(asmstate.sc, asmstate.loc); +#if 0 + if (!v->isDataseg() && v->parent != asmstate.sc->parent && v->parent) + { + asmerr(EM_uplevel, v->toChars()); + } +#endif + if (v->storage_class & STCfield) + { + o1->disp += v->offset; + goto L2; + } + if ((v->isConst() +#if DMDV2 + || v->isImmutable() || v->storage_class & STCmanifest +#endif + ) && !v->type->isfloating() && v->init) + { ExpInitializer *ei = v->init->isExpInitializer(); + + if (ei) + { + o1->disp = ei->exp->toInteger(); + return; + } + } + } + em = s->isEnumMember(); + if (em) + { + o1->disp = em->value->toInteger(); + return; + } + o1->s = s; // a C identifier +L2: + Declaration *d = s->isDeclaration(); + if (!d) + { + asmerr("%s %s is not a declaration", s->kind(), s->toChars()); + } + else if (d->getType()) + asmerr(EM_type_as_operand, d->getType()->toChars()); + else if (d->isTupleDeclaration()) + ; + else + o1->ptype = d->type->toBasetype(); +} + +/**************************** + * Fill in the modregrm and sib bytes of code. + */ + +STATIC void asm_make_modrm_byte( +#ifdef DEBUG + unsigned char *puchOpcode, unsigned *pusIdx, +#endif + code *pc, + unsigned usFlags, + OPND *popnd, OPND *popnd2) +{ + #undef modregrm + + typedef union { + unsigned char uchOpcode; + struct { + unsigned rm : 3; + unsigned reg : 3; + unsigned mod : 2; + } modregrm; + } MODRM_BYTE; // mrmb + + typedef union { + unsigned char uchOpcode; + struct { + unsigned base : 3; + unsigned index : 3; + unsigned ss : 2; + } sib; + } SIB_BYTE; + + + MODRM_BYTE mrmb = { 0 }; + SIB_BYTE sib = { 0 }; + char bSib = FALSE; + char bDisp = FALSE; +#ifdef DEBUG + unsigned char *puc; +#endif + char bModset = FALSE; + Dsymbol *s; + + unsigned uSizemask =0; + ASM_OPERAND_TYPE aopty; + ASM_MODIFIERS amod; + unsigned char bOffsetsym = FALSE; + +#if 0 + printf("asm_make_modrm_byte(usFlags = x%x)\n", usFlags); + printf("op1: "); + asm_output_flags(popnd->usFlags); + if (popnd2) + { printf(" op2: "); + asm_output_flags(popnd2->usFlags); + } + printf("\n"); +#endif + + uSizemask = ASM_GET_uSizemask(popnd->usFlags); + aopty = ASM_GET_aopty(popnd->usFlags); + amod = ASM_GET_amod(popnd->usFlags); + s = popnd->s; + if (s) + { + Declaration *d = s->isDeclaration(); + + if (amod == _fn16 && aopty == _rel && popnd2) + { aopty = _m; + goto L1; + } + + if (amod == _fn16 || amod == _fn32) + { + pc->Iflags |= CFoff; +#ifdef DEBUG + puchOpcode[(*pusIdx)++] = 0; + puchOpcode[(*pusIdx)++] = 0; +#endif + if (aopty == _m || aopty == _mnoi) + { + pc->IFL1 = FLdata; + pc->IEVdsym1 = d; + pc->IEVoffset1 = 0; + } + else + { + if (aopty == _p) + pc->Iflags |= CFseg; +#ifdef DEBUG + if (aopty == _p || aopty == _rel) + { puchOpcode[(*pusIdx)++] = 0; + puchOpcode[(*pusIdx)++] = 0; + } +#endif + pc->IFL2 = FLfunc; + pc->IEVdsym2 = d; + pc->IEVoffset2 = 0; + //return; + } + } + else + { + L1: + LabelDsymbol *label = s->isLabel(); + if (label) + { + if (s == asmstate.psDollar) + { + pc->IFL1 = FLconst; + if (uSizemask & (_8 | _16)) + pc->IEVint1 = popnd->disp; + else if (uSizemask & _32) + pc->IEVpointer1 = (targ_size_t) popnd->disp; + } + else + { pc->IFL1 = FLblockoff; + pc->IEVlsym1 = label; + } + } + else if (s == asmstate.psLocalsize) + { + pc->IFL1 = FLlocalsize; + pc->IEVdsym1 = NULL; + pc->Iflags |= CFoff; + pc->IEVoffset1 = popnd->disp; + } + else if (s->isFuncDeclaration()) + { + pc->IFL1 = FLfunc; + pc->IEVdsym1 = d; + pc->IEVoffset1 = popnd->disp; + } + else + { +#ifdef DEBUG + if (debuga) + printf("Setting up symbol %s\n", d->ident->toChars()); +#endif + pc->IFL1 = FLdsymbol; + pc->IEVdsym1 = d; + pc->Iflags |= CFoff; + pc->IEVoffset1 = popnd->disp; + } + } + } + mrmb.modregrm.reg = usFlags & NUM_MASK; + + if (s && (aopty == _m || aopty == _mnoi) && !s->isLabel()) + { + if (s == asmstate.psLocalsize) + { + DATA_REF: + mrmb.modregrm.rm = BPRM; + if (amod == _addr16 || amod == _addr32) + mrmb.modregrm.mod = 0x2; + else + mrmb.modregrm.mod = 0x0; + } + else + { + Declaration *d = s->isDeclaration(); + assert(d); + if (d->isDataseg() || d->isCodeseg()) + { + if ((I32 && amod == _addr16) || + (I16 && amod == _addr32)) + asmerr(EM_bad_addr_mode); // illegal addressing mode + goto DATA_REF; + } + mrmb.modregrm.rm = BPRM; + mrmb.modregrm.mod = 0x2; + } + } + + if (aopty == _reg || amod == _rspecial) { + mrmb.modregrm.mod = 0x3; + mrmb.modregrm.rm |= popnd->base->val; + if (popnd->base->val & NUM_MASKR) + pc->Irex |= REX_B; + } + else if (amod == _addr16 || (amod == _flbl && I16)) + { unsigned rm; + +#ifdef DEBUG + if (debuga) + printf("This is an ADDR16\n"); +#endif + if (!popnd->pregDisp1) + { rm = 0x6; + if (!s) + bDisp = TRUE; + } + else + { unsigned r1r2; + #define X(r1,r2) (((r1) * 16) + (r2)) + #define Y(r1) X(r1,9) + + + if (popnd->pregDisp2) + r1r2 = X(popnd->pregDisp1->val,popnd->pregDisp2->val); + else + r1r2 = Y(popnd->pregDisp1->val); + switch (r1r2) + { + case X(_BX,_SI): rm = 0; break; + case X(_BX,_DI): rm = 1; break; + case Y(_BX): rm = 7; break; + + case X(_BP,_SI): rm = 2; break; + case X(_BP,_DI): rm = 3; break; + case Y(_BP): rm = 6; bDisp = TRUE; break; + + case X(_SI,_BX): rm = 0; break; + case X(_SI,_BP): rm = 2; break; + case Y(_SI): rm = 4; break; + + case X(_DI,_BX): rm = 1; break; + case X(_DI,_BP): rm = 3; break; + case Y(_DI): rm = 5; break; + + default: + asmerr("bad 16 bit index address mode"); + } + #undef X + #undef Y + } + mrmb.modregrm.rm = rm; + +#ifdef DEBUG + if (debuga) + printf("This is an mod = %d, popnd->s =%p, popnd->disp = %lld\n", + mrmb.modregrm.mod, s, (long long)popnd->disp); +#endif + if (!s || (!mrmb.modregrm.mod && popnd->disp)) + { + if ((!popnd->disp && !bDisp) || + !popnd->pregDisp1) + mrmb.modregrm.mod = 0x0; + else + if (popnd->disp >= CHAR_MIN && + popnd->disp <= SCHAR_MAX) + mrmb.modregrm.mod = 0x1; + else + mrmb.modregrm.mod = 0X2; + } + else + bOffsetsym = TRUE; + + } + else if (amod == _addr32 || (amod == _flbl && I32)) + { +#ifdef DEBUG + if (debuga) + printf("This is an ADDR32\n"); +#endif + if (!popnd->pregDisp1) + mrmb.modregrm.rm = 0x5; + else if (popnd->pregDisp2 || + popnd->uchMultiplier || + popnd->pregDisp1->val == _ESP) + { + if (popnd->pregDisp2) + { if (popnd->pregDisp2->val == _ESP) + asmerr(EM_bad_addr_mode); // illegal addressing mode + } + else + { if (popnd->uchMultiplier && + popnd->pregDisp1->val ==_ESP) + asmerr(EM_bad_addr_mode); // illegal addressing mode + bDisp = TRUE; + } + + mrmb.modregrm.rm = 0x4; + bSib = TRUE; + if (bDisp) + { + if (!popnd->uchMultiplier && + popnd->pregDisp1->val==_ESP) + { + sib.sib.base = popnd->pregDisp1->val; + sib.sib.index = 0x4; + } + else + { +#ifdef DEBUG + if (debuga) + printf("Resetting the mod to 0\n"); +#endif + if (popnd->pregDisp2) + { + if (popnd->pregDisp2->val != _EBP) + asmerr(EM_bad_addr_mode); // illegal addressing mode + } + else + { mrmb.modregrm.mod = 0x0; + bModset = TRUE; + } + + sib.sib.base = 0x5; + sib.sib.index = popnd->pregDisp1->val; + } + } + else + { + sib.sib.base = popnd->pregDisp1->val; + if (popnd->pregDisp1->val & NUM_MASKR) + pc->Irex |= REX_B; + // + // This is to handle the special case + // of using the EBP (or R13) register and no + // displacement. You must put in an + // 8 byte displacement in order to + // get the correct opcodes. + // + if ((popnd->pregDisp1->val == _EBP || + popnd->pregDisp1->val == _R13) && + (!popnd->disp && !s)) + { +#ifdef DEBUG + if (debuga) + printf("Setting the mod to 1 in the _EBP case\n"); +#endif + mrmb.modregrm.mod = 0x1; + bDisp = TRUE; // Need a + // displacement + bModset = TRUE; + } + + sib.sib.index = popnd->pregDisp2->val; + if (popnd->pregDisp2->val & NUM_MASKR) + pc->Irex |= REX_X; + + } + switch (popnd->uchMultiplier) + { + case 0: sib.sib.ss = 0; break; + case 1: sib.sib.ss = 0; break; + case 2: sib.sib.ss = 1; break; + case 4: sib.sib.ss = 2; break; + case 8: sib.sib.ss = 3; break; + + default: + asmerr(EM_bad_addr_mode); // illegal addressing mode + break; + } + } + else + { unsigned rm; + + if (popnd->uchMultiplier) + asmerr(EM_bad_addr_mode); // illegal addressing mode + switch (popnd->pregDisp1->val & NUM_MASK) + { + case _EAX: rm = 0; break; + case _ECX: rm = 1; break; + case _EDX: rm = 2; break; + case _EBX: rm = 3; break; + case _ESI: rm = 6; break; + case _EDI: rm = 7; break; + + case _EBP: + if (!popnd->disp && !s) + { + mrmb.modregrm.mod = 0x1; + bDisp = TRUE; // Need a displacement + bModset = TRUE; + } + rm = 5; + break; + + default: + asmerr(EM_bad_addr_mode); // illegal addressing mode + rm = 0; // no uninitialized data + break; + } + if (popnd->pregDisp1->val & NUM_MASKR) + pc->Irex |= REX_B; + mrmb.modregrm.rm = rm; + } + + if (!bModset && (!s || + (!mrmb.modregrm.mod && popnd->disp))) + { + if ((!popnd->disp && !mrmb.modregrm.mod) || + (!popnd->pregDisp1 && !popnd->pregDisp2)) + { mrmb.modregrm.mod = 0x0; + bDisp = TRUE; + } + else if (popnd->disp >= CHAR_MIN && + popnd->disp <= SCHAR_MAX) + mrmb.modregrm.mod = 0x1; + else + mrmb.modregrm.mod = 0x2; + } + else + bOffsetsym = TRUE; + } + if (popnd2 && !mrmb.modregrm.reg && + asmstate.ucItype != ITshift && + (ASM_GET_aopty(popnd2->usFlags) == _reg || + ASM_GET_amod(popnd2->usFlags) == _rseg || + ASM_GET_amod(popnd2->usFlags) == _rspecial)) + { + mrmb.modregrm.reg = popnd2->base->val; + if (popnd2->base->val & NUM_MASKR) + pc->Irex |= REX_R; + } +#ifdef DEBUG + puchOpcode[ (*pusIdx)++ ] = mrmb.uchOpcode; +#endif + pc->Irm = mrmb.uchOpcode; + //printf("Irm = %02x\n", pc->Irm); + if (bSib) + { +#ifdef DEBUG + puchOpcode[ (*pusIdx)++ ] = sib.uchOpcode; +#endif + pc->Isib= sib.uchOpcode; + } + if ((!s || (popnd->pregDisp1 && !bOffsetsym)) && + aopty != _imm && + (popnd->disp || bDisp)) + { + if (popnd->usFlags & _a16) + { +#ifdef DEBUG + puc = ((unsigned char *) &(popnd->disp)); + puchOpcode[(*pusIdx)++] = puc[1]; + puchOpcode[(*pusIdx)++] = puc[0]; +#endif + if (usFlags & (_modrm | NUM_MASK)) { +#ifdef DEBUG + if (debuga) + printf("Setting up value %lld\n", (long long)popnd->disp); +#endif + pc->IEVint1 = popnd->disp; + pc->IFL1 = FLconst; + } + else { + pc->IEVint2 = popnd->disp; + pc->IFL2 = FLconst; + } + + } + else + { +#ifdef DEBUG + puc = ((unsigned char *) &(popnd->disp)); + puchOpcode[(*pusIdx)++] = puc[3]; + puchOpcode[(*pusIdx)++] = puc[2]; + puchOpcode[(*pusIdx)++] = puc[1]; + puchOpcode[(*pusIdx)++] = puc[0]; +#endif + if (usFlags & (_modrm | NUM_MASK)) { +#ifdef DEBUG + if (debuga) + printf("Setting up value %lld\n", (long long)popnd->disp); +#endif + pc->IEVpointer1 = (targ_size_t) popnd->disp; + pc->IFL1 = FLconst; + } + else { + pc->IEVpointer2 = (targ_size_t) popnd->disp; + pc->IFL2 = FLconst; + } + + } + } +} + +/******************************* + */ + +STATIC regm_t asm_modify_regs(PTRNTAB ptb, OPND *popnd1, OPND *popnd2) +{ + regm_t usRet = 0; + + switch (ptb.pptb0->usFlags & MOD_MASK) { + case _modsi: + usRet |= mSI; + break; + case _moddx: + usRet |= mDX; + break; + case _mod2: + if (popnd2) + usRet |= asm_modify_regs(ptb, popnd2, NULL); + break; + case _modax: + usRet |= mAX; + break; + case _modnot1: + popnd1 = NULL; + break; + case _modaxdx: + usRet |= (mAX | mDX); + break; + case _moddi: + usRet |= mDI; + break; + case _modsidi: + usRet |= (mSI | mDI); + break; + case _modcx: + usRet |= mCX; + break; + case _modes: + /*usRet |= mES;*/ + break; + case _modall: + asmstate.bReturnax = TRUE; + return /*mES |*/ ALLREGS; + case _modsiax: + usRet |= (mSI | mAX); + break; + case _modsinot1: + usRet |= mSI; + popnd1 = NULL; + break; + case _modcxr11: + usRet |= (mCX | mR11); + break; + case _modxmm0: + usRet |= mXMM0; + break; + } + if (popnd1 && ASM_GET_aopty(popnd1->usFlags) == _reg) + { + switch (ASM_GET_amod(popnd1->usFlags)) + { + default: + usRet |= 1 << popnd1->base->val; + usRet &= ~(mBP | mSP); // ignore changing these + break; + + case _rseg: + //if (popnd1->base->val == _ES) + //usRet |= mES; + break; + + case _rspecial: + break; + } + } + if (usRet & mAX) + asmstate.bReturnax = TRUE; + + return usRet; +} + +/******************************* + * Match flags in operand against flags in opcode table. + * Returns: + * !=0 if match + */ + +STATIC unsigned char asm_match_flags(opflag_t usOp, opflag_t usTable) +{ + ASM_OPERAND_TYPE aoptyTable; + ASM_OPERAND_TYPE aoptyOp; + ASM_MODIFIERS amodTable; + ASM_MODIFIERS amodOp; + unsigned uRegmaskTable; + unsigned uRegmaskOp; + unsigned char bRegmatch; + unsigned char bRetval = FALSE; + unsigned uSizemaskOp; + unsigned uSizemaskTable; + unsigned bSizematch; + + //printf("asm_match_flags(usOp = x%x, usTable = x%x)\n", usOp, usTable); + if (asmstate.ucItype == ITfloat) + { + bRetval = asm_match_float_flags(usOp, usTable); + goto EXIT; + } + + uSizemaskOp = ASM_GET_uSizemask(usOp); + uSizemaskTable = ASM_GET_uSizemask(usTable); + + // Check #1, if the sizes do not match, NO match + bSizematch = (uSizemaskOp & uSizemaskTable); + + amodOp = ASM_GET_amod(usOp); + + aoptyTable = ASM_GET_aopty(usTable); + aoptyOp = ASM_GET_aopty(usOp); + + // _mmm64 matches with a 64 bit mem or an MMX register + if (usTable == _mmm64) + { + if (usOp == _mm) + goto Lmatch; + if (aoptyOp == _m && (bSizematch || uSizemaskOp == _anysize)) + goto Lmatch; + goto EXIT; + } + + // _xmm_m32, _xmm_m64, _xmm_m128 match with XMM register or memory + if (usTable == _xmm_m16 || + usTable == _xmm_m32 || + usTable == _xmm_m64 || + usTable == _xmm_m128) + { + if (usOp == _xmm || usOp == (_xmm|_xmm0)) + goto Lmatch; + if (aoptyOp == _m && (bSizematch || uSizemaskOp == _anysize)) + goto Lmatch; + } + + if (usTable == _ymm_m256) + { + if (usOp == _ymm) + goto Lmatch; + if (aoptyOp == _m && (bSizematch || uSizemaskOp == _anysize)) + goto Lmatch; + } + + if (!bSizematch && uSizemaskTable) + { + //printf("no size match\n"); + goto EXIT; + } + + +// +// The operand types must match, otherwise return FALSE. +// There is one exception for the _rm which is a table entry which matches +// _reg or _m +// + if (aoptyTable != aoptyOp) + { + if (aoptyTable == _rm && (aoptyOp == _reg || + aoptyOp == _m || + aoptyOp == _rel)) + goto Lok; + if (aoptyTable == _mnoi && aoptyOp == _m && + (uSizemaskOp == _32 && amodOp == _addr16 || + uSizemaskOp == _48 && amodOp == _addr32 || + uSizemaskOp == _48 && amodOp == _normal) + ) + goto Lok; + goto EXIT; + } +Lok: + +// +// Looks like a match so far, check to see if anything special is going on +// + amodTable = ASM_GET_amod(usTable); + uRegmaskOp = ASM_GET_uRegmask(usOp); + uRegmaskTable = ASM_GET_uRegmask(usTable); + bRegmatch = ((!uRegmaskTable && !uRegmaskOp) || + (uRegmaskTable & uRegmaskOp)); + + switch (amodTable) + { + case _normal: // Normal's match with normals + switch(amodOp) { + case _normal: + case _addr16: + case _addr32: + case _fn16: + case _fn32: + case _flbl: + bRetval = (bSizematch || bRegmatch); + goto EXIT; + default: + goto EXIT; + } + case _rseg: + case _rspecial: + bRetval = (amodOp == amodTable && bRegmatch); + goto EXIT; + default: + assert(0); + } +EXIT: +#if 0 + printf("OP : "); + asm_output_flags(usOp); + printf("\nTBL: "); + asm_output_flags(usTable); + printf(": %s\n", bRetval ? "MATCH" : "NOMATCH"); +#endif + return bRetval; + +Lmatch: + //printf("match\n"); + return 1; +} + +/******************************* + */ + +STATIC unsigned char asm_match_float_flags(opflag_t usOp, opflag_t usTable) +{ + ASM_OPERAND_TYPE aoptyTable; + ASM_OPERAND_TYPE aoptyOp; + ASM_MODIFIERS amodTable; + ASM_MODIFIERS amodOp; + unsigned uRegmaskTable; + unsigned uRegmaskOp; + unsigned bRegmatch; + + +// +// Check #1, if the sizes do not match, NO match +// + uRegmaskOp = ASM_GET_uRegmask(usOp); + uRegmaskTable = ASM_GET_uRegmask(usTable); + bRegmatch = (uRegmaskTable & uRegmaskOp); + + if (!(ASM_GET_uSizemask(usTable) & ASM_GET_uSizemask(usOp) || + bRegmatch)) + return(FALSE); + + aoptyTable = ASM_GET_aopty(usTable); + aoptyOp = ASM_GET_aopty(usOp); +// +// The operand types must match, otherwise return FALSE. +// There is one exception for the _rm which is a table entry which matches +// _reg or _m +// + if (aoptyTable != aoptyOp) + { + if (aoptyOp != _float) + return(FALSE); + } + +// +// Looks like a match so far, check to see if anything special is going on +// + amodOp = ASM_GET_amod(usOp); + amodTable = ASM_GET_amod(usTable); + switch (amodTable) + { + // Normal's match with normals + case _normal: + switch(amodOp) + { + case _normal: + case _addr16: + case _addr32: + case _fn16: + case _fn32: + case _flbl: + return(TRUE); + default: + return(FALSE); + } + case _rseg: + case _rspecial: + return(FALSE); + default: + assert(0); + return 0; + } +} + +#ifdef DEBUG + +/******************************* + */ + +STATIC void asm_output_flags(opflag_t opflags) +{ + ASM_OPERAND_TYPE aopty = ASM_GET_aopty(opflags); + ASM_MODIFIERS amod = ASM_GET_amod(opflags); + unsigned uRegmask = ASM_GET_uRegmask(opflags); + unsigned uSizemask = ASM_GET_uSizemask(opflags); + + if (uSizemask == _anysize) + printf("_anysize "); + else if (uSizemask == 0) + printf("0 "); + else + { + if (uSizemask & _8) + printf("_8 "); + if (uSizemask & _16) + printf("_16 "); + if (uSizemask & _32) + printf("_32 "); + if (uSizemask & _48) + printf("_48 "); + if (uSizemask & _64) + printf("_64 "); + } + + printf("_"); + switch (aopty) { + case _reg: + printf("reg "); + break; + case _m: + printf("m "); + break; + case _imm: + printf("imm "); + break; + case _rel: + printf("rel "); + break; + case _mnoi: + printf("mnoi "); + break; + case _p: + printf("p "); + break; + case _rm: + printf("rm "); + break; + case _float: + printf("float "); + break; + default: + printf(" UNKNOWN "); + } + + printf("_"); + switch (amod) { + case _normal: + printf("normal "); + if (uRegmask & 1) printf("_al "); + if (uRegmask & 2) printf("_ax "); + if (uRegmask & 4) printf("_eax "); + if (uRegmask & 8) printf("_dx "); + if (uRegmask & 0x10) printf("_cl "); + if (uRegmask & 0x40) printf("_rax "); + if (uRegmask & 0x20) printf("_rplus_r "); + return; + case _rseg: + printf("rseg "); + break; + case _rspecial: + printf("rspecial "); + break; + case _addr16: + printf("addr16 "); + break; + case _addr32: + printf("addr32 "); + break; + case _fn16: + printf("fn16 "); + break; + case _fn32: + printf("fn32 "); + break; + case _flbl: + printf("flbl "); + break; + default: + printf("UNKNOWN "); + break; + } + printf("uRegmask=x%02x", uRegmask); + +} + +/******************************* + */ + +STATIC void asm_output_popnd(OPND *popnd) +{ + if (popnd->segreg) + printf("%s:", popnd->segreg->regstr); + + if (popnd->s) + printf("%s", popnd->s->ident->toChars()); + + if (popnd->base) + printf("%s", popnd->base->regstr); + if (popnd->pregDisp1) { + if (popnd->pregDisp2) { + if (popnd->usFlags & _a32) + if (popnd->uchMultiplier) + printf("[%s][%s*%d]", + popnd->pregDisp1->regstr, + popnd->pregDisp2->regstr, + popnd->uchMultiplier); + else + printf("[%s][%s]", + popnd->pregDisp1->regstr, + popnd->pregDisp2->regstr); + else + printf("[%s+%s]", + popnd->pregDisp1->regstr, + popnd->pregDisp2->regstr); + } + else { + if (popnd->uchMultiplier) + printf("[%s*%d]", + popnd->pregDisp1->regstr, + popnd->uchMultiplier); + else + printf("[%s]", + popnd->pregDisp1->regstr); + } + } + if (ASM_GET_aopty(popnd->usFlags) == _imm) + printf("%llxh", (long long)popnd->disp); + else + if (popnd->disp) + printf("+%llxh", (long long)popnd->disp); +} + +#endif + +/******************************* + */ + +STATIC REG *asm_reg_lookup(char *s) +{ + int i; + + //dbg_printf("asm_reg_lookup('%s')\n",s); + + for (i = 0; i < sizeof(regtab) / sizeof(regtab[0]); i++) + { + if (strcmp(s,regtab[i].regstr) == 0) + { + return ®tab[i]; + } + } + if (I64) + { + for (i = 0; i < sizeof(regtab64) / sizeof(regtab64[0]); i++) + { + if (strcmp(s,regtab64[i].regstr) == 0) + { + return ®tab64[i]; + } + } + } + return NULL; +} + + +/******************************* + */ + +STATIC void asm_token() +{ + if (asmtok) + asmtok = asmtok->next; + asm_token_trans(asmtok); +} + +/******************************* + */ + +STATIC void asm_token_trans(Token *tok) +{ + tok_value = TOKeof; + if (tok) + { + tok_value = tok->value; + if (tok_value == TOKidentifier) + { size_t len; + char *id; + + id = tok->ident->toChars(); + len = strlen(id); + if (len < 20) + { + ASMTK asmtk = (ASMTK) binary(id, apszAsmtk, ASMTKmax); + if ((int)asmtk >= 0) + tok_value = (enum TOK) (asmtk + TOKMAX + 1); + } + } + } +} + +/******************************* + */ + +STATIC unsigned asm_type_size(Type * ptype) +{ unsigned u; + + //if (ptype) printf("asm_type_size('%s') = %d\n", ptype->toChars(), (int)ptype->size()); + u = _anysize; + if (ptype && ptype->ty != Tfunction /*&& ptype->isscalar()*/) + { + switch ((int)ptype->size()) + { + case 0: asmerr(EM_bad_op, "0 size"); break; + case 1: u = _8; break; + case 2: u = _16; break; + case 4: u = _32; break; + case 6: u = _48; break; + case 8: if (I64) u = _64; break; + } + } + return u; +} + +/******************************* + * start of inline assemblers expression parser + * NOTE: functions in call order instead of alphabetical + */ + +/******************************************* + * Parse DA expression + * + * Very limited define address to place a code + * address in the assembly + * Problems: + * o Should use dw offset and dd offset instead, + * for near/far support. + * o Should be able to add an offset to the label address. + * o Blocks addressed by DA should get their Bpred set correctly + * for optimizer. + */ + +STATIC code *asm_da_parse(OP *pop) +{ + code *clst = NULL; + + while (1) + { code *c; + + if (tok_value == TOKidentifier) + { + LabelDsymbol *label; + + label = asmstate.sc->func->searchLabel(asmtok->ident); + if (!label) + error(asmstate.loc, "label '%s' not found\n", asmtok->ident->toChars()); + + c = code_calloc(); + c->Iop = ASM; + c->Iflags = CFaddrsize; + c->IFL1 = FLblockoff; + c->IEVlsym1 = label; + c = asm_genloc(asmstate.loc, c); + clst = cat(clst,c); + } + else + asmerr(EM_bad_addr_mode); // illegal addressing mode + asm_token(); + if (tok_value != TOKcomma) + break; + asm_token(); + } + + asmstate.statement->regs |= mES|ALLREGS; + asmstate.bReturnax = TRUE; + + return clst; +} + +/******************************************* + * Parse DB, DW, DD, DQ and DT expressions. + */ + +STATIC code *asm_db_parse(OP *pop) +{ + unsigned usSize; + unsigned usMaxbytes; + unsigned usBytes; + union DT + { targ_ullong ul; + targ_float f; + targ_double d; + targ_ldouble ld; + char value[10]; + } dt; + code *c; + unsigned op; + static unsigned char opsize[] = { 1,2,4,8,4,8,10 }; + + op = pop->usNumops & ITSIZE; + usSize = opsize[op]; + + usBytes = 0; + usMaxbytes = 0; + c = code_calloc(); + c->Iop = ASM; + + while (1) + { + size_t len; + unsigned char *q; + + if (usBytes+usSize > usMaxbytes) + { usMaxbytes = usBytes + usSize + 10; + c->IEV1.as.bytes = (char *)mem_realloc(c->IEV1.as.bytes,usMaxbytes); + } + switch (tok_value) + { + case TOKint32v: + dt.ul = asmtok->int32value; + goto L1; + case TOKuns32v: + dt.ul = asmtok->uns32value; + goto L1; + case TOKint64v: + dt.ul = asmtok->int64value; + goto L1; + case TOKuns64v: + dt.ul = asmtok->uns64value; + goto L1; + L1: + switch (op) + { + case OPdb: + case OPds: + case OPdi: + case OPdl: + break; + default: + asmerr(EM_float); + } + goto L2; + + case TOKfloat32v: + case TOKfloat64v: + case TOKfloat80v: + switch (op) + { + case OPdf: + dt.f = asmtok->float80value; + break; + case OPdd: + dt.d = asmtok->float80value; + break; + case OPde: + dt.ld = asmtok->float80value; + break; + default: + asmerr(EM_num); + } + goto L2; + + L2: + memcpy(c->IEV1.as.bytes + usBytes,&dt,usSize); + usBytes += usSize; + break; + + case TOKstring: + len = asmtok->len; + q = asmtok->ustring; + L3: + if (len) + { + usMaxbytes += len * usSize; + c->IEV1.as.bytes = + (char *)mem_realloc(c->IEV1.as.bytes,usMaxbytes); + memcpy(c->IEV1.as.bytes + usBytes,asmtok->ustring,len); + + char *p = c->IEV1.as.bytes + usBytes; + for (size_t i = 0; i < len; i++) + { + // Be careful that this works + memset(p, 0, usSize); + switch (op) + { + case OPdb: + *p = (unsigned char)*q; + if (*p != *q) + asmerr(EM_char); + break; + + case OPds: + *(short *)p = *(unsigned char *)q; + if (*(short *)p != *q) + asmerr(EM_char); + break; + + case OPdi: + case OPdl: + *(long *)p = *q; + break; + + default: + asmerr(EM_float); + } + q++; + p += usSize; + } + + usBytes += len * usSize; + } + break; + + case TOKidentifier: + { Expression *e = new IdentifierExp(asmstate.loc, asmtok->ident); + e = e->semantic(asmstate.sc); + e = e->optimize(WANTvalue | WANTinterpret); + if (e->op == TOKint64) + { dt.ul = e->toInteger(); + goto L2; + } + else if (e->op == TOKfloat64) + { + switch (op) + { + case OPdf: + dt.f = e->toReal(); + break; + case OPdd: + dt.d = e->toReal(); + break; + case OPde: + dt.ld = e->toReal(); + break; + default: + asmerr(EM_num); + } + goto L2; + } + else if (e->op == TOKstring) + { StringExp *se = (StringExp *)e; + q = (unsigned char *)se->string; + len = se->len; + goto L3; + } + goto Ldefault; + } + + default: + Ldefault: + asmerr(EM_const_init); // constant initializer + break; + } + c->IEV1.as.len = usBytes; + + asm_token(); + if (tok_value != TOKcomma) + break; + asm_token(); + } + + c = asm_genloc(asmstate.loc, c); + + asmstate.statement->regs |= /* mES| */ ALLREGS; + asmstate.bReturnax = TRUE; + + return c; +} + +/********************************** + * Parse and get integer expression. + */ + +int asm_getnum() +{ int v; + dinteger_t i; + + switch (tok_value) + { + case TOKint32v: + v = asmtok->int32value; + break; + + case TOKuns32v: + v = asmtok->uns32value; + break; + + case TOKidentifier: + Expression *e; + + e = new IdentifierExp(asmstate.loc, asmtok->ident); + e = e->semantic(asmstate.sc); + e = e->optimize(WANTvalue | WANTinterpret); + i = e->toInteger(); + v = (int) i; + if (v != i) + asmerr(EM_num); + break; + + default: + asmerr(EM_num); + v = 0; // no uninitialized values + break; + } + asm_token(); + return v; +} + +/******************************* + */ + +STATIC OPND *asm_cond_exp() +{ + OPND *o1,*o2,*o3; + + //printf("asm_cond_exp()\n"); + o1 = asm_log_or_exp(); + if (tok_value == TOKquestion) + { + asm_token(); + o2 = asm_cond_exp(); + asm_token(); + asm_chktok(TOKcolon,EM_colon); + o3 = asm_cond_exp(); + o1 = (o1->disp) ? o2 : o3; + } + return o1; +} + +/******************************* + */ + +STATIC OPND *asm_log_or_exp() +{ + OPND *o1,*o2; + + o1 = asm_log_and_exp(); + while (tok_value == TOKoror) + { + asm_token(); + o2 = asm_log_and_exp(); + if (asm_isint(o1) && asm_isint(o2)) + o1->disp = o1->disp || o2->disp; + else + asmerr(EM_bad_integral_operand); // illegal operand + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + } + return o1; +} + +/******************************* + */ + +STATIC OPND *asm_log_and_exp() +{ + OPND *o1,*o2; + + o1 = asm_inc_or_exp(); + while (tok_value == TOKandand) + { + asm_token(); + o2 = asm_inc_or_exp(); + if (asm_isint(o1) && asm_isint(o2)) + o1->disp = o1->disp && o2->disp; + else { + asmerr(EM_bad_integral_operand); // illegal operand + } + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + } + return o1; +} + +/******************************* + */ + +STATIC OPND *asm_inc_or_exp() +{ + OPND *o1,*o2; + + o1 = asm_xor_exp(); + while (tok_value == TOKor) + { + asm_token(); + o2 = asm_xor_exp(); + if (asm_isint(o1) && asm_isint(o2)) + o1->disp |= o2->disp; + else { + asmerr(EM_bad_integral_operand); // illegal operand + } + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + } + return o1; +} + +/******************************* + */ + +STATIC OPND *asm_xor_exp() +{ + OPND *o1,*o2; + + o1 = asm_and_exp(); + while (tok_value == TOKxor) + { + asm_token(); + o2 = asm_and_exp(); + if (asm_isint(o1) && asm_isint(o2)) + o1->disp ^= o2->disp; + else { + asmerr(EM_bad_integral_operand); // illegal operand + } + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + } + return o1; +} + +/******************************* + */ + +STATIC OPND *asm_and_exp() +{ + OPND *o1,*o2; + + o1 = asm_equal_exp(); + while (tok_value == TOKand) + { + asm_token(); + o2 = asm_equal_exp(); + if (asm_isint(o1) && asm_isint(o2)) + o1->disp &= o2->disp; + else { + asmerr(EM_bad_integral_operand); // illegal operand + } + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + } + return o1; +} + +/******************************* + */ + +STATIC OPND *asm_equal_exp() +{ + OPND *o1,*o2; + + o1 = asm_rel_exp(); + while (1) + { + switch (tok_value) + { + case TOKequal: + asm_token(); + o2 = asm_rel_exp(); + if (asm_isint(o1) && asm_isint(o2)) + o1->disp = o1->disp == o2->disp; + else { + asmerr(EM_bad_integral_operand); // illegal operand + } + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + break; + + case TOKnotequal: + asm_token(); + o2 = asm_rel_exp(); + if (asm_isint(o1) && asm_isint(o2)) + o1->disp = o1->disp != o2->disp; + else { + asmerr(EM_bad_integral_operand); + } + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + break; + + default: + return o1; + } + } +} + +/******************************* + */ + +STATIC OPND *asm_rel_exp() +{ + OPND *o1,*o2; + enum TOK tok_save; + + o1 = asm_shift_exp(); + while (1) + { + switch (tok_value) + { + case TOKgt: + case TOKge: + case TOKlt: + case TOKle: + tok_save = tok_value; + asm_token(); + o2 = asm_shift_exp(); + if (asm_isint(o1) && asm_isint(o2)) + { + switch (tok_save) + { + case TOKgt: + o1->disp = o1->disp > o2->disp; + break; + case TOKge: + o1->disp = o1->disp >= o2->disp; + break; + case TOKlt: + o1->disp = o1->disp < o2->disp; + break; + case TOKle: + o1->disp = o1->disp <= o2->disp; + break; + } + } + else + asmerr(EM_bad_integral_operand); + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + break; + + default: + return o1; + } + } +} + +/******************************* + */ + +STATIC OPND *asm_shift_exp() +{ + OPND *o1,*o2; + enum TOK tk; + + o1 = asm_add_exp(); + while (tok_value == TOKshl || tok_value == TOKshr || tok_value == TOKushr) + { tk = tok_value; + asm_token(); + o2 = asm_add_exp(); + if (asm_isint(o1) && asm_isint(o2)) + { if (tk == TOKshl) + o1->disp <<= o2->disp; + else if (tk == TOKushr) + o1->disp = (unsigned)o1->disp >> o2->disp; + else + o1->disp >>= o2->disp; + } + else + asmerr(EM_bad_integral_operand); + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + } + return o1; +} + +/******************************* + */ + +STATIC OPND *asm_add_exp() +{ + OPND *o1,*o2; + + o1 = asm_mul_exp(); + while (1) + { + switch (tok_value) + { + case TOKadd: + asm_token(); + o2 = asm_mul_exp(); + o1 = asm_merge_opnds(o1, o2); + break; + + case TOKmin: + asm_token(); + o2 = asm_mul_exp(); + if (asm_isint(o1) && asm_isint(o2)) + { + o1->disp -= o2->disp; + o2->disp = 0; + } + else + o2->disp = - o2->disp; + o1 = asm_merge_opnds(o1, o2); + break; + + default: + return o1; + } + } +} + +/******************************* + */ + +STATIC OPND *asm_mul_exp() +{ + OPND *o1,*o2; + OPND *popndTmp; + + //printf("+asm_mul_exp()\n"); + o1 = asm_br_exp(); + while (1) + { + switch (tok_value) + { + case TOKmul: + asm_token(); + o2 = asm_br_exp(); +#ifdef EXTRA_DEBUG + printf("Star o1.isint=%d, o2.isint=%d, lbra_seen=%d\n", + asm_isint(o1), asm_isint(o2), asm_TKlbra_seen ); +#endif + if (asm_isNonZeroInt(o1) && asm_isNonZeroInt(o2)) + o1->disp *= o2->disp; + else if (asm_TKlbra_seen && o1->pregDisp1 && asm_isNonZeroInt(o2)) + { + o1->uchMultiplier = o2->disp; +#ifdef EXTRA_DEBUG + printf("Multiplier: %d\n", o1->uchMultiplier); +#endif + } + else if (asm_TKlbra_seen && o2->pregDisp1 && asm_isNonZeroInt(o1)) + { + popndTmp = o2; + o2 = o1; + o1 = popndTmp; + o1->uchMultiplier = o2->disp; +#ifdef EXTRA_DEBUG + printf("Multiplier: %d\n", + o1->uchMultiplier); +#endif + } + else if (asm_isint(o1) && asm_isint(o2)) + o1->disp *= o2->disp; + else + asmerr(EM_bad_operand); + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + break; + + case TOKdiv: + asm_token(); + o2 = asm_br_exp(); + if (asm_isint(o1) && asm_isint(o2)) + o1->disp /= o2->disp; + else + asmerr(EM_bad_integral_operand); + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + break; + + case TOKmod: + asm_token(); + o2 = asm_br_exp(); + if (asm_isint(o1) && asm_isint(o2)) + o1->disp %= o2->disp; + else + asmerr(EM_bad_integral_operand); + o2->disp = 0; + o1 = asm_merge_opnds(o1, o2); + break; + + default: + return o1; + } + } + return o1; +} + +/******************************* + */ + +STATIC OPND *asm_br_exp() +{ + OPND *o1,*o2; + + //printf("asm_br_exp()\n"); + o1 = asm_una_exp(); + while (1) + { + switch (tok_value) + { + case TOKlbracket: + { +#ifdef EXTRA_DEBUG + printf("Saw a left bracket\n"); +#endif + asm_token(); + asm_TKlbra_seen++; + o2 = asm_cond_exp(); + asm_TKlbra_seen--; + asm_chktok(TOKrbracket,EM_rbra); +#ifdef EXTRA_DEBUG + printf("Saw a right bracket\n"); +#endif + o1 = asm_merge_opnds(o1, o2); + if (tok_value == TOKidentifier) + { o2 = asm_una_exp(); + o1 = asm_merge_opnds(o1, o2); + } + break; + } + default: + return o1; + } + } +} + +/******************************* + */ + +STATIC OPND *asm_una_exp() +{ + OPND *o1; + Type *ptype; + ASM_JUMPTYPE ajt = ASM_JUMPTYPE_UNSPECIFIED; + char bPtr = 0; + + switch ((int)tok_value) + { +#if 0 + case TOKand: + asm_token(); + o1 = asm_una_exp(); + break; + + case TOKmul: + asm_token(); + o1 = asm_una_exp(); + ++o1->indirect; + break; +#endif + case TOKadd: + asm_token(); + o1 = asm_una_exp(); + break; + + case TOKmin: + asm_token(); + o1 = asm_una_exp(); + if (asm_isint(o1)) + o1->disp = -o1->disp; + break; + + case TOKnot: + asm_token(); + o1 = asm_una_exp(); + if (asm_isint(o1)) + o1->disp = !o1->disp; + break; + + case TOKtilde: + asm_token(); + o1 = asm_una_exp(); + if (asm_isint(o1)) + o1->disp = ~o1->disp; + break; + +#if 0 + case TOKlparen: + // stoken() is called directly here because we really + // want the INT token to be an INT. + stoken(); + if (type_specifier(&ptypeSpec)) /* if type_name */ + { + + ptype = declar_abstract(ptypeSpec); + /* read abstract_declarator */ + fixdeclar(ptype);/* fix declarator */ + type_free(ptypeSpec);/* the declar() function + allocates the typespec again */ + chktok(TOKrparen,EM_rpar); + ptype->Tcount--; + goto CAST_REF; + } + else + { + type_free(ptypeSpec); + o1 = asm_cond_exp(); + chktok(TOKrparen, EM_rpar); + } + break; +#endif + + case TOKidentifier: + // Check for offset keyword + if (asmtok->ident == Id::offset) + { + if (!global.params.useDeprecated) + error(asmstate.loc, "offset deprecated, use offsetof"); + goto Loffset; + } + if (asmtok->ident == Id::offsetof) + { + Loffset: + asm_token(); + o1 = asm_cond_exp(); + if (!o1) + o1 = opnd_calloc(); + o1->bOffset= TRUE; + } + else + o1 = asm_primary_exp(); + break; + + case ASMTKseg: + asm_token(); + o1 = asm_cond_exp(); + if (!o1) + o1 = opnd_calloc(); + o1->bSeg= TRUE; + break; + + case TOKint16: + if (asmstate.ucItype != ITjump) + { + ptype = Type::tint16; + goto TYPE_REF; + } + ajt = ASM_JUMPTYPE_SHORT; + asm_token(); + goto JUMP_REF2; + + case ASMTKnear: + ajt = ASM_JUMPTYPE_NEAR; + goto JUMP_REF; + + case ASMTKfar: + ajt = ASM_JUMPTYPE_FAR; +JUMP_REF: + asm_token(); + asm_chktok((enum TOK) ASMTKptr, EM_ptr_exp); +JUMP_REF2: + o1 = asm_cond_exp(); + if (!o1) + o1 = opnd_calloc(); + o1->ajt= ajt; + break; + + case TOKint8: + ptype = Type::tint8; + goto TYPE_REF; + case TOKint32: + case ASMTKdword: + ptype = Type::tint32; + goto TYPE_REF; + case TOKfloat32: + ptype = Type::tfloat32; + goto TYPE_REF; + case ASMTKqword: + case TOKfloat64: + ptype = Type::tfloat64; + goto TYPE_REF; + case TOKfloat80: + ptype = Type::tfloat80; + goto TYPE_REF; + case ASMTKword: + ptype = Type::tint16; +TYPE_REF: + bPtr = 1; + asm_token(); + asm_chktok((enum TOK) ASMTKptr, EM_ptr_exp); + o1 = asm_cond_exp(); + if (!o1) + o1 = opnd_calloc(); + o1->ptype = ptype; + o1->bPtr = bPtr; + break; + + default: + o1 = asm_primary_exp(); + break; + } + return o1; +} + +/******************************* + */ + +STATIC OPND *asm_primary_exp() +{ + OPND *o1 = NULL; + OPND *o2 = NULL; + Dsymbol *s; + Dsymbol *scopesym; + + REG *regp; + + switch (tok_value) + { + case TOKdollar: + o1 = opnd_calloc(); + o1->s = asmstate.psDollar; + asm_token(); + break; + +#if 0 + case TOKthis: + strcpy(tok.TKid,cpp_name_this); +#endif + case TOKthis: + case TOKidentifier: + o1 = opnd_calloc(); + regp = asm_reg_lookup(asmtok->ident->toChars()); + if (regp != NULL) + { + asm_token(); + // see if it is segment override (like SS:) + if (!asm_TKlbra_seen && + (regp->ty & _seg) && + tok_value == TOKcolon) + { + o1->segreg = regp; + asm_token(); + o2 = asm_cond_exp(); + o1 = asm_merge_opnds(o1, o2); + } + else if (asm_TKlbra_seen) + { // should be a register + if (o1->pregDisp1) + asmerr(EM_bad_operand); + else + o1->pregDisp1 = regp; + } + else + { if (o1->base == NULL) + o1->base = regp; + else + asmerr(EM_bad_operand); + } + break; + } + // If floating point instruction and id is a floating register + else if (asmstate.ucItype == ITfloat && + asm_is_fpreg(asmtok->ident->toChars())) + { + asm_token(); + if (tok_value == TOKlparen) + { unsigned n; + + asm_token(); + asm_chktok(TOKint32v, EM_num); + n = (unsigned)asmtok->uns64value; + if (n > 7) + asmerr(EM_bad_operand); + o1->base = &(aregFp[n]); + asm_chktok(TOKrparen, EM_rpar); + } + else + o1->base = ®Fp; + } + else + { + if (asmstate.ucItype == ITjump) + { + s = NULL; + if (asmstate.sc->func->labtab) + s = asmstate.sc->func->labtab->lookup(asmtok->ident); + if (!s) + s = asmstate.sc->search(0, asmtok->ident, &scopesym); + if (!s) + { // Assume it is a label, and define that label + s = asmstate.sc->func->searchLabel(asmtok->ident); + } + } + else + s = asmstate.sc->search(0, asmtok->ident, &scopesym); + if (!s) + asmerr(EM_undefined, asmtok->toChars()); + + Identifier *id = asmtok->ident; + asm_token(); + if (tok_value == TOKdot) + { Expression *e; + VarExp *v; + + e = new IdentifierExp(asmstate.loc, id); + while (1) + { + asm_token(); + if (tok_value == TOKidentifier) + { + e = new DotIdExp(asmstate.loc, e, asmtok->ident); + asm_token(); + if (tok_value != TOKdot) + break; + } + else + { + asmerr(EM_ident_exp); + break; + } + } + e = e->semantic(asmstate.sc); + e = e->optimize(WANTvalue | WANTinterpret); + if (e->isConst()) + { + if (e->type->isintegral()) + { + o1->disp = e->toInteger(); + goto Lpost; + } + else if (e->type->isreal()) + { + o1->real = e->toReal(); + o1->ptype = e->type; + goto Lpost; + } + else + { + asmerr(EM_bad_op, e->toChars()); + } + } + else if (e->op == TOKvar) + { + v = (VarExp *)(e); + s = v->var; + } + else + { + asmerr(EM_bad_op, e->toChars()); + } + } + + asm_merge_symbol(o1,s); + + /* This attempts to answer the question: is + * char[8] foo; + * of size 1 or size 8? Presume it is 8 if foo + * is the last token of the operand. + */ + if (o1->ptype && tok_value != TOKcomma && tok_value != TOKeof) + { + for (; + o1->ptype->ty == Tsarray; + o1->ptype = o1->ptype->nextOf()) + { + ; + } + } + + Lpost: +#if 0 + // for [] + if (tok_value == TOKlbracket) + o1 = asm_prim_post(o1); +#endif + goto Lret; + } + break; + + case TOKint32v: + case TOKuns32v: + o1 = opnd_calloc(); + o1->disp = asmtok->int32value; + asm_token(); + break; + + case TOKint64v: + case TOKuns64v: + o1 = opnd_calloc(); + o1->disp = asmtok->int64value; + asm_token(); + break; + + case TOKfloat32v: + o1 = opnd_calloc(); + o1->real = asmtok->float80value; + o1->ptype = Type::tfloat32; + asm_token(); + break; + + case TOKfloat64v: + o1 = opnd_calloc(); + o1->real = asmtok->float80value; + o1->ptype = Type::tfloat64; + asm_token(); + break; + + case TOKfloat80v: + o1 = opnd_calloc(); + o1->real = asmtok->float80value; + o1->ptype = Type::tfloat80; + asm_token(); + break; + + case ASMTKlocalsize: + o1 = opnd_calloc(); + o1->s = asmstate.psLocalsize; + o1->ptype = Type::tint32; + asm_token(); + break; + } +Lret: + return o1; +} + +/******************************* + */ + +#if 0 +STATIC OPND *asm_prim_post(OPND *o1) +{ + OPND *o2; + Declaration *d = o1->s ? o1->s->isDeclaration() : NULL; + Type *t; + + t = d ? d->type : o1->ptype; + while (1) + { + switch (tok_value) + { +#if 0 + case TKarrow: + if (++o1->indirect > 1) + { + BAD_OPERAND: + asmerr(EM_bad_operand); + } + if (s->Sclass != SCregister) + goto BAD_OPERAND; + if (!typtr(t->Tty)) + { + asmerr(EM_pointer,t,(type *) NULL); + } + else + t = t->Tnext; + case TKcolcol: + if (tybasic(t->Tty) != TYstruct) + asmerr(EM_not_struct); // not a struct or union type + goto L1; + + case TOKdot: + for (; t && tybasic(t->Tty) != TYstruct; + t = t->Tnext) + ; + if (!t) + asmerr(EM_not_struct); + L1: + /* try to find the symbol */ + asm_token(); + if (tok_value != TOKidentifier) + asmerr(EM_ident_exp); + s = n2_searchmember(t->Ttag,tok.TKid); + if (!s) + { + err_notamember(tok.TKid,t->Ttag); + } + else + { + asm_merge_symbol(o1,s); + t = s->Stype; + asm_token(); + } + break; +#endif + + case TOKlbracket: + asm_token(); + asm_TKlbra_seen++; + o2 = asm_cond_exp(); + asm_chktok(TOKrbracket,EM_rbra); + asm_TKlbra_seen--; + return asm_merge_opnds(o1, o2); + + default: + return o1; + } + } +} +#endif + +/******************************* + */ + +void iasm_term() +{ + if (asmstate.bInit) + { + asmstate.psDollar = NULL; + asmstate.psLocalsize = NULL; + asmstate.bInit = 0; + } +} + +/********************************** + * Return mask of registers used by block bp. + */ + +regm_t iasm_regs(block *bp) +{ +#ifdef DEBUG + if (debuga) + printf("Block iasm regs = 0x%X\n", bp->usIasmregs); +#endif + + refparam |= bp->bIasmrefparam; + return bp->usIasmregs; +} + + +/************************ AsmStatement ***************************************/ + +Statement *AsmStatement::semantic(Scope *sc) +{ + //printf("AsmStatement::semantic()\n"); + + assert(sc->func); +#if DMDV2 + if (sc->func->setUnsafe()) + error("inline assembler not allowed in @safe function %s", sc->func->toChars()); +#endif + + OP *o; + OPND *o1 = NULL,*o2 = NULL, *o3 = NULL, *o4 = NULL; + PTRNTAB ptb; + unsigned usNumops; + unsigned char uchPrefix = 0; + char *pszLabel = NULL; + FuncDeclaration *fd = sc->parent->isFuncDeclaration(); + + assert(fd); +#if DMDV1 + fd->inlineAsm = 1; +#endif + + if (!tokens) + return NULL; + + memset(&asmstate, 0, sizeof(asmstate)); + + asmstate.statement = this; + asmstate.sc = sc; + +#if 0 // don't use bReturnax anymore, and will fail anyway if we use return type inference + // Scalar return values will always be in AX. So if it is a scalar + // then asm block sets return value if it modifies AX, if it is non-scalar + // then always assume that the ASM block sets up an appropriate return + // value. + + asmstate.bReturnax = 1; + if (sc->func->type->nextOf()->isscalar()) + asmstate.bReturnax = 0; +#endif + + // Assume assembler code takes care of setting the return value + sc->func->hasReturnExp |= 8; + + if (!asmstate.bInit) + { + asmstate.bInit = TRUE; + init_optab(); + asmstate.psDollar = new LabelDsymbol(Id::__dollar); + asmstate.psLocalsize = new Dsymbol(Id::__LOCAL_SIZE); + } + + asmstate.loc = loc; + + asmtok = tokens; + asm_token_trans(asmtok); + if (setjmp(asmstate.env)) + { asmtok = NULL; // skip rest of line + tok_value = TOKeof; + exit(EXIT_FAILURE); + goto AFTER_EMIT; + } + + switch (tok_value) + { + case ASMTKnaked: + naked = TRUE; + sc->func->naked = TRUE; + asm_token(); + break; + + case ASMTKeven: + asm_token(); + asmalign = 2; + break; + + case TOKalign: + { unsigned align; + + asm_token(); + align = asm_getnum(); + if (ispow2(align) == -1) + asmerr(EM_align, align); // power of 2 expected + else + asmalign = align; + break; + } + + // The following three convert the keywords 'int', 'in', 'out' + // to identifiers, since they are x86 instructions. + case TOKint32: + o = asm_op_lookup(Id::__int->toChars()); + goto Lopcode; + + case TOKin: + o = asm_op_lookup(Id::___in->toChars()); + goto Lopcode; + + case TOKout: + o = asm_op_lookup(Id::___out->toChars()); + goto Lopcode; + + case TOKidentifier: + o = asm_op_lookup(asmtok->ident->toChars()); + if (!o) + goto OPCODE_EXPECTED; + + Lopcode: + asmstate.ucItype = o->usNumops & ITMASK; + asm_token(); + if (o->usNumops > 4) + { + switch (asmstate.ucItype) + { + case ITdata: + asmcode = asm_db_parse(o); + goto AFTER_EMIT; + + case ITaddr: + asmcode = asm_da_parse(o); + goto AFTER_EMIT; + } + } + // get the first part of an expr + o1 = asm_cond_exp(); + if (tok_value == TOKcomma) + { + asm_token(); + o2 = asm_cond_exp(); + } + if (tok_value == TOKcomma) + { + asm_token(); + o3 = asm_cond_exp(); + } + if (tok_value == TOKcomma) + { + asm_token(); + o4 = asm_cond_exp(); + } + // match opcode and operands in ptrntab to verify legal inst and + // generate + + ptb = asm_classify(o, o1, o2, o3, o4, &usNumops); + assert(ptb.pptb0); + + // + // The Multiply instruction takes 3 operands, but if only 2 are seen + // then the third should be the second and the second should + // be a duplicate of the first. + // + + if (asmstate.ucItype == ITopt && + (usNumops == 2) && + (ASM_GET_aopty(o2->usFlags) == _imm) && + ((o->usNumops & ITSIZE) == 3)) + { + o3 = o2; + o2 = opnd_calloc(); + *o2 = *o1; + + // Re-classify the opcode because the first classification + // assumed 2 operands. + + ptb = asm_classify(o, o1, o2, o3, o4, &usNumops); + } +#if 0 + else + if (asmstate.ucItype == ITshift && (ptb.pptb2->usOp2 == 0 || + (ptb.pptb2->usOp2 & _cl))) { + opnd_free(o2); + o2 = NULL; + usNumops = 1; + } +#endif + asmcode = asm_emit(loc, usNumops, ptb, o, o1, o2, o3, o4); + break; + + default: + OPCODE_EXPECTED: + asmerr(EM_opcode_exp, asmtok->toChars()); // assembler opcode expected + break; + } + +AFTER_EMIT: + opnd_free(o1); + opnd_free(o2); + opnd_free(o3); + o1 = o2 = o3 = NULL; + + if (tok_value != TOKeof) + asmerr(EM_eol); // end of line expected + //return asmstate.bReturnax; + return this; +} + diff --git a/identifier.c b/identifier.c new file mode 100644 index 00000000..178ae12b --- /dev/null +++ b/identifier.c @@ -0,0 +1,102 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "root.h" +#include "identifier.h" +#include "mars.h" +#include "lexer.h" +#include "id.h" + +Identifier::Identifier(const char *string, int value) +{ + //printf("Identifier('%s', %d)\n", string, value); + this->string = string; + this->value = value; + this->len = strlen(string); +} + +hash_t Identifier::hashCode() +{ + return String::calcHash(string); +} + +int Identifier::equals(Object *o) +{ + return this == o || memcmp(string,o->toChars(),len+1) == 0; +} + +int Identifier::compare(Object *o) +{ + return memcmp(string, o->toChars(), len + 1); +} + +char *Identifier::toChars() +{ + return (char *)string; +} + +const char *Identifier::toHChars2() +{ + const char *p = NULL; + + if (this == Id::ctor) p = "this"; + else if (this == Id::dtor) p = "~this"; + else if (this == Id::classInvariant) p = "invariant"; + else if (this == Id::unitTest) p = "unittest"; + else if (this == Id::dollar) p = "$"; + else if (this == Id::withSym) p = "with"; + else if (this == Id::result) p = "result"; + else if (this == Id::returnLabel) p = "return"; + else + { p = toChars(); + if (*p == '_') + { + if (memcmp(p, "_staticCtor", 11) == 0) + p = "static this"; + else if (memcmp(p, "_staticDtor", 11) == 0) + p = "static ~this"; + } + } + + return p; +} + +void Identifier::print() +{ + fprintf(stdmsg, "%s",string); +} + +int Identifier::dyncast() +{ + return DYNCAST_IDENTIFIER; +} + +// BUG: these are redundant with Lexer::uniqueId() + +Identifier *Identifier::generateId(const char *prefix) +{ + static size_t i; + + return generateId(prefix, ++i); +} + +Identifier *Identifier::generateId(const char *prefix, size_t i) +{ OutBuffer buf; + + buf.writestring(prefix); + buf.printf("%zu", i); + + char *id = buf.toChars(); + buf.data = NULL; + return Lexer::idPool(id); +} diff --git a/identifier.h b/identifier.h new file mode 100644 index 00000000..b786fca9 --- /dev/null +++ b/identifier.h @@ -0,0 +1,40 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_IDENTIFIER_H +#define DMD_IDENTIFIER_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "root.h" + +struct Identifier : Object +{ + int value; + const char *string; + unsigned len; + + Identifier(const char *string, int value); + int equals(Object *o); + hash_t hashCode(); + int compare(Object *o); + void print(); + char *toChars(); + char *toHChars(); + const char *toHChars2(); + int dyncast(); + + static Identifier *generateId(const char *prefix); + static Identifier *generateId(const char *prefix, size_t i); +}; + +#endif /* DMD_IDENTIFIER_H */ diff --git a/idgen.c b/idgen.c new file mode 100644 index 00000000..075c73d0 --- /dev/null +++ b/idgen.c @@ -0,0 +1,412 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// http://www.dsource.org/projects/dmd/browser/trunk/src/idgen.c +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +// Program to generate string files in d data structures. +// Saves much tedious typing, and eliminates typo problems. +// Generates: +// id.h +// id.c + +#include +#include +#include +#include + +struct Msgtable +{ + const char *ident; // name to use in DMD source + const char *name; // name in D executable +}; + +Msgtable msgtable[] = +{ + { "IUnknown" }, + { "Object" }, + { "object" }, + { "max" }, + { "min" }, + { "This", "this" }, + { "super" }, + { "ctor", "__ctor" }, + { "dtor", "__dtor" }, + { "cpctor", "__cpctor" }, + { "_postblit", "__postblit" }, + { "classInvariant", "__invariant" }, + { "unitTest", "__unitTest" }, + { "require", "__require" }, + { "ensure", "__ensure" }, + { "init" }, + { "size" }, + { "__sizeof", "sizeof" }, + { "__xalignof", "alignof" }, + { "mangleof" }, + { "stringof" }, + { "tupleof" }, + { "length" }, + { "remove" }, + { "ptr" }, + { "array" }, + { "funcptr" }, + { "dollar", "__dollar" }, + { "ctfe", "__ctfe" }, + { "offset" }, + { "offsetof" }, + { "ModuleInfo" }, + { "ClassInfo" }, + { "classinfo" }, + { "typeinfo" }, + { "outer" }, + { "Exception" }, + { "AssociativeArray" }, + { "Throwable" }, + { "Error" }, + { "withSym", "__withSym" }, + { "result", "__result" }, + { "returnLabel", "__returnLabel" }, + { "delegate" }, + { "line" }, + { "empty", "" }, + { "p" }, + { "q" }, + { "coverage", "__coverage" }, + { "__vptr" }, + { "__monitor" }, + + { "TypeInfo" }, + { "TypeInfo_Class" }, + { "TypeInfo_Interface" }, + { "TypeInfo_Struct" }, + { "TypeInfo_Enum" }, + { "TypeInfo_Typedef" }, + { "TypeInfo_Pointer" }, + { "TypeInfo_Vector" }, + { "TypeInfo_Array" }, + { "TypeInfo_StaticArray" }, + { "TypeInfo_AssociativeArray" }, + { "TypeInfo_Function" }, + { "TypeInfo_Delegate" }, + { "TypeInfo_Tuple" }, + { "TypeInfo_Const" }, + { "TypeInfo_Invariant" }, + { "TypeInfo_Shared" }, + { "TypeInfo_Wild", "TypeInfo_Inout" }, + { "elements" }, + { "_arguments_typeinfo" }, + { "_arguments" }, + { "_argptr" }, + { "_match" }, + { "destroy" }, + { "postblit" }, + + { "LINE", "__LINE__" }, + { "FILE", "__FILE__" }, + { "DATE", "__DATE__" }, + { "TIME", "__TIME__" }, + { "TIMESTAMP", "__TIMESTAMP__" }, + { "VENDOR", "__VENDOR__" }, + { "VERSIONX", "__VERSION__" }, + { "EOFX", "__EOF__" }, + + { "nan" }, + { "infinity" }, + { "dig" }, + { "epsilon" }, + { "mant_dig" }, + { "max_10_exp" }, + { "max_exp" }, + { "min_10_exp" }, + { "min_exp" }, + { "min_normal" }, + { "re" }, + { "im" }, + + { "C" }, + { "D" }, + { "Windows" }, + { "Pascal" }, + { "System" }, + + { "exit" }, + { "success" }, + { "failure" }, + + { "keys" }, + { "values" }, + { "rehash" }, + + { "sort" }, + { "reverse" }, + { "dup" }, + { "idup" }, + + { "property" }, + { "safe" }, + { "trusted" }, + { "system" }, + { "disable" }, + + // For inline assembler + { "___out", "out" }, + { "___in", "in" }, + { "__int", "int" }, + { "__dollar", "$" }, + { "__LOCAL_SIZE" }, + + // For operator overloads + { "uadd", "opPos" }, + { "neg", "opNeg" }, + { "com", "opCom" }, + { "add", "opAdd" }, + { "add_r", "opAdd_r" }, + { "sub", "opSub" }, + { "sub_r", "opSub_r" }, + { "mul", "opMul" }, + { "mul_r", "opMul_r" }, + { "div", "opDiv" }, + { "div_r", "opDiv_r" }, + { "mod", "opMod" }, + { "mod_r", "opMod_r" }, + { "eq", "opEquals" }, + { "cmp", "opCmp" }, + { "iand", "opAnd" }, + { "iand_r", "opAnd_r" }, + { "ior", "opOr" }, + { "ior_r", "opOr_r" }, + { "ixor", "opXor" }, + { "ixor_r", "opXor_r" }, + { "shl", "opShl" }, + { "shl_r", "opShl_r" }, + { "shr", "opShr" }, + { "shr_r", "opShr_r" }, + { "ushr", "opUShr" }, + { "ushr_r", "opUShr_r" }, + { "cat", "opCat" }, + { "cat_r", "opCat_r" }, + { "assign", "opAssign" }, + { "addass", "opAddAssign" }, + { "subass", "opSubAssign" }, + { "mulass", "opMulAssign" }, + { "divass", "opDivAssign" }, + { "modass", "opModAssign" }, + { "andass", "opAndAssign" }, + { "orass", "opOrAssign" }, + { "xorass", "opXorAssign" }, + { "shlass", "opShlAssign" }, + { "shrass", "opShrAssign" }, + { "ushrass", "opUShrAssign" }, + { "catass", "opCatAssign" }, + { "postinc", "opPostInc" }, + { "postdec", "opPostDec" }, + { "index", "opIndex" }, + { "indexass", "opIndexAssign" }, + { "slice", "opSlice" }, + { "sliceass", "opSliceAssign" }, + { "call", "opCall" }, + { "cast", "opCast" }, + { "match", "opMatch" }, + { "next", "opNext" }, + { "opIn" }, + { "opIn_r" }, + { "opStar" }, + { "opDot" }, + { "opDispatch" }, + { "opDollar" }, + { "opUnary" }, + { "opIndexUnary" }, + { "opSliceUnary" }, + { "opBinary" }, + { "opBinaryRight" }, + { "opOpAssign" }, + { "opIndexOpAssign" }, + { "opSliceOpAssign" }, + { "pow", "opPow" }, + { "pow_r", "opPow_r" }, + { "powass", "opPowAssign" }, + + { "classNew", "new" }, + { "classDelete", "delete" }, + + // For foreach + { "apply", "opApply" }, + { "applyReverse", "opApplyReverse" }, + + // Ranges + { "Fempty", "empty" }, + { "Ffront", "front" }, + { "Fback", "back" }, + { "FpopFront", "popFront" }, + { "FpopBack", "popBack" }, + + { "adDup", "_adDupT" }, + { "adReverse", "_adReverse" }, + + // For internal functions + { "aaLen", "_aaLen" }, + { "aaKeys", "_aaKeys" }, + { "aaValues", "_aaValues" }, + { "aaRehash", "_aaRehash" }, + { "monitorenter", "_d_monitorenter" }, + { "monitorexit", "_d_monitorexit" }, + { "criticalenter", "_d_criticalenter" }, + { "criticalexit", "_d_criticalexit" }, + { "_ArrayEq" }, + + // For pragma's + { "GNU_asm" }, + { "lib" }, + { "msg" }, + { "startaddress" }, + + // For special functions + { "tohash", "toHash" }, + { "tostring", "toString" }, + { "getmembers", "getMembers" }, + + // Special functions + { "alloca" }, + { "main" }, + { "WinMain" }, + { "DllMain" }, + { "tls_get_addr", "___tls_get_addr" }, + + // varargs implementation + { "va_argsave_t", "__va_argsave_t" }, + { "va_argsave", "__va_argsave" }, + + // Builtin functions + { "std" }, + { "core" }, + { "math" }, + { "sin" }, + { "cos" }, + { "tan" }, + { "_sqrt", "sqrt" }, + { "_pow", "pow" }, + { "atan2" }, + { "rndtol" }, + { "expm1" }, + { "exp2" }, + { "yl2x" }, + { "yl2xp1" }, + { "fabs" }, + { "bitop" }, + { "bsf" }, + { "bsr" }, + { "bswap" }, + + // Traits + { "isAbstractClass" }, + { "isArithmetic" }, + { "isAssociativeArray" }, + { "isFinalClass" }, + { "isFloating" }, + { "isIntegral" }, + { "isScalar" }, + { "isStaticArray" }, + { "isUnsigned" }, + { "isVirtualFunction" }, + { "isVirtualMethod" }, + { "isAbstractFunction" }, + { "isFinalFunction" }, + { "isStaticFunction" }, + { "isRef" }, + { "isOut" }, + { "isLazy" }, + { "hasMember" }, + { "identifier" }, + { "parent" }, + { "getMember" }, + { "getOverloads" }, + { "getVirtualFunctions" }, + { "getVirtualMethods" }, + { "classInstanceSize" }, + { "allMembers" }, + { "derivedMembers" }, + { "isSame" }, + { "compiles" }, +}; + + +int main() +{ + FILE *fp; + unsigned i; + + { + fp = fopen("id.h","w"); + if (!fp) + { printf("can't open id.h\n"); + exit(EXIT_FAILURE); + } + + fprintf(fp, "// File generated by idgen.c\n"); +#if __DMC__ + fprintf(fp, "#pragma once\n"); +#endif + fprintf(fp, "#ifndef DMD_ID_H\n"); + fprintf(fp, "#define DMD_ID_H 1\n"); + fprintf(fp, "struct Identifier;\n"); + fprintf(fp, "struct Id\n"); + fprintf(fp, "{\n"); + + for (i = 0; i < sizeof(msgtable) / sizeof(msgtable[0]); i++) + { const char *id = msgtable[i].ident; + + fprintf(fp," static Identifier *%s;\n", id); + } + + fprintf(fp, " static void initialize();\n"); + fprintf(fp, "};\n"); + fprintf(fp, "#endif\n"); + + fclose(fp); + } + + { + fp = fopen("id.c","w"); + if (!fp) + { printf("can't open id.c\n"); + exit(EXIT_FAILURE); + } + + fprintf(fp, "// File generated by idgen.c\n"); + fprintf(fp, "#include \"id.h\"\n"); + fprintf(fp, "#include \"identifier.h\"\n"); + fprintf(fp, "#include \"lexer.h\"\n"); + + for (i = 0; i < sizeof(msgtable) / sizeof(msgtable[0]); i++) + { const char *id = msgtable[i].ident; + const char *p = msgtable[i].name; + + if (!p) + p = id; + fprintf(fp,"Identifier *Id::%s;\n", id); + } + + fprintf(fp, "void Id::initialize()\n"); + fprintf(fp, "{\n"); + + for (i = 0; i < sizeof(msgtable) / sizeof(msgtable[0]); i++) + { const char *id = msgtable[i].ident; + const char *p = msgtable[i].name; + + if (!p) + p = id; + fprintf(fp," %s = Lexer::idPool(\"%s\");\n", id, p); + } + + fprintf(fp, "}\n"); + + fclose(fp); + } + + return EXIT_SUCCESS; +} diff --git a/impcnvgen.c b/impcnvgen.c new file mode 100644 index 00000000..8f182364 --- /dev/null +++ b/impcnvgen.c @@ -0,0 +1,427 @@ + +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "mtype.h" + +TY impcnvResult[TMAX][TMAX]; +TY impcnvType1[TMAX][TMAX]; +TY impcnvType2[TMAX][TMAX]; +int impcnvWarn[TMAX][TMAX]; + +int integral_promotion(int t) +{ + switch (t) + { + case Tchar: + case Twchar: + case Tbool: + case Tint8: + case Tuns8: + case Tint16: + case Tuns16: return Tint32; + case Tdchar: return Tuns32; + default: return t; + } +} + +void init() +{ int i, j; + + // Set conversion tables + for (i = 0; i < TMAX; i++) + for (j = 0; j < TMAX; j++) + { impcnvResult[i][j] = Terror; + impcnvType1[i][j] = Terror; + impcnvType2[i][j] = Terror; + impcnvWarn[i][j] = 0; + } + +#define X(t1,t2, nt1,nt2, rt) \ + impcnvResult[t1][t2] = rt; \ + impcnvType1[t1][t2] = nt1; \ + impcnvType2[t1][t2] = nt2; + + + /* ======================= */ + + X(Tbool,Tbool, Tbool,Tbool, Tbool) + X(Tbool,Tint8, Tint32,Tint32, Tint32) + X(Tbool,Tuns8, Tint32,Tint32, Tint32) + X(Tbool,Tint16, Tint32,Tint32, Tint32) + X(Tbool,Tuns16, Tint32,Tint32, Tint32) + X(Tbool,Tint32, Tint32,Tint32, Tint32) + X(Tbool,Tuns32, Tuns32,Tuns32, Tuns32) + X(Tbool,Tint64, Tint64,Tint64, Tint64) + X(Tbool,Tuns64, Tuns64,Tuns64, Tuns64) + + X(Tbool,Tfloat32, Tfloat32,Tfloat32, Tfloat32) + X(Tbool,Tfloat64, Tfloat64,Tfloat64, Tfloat64) + X(Tbool,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + X(Tbool,Timaginary32, Tfloat32,Timaginary32, Tfloat32) + X(Tbool,Timaginary64, Tfloat64,Timaginary64, Tfloat64) + X(Tbool,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + X(Tbool,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32) + X(Tbool,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) + X(Tbool,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tint8,Tint8, Tint32,Tint32, Tint32) + X(Tint8,Tuns8, Tint32,Tint32, Tint32) + X(Tint8,Tint16, Tint32,Tint32, Tint32) + X(Tint8,Tuns16, Tint32,Tint32, Tint32) + X(Tint8,Tint32, Tint32,Tint32, Tint32) + X(Tint8,Tuns32, Tuns32,Tuns32, Tuns32) + X(Tint8,Tint64, Tint64,Tint64, Tint64) + X(Tint8,Tuns64, Tuns64,Tuns64, Tuns64) + + X(Tint8,Tfloat32, Tfloat32,Tfloat32, Tfloat32) + X(Tint8,Tfloat64, Tfloat64,Tfloat64, Tfloat64) + X(Tint8,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + X(Tint8,Timaginary32, Tfloat32,Timaginary32, Tfloat32) + X(Tint8,Timaginary64, Tfloat64,Timaginary64, Tfloat64) + X(Tint8,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + X(Tint8,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32) + X(Tint8,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) + X(Tint8,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tuns8,Tuns8, Tint32,Tint32, Tint32) + X(Tuns8,Tint16, Tint32,Tint32, Tint32) + X(Tuns8,Tuns16, Tint32,Tint32, Tint32) + X(Tuns8,Tint32, Tint32,Tint32, Tint32) + X(Tuns8,Tuns32, Tuns32,Tuns32, Tuns32) + X(Tuns8,Tint64, Tint64,Tint64, Tint64) + X(Tuns8,Tuns64, Tuns64,Tuns64, Tuns64) + + X(Tuns8,Tfloat32, Tfloat32,Tfloat32, Tfloat32) + X(Tuns8,Tfloat64, Tfloat64,Tfloat64, Tfloat64) + X(Tuns8,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + X(Tuns8,Timaginary32, Tfloat32,Timaginary32, Tfloat32) + X(Tuns8,Timaginary64, Tfloat64,Timaginary64, Tfloat64) + X(Tuns8,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + X(Tuns8,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32) + X(Tuns8,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) + X(Tuns8,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tint16,Tint16, Tint32,Tint32, Tint32) + X(Tint16,Tuns16, Tint32,Tint32, Tint32) + X(Tint16,Tint32, Tint32,Tint32, Tint32) + X(Tint16,Tuns32, Tuns32,Tuns32, Tuns32) + X(Tint16,Tint64, Tint64,Tint64, Tint64) + X(Tint16,Tuns64, Tuns64,Tuns64, Tuns64) + + X(Tint16,Tfloat32, Tfloat32,Tfloat32, Tfloat32) + X(Tint16,Tfloat64, Tfloat64,Tfloat64, Tfloat64) + X(Tint16,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + X(Tint16,Timaginary32, Tfloat32,Timaginary32, Tfloat32) + X(Tint16,Timaginary64, Tfloat64,Timaginary64, Tfloat64) + X(Tint16,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + X(Tint16,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32) + X(Tint16,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) + X(Tint16,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tuns16,Tuns16, Tint32,Tint32, Tint32) + X(Tuns16,Tint32, Tint32,Tint32, Tint32) + X(Tuns16,Tuns32, Tuns32,Tuns32, Tuns32) + X(Tuns16,Tint64, Tint64,Tint64, Tint64) + X(Tuns16,Tuns64, Tuns64,Tuns64, Tuns64) + + X(Tuns16,Tfloat32, Tfloat32,Tfloat32, Tfloat32) + X(Tuns16,Tfloat64, Tfloat64,Tfloat64, Tfloat64) + X(Tuns16,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + X(Tuns16,Timaginary32, Tfloat32,Timaginary32, Tfloat32) + X(Tuns16,Timaginary64, Tfloat64,Timaginary64, Tfloat64) + X(Tuns16,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + X(Tuns16,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32) + X(Tuns16,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) + X(Tuns16,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tint32,Tint32, Tint32,Tint32, Tint32) + X(Tint32,Tuns32, Tuns32,Tuns32, Tuns32) + X(Tint32,Tint64, Tint64,Tint64, Tint64) + X(Tint32,Tuns64, Tuns64,Tuns64, Tuns64) + + X(Tint32,Tfloat32, Tfloat32,Tfloat32, Tfloat32) + X(Tint32,Tfloat64, Tfloat64,Tfloat64, Tfloat64) + X(Tint32,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + X(Tint32,Timaginary32, Tfloat32,Timaginary32, Tfloat32) + X(Tint32,Timaginary64, Tfloat64,Timaginary64, Tfloat64) + X(Tint32,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + X(Tint32,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32) + X(Tint32,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) + X(Tint32,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tuns32,Tuns32, Tuns32,Tuns32, Tuns32) + X(Tuns32,Tint64, Tint64,Tint64, Tint64) + X(Tuns32,Tuns64, Tuns64,Tuns64, Tuns64) + + X(Tuns32,Tfloat32, Tfloat32,Tfloat32, Tfloat32) + X(Tuns32,Tfloat64, Tfloat64,Tfloat64, Tfloat64) + X(Tuns32,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + X(Tuns32,Timaginary32, Tfloat32,Timaginary32, Tfloat32) + X(Tuns32,Timaginary64, Tfloat64,Timaginary64, Tfloat64) + X(Tuns32,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + X(Tuns32,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32) + X(Tuns32,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) + X(Tuns32,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tint64,Tint64, Tint64,Tint64, Tint64) + X(Tint64,Tuns64, Tuns64,Tuns64, Tuns64) + + X(Tint64,Tfloat32, Tfloat32,Tfloat32, Tfloat32) + X(Tint64,Tfloat64, Tfloat64,Tfloat64, Tfloat64) + X(Tint64,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + X(Tint64,Timaginary32, Tfloat32,Timaginary32, Tfloat32) + X(Tint64,Timaginary64, Tfloat64,Timaginary64, Tfloat64) + X(Tint64,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + X(Tint64,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32) + X(Tint64,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) + X(Tint64,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tuns64,Tuns64, Tuns64,Tuns64, Tuns64) + + X(Tuns64,Tfloat32, Tfloat32,Tfloat32, Tfloat32) + X(Tuns64,Tfloat64, Tfloat64,Tfloat64, Tfloat64) + X(Tuns64,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + X(Tuns64,Timaginary32, Tfloat32,Timaginary32, Tfloat32) + X(Tuns64,Timaginary64, Tfloat64,Timaginary64, Tfloat64) + X(Tuns64,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + X(Tuns64,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32) + X(Tuns64,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) + X(Tuns64,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tfloat32,Tfloat32, Tfloat32,Tfloat32, Tfloat32) + X(Tfloat32,Tfloat64, Tfloat64,Tfloat64, Tfloat64) + X(Tfloat32,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + + X(Tfloat32,Timaginary32, Tfloat32,Timaginary32, Tfloat32) + X(Tfloat32,Timaginary64, Tfloat64,Timaginary64, Tfloat64) + X(Tfloat32,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + + X(Tfloat32,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32) + X(Tfloat32,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) + X(Tfloat32,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tfloat64,Tfloat64, Tfloat64,Tfloat64, Tfloat64) + X(Tfloat64,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + + X(Tfloat64,Timaginary32, Tfloat64,Timaginary64, Tfloat64) + X(Tfloat64,Timaginary64, Tfloat64,Timaginary64, Tfloat64) + X(Tfloat64,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + + X(Tfloat64,Tcomplex32, Tfloat64,Tcomplex64, Tcomplex64) + X(Tfloat64,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) + X(Tfloat64,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tfloat80,Tfloat80, Tfloat80,Tfloat80, Tfloat80) + + X(Tfloat80,Timaginary32, Tfloat80,Timaginary80, Tfloat80) + X(Tfloat80,Timaginary64, Tfloat80,Timaginary80, Tfloat80) + X(Tfloat80,Timaginary80, Tfloat80,Timaginary80, Tfloat80) + + X(Tfloat80,Tcomplex32, Tfloat80,Tcomplex80, Tcomplex80) + X(Tfloat80,Tcomplex64, Tfloat80,Tcomplex80, Tcomplex80) + X(Tfloat80,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Timaginary32,Timaginary32, Timaginary32,Timaginary32, Timaginary32) + X(Timaginary32,Timaginary64, Timaginary64,Timaginary64, Timaginary64) + X(Timaginary32,Timaginary80, Timaginary80,Timaginary80, Timaginary80) + + X(Timaginary32,Tcomplex32, Timaginary32,Tcomplex32, Tcomplex32) + X(Timaginary32,Tcomplex64, Timaginary64,Tcomplex64, Tcomplex64) + X(Timaginary32,Tcomplex80, Timaginary80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Timaginary64,Timaginary64, Timaginary64,Timaginary64, Timaginary64) + X(Timaginary64,Timaginary80, Timaginary80,Timaginary80, Timaginary80) + + X(Timaginary64,Tcomplex32, Timaginary64,Tcomplex64, Tcomplex64) + X(Timaginary64,Tcomplex64, Timaginary64,Tcomplex64, Tcomplex64) + X(Timaginary64,Tcomplex80, Timaginary80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Timaginary80,Timaginary80, Timaginary80,Timaginary80, Timaginary80) + + X(Timaginary80,Tcomplex32, Timaginary80,Tcomplex80, Tcomplex80) + X(Timaginary80,Tcomplex64, Timaginary80,Tcomplex80, Tcomplex80) + X(Timaginary80,Tcomplex80, Timaginary80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tcomplex32,Tcomplex32, Tcomplex32,Tcomplex32, Tcomplex32) + X(Tcomplex32,Tcomplex64, Tcomplex64,Tcomplex64, Tcomplex64) + X(Tcomplex32,Tcomplex80, Tcomplex80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tcomplex64,Tcomplex64, Tcomplex64,Tcomplex64, Tcomplex64) + X(Tcomplex64,Tcomplex80, Tcomplex80,Tcomplex80, Tcomplex80) + + /* ======================= */ + + X(Tcomplex80,Tcomplex80, Tcomplex80,Tcomplex80, Tcomplex80) + +#undef X + +#define Y(t1,t2) impcnvWarn[t1][t2] = 1; + + Y(Tuns8, Tint8) + Y(Tint16, Tint8) + Y(Tuns16, Tint8) + Y(Tint32, Tint8) + Y(Tuns32, Tint8) + Y(Tint64, Tint8) + Y(Tuns64, Tint8) + + Y(Tint8, Tuns8) + Y(Tint16, Tuns8) + Y(Tuns16, Tuns8) + Y(Tint32, Tuns8) + Y(Tuns32, Tuns8) + Y(Tint64, Tuns8) + Y(Tuns64, Tuns8) + + Y(Tint8, Tchar) + Y(Tint16, Tchar) + Y(Tuns16, Tchar) + Y(Tint32, Tchar) + Y(Tuns32, Tchar) + Y(Tint64, Tchar) + Y(Tuns64, Tchar) + + Y(Tuns16, Tint16) + Y(Tint32, Tint16) + Y(Tuns32, Tint16) + Y(Tint64, Tint16) + Y(Tuns64, Tint16) + + Y(Tint16, Tuns16) + Y(Tint32, Tuns16) + Y(Tuns32, Tuns16) + Y(Tint64, Tuns16) + Y(Tuns64, Tuns16) + + Y(Tint16, Twchar) + Y(Tint32, Twchar) + Y(Tuns32, Twchar) + Y(Tint64, Twchar) + Y(Tuns64, Twchar) + +// Y(Tuns32, Tint32) + Y(Tint64, Tint32) + Y(Tuns64, Tint32) + +// Y(Tint32, Tuns32) + Y(Tint64, Tuns32) + Y(Tuns64, Tuns32) + + Y(Tint64, Tdchar) + Y(Tuns64, Tdchar) + +// Y(Tint64, Tuns64) +// Y(Tuns64, Tint64) + + for (i = 0; i < TMAX; i++) + for (j = 0; j < TMAX; j++) + { + if (impcnvResult[i][j] == Terror) + { + impcnvResult[i][j] = impcnvResult[j][i]; + impcnvType1[i][j] = impcnvType2[j][i]; + impcnvType2[i][j] = impcnvType1[j][i]; + } + } +} + +int main() +{ FILE *fp; + int i; + int j; + + init(); + + fp = fopen("impcnvtab.c","w"); + + fprintf(fp,"// This file is generated by impcnvgen.c\n"); + fprintf(fp,"#include \"mtype.h\"\n"); + + fprintf(fp,"unsigned char Type::impcnvResult[TMAX][TMAX] =\n{\n"); + for (i = 0; i < TMAX; i++) + { + for (j = 0; j < TMAX; j++) + { + fprintf(fp, "%d,",impcnvResult[i][j]); + } + fprintf(fp, "\n"); + } + fprintf(fp,"};\n"); + + fprintf(fp,"unsigned char Type::impcnvType1[TMAX][TMAX] =\n{\n"); + for (i = 0; i < TMAX; i++) + { + for (j = 0; j < TMAX; j++) + { + fprintf(fp, "%d,",impcnvType1[i][j]); + } + fprintf(fp, "\n"); + } + fprintf(fp,"};\n"); + + fprintf(fp,"unsigned char Type::impcnvType2[TMAX][TMAX] =\n{\n"); + for (i = 0; i < TMAX; i++) + { + for (j = 0; j < TMAX; j++) + { + fprintf(fp, "%d,",impcnvType2[i][j]); + } + fprintf(fp, "\n"); + } + fprintf(fp,"};\n"); + + fprintf(fp,"unsigned char Type::impcnvWarn[TMAX][TMAX] =\n{\n"); + for (i = 0; i < TMAX; i++) + { + for (j = 0; j < TMAX; j++) + { + fprintf(fp, "%d,",impcnvWarn[i][j]); + } + fprintf(fp, "\n"); + } + fprintf(fp,"};\n"); + + fclose(fp); + return EXIT_SUCCESS; +} diff --git a/imphint.c b/imphint.c new file mode 100644 index 00000000..09b057d8 --- /dev/null +++ b/imphint.c @@ -0,0 +1,88 @@ + + +// Compiler implementation of the D programming language +// Copyright (c) 2010 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include +#include + +#include "mars.h" + +/****************************************** + * Looks for undefined identifier s to see + * if it might be undefined because an import + * was not specified. + * Not meant to be a comprehensive list of names in each module, + * just the most common ones. + */ + +const char *importHint(const char *s) +{ +#if DMDV1 + static const char *modules[] = + { "std.c.stdio", + "std.stdio", + "std.math", + "std.c.stdarg", + }; + static const char *names[] = + { + "printf", NULL, + "writefln", NULL, + "sin", "cos", "sqrt", "fabs", NULL, + "__va_argsave_t", NULL, + }; +#else + static const char *modules[] = + { "core.stdc.stdio", + "std.stdio", + "std.math", + "core.vararg", + }; + static const char *names[] = + { + "printf", NULL, + "writeln", NULL, + "sin", "cos", "sqrt", "fabs", NULL, + "__va_argsave_t", NULL, + }; +#endif + int m = 0; + for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) + { + const char *p = names[n]; + if (p == NULL) + { m++; + continue; + } + assert(m < sizeof(modules)/sizeof(modules[0])); + if (strcmp(s, p) == 0) + return modules[m]; + } + return NULL; // didn't find it +} + +#if UNITTEST + +void unittest_importHint() +{ + const char *p; + + p = importHint("printf"); + assert(p); + p = importHint("fabs"); + assert(p); + p = importHint("xxxxx"); + assert(!p); +} + +#endif diff --git a/import.c b/import.c new file mode 100644 index 00000000..bd43af90 --- /dev/null +++ b/import.c @@ -0,0 +1,397 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "root.h" +#include "dsymbol.h" +#include "import.h" +#include "identifier.h" +#include "module.h" +#include "scope.h" +#include "hdrgen.h" +#include "mtype.h" +#include "declaration.h" +#include "id.h" +#include "attrib.h" + +/********************************* Import ****************************/ + +Import::Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId, + int isstatic) + : Dsymbol(NULL) +{ + assert(id); + this->loc = loc; + this->packages = packages; + this->id = id; + this->aliasId = aliasId; + this->isstatic = isstatic; + this->protection = PROTprivate; // default to private + this->pkg = NULL; + this->mod = NULL; + + // Set symbol name (bracketed) + // import [cstdio] = std.stdio; + if (aliasId) + this->ident = aliasId; + // import [std].stdio; + else if (packages && packages->dim) + this->ident = packages->tdata()[0]; + // import [foo]; + else + this->ident = id; +} + +void Import::addAlias(Identifier *name, Identifier *alias) +{ + if (isstatic) + error("cannot have an import bind list"); + + if (!aliasId) + this->ident = NULL; // make it an anonymous import + + names.push(name); + aliases.push(alias); +} + +const char *Import::kind() +{ + return isstatic ? (char *)"static import" : (char *)"import"; +} + +enum PROT Import::prot() +{ + return protection; +} + +Dsymbol *Import::syntaxCopy(Dsymbol *s) +{ + assert(!s); + + Import *si; + + si = new Import(loc, packages, id, aliasId, isstatic); + + for (size_t i = 0; i < names.dim; i++) + { + si->addAlias(names.tdata()[i], aliases.tdata()[i]); + } + + return si; +} + +void Import::load(Scope *sc) +{ + //printf("Import::load('%s')\n", toChars()); + + // See if existing module + DsymbolTable *dst = Package::resolve(packages, NULL, &pkg); + + Dsymbol *s = dst->lookup(id); + if (s) + { +#if TARGET_NET + mod = (Module *)s; +#else + if (s->isModule()) + mod = (Module *)s; + else + error("package and module have the same name"); +#endif + } + + if (!mod) + { + // Load module + mod = Module::load(loc, packages, id); + dst->insert(id, mod); // id may be different from mod->ident, + // if so then insert alias + if (!mod->importedFrom) + mod->importedFrom = sc ? sc->module->importedFrom : Module::rootModule; + } + if (!pkg) + pkg = mod; + + //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg); +} + +void escapePath(OutBuffer *buf, const char *fname) +{ + while (1) + { + switch (*fname) + { + case 0: + return; + case '(': + case ')': + case '\\': + buf->writebyte('\\'); + default: + buf->writebyte(*fname); + break; + } + fname++; + } +} + +void Import::importAll(Scope *sc) +{ + if (!mod) + { + load(sc); + mod->importAll(0); + + if (!isstatic && !aliasId && !names.dim) + { + if (sc->explicitProtection) + protection = sc->protection; + sc->scopesym->importScope(mod, protection); + } + } +} + +void Import::semantic(Scope *sc) +{ + //printf("Import::semantic('%s')\n", toChars()); + + // Load if not already done so + if (!mod) + { load(sc); + mod->importAll(0); + } + + if (mod) + { +#if 0 + if (mod->loc.linnum != 0) + { /* If the line number is not 0, then this is not + * a 'root' module, i.e. it was not specified on the command line. + */ + mod->importedFrom = sc->module->importedFrom; + assert(mod->importedFrom); + } +#endif + + // Modules need a list of each imported module + //printf("%s imports %s\n", sc->module->toChars(), mod->toChars()); + sc->module->aimports.push(mod); + + if (!isstatic && !aliasId && !names.dim) + { + if (sc->explicitProtection) + protection = sc->protection; + for (Scope *scd = sc; scd; scd = scd->enclosing) + { + if (scd->scopesym) + { + scd->scopesym->importScope(mod, protection); + break; + } + } + } + + mod->semantic(); + + if (mod->needmoduleinfo) + { //printf("module4 %s because of %s\n", sc->module->toChars(), mod->toChars()); + sc->module->needmoduleinfo = 1; + } + + sc = sc->push(mod); + /* BUG: Protection checks can't be enabled yet. The issue is + * that Dsymbol::search errors before overload resolution. + */ +#if 0 + sc->protection = protection; +#else + sc->protection = PROTpublic; +#endif + for (size_t i = 0; i < aliasdecls.dim; i++) + { Dsymbol *s = aliasdecls.tdata()[i]; + + //printf("\tImport alias semantic('%s')\n", s->toChars()); + if (!mod->search(loc, names.tdata()[i], 0)) + error("%s not found", (names.tdata()[i])->toChars()); + + s->semantic(sc); + } + sc = sc->pop(); + } + + if (global.params.moduleDeps != NULL) + { + /* The grammar of the file is: + * ImportDeclaration + * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> " + * ModuleAliasIdentifier ] "\n" + * + * BasicImportDeclaration + * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection + * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")" + * + * FilePath + * - any string with '(', ')' and '\' escaped with the '\' character + */ + + OutBuffer *ob = global.params.moduleDeps; + + ob->writestring(sc->module->toPrettyChars()); + ob->writestring(" ("); + escapePath(ob, sc->module->srcfile->toChars()); + ob->writestring(") : "); + + ProtDeclaration::protectionToCBuffer(ob, sc->protection); + if (isstatic) + StorageClassDeclaration::stcToCBuffer(ob, STCstatic); + ob->writestring(": "); + + if (packages) + { + for (size_t i = 0; i < packages->dim; i++) + { + Identifier *pid = packages->tdata()[i]; + ob->printf("%s.", pid->toChars()); + } + } + + ob->writestring(id->toChars()); + ob->writestring(" ("); + if (mod) + escapePath(ob, mod->srcfile->toChars()); + else + ob->writestring("???"); + ob->writebyte(')'); + + for (size_t i = 0; i < names.dim; i++) + { + if (i == 0) + ob->writebyte(':'); + else + ob->writebyte(','); + + Identifier *name = names.tdata()[i]; + Identifier *alias = aliases.tdata()[i]; + + if (!alias) + { + ob->printf("%s", name->toChars()); + alias = name; + } + else + ob->printf("%s=%s", alias->toChars(), name->toChars()); + } + + if (aliasId) + ob->printf(" -> %s", aliasId->toChars()); + + ob->writenl(); + } + + //printf("-Import::semantic('%s'), pkg = %p\n", toChars(), pkg); +} + +void Import::semantic2(Scope *sc) +{ + //printf("Import::semantic2('%s')\n", toChars()); + mod->semantic2(); + if (mod->needmoduleinfo) + { //printf("module5 %s because of %s\n", sc->module->toChars(), mod->toChars()); + sc->module->needmoduleinfo = 1; + } +} + +Dsymbol *Import::toAlias() +{ + if (aliasId) + return mod; + return this; +} + +/***************************** + * Add import to sd's symbol table. + */ + +int Import::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +{ + int result = 0; + + if (names.dim == 0) + return Dsymbol::addMember(sc, sd, memnum); + + if (aliasId) + result = Dsymbol::addMember(sc, sd, memnum); + + /* Instead of adding the import to sd's symbol table, + * add each of the alias=name pairs + */ + for (size_t i = 0; i < names.dim; i++) + { + Identifier *name = names.tdata()[i]; + Identifier *alias = aliases.tdata()[i]; + + if (!alias) + alias = name; + + TypeIdentifier *tname = new TypeIdentifier(loc, name); + AliasDeclaration *ad = new AliasDeclaration(loc, alias, tname); + result |= ad->addMember(sc, sd, memnum); + + aliasdecls.push(ad); + } + + return result; +} + +Dsymbol *Import::search(Loc loc, Identifier *ident, int flags) +{ + //printf("%s.Import::search(ident = '%s', flags = x%x)\n", toChars(), ident->toChars(), flags); + + if (!pkg) + { load(NULL); + mod->semantic(); + } + + // Forward it to the package/module + return pkg->search(loc, ident, flags); +} + +int Import::overloadInsert(Dsymbol *s) +{ + // Allow multiple imports of the same name + return s->isImport() != NULL; +} + +void Import::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (hgs->hdrgen && id == Id::object) + return; // object is imported by default + + if (isstatic) + buf->writestring("static "); + buf->writestring("import "); + if (aliasId) + { + buf->printf("%s = ", aliasId->toChars()); + } + if (packages && packages->dim) + { + for (size_t i = 0; i < packages->dim; i++) + { Identifier *pid = packages->tdata()[i]; + + buf->printf("%s.", pid->toChars()); + } + } + buf->printf("%s;", id->toChars()); + buf->writenl(); +} + diff --git a/import.h b/import.h new file mode 100644 index 00000000..a3ef1a55 --- /dev/null +++ b/import.h @@ -0,0 +1,66 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2007 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_IMPORT_H +#define DMD_IMPORT_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "dsymbol.h" + + +struct Identifier; +struct Scope; +struct OutBuffer; +struct Module; +struct Package; +struct AliasDeclaration; +struct HdrGenState; + +struct Import : Dsymbol +{ + Identifiers *packages; // array of Identifier's representing packages + Identifier *id; // module Identifier + Identifier *aliasId; + int isstatic; // !=0 if static import + enum PROT protection; + + // Pairs of alias=name to bind into current namespace + Identifiers names; + Identifiers aliases; + + AliasDeclarations aliasdecls; // AliasDeclarations for names/aliases + + Module *mod; + Package *pkg; // leftmost package/module + + Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId, + int isstatic); + void addAlias(Identifier *name, Identifier *alias); + + const char *kind(); + enum PROT prot(); + Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees + void load(Scope *sc); + void importAll(Scope *sc); + void semantic(Scope *sc); + void semantic2(Scope *sc); + Dsymbol *toAlias(); + int addMember(Scope *sc, ScopeDsymbol *s, int memnum); + Dsymbol *search(Loc loc, Identifier *ident, int flags); + int overloadInsert(Dsymbol *s); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + Import *isImport() { return this; } +}; + +#endif /* DMD_IMPORT_H */ diff --git a/inifile.c b/inifile.c new file mode 100644 index 00000000..7343c124 --- /dev/null +++ b/inifile.c @@ -0,0 +1,332 @@ +/* + * Some portions copyright (c) 1994-1995 by Symantec + * Copyright (c) 1999-2011 by Digital Mars + * All Rights Reserved + * http://www.digitalmars.com + * Written by Walter Bright + * + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#include +#include +#include +#include + +#if _WIN32 +#include +#endif + +#if __APPLE__ +#include +#endif + +#if __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +// for PATH_MAX +#include +#endif + +#if __sun&&__SVR4 +#include +#endif + +#include "root.h" +#include "rmem.h" + +#define LOG 0 + +char *skipspace(const char *p); + +#if __GNUC__ +char *strupr(char *s) +{ + char *t = s; + + while (*s) + { + *s = toupper(*s); + s++; + } + + return t; +} +#endif + +/***************************** + * Read and analyze .ini file. + * Input: + * argv0 program name (argv[0]) + * inifile .ini file name + * Returns: + * file name of ini file + * Note: this is a memory leak + */ + +const char *inifile(const char *argv0x, const char *inifilex) +{ + char *argv0 = (char *)argv0x; + char *inifile = (char *)inifilex; // do const-correct later + char *path; // need path for @P macro + char *filename; + OutBuffer buf; + int envsection = 0; + +#if LOG + printf("inifile(argv0 = '%s', inifile = '%s')\n", argv0, inifile); +#endif + if (FileName::absolute(inifile)) + { + filename = inifile; + } + else + { + /* Look for inifile in the following sequence of places: + * o current directory + * o home directory + * o directory off of argv0 + * o /etc/ + */ + if (FileName::exists(inifile)) + { + filename = inifile; + } + else + { + filename = FileName::combine(getenv("HOME"), inifile); + if (!FileName::exists(filename)) + { +#if _WIN32 // This fix by Tim Matthews + char resolved_name[MAX_PATH + 1]; + if(GetModuleFileName(NULL, resolved_name, MAX_PATH + 1) && FileName::exists(resolved_name)) + { + filename = (char *)FileName::replaceName(resolved_name, inifile); + if(FileName::exists(filename)) + goto Ldone; + } +#endif + filename = (char *)FileName::replaceName(argv0, inifile); + if (!FileName::exists(filename)) + { +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#if __GLIBC__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 // This fix by Thomas Kuehne + /* argv0 might be a symbolic link, + * so try again looking past it to the real path + */ +#if __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + char resolved_name[PATH_MAX + 1]; + char* real_argv0 = realpath(argv0, resolved_name); +#else + char* real_argv0 = realpath(argv0, NULL); +#endif + //printf("argv0 = %s, real_argv0 = %p\n", argv0, real_argv0); + if (real_argv0) + { + filename = (char *)FileName::replaceName(real_argv0, inifile); +#if linux + free(real_argv0); +#endif + if (FileName::exists(filename)) + goto Ldone; + } +#else +#error use of glibc non-standard extension realpath(char*, NULL) +#endif + if (1){ + // Search PATH for argv0 + const char *p = getenv("PATH"); +#if LOG + printf("\tPATH='%s'\n", p); +#endif + Strings *paths = FileName::splitPath(p); + filename = FileName::searchPath(paths, argv0, 0); + if (!filename) + goto Letc; // argv0 not found on path + filename = (char *)FileName::replaceName(filename, inifile); + if (FileName::exists(filename)) + goto Ldone; + } + // Search /etc/ for inifile + Letc: +#endif + filename = FileName::combine((char *)"/etc/", inifile); + + Ldone: + ; + } + } + } + } + path = FileName::path(filename); +#if LOG + printf("\tpath = '%s', filename = '%s'\n", path, filename); +#endif + + File file(filename); + + if (file.read()) + return filename; // error reading file + + // Parse into lines + int eof = 0; + for (size_t i = 0; i < file.len && !eof; i++) + { + size_t linestart = i; + + for (; i < file.len; i++) + { + switch (file.buffer[i]) + { + case '\r': + break; + + case '\n': + // Skip if it was preceded by '\r' + if (i && file.buffer[i - 1] == '\r') + goto Lskip; + break; + + case 0: + case 0x1A: + eof = 1; + break; + + default: + continue; + } + break; + } + + // The line is file.buffer[linestart..i] + char *line; + size_t len; + char *p; + char *pn; + + line = (char *)&file.buffer[linestart]; + len = i - linestart; + + buf.reset(); + + // First, expand the macros. + // Macros are bracketed by % characters. + + for (size_t k = 0; k < len; k++) + { + if (line[k] == '%') + { + for (size_t j = k + 1; j < len; j++) + { + if (line[j] == '%') + { + if (j - k == 3 && memicmp(&line[k + 1], "@P", 2) == 0) + { + // %@P% is special meaning the path to the .ini file + p = path; + if (!*p) + p = (char *)"."; + } + else + { size_t len2 = j - k; + char tmp[10]; // big enough most of the time + + if (len2 <= sizeof(tmp)) + p = tmp; + else + p = (char *)alloca(len2); + len2--; + memcpy(p, &line[k + 1], len2); + p[len2] = 0; + strupr(p); + p = getenv(p); + if (!p) + p = (char *)""; + } + buf.writestring(p); + k = j; + goto L1; + } + } + } + buf.writeByte(line[k]); + L1: + ; + } + + // Remove trailing spaces + while (buf.offset && isspace(buf.data[buf.offset - 1])) + buf.offset--; + + p = buf.toChars(); + + // The expanded line is in p. + // Now parse it for meaning. + + p = skipspace(p); + switch (*p) + { + case ';': // comment + case 0: // blank + break; + + case '[': // look for [Environment] + p = skipspace(p + 1); + for (pn = p; isalnum((unsigned char)*pn); pn++) + ; + if (pn - p == 11 && + memicmp(p, "Environment", 11) == 0 && + *skipspace(pn) == ']' + ) + envsection = 1; + else + envsection = 0; + break; + + default: + if (envsection) + { + pn = p; + + // Convert name to upper case; + // remove spaces bracketing = + for (p = pn; *p; p++) + { if (islower((unsigned char)*p)) + *p &= ~0x20; + else if (isspace((unsigned char)*p)) + memmove(p, p + 1, strlen(p)); + else if (*p == '=') + { + p++; + while (isspace((unsigned char)*p)) + memmove(p, p + 1, strlen(p)); + break; + } + } + + putenv(strdup(pn)); +#if LOG + printf("\tputenv('%s')\n", pn); + //printf("getenv(\"TEST\") = '%s'\n",getenv("TEST")); +#endif + } + break; + } + + Lskip: + ; + } + return filename; +} + +/******************** + * Skip spaces. + */ + +char *skipspace(const char *p) +{ + while (isspace((unsigned char)*p)) + p++; + return (char *)p; +} + diff --git a/init.c b/init.c new file mode 100644 index 00000000..d18c434c --- /dev/null +++ b/init.c @@ -0,0 +1,897 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "mars.h" +#include "init.h" +#include "expression.h" +#include "statement.h" +#include "identifier.h" +#include "declaration.h" +#include "aggregate.h" +#include "scope.h" +#include "mtype.h" +#include "hdrgen.h" + +/********************************** Initializer *******************************/ + +Initializer::Initializer(Loc loc) +{ + this->loc = loc; +} + +Initializer *Initializer::syntaxCopy() +{ + return this; +} + +Initializer *Initializer::semantic(Scope *sc, Type *t, int needInterpret) +{ + return this; +} + +Type *Initializer::inferType(Scope *sc) +{ + error(loc, "cannot infer type from initializer"); + return Type::terror; +} + +Initializers *Initializer::arraySyntaxCopy(Initializers *ai) +{ Initializers *a = NULL; + + if (ai) + { + a = new Initializers(); + a->setDim(ai->dim); + for (size_t i = 0; i < a->dim; i++) + { Initializer *e = ai->tdata()[i]; + + e = e->syntaxCopy(); + a->tdata()[i] = e; + } + } + return a; +} + +char *Initializer::toChars() +{ OutBuffer *buf; + HdrGenState hgs; + + memset(&hgs, 0, sizeof(hgs)); + buf = new OutBuffer(); + toCBuffer(buf, &hgs); + return buf->toChars(); +} + +/********************************** VoidInitializer ***************************/ + +VoidInitializer::VoidInitializer(Loc loc) + : Initializer(loc) +{ + type = NULL; +} + + +Initializer *VoidInitializer::syntaxCopy() +{ + return new VoidInitializer(loc); +} + + +Initializer *VoidInitializer::semantic(Scope *sc, Type *t, int needInterpret) +{ + //printf("VoidInitializer::semantic(t = %p)\n", t); + type = t; + return this; +} + + +Expression *VoidInitializer::toExpression() +{ + error(loc, "void initializer has no value"); + return new IntegerExp(0); +} + + +void VoidInitializer::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("void"); +} + + +/********************************** StructInitializer *************************/ + +StructInitializer::StructInitializer(Loc loc) + : Initializer(loc) +{ + ad = NULL; +} + +Initializer *StructInitializer::syntaxCopy() +{ + StructInitializer *ai = new StructInitializer(loc); + + assert(field.dim == value.dim); + ai->field.setDim(field.dim); + ai->value.setDim(value.dim); + for (size_t i = 0; i < field.dim; i++) + { + ai->field.tdata()[i] = field.tdata()[i]; + + Initializer *init = value.tdata()[i]; + init = init->syntaxCopy(); + ai->value.tdata()[i] = init; + } + return ai; +} + +void StructInitializer::addInit(Identifier *field, Initializer *value) +{ + //printf("StructInitializer::addInit(field = %p, value = %p)\n", field, value); + this->field.push(field); + this->value.push(value); +} + +Initializer *StructInitializer::semantic(Scope *sc, Type *t, int needInterpret) +{ + int errors = 0; + + //printf("StructInitializer::semantic(t = %s) %s\n", t->toChars(), toChars()); + vars.setDim(field.dim); + t = t->toBasetype(); + if (t->ty == Tstruct) + { + unsigned fieldi = 0; + + TypeStruct *ts = (TypeStruct *)t; + ad = ts->sym; + if (ad->ctor) + error(loc, "%s %s has constructors, cannot use { initializers }, use %s( initializers ) instead", + ad->kind(), ad->toChars(), ad->toChars()); + size_t nfields = ad->fields.dim; + if (((StructDeclaration *)ad)->isnested) nfields--; + for (size_t i = 0; i < field.dim; i++) + { + Identifier *id = field.tdata()[i]; + Initializer *val = value.tdata()[i]; + Dsymbol *s; + VarDeclaration *v; + + if (id == NULL) + { + if (fieldi >= nfields) + { error(loc, "too many initializers for %s", ad->toChars()); + errors = 1; + field.remove(i); + i--; + continue; + } + else + { + s = ad->fields.tdata()[fieldi]; + } + } + else + { + //s = ad->symtab->lookup(id); + s = ad->search(loc, id, 0); + if (!s) + { + error(loc, "'%s' is not a member of '%s'", id->toChars(), t->toChars()); + errors = 1; + continue; + } + s = s->toAlias(); + + // Find out which field index it is + for (fieldi = 0; 1; fieldi++) + { + if (fieldi >= nfields) + { + error(loc, "%s.%s is not a per-instance initializable field", + t->toChars(), s->toChars()); + errors = 1; + break; + } + if (s == ad->fields.tdata()[fieldi]) + break; + } + } + if (s && (v = s->isVarDeclaration()) != NULL) + { + val = val->semantic(sc, v->type, needInterpret); + value.tdata()[i] = val; + vars.tdata()[i] = v; + } + else + { error(loc, "%s is not a field of %s", id ? id->toChars() : s->toChars(), ad->toChars()); + errors = 1; + } + fieldi++; + } + } + else if (t->ty == Tdelegate && value.dim == 0) + { /* Rewrite as empty delegate literal { } + */ + Parameters *arguments = new Parameters; + Type *tf = new TypeFunction(arguments, NULL, 0, LINKd); + FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, 0, tf, TOKdelegate, NULL); + fd->fbody = new CompoundStatement(loc, new Statements()); + fd->endloc = loc; + Expression *e = new FuncExp(loc, fd); + ExpInitializer *ie = new ExpInitializer(loc, e); + return ie->semantic(sc, t, needInterpret); + } + else + { + error(loc, "a struct is not a valid initializer for a %s", t->toChars()); + errors = 1; + } + if (errors) + { + field.setDim(0); + value.setDim(0); + vars.setDim(0); + } + return this; +} + +/*************************************** + * This works by transforming a struct initializer into + * a struct literal. In the future, the two should be the + * same thing. + */ +Expression *StructInitializer::toExpression() +{ Expression *e; + size_t offset; + + //printf("StructInitializer::toExpression() %s\n", toChars()); + if (!ad) // if fwd referenced + { + return NULL; + } + StructDeclaration *sd = ad->isStructDeclaration(); + if (!sd) + return NULL; + Expressions *elements = new Expressions(); + size_t nfields = ad->fields.dim; +#if DMDV2 + if (sd->isnested) + nfields--; +#endif + elements->setDim(nfields); + for (size_t i = 0; i < elements->dim; i++) + { + elements->tdata()[i] = NULL; + } + unsigned fieldi = 0; + for (size_t i = 0; i < value.dim; i++) + { + Identifier *id = field.tdata()[i]; + if (id) + { + Dsymbol * s = ad->search(loc, id, 0); + if (!s) + { + error(loc, "'%s' is not a member of '%s'", id->toChars(), sd->toChars()); + goto Lno; + } + s = s->toAlias(); + + // Find out which field index it is + for (fieldi = 0; 1; fieldi++) + { + if (fieldi >= nfields) + { + s->error("is not a per-instance initializable field"); + goto Lno; + } + if (s == ad->fields.tdata()[fieldi]) + break; + } + } + else if (fieldi >= nfields) + { error(loc, "too many initializers for '%s'", ad->toChars()); + goto Lno; + } + Initializer *iz = value.tdata()[i]; + if (!iz) + goto Lno; + Expression *ex = iz->toExpression(); + if (!ex) + goto Lno; + if (elements->tdata()[fieldi]) + { error(loc, "duplicate initializer for field '%s'", + ad->fields.tdata()[fieldi]->toChars()); + goto Lno; + } + elements->tdata()[fieldi] = ex; + ++fieldi; + } + // Now, fill in any missing elements with default initializers. + // We also need to validate any anonymous unions + offset = 0; + for (size_t i = 0; i < elements->dim; ) + { + VarDeclaration * vd = ad->fields.tdata()[i]->isVarDeclaration(); + + //printf("test2 [%d] : %s %d %d\n", i, vd->toChars(), (int)offset, (int)vd->offset); + if (vd->offset < offset) + { + // Only the first field of a union can have an initializer + if (elements->tdata()[i]) + goto Lno; + } + else + { + if (!elements->tdata()[i]) + // Default initialize + elements->tdata()[i] = vd->type->defaultInit(); + } + offset = vd->offset + vd->type->size(); + i++; +#if 0 + int unionSize = ad->numFieldsInUnion(i); + if (unionSize == 1) + { // Not a union -- default initialize if missing + if (!elements->tdata()[i]) + elements->tdata()[i] = vd->type->defaultInit(); + } + else + { // anonymous union -- check for errors + int found = -1; // index of the first field with an initializer + for (int j = i; j < i + unionSize; ++j) + { + if (!elements->tdata()[j]) + continue; + if (found >= 0) + { + VarDeclaration * v1 = ((Dsymbol *)ad->fields.data[found])->isVarDeclaration(); + VarDeclaration * v = ((Dsymbol *)ad->fields.data[j])->isVarDeclaration(); + error(loc, "%s cannot have initializers for fields %s and %s in same union", + ad->toChars(), + v1->toChars(), v->toChars()); + goto Lno; + } + found = j; + } + if (found == -1) + { + error(loc, "no initializer for union that contains field %s", + vd->toChars()); + goto Lno; + } + } + i += unionSize; +#endif + } + e = new StructLiteralExp(loc, sd, elements); + e->type = sd->type; + return e; + +Lno: + delete elements; + return NULL; +} + + +void StructInitializer::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + //printf("StructInitializer::toCBuffer()\n"); + buf->writebyte('{'); + for (size_t i = 0; i < field.dim; i++) + { + if (i > 0) + buf->writebyte(','); + Identifier *id = field.tdata()[i]; + if (id) + { + buf->writestring(id->toChars()); + buf->writebyte(':'); + } + Initializer *iz = value.tdata()[i]; + if (iz) + iz->toCBuffer(buf, hgs); + } + buf->writebyte('}'); +} + +/********************************** ArrayInitializer ************************************/ + +ArrayInitializer::ArrayInitializer(Loc loc) + : Initializer(loc) +{ + dim = 0; + type = NULL; + sem = 0; +} + +Initializer *ArrayInitializer::syntaxCopy() +{ + //printf("ArrayInitializer::syntaxCopy()\n"); + + ArrayInitializer *ai = new ArrayInitializer(loc); + + assert(index.dim == value.dim); + ai->index.setDim(index.dim); + ai->value.setDim(value.dim); + for (size_t i = 0; i < ai->value.dim; i++) + { Expression *e = index.tdata()[i]; + if (e) + e = e->syntaxCopy(); + ai->index.tdata()[i] = e; + + Initializer *init = value.tdata()[i]; + init = init->syntaxCopy(); + ai->value.tdata()[i] = init; + } + return ai; +} + +void ArrayInitializer::addInit(Expression *index, Initializer *value) +{ + this->index.push(index); + this->value.push(value); + dim = 0; + type = NULL; +} + +Initializer *ArrayInitializer::semantic(Scope *sc, Type *t, int needInterpret) +{ unsigned i; + unsigned length; + const unsigned amax = 0x80000000; + + //printf("ArrayInitializer::semantic(%s)\n", t->toChars()); + if (sem) // if semantic() already run + return this; + sem = 1; + type = t; + t = t->toBasetype(); + switch (t->ty) + { + case Tpointer: + case Tsarray: + case Tarray: + break; + + default: + error(loc, "cannot use array to initialize %s", type->toChars()); + goto Lerr; + } + + length = 0; + for (i = 0; i < index.dim; i++) + { + Expression *idx = index.tdata()[i]; + if (idx) + { idx = idx->semantic(sc); + idx = idx->optimize(WANTvalue | WANTinterpret); + index.tdata()[i] = idx; + length = idx->toInteger(); + } + + Initializer *val = value.tdata()[i]; + val = val->semantic(sc, t->nextOf(), needInterpret); + value.tdata()[i] = val; + length++; + if (length == 0) + { error(loc, "array dimension overflow"); + goto Lerr; + } + if (length > dim) + dim = length; + } + if (t->ty == Tsarray) + { + dinteger_t edim = ((TypeSArray *)t)->dim->toInteger(); + if (dim > edim) + { + error(loc, "array initializer has %u elements, but array length is %jd", dim, edim); + goto Lerr; + } + } + + if ((unsigned long) dim * t->nextOf()->size() >= amax) + { error(loc, "array dimension %u exceeds max of %u", dim, amax / t->nextOf()->size()); + goto Lerr; + } + return this; + +Lerr: + return new ExpInitializer(loc, new ErrorExp()); +} + +/******************************** + * If possible, convert array initializer to array literal. + * Otherwise return NULL. + */ + +Expression *ArrayInitializer::toExpression() +{ Expressions *elements; + + //printf("ArrayInitializer::toExpression(), dim = %d\n", dim); + //static int i; if (++i == 2) halt(); + + size_t edim; + Type *t = NULL; + if (type) + { + if (type == Type::terror) + return new ErrorExp(); + + t = type->toBasetype(); + switch (t->ty) + { + case Tsarray: + edim = ((TypeSArray *)t)->dim->toInteger(); + break; + + case Tpointer: + case Tarray: + edim = dim; + break; + + default: + assert(0); + } + } + else + { + edim = value.dim; + for (size_t i = 0, j = 0; i < value.dim; i++, j++) + { + if (index.tdata()[i]) + j = index.tdata()[i]->toInteger(); + if (j >= edim) + edim = j + 1; + } + } + + elements = new Expressions(); + elements->setDim(edim); + elements->zero(); + for (size_t i = 0, j = 0; i < value.dim; i++, j++) + { + if (index.tdata()[i]) + j = (index.tdata()[i])->toInteger(); + assert(j < edim); + Initializer *iz = value.tdata()[i]; + if (!iz) + goto Lno; + Expression *ex = iz->toExpression(); + if (!ex) + { + goto Lno; + } + elements->tdata()[j] = ex; + } + + /* Fill in any missing elements with the default initializer + */ + { + Expression *init = NULL; + for (size_t i = 0; i < edim; i++) + { + if (!elements->tdata()[i]) + { + if (!type) + goto Lno; + if (!init) + init = ((TypeNext *)t)->next->defaultInit(); + elements->tdata()[i] = init; + } + } + + Expression *e = new ArrayLiteralExp(loc, elements); + e->type = type; + return e; + } + +Lno: + return NULL; +} + + +/******************************** + * If possible, convert array initializer to associative array initializer. + */ + +Expression *ArrayInitializer::toAssocArrayLiteral() +{ + Expression *e; + + //printf("ArrayInitializer::toAssocArrayInitializer()\n"); + //static int i; if (++i == 2) halt(); + Expressions *keys = new Expressions(); + keys->setDim(value.dim); + Expressions *values = new Expressions(); + values->setDim(value.dim); + + for (size_t i = 0; i < value.dim; i++) + { + e = index.tdata()[i]; + if (!e) + goto Lno; + keys->tdata()[i] = e; + + Initializer *iz = value.tdata()[i]; + if (!iz) + goto Lno; + e = iz->toExpression(); + if (!e) + goto Lno; + values->tdata()[i] = e; + } + e = new AssocArrayLiteralExp(loc, keys, values); + return e; + +Lno: + delete keys; + delete values; + error(loc, "not an associative array initializer"); + return new ErrorExp(); +} + +int ArrayInitializer::isAssociativeArray() +{ + for (size_t i = 0; i < value.dim; i++) + { + if (index.tdata()[i]) + return 1; + } + return 0; +} + +Type *ArrayInitializer::inferType(Scope *sc) +{ + //printf("ArrayInitializer::inferType() %s\n", toChars()); + assert(0); + return NULL; +#if 0 + type = Type::terror; + for (size_t i = 0; i < value.dim; i++) + { + if (index.data[i]) + goto Laa; + } + for (size_t i = 0; i < value.dim; i++) + { + Initializer *iz = (Initializer *)value.data[i]; + if (iz) + { Type *t = iz->inferType(sc); + if (i == 0) + { /* BUG: This gets the type from the first element. + * Fix to use all the elements to figure out the type. + */ + t = new TypeSArray(t, new IntegerExp(value.dim)); + t = t->semantic(loc, sc); + type = t; + } + } + } + return type; + +Laa: + /* It's possibly an associative array initializer. + * BUG: inferring type from first member. + */ + Initializer *iz = (Initializer *)value.data[0]; + Expression *indexinit = (Expression *)index.data[0]; + if (iz && indexinit) + { Type *t = iz->inferType(sc); + indexinit = indexinit->semantic(sc); + Type *indext = indexinit->type; + t = new TypeAArray(t, indext); + type = t->semantic(loc, sc); + } + else + error(loc, "cannot infer type from this array initializer"); + return type; +#endif +} + + +void ArrayInitializer::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writebyte('['); + for (size_t i = 0; i < index.dim; i++) + { + if (i > 0) + buf->writebyte(','); + Expression *ex = index.tdata()[i]; + if (ex) + { + ex->toCBuffer(buf, hgs); + buf->writebyte(':'); + } + Initializer *iz = value.tdata()[i]; + if (iz) + iz->toCBuffer(buf, hgs); + } + buf->writebyte(']'); +} + + +/********************************** ExpInitializer ************************************/ + +ExpInitializer::ExpInitializer(Loc loc, Expression *exp) + : Initializer(loc) +{ + this->exp = exp; +} + +Initializer *ExpInitializer::syntaxCopy() +{ + return new ExpInitializer(loc, exp->syntaxCopy()); +} + +bool arrayHasNonConstPointers(Expressions *elems); + +bool hasNonConstPointers(Expression *e) +{ + if (e->op == TOKnull) + return false; + if (e->op == TOKstructliteral) + { + StructLiteralExp *se = (StructLiteralExp *)e; + return arrayHasNonConstPointers(se->elements); + } + if (e->op == TOKarrayliteral) + { + if (!e->type->nextOf()->hasPointers()) + return false; + ArrayLiteralExp *ae = (ArrayLiteralExp *)e; + return arrayHasNonConstPointers(ae->elements); + } + if (e->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e; + if (ae->type->nextOf()->hasPointers() && + arrayHasNonConstPointers(ae->values)) + return true; + if (((TypeAArray *)ae->type)->index->hasPointers()) + return arrayHasNonConstPointers(ae->keys); + return false; + } + if (e->type->ty== Tpointer && e->type->nextOf()->ty != Tfunction) + { + if (e->op == TOKsymoff) // address of a global is OK + return false; + if (e->op == TOKint64) // cast(void *)int is OK + return false; + if (e->op == TOKstring) // "abc".ptr is OK + return false; + return true; + } + return false; +} + +bool arrayHasNonConstPointers(Expressions *elems) +{ + for (size_t i = 0; i < elems->dim; i++) + { + if (!elems->tdata()[i]) + continue; + if (hasNonConstPointers(elems->tdata()[i])) + return true; + } + return false; +} + + + +Initializer *ExpInitializer::semantic(Scope *sc, Type *t, int needInterpret) +{ + //printf("ExpInitializer::semantic(%s), type = %s\n", exp->toChars(), t->toChars()); + exp = exp->semantic(sc); + exp = resolveProperties(sc, exp); + int wantOptimize = needInterpret ? WANTinterpret|WANTvalue : WANTvalue; + + int olderrors = global.errors; + exp = exp->optimize(wantOptimize); + if (!global.gag && olderrors != global.errors) + return this; // Failed, suppress duplicate error messages + + if (exp->op == TOKtype) + error("initializer must be an expression, not '%s'", exp->toChars()); + + // Make sure all pointers are constants + if (needInterpret && hasNonConstPointers(exp)) + { + exp->error("cannot use non-constant CTFE pointer in an initializer '%s'", exp->toChars()); + return this; + } + + Type *tb = t->toBasetype(); + + /* Look for case of initializing a static array with a too-short + * string literal, such as: + * char[5] foo = "abc"; + * Allow this by doing an explicit cast, which will lengthen the string + * literal. + */ + if (exp->op == TOKstring && tb->ty == Tsarray && exp->type->ty == Tsarray) + { StringExp *se = (StringExp *)exp; + + if (!se->committed && se->type->ty == Tsarray && + ((TypeSArray *)se->type)->dim->toInteger() < + ((TypeSArray *)t)->dim->toInteger()) + { + exp = se->castTo(sc, t); + goto L1; + } + } + + // Look for the case of statically initializing an array + // with a single member. + if (tb->ty == Tsarray && + !tb->nextOf()->equals(exp->type->toBasetype()->nextOf()) && + exp->implicitConvTo(tb->nextOf()) + ) + { + t = tb->nextOf(); + } + + exp = exp->implicitCastTo(sc, t); +L1: + exp = exp->optimize(wantOptimize); + //printf("-ExpInitializer::semantic(): "); exp->print(); + return this; +} + +Type *ExpInitializer::inferType(Scope *sc) +{ + //printf("ExpInitializer::inferType() %s\n", toChars()); + if (exp->op == TOKfunction && ((FuncExp *)exp)->td) + { + exp->error("cannot infer type from ambiguous function literal %s", exp->toChars()); + return Type::terror; + } + + exp = exp->semantic(sc); + exp = resolveProperties(sc, exp); + + // Give error for overloaded function addresses + if (exp->op == TOKsymoff) + { SymOffExp *se = (SymOffExp *)exp; + if (se->hasOverloads && !se->var->isFuncDeclaration()->isUnique()) + exp->error("cannot infer type from overloaded function symbol %s", exp->toChars()); + } + + // Give error for overloaded function addresses + if (exp->op == TOKdelegate) + { DelegateExp *se = (DelegateExp *)exp; + if ( + se->func->isFuncDeclaration() && + !se->func->isFuncDeclaration()->isUnique()) + exp->error("cannot infer type from overloaded function symbol %s", exp->toChars()); + } + + Type *t = exp->type; + if (!t) + t = Initializer::inferType(sc); + return t; +} + +Expression *ExpInitializer::toExpression() +{ + return exp; +} + + +void ExpInitializer::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + exp->toCBuffer(buf, hgs); +} + + + diff --git a/init.h b/init.h new file mode 100644 index 00000000..aed6cca4 --- /dev/null +++ b/init.h @@ -0,0 +1,130 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2007 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef INIT_H +#define INIT_H + +#include "root.h" + +#include "mars.h" +#include "arraytypes.h" + +struct Identifier; +struct Expression; +struct Scope; +struct Type; +struct dt_t; +struct AggregateDeclaration; +struct VoidInitializer; +struct StructInitializer; +struct ArrayInitializer; +struct ExpInitializer; +struct HdrGenState; + + +struct Initializer : Object +{ + Loc loc; + + Initializer(Loc loc); + virtual Initializer *syntaxCopy(); + // needInterpret is WANTinterpret if must be a manifest constant, 0 if not. + virtual Initializer *semantic(Scope *sc, Type *t, int needInterpret); + virtual Type *inferType(Scope *sc); + virtual Expression *toExpression() = 0; + virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs) = 0; + char *toChars(); + + static Initializers *arraySyntaxCopy(Initializers *ai); + + virtual dt_t *toDt(); + + virtual VoidInitializer *isVoidInitializer() { return NULL; } + virtual StructInitializer *isStructInitializer() { return NULL; } + virtual ArrayInitializer *isArrayInitializer() { return NULL; } + virtual ExpInitializer *isExpInitializer() { return NULL; } +}; + +struct VoidInitializer : Initializer +{ + Type *type; // type that this will initialize to + + VoidInitializer(Loc loc); + Initializer *syntaxCopy(); + Initializer *semantic(Scope *sc, Type *t, int needInterpret); + Expression *toExpression(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + dt_t *toDt(); + + virtual VoidInitializer *isVoidInitializer() { return this; } +}; + +struct StructInitializer : Initializer +{ + Identifiers field; // of Identifier *'s + Initializers value; // parallel array of Initializer *'s + + VarDeclarations vars; // parallel array of VarDeclaration *'s + AggregateDeclaration *ad; // which aggregate this is for + + StructInitializer(Loc loc); + Initializer *syntaxCopy(); + void addInit(Identifier *field, Initializer *value); + Initializer *semantic(Scope *sc, Type *t, int needInterpret); + Expression *toExpression(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + dt_t *toDt(); + + StructInitializer *isStructInitializer() { return this; } +}; + +struct ArrayInitializer : Initializer +{ + Expressions index; // indices + Initializers value; // of Initializer *'s + unsigned dim; // length of array being initialized + Type *type; // type that array will be used to initialize + int sem; // !=0 if semantic() is run + + ArrayInitializer(Loc loc); + Initializer *syntaxCopy(); + void addInit(Expression *index, Initializer *value); + Initializer *semantic(Scope *sc, Type *t, int needInterpret); + int isAssociativeArray(); + Type *inferType(Scope *sc); + Expression *toExpression(); + Expression *toAssocArrayLiteral(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + dt_t *toDt(); + dt_t *toDtBit(); // for bit arrays + + ArrayInitializer *isArrayInitializer() { return this; } +}; + +struct ExpInitializer : Initializer +{ + Expression *exp; + + ExpInitializer(Loc loc, Expression *exp); + Initializer *syntaxCopy(); + Initializer *semantic(Scope *sc, Type *t, int needInterpret); + Type *inferType(Scope *sc); + Expression *toExpression(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + dt_t *toDt(); + + virtual ExpInitializer *isExpInitializer() { return this; } +}; + +#endif diff --git a/inline.c b/inline.c new file mode 100644 index 00000000..e0ebe6c1 --- /dev/null +++ b/inline.c @@ -0,0 +1,1797 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +// Routines to perform function inlining + +#define LOG 0 + +#include +#include +#include + +#include "id.h" +#include "init.h" +#include "declaration.h" +#include "aggregate.h" +#include "expression.h" +#include "statement.h" +#include "mtype.h" +#include "scope.h" + +/* ========== Compute cost of inlining =============== */ + +/* Walk trees to determine if inlining can be done, and if so, + * if it is too complex to be worth inlining or not. + */ + +struct InlineCostState +{ + int nested; + int hasthis; + int hdrscan; // !=0 if inline scan for 'header' content + FuncDeclaration *fd; +}; + +const int COST_MAX = 250; +const int STATEMENT_COST = 0x1000; +const int STATEMENT_COST_MAX = 250 * 0x1000; + +// STATEMENT_COST be power of 2 and greater than COST_MAX +//static assert((STATEMENT_COST & (STATEMENT_COST - 1)) == 0); +//static assert(STATEMENT_COST > COST_MAX); + +bool tooCostly(int cost) { return ((cost & (STATEMENT_COST - 1)) >= COST_MAX); } + +int expressionInlineCost(Expression *e, InlineCostState *ics); + +int Statement::inlineCost(InlineCostState *ics) +{ + //printf("Statement::inlineCost = %d\n", COST_MAX); + //printf("%p\n", isScopeStatement()); + //printf("%s\n", toChars()); + return COST_MAX; // default is we can't inline it +} + +int ExpStatement::inlineCost(InlineCostState *ics) +{ + return expressionInlineCost(exp, ics); + //return exp ? exp->inlineCost(ics) : 0; +} + +int CompoundStatement::inlineCost(InlineCostState *ics) +{ int cost = 0; + + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + { + cost += s->inlineCost(ics); + if (tooCostly(cost)) + break; + } + } + //printf("CompoundStatement::inlineCost = %d\n", cost); + return cost; +} + +int UnrolledLoopStatement::inlineCost(InlineCostState *ics) +{ int cost = 0; + + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + { + cost += s->inlineCost(ics); + if (tooCostly(cost)) + break; + } + } + return cost; +} + +int ScopeStatement::inlineCost(InlineCostState *ics) +{ + return statement ? 1 + statement->inlineCost(ics) : 1; +} + +int IfStatement::inlineCost(InlineCostState *ics) +{ + int cost; + + /* Can't declare variables inside ?: expressions, so + * we cannot inline if a variable is declared. + */ + if (arg) + return COST_MAX; + + cost = expressionInlineCost(condition, ics); + + /* Specifically allow: + * if (condition) + * return exp1; + * else + * return exp2; + * Otherwise, we can't handle return statements nested in if's. + */ + + if (elsebody && ifbody && + ifbody->isReturnStatement() && + elsebody->isReturnStatement()) + { + cost += ifbody->inlineCost(ics); + cost += elsebody->inlineCost(ics); + //printf("cost = %d\n", cost); + } + else + { + ics->nested += 1; + if (ifbody) + cost += ifbody->inlineCost(ics); + if (elsebody) + cost += elsebody->inlineCost(ics); + ics->nested -= 1; + } + //printf("IfStatement::inlineCost = %d\n", cost); + return cost; +} + +int ReturnStatement::inlineCost(InlineCostState *ics) +{ + // Can't handle return statements nested in if's + if (ics->nested) + return COST_MAX; + return expressionInlineCost(exp, ics); +} + +#if DMDV2 +int ImportStatement::inlineCost(InlineCostState *ics) +{ + return 0; +} +#endif + +int ForStatement::inlineCost(InlineCostState *ics) +{ + //return COST_MAX; + int cost = STATEMENT_COST; + if (init) + cost += init->inlineCost(ics); + if (condition) + cost += expressionInlineCost(condition, ics); + if (increment) + cost += expressionInlineCost(increment, ics); + if (body) + cost += body->inlineCost(ics); + //printf("ForStatement: inlineCost = %d\n", cost); + return cost; +} + + +/* -------------------------- */ + +struct ICS2 +{ + int cost; + InlineCostState *ics; +}; + +int lambdaInlineCost(Expression *e, void *param) +{ + ICS2 *ics2 = (ICS2 *)param; + ics2->cost += e->inlineCost3(ics2->ics); + return (ics2->cost >= COST_MAX); +} + +int expressionInlineCost(Expression *e, InlineCostState *ics) +{ + //printf("expressionInlineCost()\n"); + //e->dump(0); + ICS2 ics2; + ics2.cost = 0; + ics2.ics = ics; + if (e) + e->apply(&lambdaInlineCost, &ics2); + return ics2.cost; +} + +int Expression::inlineCost3(InlineCostState *ics) +{ + return 1; +} + +int VarExp::inlineCost3(InlineCostState *ics) +{ + //printf("VarExp::inlineCost3() %s\n", toChars()); + Type *tb = type->toBasetype(); + if (tb->ty == Tstruct) + { + StructDeclaration *sd = ((TypeStruct *)tb)->sym; + if (sd->isnested) + /* An inner struct will be nested inside another function hierarchy than where + * we're inlining into, so don't inline it. + * At least not until we figure out how to 'move' the struct to be nested + * locally. Example: + * struct S(alias pred) { void unused_func(); } + * void abc() { int w; S!(w) m; } + * void bar() { abc(); } + */ + return COST_MAX; + } + FuncDeclaration *fd = var->isFuncDeclaration(); + if (fd && fd->isNested()) // see Bugzilla 7199 for test case + return COST_MAX; + return 1; +} + +int ThisExp::inlineCost3(InlineCostState *ics) +{ + //printf("ThisExp::inlineCost3() %s\n", toChars()); + FuncDeclaration *fd = ics->fd; + if (!fd) + return COST_MAX; + if (!ics->hdrscan) + if (fd->isNested() || !ics->hasthis) + return COST_MAX; + return 1; +} + +int StructLiteralExp::inlineCost3(InlineCostState *ics) +{ + //printf("StructLiteralExp::inlineCost3() %s\n", toChars()); +#if DMDV2 + if (sd->isnested) + return COST_MAX; +#endif + return 1; +} + +int FuncExp::inlineCost3(InlineCostState *ics) +{ + //printf("FuncExp::inlineCost3()\n"); + // Right now, this makes the function be output to the .obj file twice. + return COST_MAX; +} + +int DelegateExp::inlineCost3(InlineCostState *ics) +{ + //printf("DelegateExp::inlineCost3()\n"); + return COST_MAX; +} + +int DeclarationExp::inlineCost3(InlineCostState *ics) +{ int cost = 0; + VarDeclaration *vd; + + //printf("DeclarationExp::inlineCost3()\n"); + vd = declaration->isVarDeclaration(); + if (vd) + { + TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); + if (td) + { +#if 1 + return COST_MAX; // finish DeclarationExp::doInline +#else + for (size_t i = 0; i < td->objects->dim; i++) + { Object *o = (*td->objects)[i]; + if (o->dyncast() != DYNCAST_EXPRESSION) + return COST_MAX; + Expression *eo = (Expression *)o; + if (eo->op != TOKdsymbol) + return COST_MAX; + } + return td->objects->dim; +#endif + } + if (!ics->hdrscan && vd->isDataseg()) + return COST_MAX; + cost += 1; + +#if DMDV2 + if (vd->edtor) // if destructor required + return COST_MAX; // needs work to make this work +#endif + // Scan initializer (vd->init) + if (vd->init) + { + ExpInitializer *ie = vd->init->isExpInitializer(); + + if (ie) + { + cost += expressionInlineCost(ie->exp, ics); + } + } + } + + // These can contain functions, which when copied, get output twice. + if (declaration->isStructDeclaration() || + declaration->isClassDeclaration() || + declaration->isFuncDeclaration() || + declaration->isTypedefDeclaration() || +#if DMDV2 + declaration->isAttribDeclaration() || +#endif + declaration->isTemplateMixin()) + return COST_MAX; + + //printf("DeclarationExp::inlineCost3('%s')\n", toChars()); + return cost; +} + +int CallExp::inlineCost3(InlineCostState *ics) +{ + //printf("CallExp::inlineCost3() %s\n", toChars()); + // Bugzilla 3500: super.func() calls must be devirtualized, and the inliner + // can't handle that at present. + if (e1->op == TOKdotvar && ((DotVarExp *)e1)->e1->op == TOKsuper) + return COST_MAX; + + return 1; +} + + +/* ======================== Perform the inlining ============================== */ + +/* Inlining is done by: + * o Converting to an Expression + * o Copying the trees of the function to be inlined + * o Renaming the variables + */ + +struct InlineDoState +{ + VarDeclaration *vthis; + Dsymbols from; // old Dsymbols + Dsymbols to; // parallel array of new Dsymbols + Dsymbol *parent; // new parent + FuncDeclaration *fd; // function being inlined (old parent) +}; + +/* -------------------------------------------------------------------- */ + +Statement *Statement::doInlineStatement(InlineDoState *ids) +{ + assert(0); + return NULL; // default is we can't inline it +} + +Statement *ExpStatement::doInlineStatement(InlineDoState *ids) +{ +#if LOG + if (exp) printf("ExpStatement::doInlineStatement() '%s'\n", exp->toChars()); +#endif + return new ExpStatement(loc, exp ? exp->doInline(ids) : NULL); +} + +Statement *CompoundStatement::doInlineStatement(InlineDoState *ids) +{ + //printf("CompoundStatement::doInlineStatement() %d\n", statements->dim); + Statements *as = new Statements(); + as->reserve(statements->dim); + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + { + as->push(s->doInlineStatement(ids)); + if (s->isReturnStatement()) + break; + + /* Check for: + * if (condition) + * return exp1; + * else + * return exp2; + */ + IfStatement *ifs = s->isIfStatement(); + if (ifs && ifs->elsebody && ifs->ifbody && + ifs->ifbody->isReturnStatement() && + ifs->elsebody->isReturnStatement() + ) + break; + } + else + as->push(NULL); + } + return new CompoundStatement(loc, as); +} + +Statement *UnrolledLoopStatement::doInlineStatement(InlineDoState *ids) +{ + //printf("UnrolledLoopStatement::doInlineStatement() %d\n", statements->dim); + Statements *as = new Statements(); + as->reserve(statements->dim); + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + { + as->push(s->doInlineStatement(ids)); + if (s->isReturnStatement()) + break; + } + else + as->push(NULL); + } + return new UnrolledLoopStatement(loc, as); +} + +Statement *ScopeStatement::doInlineStatement(InlineDoState *ids) +{ + //printf("ScopeStatement::doInlineStatement() %d\n", statements->dim); + return statement ? new ScopeStatement(loc, statement->doInlineStatement(ids)) : this; +} + +Statement *IfStatement::doInlineStatement(InlineDoState *ids) +{ + assert(!arg); + + Expression *condition = this->condition ? this->condition->doInline(ids) : NULL; + Statement *ifbody = this->ifbody ? this->ifbody->doInlineStatement(ids) : NULL; + Statement *elsebody = this->elsebody ? this->elsebody->doInlineStatement(ids) : NULL; + + return new IfStatement(loc, arg, condition, ifbody, elsebody); +} + +Statement *ReturnStatement::doInlineStatement(InlineDoState *ids) +{ + //printf("ReturnStatement::doInlineStatement() '%s'\n", exp ? exp->toChars() : ""); + return new ReturnStatement(loc, exp ? exp->doInline(ids) : NULL); +} + +#if DMDV2 +Statement *ImportStatement::doInlineStatement(InlineDoState *ids) +{ + return NULL; +} +#endif + +Statement *ForStatement::doInlineStatement(InlineDoState *ids) +{ + //printf("ForStatement::doInlineStatement()\n"); + Statement *init = this->init ? this->init->doInlineStatement(ids) : NULL; + Expression *condition = this->condition ? this->condition->doInline(ids) : NULL; + Expression *increment = this->increment ? this->increment->doInline(ids) : NULL; + Statement *body = this->body ? this->body->doInlineStatement(ids) : NULL; + return new ForStatement(loc, init, condition, increment, body); +} + +/* -------------------------------------------------------------------- */ + +Expression *Statement::doInline(InlineDoState *ids) +{ + printf("Statement::doInline()\n%s\n", toChars()); + fflush(stdout); + assert(0); + return NULL; // default is we can't inline it +} + +Expression *ExpStatement::doInline(InlineDoState *ids) +{ +#if LOG + if (exp) printf("ExpStatement::doInline() '%s'\n", exp->toChars()); +#endif + return exp ? exp->doInline(ids) : NULL; +} + +Expression *CompoundStatement::doInline(InlineDoState *ids) +{ + Expression *e = NULL; + + //printf("CompoundStatement::doInline() %d\n", statements->dim); + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + { + Expression *e2 = s->doInline(ids); + e = Expression::combine(e, e2); + if (s->isReturnStatement()) + break; + + /* Check for: + * if (condition) + * return exp1; + * else + * return exp2; + */ + IfStatement *ifs = s->isIfStatement(); + if (ifs && ifs->elsebody && ifs->ifbody && + ifs->ifbody->isReturnStatement() && + ifs->elsebody->isReturnStatement() + ) + break; + + } + } + return e; +} + +Expression *UnrolledLoopStatement::doInline(InlineDoState *ids) +{ + Expression *e = NULL; + + //printf("UnrolledLoopStatement::doInline() %d\n", statements->dim); + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + { + Expression *e2 = s->doInline(ids); + e = Expression::combine(e, e2); + if (s->isReturnStatement()) + break; + } + } + return e; +} + +Expression *ScopeStatement::doInline(InlineDoState *ids) +{ + return statement ? statement->doInline(ids) : NULL; +} + +Expression *IfStatement::doInline(InlineDoState *ids) +{ + Expression *econd; + Expression *e1; + Expression *e2; + Expression *e; + + assert(!arg); + econd = condition->doInline(ids); + assert(econd); + if (ifbody) + e1 = ifbody->doInline(ids); + else + e1 = NULL; + if (elsebody) + e2 = elsebody->doInline(ids); + else + e2 = NULL; + if (e1 && e2) + { + e = new CondExp(econd->loc, econd, e1, e2); + e->type = e1->type; + } + else if (e1) + { + e = new AndAndExp(econd->loc, econd, e1); + e->type = Type::tvoid; + } + else if (e2) + { + e = new OrOrExp(econd->loc, econd, e2); + e->type = Type::tvoid; + } + else + { + e = econd; + } + return e; +} + +Expression *ReturnStatement::doInline(InlineDoState *ids) +{ + //printf("ReturnStatement::doInline() '%s'\n", exp ? exp->toChars() : ""); + return exp ? exp->doInline(ids) : 0; +} + +#if DMDV2 +Expression *ImportStatement::doInline(InlineDoState *ids) +{ + return NULL; +} +#endif + +/* --------------------------------------------------------------- */ + +/****************************** + * Perform doInline() on an array of Expressions. + */ + +Expressions *arrayExpressiondoInline(Expressions *a, InlineDoState *ids) +{ Expressions *newa = NULL; + + if (a) + { + newa = new Expressions(); + newa->setDim(a->dim); + + for (size_t i = 0; i < a->dim; i++) + { Expression *e = a->tdata()[i]; + + if (e) + e = e->doInline(ids); + newa->tdata()[i] = e; + } + } + return newa; +} + +Expression *Expression::doInline(InlineDoState *ids) +{ + //printf("Expression::doInline(%s): %s\n", Token::toChars(op), toChars()); + return copy(); +} + +Expression *SymOffExp::doInline(InlineDoState *ids) +{ + //printf("SymOffExp::doInline(%s)\n", toChars()); + for (size_t i = 0; i < ids->from.dim; i++) + { + if (var == ids->from.tdata()[i]) + { + SymOffExp *se = (SymOffExp *)copy(); + + se->var = (Declaration *)ids->to.tdata()[i]; + return se; + } + } + return this; +} + +Expression *VarExp::doInline(InlineDoState *ids) +{ + //printf("VarExp::doInline(%s)\n", toChars()); + for (size_t i = 0; i < ids->from.dim; i++) + { + if (var == ids->from.tdata()[i]) + { + VarExp *ve = (VarExp *)copy(); + + ve->var = (Declaration *)ids->to.tdata()[i]; + return ve; + } + } + if (ids->fd && var == ids->fd->vthis) + { VarExp *ve = new VarExp(loc, ids->vthis); + ve->type = type; + return ve; + } + + return this; +} + +Expression *ThisExp::doInline(InlineDoState *ids) +{ + //if (!ids->vthis) + //error("no 'this' when inlining %s", ids->parent->toChars()); + if (!ids->vthis) + { + return this; + } + + VarExp *ve = new VarExp(loc, ids->vthis); + ve->type = type; + return ve; +} + +Expression *SuperExp::doInline(InlineDoState *ids) +{ + assert(ids->vthis); + + VarExp *ve = new VarExp(loc, ids->vthis); + ve->type = type; + return ve; +} + +Expression *DeclarationExp::doInline(InlineDoState *ids) +{ DeclarationExp *de = (DeclarationExp *)copy(); + VarDeclaration *vd; + + //printf("DeclarationExp::doInline(%s)\n", toChars()); + vd = declaration->isVarDeclaration(); + if (vd) + { +#if 0 + // Need to figure this out before inlining can work for tuples + TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); + if (td) + { + for (size_t i = 0; i < td->objects->dim; i++) + { DsymbolExp *se = td->objects->tdata()[i]; + assert(se->op == TOKdsymbol); + se->s; + } + return st->objects->dim; + } +#endif + if (vd->isStatic()) + ; + else + { + VarDeclaration *vto; + + vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); + *vto = *vd; + vto->parent = ids->parent; + vto->csym = NULL; + vto->isym = NULL; + + ids->from.push(vd); + ids->to.push(vto); + + if (vd->init) + { + if (vd->init->isVoidInitializer()) + { + vto->init = new VoidInitializer(vd->init->loc); + } + else + { + Expression *e = vd->init->toExpression(); + assert(e); + vto->init = new ExpInitializer(e->loc, e->doInline(ids)); + } + } + de->declaration = (Dsymbol *) (void *)vto; + } + } + /* This needs work, like DeclarationExp::toElem(), if we are + * to handle TemplateMixin's. For now, we just don't inline them. + */ + return de; +} + +Expression *NewExp::doInline(InlineDoState *ids) +{ + //printf("NewExp::doInline(): %s\n", toChars()); + NewExp *ne = (NewExp *)copy(); + + if (thisexp) + ne->thisexp = thisexp->doInline(ids); + ne->newargs = arrayExpressiondoInline(ne->newargs, ids); + ne->arguments = arrayExpressiondoInline(ne->arguments, ids); + return ne; +} + +Expression *UnaExp::doInline(InlineDoState *ids) +{ + UnaExp *ue = (UnaExp *)copy(); + + ue->e1 = e1->doInline(ids); + return ue; +} + +Expression *AssertExp::doInline(InlineDoState *ids) +{ + AssertExp *ae = (AssertExp *)copy(); + + ae->e1 = e1->doInline(ids); + if (msg) + ae->msg = msg->doInline(ids); + return ae; +} + +Expression *BinExp::doInline(InlineDoState *ids) +{ + BinExp *be = (BinExp *)copy(); + + be->e1 = e1->doInline(ids); + be->e2 = e2->doInline(ids); + return be; +} + +Expression *CallExp::doInline(InlineDoState *ids) +{ + CallExp *ce; + + ce = (CallExp *)copy(); + ce->e1 = e1->doInline(ids); + ce->arguments = arrayExpressiondoInline(arguments, ids); + return ce; +} + + +Expression *IndexExp::doInline(InlineDoState *ids) +{ + IndexExp *are = (IndexExp *)copy(); + + are->e1 = e1->doInline(ids); + + if (lengthVar) + { //printf("lengthVar\n"); + VarDeclaration *vd = lengthVar; + ExpInitializer *ie; + ExpInitializer *ieto; + VarDeclaration *vto; + + vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); + *vto = *vd; + vto->parent = ids->parent; + vto->csym = NULL; + vto->isym = NULL; + + ids->from.push(vd); + ids->to.push(vto); + + if (vd->init && !vd->init->isVoidInitializer()) + { + ie = vd->init->isExpInitializer(); + assert(ie); + ieto = new ExpInitializer(ie->loc, ie->exp->doInline(ids)); + vto->init = ieto; + } + + are->lengthVar = (VarDeclaration *) (void *)vto; + } + are->e2 = e2->doInline(ids); + return are; +} + + +Expression *SliceExp::doInline(InlineDoState *ids) +{ + SliceExp *are = (SliceExp *)copy(); + + are->e1 = e1->doInline(ids); + + if (lengthVar) + { //printf("lengthVar\n"); + VarDeclaration *vd = lengthVar; + ExpInitializer *ie; + ExpInitializer *ieto; + VarDeclaration *vto; + + vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); + *vto = *vd; + vto->parent = ids->parent; + vto->csym = NULL; + vto->isym = NULL; + + ids->from.push(vd); + ids->to.push(vto); + + if (vd->init && !vd->init->isVoidInitializer()) + { + ie = vd->init->isExpInitializer(); + assert(ie); + ieto = new ExpInitializer(ie->loc, ie->exp->doInline(ids)); + vto->init = ieto; + } + + are->lengthVar = (VarDeclaration *) (void *)vto; + } + if (lwr) + are->lwr = lwr->doInline(ids); + if (upr) + are->upr = upr->doInline(ids); + return are; +} + + +Expression *TupleExp::doInline(InlineDoState *ids) +{ + TupleExp *ce; + + ce = (TupleExp *)copy(); + ce->exps = arrayExpressiondoInline(exps, ids); + return ce; +} + + +Expression *ArrayLiteralExp::doInline(InlineDoState *ids) +{ + ArrayLiteralExp *ce; + + ce = (ArrayLiteralExp *)copy(); + ce->elements = arrayExpressiondoInline(elements, ids); + return ce; +} + + +Expression *AssocArrayLiteralExp::doInline(InlineDoState *ids) +{ + AssocArrayLiteralExp *ce; + + ce = (AssocArrayLiteralExp *)copy(); + ce->keys = arrayExpressiondoInline(keys, ids); + ce->values = arrayExpressiondoInline(values, ids); + return ce; +} + + +Expression *StructLiteralExp::doInline(InlineDoState *ids) +{ + StructLiteralExp *ce; + + ce = (StructLiteralExp *)copy(); + ce->elements = arrayExpressiondoInline(elements, ids); + return ce; +} + + +Expression *ArrayExp::doInline(InlineDoState *ids) +{ + ArrayExp *ce; + + ce = (ArrayExp *)copy(); + ce->e1 = e1->doInline(ids); + ce->arguments = arrayExpressiondoInline(arguments, ids); + return ce; +} + + +Expression *CondExp::doInline(InlineDoState *ids) +{ + CondExp *ce = (CondExp *)copy(); + + ce->econd = econd->doInline(ids); + ce->e1 = e1->doInline(ids); + ce->e2 = e2->doInline(ids); + return ce; +} + + +/* ========== Walk the parse trees, and inline expand functions ============= */ + +/* Walk the trees, looking for functions to inline. + * Inline any that can be. + */ + +struct InlineScanState +{ + FuncDeclaration *fd; // function being scanned +}; + +Statement *Statement::inlineScan(InlineScanState *iss) +{ + return this; +} + +Statement *ExpStatement::inlineScan(InlineScanState *iss) +{ +#if LOG + printf("ExpStatement::inlineScan(%s)\n", toChars()); +#endif + if (exp) + { + exp = exp->inlineScan(iss); + + /* See if we can inline as a statement rather than as + * an Expression. + */ + if (exp && exp->op == TOKcall) + { + CallExp *ce = (CallExp *)exp; + if (ce->e1->op == TOKvar) + { + VarExp *ve = (VarExp *)ce->e1; + FuncDeclaration *fd = ve->var->isFuncDeclaration(); + + if (fd && fd != iss->fd && fd->canInline(0, 0, 1)) + { + Statement *s; + fd->expandInline(iss, NULL, ce->arguments, &s); + return s; + } + } + } + } + return this; +} + +Statement *CompoundStatement::inlineScan(InlineScanState *iss) +{ + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + (*statements)[i] = s->inlineScan(iss); + } + return this; +} + +Statement *UnrolledLoopStatement::inlineScan(InlineScanState *iss) +{ + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + (*statements)[i] = s->inlineScan(iss); + } + return this; +} + +Statement *ScopeStatement::inlineScan(InlineScanState *iss) +{ + if (statement) + statement = statement->inlineScan(iss); + return this; +} + +Statement *WhileStatement::inlineScan(InlineScanState *iss) +{ + condition = condition->inlineScan(iss); + body = body ? body->inlineScan(iss) : NULL; + return this; +} + + +Statement *DoStatement::inlineScan(InlineScanState *iss) +{ + body = body ? body->inlineScan(iss) : NULL; + condition = condition->inlineScan(iss); + return this; +} + + +Statement *ForStatement::inlineScan(InlineScanState *iss) +{ + if (init) + init = init->inlineScan(iss); + if (condition) + condition = condition->inlineScan(iss); + if (increment) + increment = increment->inlineScan(iss); + if (body) + body = body->inlineScan(iss); + return this; +} + + +Statement *ForeachStatement::inlineScan(InlineScanState *iss) +{ + aggr = aggr->inlineScan(iss); + if (body) + body = body->inlineScan(iss); + return this; +} + + +#if DMDV2 +Statement *ForeachRangeStatement::inlineScan(InlineScanState *iss) +{ + lwr = lwr->inlineScan(iss); + upr = upr->inlineScan(iss); + if (body) + body = body->inlineScan(iss); + return this; +} +#endif + + +Statement *IfStatement::inlineScan(InlineScanState *iss) +{ + condition = condition->inlineScan(iss); + if (ifbody) + ifbody = ifbody->inlineScan(iss); + if (elsebody) + elsebody = elsebody->inlineScan(iss); + return this; +} + + +Statement *SwitchStatement::inlineScan(InlineScanState *iss) +{ + //printf("SwitchStatement::inlineScan()\n"); + condition = condition->inlineScan(iss); + body = body ? body->inlineScan(iss) : NULL; + if (sdefault) + sdefault = (DefaultStatement *)sdefault->inlineScan(iss); + if (cases) + { + for (size_t i = 0; i < cases->dim; i++) + { CaseStatement *s; + + s = cases->tdata()[i]; + cases->tdata()[i] = (CaseStatement *)s->inlineScan(iss); + } + } + return this; +} + + +Statement *CaseStatement::inlineScan(InlineScanState *iss) +{ + //printf("CaseStatement::inlineScan()\n"); + exp = exp->inlineScan(iss); + if (statement) + statement = statement->inlineScan(iss); + return this; +} + + +Statement *DefaultStatement::inlineScan(InlineScanState *iss) +{ + if (statement) + statement = statement->inlineScan(iss); + return this; +} + + +Statement *ReturnStatement::inlineScan(InlineScanState *iss) +{ + //printf("ReturnStatement::inlineScan()\n"); + if (exp) + { + exp = exp->inlineScan(iss); + } + return this; +} + + +Statement *SynchronizedStatement::inlineScan(InlineScanState *iss) +{ + if (exp) + exp = exp->inlineScan(iss); + if (body) + body = body->inlineScan(iss); + return this; +} + + +Statement *WithStatement::inlineScan(InlineScanState *iss) +{ + if (exp) + exp = exp->inlineScan(iss); + if (body) + body = body->inlineScan(iss); + return this; +} + + +Statement *TryCatchStatement::inlineScan(InlineScanState *iss) +{ + if (body) + body = body->inlineScan(iss); + if (catches) + { + for (size_t i = 0; i < catches->dim; i++) + { Catch *c = catches->tdata()[i]; + + if (c->handler) + c->handler = c->handler->inlineScan(iss); + } + } + return this; +} + + +Statement *TryFinallyStatement::inlineScan(InlineScanState *iss) +{ + if (body) + body = body->inlineScan(iss); + if (finalbody) + finalbody = finalbody->inlineScan(iss); + return this; +} + + +Statement *ThrowStatement::inlineScan(InlineScanState *iss) +{ + if (exp) + exp = exp->inlineScan(iss); + return this; +} + + +Statement *VolatileStatement::inlineScan(InlineScanState *iss) +{ + if (statement) + statement = statement->inlineScan(iss); + return this; +} + + +Statement *LabelStatement::inlineScan(InlineScanState *iss) +{ + if (statement) + statement = statement->inlineScan(iss); + return this; +} + +/* -------------------------- */ + +void arrayInlineScan(InlineScanState *iss, Expressions *arguments) +{ + if (arguments) + { + for (size_t i = 0; i < arguments->dim; i++) + { Expression *e = arguments->tdata()[i]; + + if (e) + { + e = e->inlineScan(iss); + arguments->tdata()[i] = e; + } + } + } +} + +Expression *Expression::inlineScan(InlineScanState *iss) +{ + return this; +} + +void scanVar(Dsymbol *s, InlineScanState *iss) +{ + VarDeclaration *vd = s->isVarDeclaration(); + if (vd) + { + TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); + if (td) + { + for (size_t i = 0; i < td->objects->dim; i++) + { DsymbolExp *se = (DsymbolExp *)td->objects->tdata()[i]; + assert(se->op == TOKdsymbol); + scanVar(se->s, iss); + } + } + else + { + // Scan initializer (vd->init) + if (vd->init) + { + ExpInitializer *ie = vd->init->isExpInitializer(); + + if (ie) + { +#if DMDV2 + if (vd->type) + { Type *tb = vd->type->toBasetype(); + if (tb->ty == Tstruct) + { StructDeclaration *sd = ((TypeStruct *)tb)->sym; + if (sd->cpctor) + { /* The problem here is that if the initializer is a + * function call that returns a struct S with a cpctor: + * S s = foo(); + * the postblit is done by the return statement in foo() + * in s2ir.c, the intermediate code generator. + * But, if foo() is inlined and now the code looks like: + * S s = x; + * the postblit is not there, because such assignments + * are rewritten as s.cpctor(&x) by the front end. + * So, the inlining won't get the postblit called. + * Work around by not inlining these cases. + * A proper fix would be to move all the postblit + * additions to the front end. + */ + return; + } + } + } +#endif + ie->exp = ie->exp->inlineScan(iss); + } + } + } + } +} + +Expression *DeclarationExp::inlineScan(InlineScanState *iss) +{ + //printf("DeclarationExp::inlineScan()\n"); + scanVar(declaration, iss); + return this; +} + +Expression *UnaExp::inlineScan(InlineScanState *iss) +{ + e1 = e1->inlineScan(iss); + return this; +} + +Expression *AssertExp::inlineScan(InlineScanState *iss) +{ + e1 = e1->inlineScan(iss); + if (msg) + msg = msg->inlineScan(iss); + return this; +} + +Expression *BinExp::inlineScan(InlineScanState *iss) +{ + e1 = e1->inlineScan(iss); + e2 = e2->inlineScan(iss); + return this; +} + + +Expression *CallExp::inlineScan(InlineScanState *iss) +{ Expression *e = this; + + //printf("CallExp::inlineScan()\n"); + e1 = e1->inlineScan(iss); + arrayInlineScan(iss, arguments); + + if (e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e1; + FuncDeclaration *fd = ve->var->isFuncDeclaration(); + + if (fd && fd != iss->fd && fd->canInline(0, 0, 0)) + { + e = fd->expandInline(iss, NULL, arguments, NULL); + } + } + else if (e1->op == TOKdotvar) + { + DotVarExp *dve = (DotVarExp *)e1; + FuncDeclaration *fd = dve->var->isFuncDeclaration(); + + if (fd && fd != iss->fd && fd->canInline(1, 0, 0)) + { + if (dve->e1->op == TOKcall && + dve->e1->type->toBasetype()->ty == Tstruct) + { + /* To create ethis, we'll need to take the address + * of dve->e1, but this won't work if dve->e1 is + * a function call. + */ + ; + } + else + e = fd->expandInline(iss, dve->e1, arguments, NULL); + } + } + + return e; +} + + +Expression *SliceExp::inlineScan(InlineScanState *iss) +{ + e1 = e1->inlineScan(iss); + if (lwr) + lwr = lwr->inlineScan(iss); + if (upr) + upr = upr->inlineScan(iss); + return this; +} + + +Expression *TupleExp::inlineScan(InlineScanState *iss) +{ Expression *e = this; + + //printf("TupleExp::inlineScan()\n"); + arrayInlineScan(iss, exps); + + return e; +} + + +Expression *ArrayLiteralExp::inlineScan(InlineScanState *iss) +{ Expression *e = this; + + //printf("ArrayLiteralExp::inlineScan()\n"); + arrayInlineScan(iss, elements); + + return e; +} + + +Expression *AssocArrayLiteralExp::inlineScan(InlineScanState *iss) +{ Expression *e = this; + + //printf("AssocArrayLiteralExp::inlineScan()\n"); + arrayInlineScan(iss, keys); + arrayInlineScan(iss, values); + + return e; +} + + +Expression *StructLiteralExp::inlineScan(InlineScanState *iss) +{ Expression *e = this; + + //printf("StructLiteralExp::inlineScan()\n"); + arrayInlineScan(iss, elements); + + return e; +} + + +Expression *ArrayExp::inlineScan(InlineScanState *iss) +{ Expression *e = this; + + //printf("ArrayExp::inlineScan()\n"); + e1 = e1->inlineScan(iss); + arrayInlineScan(iss, arguments); + + return e; +} + + +Expression *CondExp::inlineScan(InlineScanState *iss) +{ + econd = econd->inlineScan(iss); + e1 = e1->inlineScan(iss); + e2 = e2->inlineScan(iss); + return this; +} + + +/* ========== =============== */ + +void FuncDeclaration::inlineScan() +{ + InlineScanState iss; + +#if LOG + printf("FuncDeclaration::inlineScan('%s')\n", toChars()); +#endif + memset(&iss, 0, sizeof(iss)); + iss.fd = this; + if (fbody && !naked) + { + inlineNest++; + fbody = fbody->inlineScan(&iss); + inlineNest--; + } +} + +int FuncDeclaration::canInline(int hasthis, int hdrscan, int statementsToo) +{ + InlineCostState ics; + int cost; + +#define CANINLINE_LOG 0 + +#if CANINLINE_LOG + printf("FuncDeclaration::canInline(hasthis = %d, statementsToo = %d, '%s')\n", hasthis, statementsToo, toChars()); +#endif + + if (needThis() && !hasthis) + return 0; + + if (inlineNest || (semanticRun < PASSsemantic3 && !hdrscan)) + { +#if CANINLINE_LOG + printf("\t1: no, inlineNest = %d, semanticRun = %d\n", inlineNest, semanticRun); +#endif + return 0; + } + +#if 1 + switch (statementsToo ? inlineStatusStmt : inlineStatusExp) + { + case ILSyes: +#if CANINLINE_LOG + printf("\t1: yes %s\n", toChars()); +#endif + return 1; + + case ILSno: +#if CANINLINE_LOG + printf("\t1: no %s\n", toChars()); +#endif + return 0; + + case ILSuninitialized: + break; + + default: + assert(0); + } +#endif + + if (type) + { assert(type->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)(type); + if (tf->varargs == 1) // no variadic parameter lists + goto Lno; + + /* Don't inline a function that returns non-void, but has + * no return expression. + * No statement inlining for non-voids. + */ + if (tf->next && tf->next->ty != Tvoid && + (!(hasReturnExp & 1) || statementsToo) && + !hdrscan) + goto Lno; + } + + if ( + !fbody || + ident == Id::ensure || // ensure() has magic properties the inliner loses + !hdrscan && + ( +#if 0 + isCtorDeclaration() || // cannot because need to convert: + // return; + // to: + // return this; +#endif + isSynchronized() || + isImportedSymbol() || + hasNestedFrameRefs() || // no nested references to this frame + (isVirtual() && !isFinal()) + )) + { + goto Lno; + } + +#if 0 + /* If any parameters are Tsarray's (which are passed by reference) + * or out parameters (also passed by reference), don't do inlining. + */ + if (parameters) + { + for (size_t i = 0; i < parameters->dim; i++) + { + VarDeclaration *v = parameters->tdata()[i]; + if (v->type->toBasetype()->ty == Tsarray) + goto Lno; + } + } +#endif + + memset(&ics, 0, sizeof(ics)); + ics.hasthis = hasthis; + ics.fd = this; + ics.hdrscan = hdrscan; + cost = fbody->inlineCost(&ics); +#if CANINLINE_LOG + printf("cost = %d for %s\n", cost, toChars()); +#endif + if (tooCostly(cost)) + goto Lno; + if (!statementsToo && cost > COST_MAX) + goto Lno; + + if (!hdrscan) + { + // Don't modify inlineStatus for header content scan + if (statementsToo) + inlineStatusStmt = ILSyes; + else + inlineStatusExp = ILSyes; + + inlineScan(); // Don't scan recursively for header content scan + + if (inlineStatusExp == ILSuninitialized) + { + // Need to redo cost computation, as some statements or expressions have been inlined + memset(&ics, 0, sizeof(ics)); + ics.hasthis = hasthis; + ics.fd = this; + ics.hdrscan = hdrscan; + cost = fbody->inlineCost(&ics); + #if CANINLINE_LOG + printf("recomputed cost = %d for %s\n", cost, toChars()); + #endif + if (tooCostly(cost)) + goto Lno; + if (!statementsToo && cost > COST_MAX) + goto Lno; + + if (statementsToo) + inlineStatusStmt = ILSyes; + else + inlineStatusExp = ILSyes; + } + } +#if CANINLINE_LOG + printf("\t2: yes %s\n", toChars()); +#endif + return 1; + +Lno: + if (!hdrscan) // Don't modify inlineStatus for header content scan + { if (statementsToo) + inlineStatusStmt = ILSno; + else + inlineStatusExp = ILSno; + } +#if CANINLINE_LOG + printf("\t2: no %s\n", toChars()); +#endif + return 0; +} + +Expression *FuncDeclaration::expandInline(InlineScanState *iss, Expression *ethis, Expressions *arguments, Statement **ps) +{ + InlineDoState ids; + DeclarationExp *de; + Expression *e = NULL; + Statements *as = NULL; + +#if LOG || CANINLINE_LOG + printf("FuncDeclaration::expandInline('%s')\n", toChars()); +#endif + + memset(&ids, 0, sizeof(ids)); + ids.parent = iss->fd; + ids.fd = this; + + if (ps) + as = new Statements(); + + // Set up vthis + if (ethis) + { + VarDeclaration *vthis; + ExpInitializer *ei; + VarExp *ve; + +#if STRUCTTHISREF + if (ethis->type->ty == Tpointer) + { Type *t = ethis->type->nextOf(); + ethis = new PtrExp(ethis->loc, ethis); + ethis->type = t; + } + ei = new ExpInitializer(ethis->loc, ethis); + + vthis = new VarDeclaration(ethis->loc, ethis->type, Id::This, ei); + if (ethis->type->ty != Tclass) + vthis->storage_class = STCref; + else + vthis->storage_class = STCin; +#else + if (ethis->type->ty != Tclass && ethis->type->ty != Tpointer) + { + ethis = ethis->addressOf(NULL); + } + + ei = new ExpInitializer(ethis->loc, ethis); + + vthis = new VarDeclaration(ethis->loc, ethis->type, Id::This, ei); + vthis->storage_class = STCin; +#endif + vthis->linkage = LINKd; + vthis->parent = iss->fd; + + ve = new VarExp(vthis->loc, vthis); + ve->type = vthis->type; + + ei->exp = new AssignExp(vthis->loc, ve, ethis); + ei->exp->type = ve->type; +#if STRUCTTHISREF + if (ethis->type->ty != Tclass) + { /* This is a reference initialization, not a simple assignment. + */ + ei->exp->op = TOKconstruct; + } +#endif + + ids.vthis = vthis; + } + + // Set up parameters + if (ethis) + { + e = new DeclarationExp(0, ids.vthis); + e->type = Type::tvoid; + if (as) + as->push(new ExpStatement(e->loc, e)); + } + + if (arguments && arguments->dim) + { + assert(parameters->dim == arguments->dim); + + for (size_t i = 0; i < arguments->dim; i++) + { + VarDeclaration *vfrom = parameters->tdata()[i]; + VarDeclaration *vto; + Expression *arg = arguments->tdata()[i]; + ExpInitializer *ei; + VarExp *ve; + + ei = new ExpInitializer(arg->loc, arg); + + vto = new VarDeclaration(vfrom->loc, vfrom->type, vfrom->ident, ei); + vto->storage_class |= vfrom->storage_class & (STCin | STCout | STClazy | STCref); + vto->linkage = vfrom->linkage; + vto->parent = iss->fd; + //printf("vto = '%s', vto->storage_class = x%x\n", vto->toChars(), vto->storage_class); + //printf("vto->parent = '%s'\n", iss->fd->toChars()); + + ve = new VarExp(vto->loc, vto); + //ve->type = vto->type; + ve->type = arg->type; + + ei->exp = new ConstructExp(vto->loc, ve, arg); + ei->exp->type = ve->type; +//ve->type->print(); +//arg->type->print(); +//ei->exp->print(); + + ids.from.push(vfrom); + ids.to.push(vto); + + de = new DeclarationExp(0, vto); + de->type = Type::tvoid; + + if (as) + as->push(new ExpStatement(0, de)); + else + e = Expression::combine(e, de); + } + } + + if (ps) + { + inlineNest++; + Statement *s = fbody->doInlineStatement(&ids); + as->push(s); + *ps = new ScopeStatement(0, new CompoundStatement(0, as)); + inlineNest--; + } + else + { + inlineNest++; + Expression *eb = fbody->doInline(&ids); + e = Expression::combine(e, eb); + inlineNest--; + //eb->type->print(); + //eb->print(); + //eb->dump(0); + } + + /* There's a problem if what the function returns is used subsequently as an + * lvalue, as in a struct return that is then used as a 'this'. + * If we take the address of the return value, we will be taking the address + * of the original, not the copy. Fix this by assigning the return value to + * a temporary, then returning the temporary. If the temporary is used as an + * lvalue, it will work. + * This only happens with struct returns. + * See Bugzilla 2127 for an example. + */ + TypeFunction *tf = (TypeFunction*)type; + if (!ps && tf->next->ty == Tstruct) + { + /* Generate a new variable to hold the result and initialize it with the + * inlined body of the function: + * tret __inlineretval = e; + */ + ExpInitializer* ei = new ExpInitializer(loc, e); + + Identifier* tmp = Identifier::generateId("__inlineretval"); + VarDeclaration* vd = new VarDeclaration(loc, tf->next, tmp, ei); + vd->storage_class = tf->isref ? STCref : 0; + vd->linkage = tf->linkage; + vd->parent = iss->fd; + + VarExp *ve = new VarExp(loc, vd); + ve->type = tf->next; + + ei->exp = new ConstructExp(loc, ve, e); + ei->exp->type = ve->type; + + DeclarationExp* de = new DeclarationExp(0, vd); + de->type = Type::tvoid; + + // Chain the two together: + // ( typeof(return) __inlineretval = ( inlined body )) , __inlineretval + e = Expression::combine(de, ve); + + //fprintf(stderr, "CallExp::inlineScan: e = "); e->print(); + } + + // Need to reevaluate whether parent can now be inlined + // in expressions, as we might have inlined statements + iss->fd->inlineStatusExp = ILSuninitialized; + return e; +} + + +/**************************************************** + * Perform the "inline copying" of a default argument for a function parameter. + */ + +Expression *Expression::inlineCopy(Scope *sc) +{ +#if 0 + /* See Bugzilla 2935 for explanation of why just a copy() is broken + */ + return copy(); +#else + InlineCostState ics; + + memset(&ics, 0, sizeof(ics)); + ics.hdrscan = 1; // so DeclarationExp:: will work on 'statics' which are not + int cost = expressionInlineCost(this, &ics); + if (cost >= COST_MAX) + { error("cannot inline default argument %s", toChars()); + return new ErrorExp(); + } + InlineDoState ids; + memset(&ids, 0, sizeof(ids)); + ids.parent = sc->parent; + Expression *e = doInline(&ids); + return e; +#endif +} diff --git a/interpret.c b/interpret.c new file mode 100644 index 00000000..3258566a --- /dev/null +++ b/interpret.c @@ -0,0 +1,6496 @@ +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include + +#include "rmem.h" + +#include "statement.h" +#include "expression.h" +#include "cond.h" +#include "init.h" +#include "staticassert.h" +#include "mtype.h" +#include "scope.h" +#include "declaration.h" +#include "aggregate.h" +#include "id.h" +#include "utf.h" +#include "attrib.h" // for AttribDeclaration + +#include "template.h" +TemplateInstance *isSpeculativeFunction(FuncDeclaration *fd); + + +#define LOG 0 +#define LOGASSIGN 0 +#define SHOWPERFORMANCE 0 + +// Maximum allowable recursive function calls in CTFE +#define CTFE_RECURSION_LIMIT 1000 + +// The values of all CTFE variables. +struct CtfeStack +{ +private: + /* The stack. Every declaration we encounter is pushed here, + together with the VarDeclaration, and the previous + stack address of that variable, so that we can restore it + when we leave the stack frame. + Ctfe Stack addresses are just 0-based integers, but we save + them as 'void *' because ArrayBase can only do pointers. + */ + Expressions values; // values on the stack + VarDeclarations vars; // corresponding variables + ArrayBase savedId; // id of the previous state of that var + + /* Global constants get saved here after evaluation, so we never + * have to redo them. This saves a lot of time and memory. + */ + Expressions globalValues; // values of global constants + size_t framepointer; // current frame pointer + size_t maxStackPointer; // most stack we've ever used +public: + CtfeStack() : framepointer(0) + { + } + size_t stackPointer() + { + return values.dim; + } + // Largest number of stack positions we've used + size_t maxStackUsage() + { + return maxStackPointer; + } + // return the previous frame + size_t startFrame() + { + size_t oldframe = framepointer; + framepointer = stackPointer(); + return oldframe; + } + void endFrame(size_t oldframe) + { + popAll(framepointer); + framepointer = oldframe; + } + Expression *getValue(VarDeclaration *v) + { + if (v->isDataseg() && !v->isCTFE()) + { + assert(v->ctfeAdrOnStack >= 0 && + v->ctfeAdrOnStack < globalValues.dim); + return globalValues.tdata()[v->ctfeAdrOnStack]; + } + assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < stackPointer()); + return values.tdata()[v->ctfeAdrOnStack]; + } + void setValue(VarDeclaration *v, Expression *e) + { + assert(!v->isDataseg() || v->isCTFE()); + assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < stackPointer()); + values.tdata()[v->ctfeAdrOnStack] = e; + } + void push(VarDeclaration *v) + { + assert(!v->isDataseg() || v->isCTFE()); + if (v->ctfeAdrOnStack!= (size_t)-1 + && v->ctfeAdrOnStack >= framepointer) + { // Already exists in this frame, reuse it. + values.tdata()[v->ctfeAdrOnStack] = NULL; + return; + } + savedId.push((void *)(v->ctfeAdrOnStack)); + v->ctfeAdrOnStack = values.dim; + vars.push(v); + values.push(NULL); + } + void pop(VarDeclaration *v) + { + assert(!v->isDataseg() || v->isCTFE()); + assert(!(v->storage_class & (STCref | STCout))); + int oldid = v->ctfeAdrOnStack; + v->ctfeAdrOnStack = (size_t)(savedId.tdata()[oldid]); + if (v->ctfeAdrOnStack == values.dim - 1) + { + values.pop(); + vars.pop(); + savedId.pop(); + } + } + void popAll(size_t stackpointer) + { + if (stackPointer() > maxStackPointer) + maxStackPointer = stackPointer(); + assert(values.dim >= stackpointer && stackpointer >= 0); + for (size_t i = stackpointer; i < values.dim; ++i) + { + VarDeclaration *v = vars.tdata()[i]; + v->ctfeAdrOnStack = (size_t)(savedId.tdata()[i]); + } + values.setDim(stackpointer); + vars.setDim(stackpointer); + savedId.setDim(stackpointer); + } + void saveGlobalConstant(VarDeclaration *v, Expression *e) + { + assert(v->isDataseg() && !v->isCTFE()); + v->ctfeAdrOnStack = globalValues.dim; + globalValues.push(e); + } +}; + +CtfeStack ctfeStack; + + +struct InterState +{ + InterState *caller; // calling function's InterState + FuncDeclaration *fd; // function being interpreted + size_t framepointer; // frame pointer of previous frame + Statement *start; // if !=NULL, start execution at this statement + Statement *gotoTarget; /* target of EXP_GOTO_INTERPRET result; also + * target of labelled EXP_BREAK_INTERPRET or + * EXP_CONTINUE_INTERPRET. (NULL if no label). + */ + Expression *localThis; // value of 'this', or NULL if none + bool awaitingLvalueReturn; // Support for ref return values: + // Any return to this function should return an lvalue. + InterState(); +}; + +InterState::InterState() +{ + memset(this, 0, sizeof(InterState)); +} + +// Global status of the CTFE engine +struct CtfeStatus +{ + static int callDepth; // current number of recursive calls + static int stackTraceCallsToSuppress; /* When printing a stack trace, + * suppress this number of calls + */ + static int maxCallDepth; // highest number of recursive calls + static int numArrayAllocs; // Number of allocated arrays + static int numAssignments; // total number of assignments executed +}; + +int CtfeStatus::callDepth = 0; +int CtfeStatus::stackTraceCallsToSuppress = 0; +int CtfeStatus::maxCallDepth = 0; +int CtfeStatus::numArrayAllocs = 0; +int CtfeStatus::numAssignments = 0; + +// CTFE diagnostic information +void printCtfePerformanceStats() +{ +#if SHOWPERFORMANCE + printf(" ---- CTFE Performance ----\n"); + printf("max call depth = %d\tmax stack = %d\n", CtfeStatus::maxCallDepth, ctfeStack.maxStackUsage()); + printf("array allocs = %d\tassignments = %d\n\n", CtfeStatus::numArrayAllocs, CtfeStatus::numAssignments); +#endif +} + + +Expression * resolveReferences(Expression *e, Expression *thisval); +Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal); +VarDeclaration *findParentVar(Expression *e, Expression *thisval); +bool needToCopyLiteral(Expression *expr); +Expression *copyLiteral(Expression *e); +Expression *paintTypeOntoLiteral(Type *type, Expression *lit); +Expression *findKeyInAA(AssocArrayLiteralExp *ae, Expression *e2); +Expression *evaluateIfBuiltin(InterState *istate, Loc loc, + FuncDeclaration *fd, Expressions *arguments, Expression *pthis); +Expression *scrubReturnValue(Loc loc, Expression *e); +bool isAssocArray(Type *t); +bool isPointer(Type *t); + +// CTFE only expressions +#define TOKclassreference ((TOK)(TOKMAX+1)) +#define TOKthrownexception ((TOK)(TOKMAX+2)) + +// Reference to a class, or an interface. We need this when we +// point to a base class (we must record what the type is). +struct ClassReferenceExp : Expression +{ + StructLiteralExp *value; + ClassReferenceExp(Loc loc, StructLiteralExp *lit, Type *type) : Expression(loc, TOKclassreference, sizeof(ClassReferenceExp)) + { + assert(lit && lit->sd && lit->sd->isClassDeclaration()); + this->value = lit; + this->type = type; + } + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue) + { + //printf("ClassReferenceExp::interpret() %s\n", value->toChars()); + return this; + } + char *toChars() + { + return value->toChars(); + } + ClassDeclaration *originalClass() + { + return value->sd->isClassDeclaration(); + } + // Return index of the field, or -1 if not found + int getFieldIndex(Type *fieldtype, size_t fieldoffset) + { + ClassDeclaration *cd = originalClass(); + size_t fieldsSoFar = 0; + for (size_t j = 0; j < value->elements->dim; j++) + { while (j - fieldsSoFar >= cd->fields.dim) + { fieldsSoFar += cd->fields.dim; + cd = cd->baseClass; + } + Dsymbol *s = cd->fields.tdata()[j - fieldsSoFar]; + VarDeclaration *v2 = s->isVarDeclaration(); + if (fieldoffset == v2->offset && + fieldtype->size() == v2->type->size()) + { return value->elements->dim - fieldsSoFar - cd->fields.dim + (j-fieldsSoFar); + } + } + return -1; + } + // Return index of the field, or -1 if not found + // Same as getFieldIndex, but checks for a direct match with the VarDeclaration + int findFieldIndexByName(VarDeclaration *v) + { + ClassDeclaration *cd = originalClass(); + size_t fieldsSoFar = 0; + for (size_t j = 0; j < value->elements->dim; j++) + { while (j - fieldsSoFar >= cd->fields.dim) + { fieldsSoFar += cd->fields.dim; + cd = cd->baseClass; + } + Dsymbol *s = cd->fields.tdata()[j - fieldsSoFar]; + VarDeclaration *v2 = s->isVarDeclaration(); + if (v == v2) + { return value->elements->dim - fieldsSoFar - cd->fields.dim + (j-fieldsSoFar); + } + } + return -1; + } +}; + +// Return index of the field, or -1 if not found +// Same as getFieldIndex, but checks for a direct match with the VarDeclaration +int findFieldIndexByName(StructDeclaration *sd, VarDeclaration *v) +{ + for (int i = 0; i < sd->fields.dim; ++i) + { + if (sd->fields.tdata()[i] == v) + return i; + } + return -1; +} + +// Fake class which holds the thrown exception. Used for implementing exception handling. +struct ThrownExceptionExp : Expression +{ + ClassReferenceExp *thrown; // the thing being tossed + ThrownExceptionExp(Loc loc, ClassReferenceExp *victim) : Expression(loc, TOKthrownexception, sizeof(ThrownExceptionExp)) + { + this->thrown = victim; + this->type = type; + } + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue) + { + assert(0); // This should never be interpreted + return this; + } + char *toChars() + { + return (char *)"CTFE ThrownException"; + } + // Generate an error message when this exception is not caught + void generateUncaughtError() + { + thrown->error("Uncaught CTFE exception %s(%s)", thrown->type->toChars(), + thrown->value->elements->tdata()[0]->toChars()); + /* Also give the line where the throw statement was. We won't have it + * in the case where the ThrowStatement is generated internally + * (eg, in ScopeStatement) + */ + if (loc.filename && !loc.equals(thrown->loc)) + errorSupplemental(loc, "thrown from here"); + } +}; + +// True if 'e' is EXP_CANT_INTERPRET, or an exception +bool exceptionOrCantInterpret(Expression *e) +{ + if (e == EXP_CANT_INTERPRET) return true; + if (!e || e == EXP_GOTO_INTERPRET || e == EXP_VOID_INTERPRET + || e == EXP_BREAK_INTERPRET || e == EXP_CONTINUE_INTERPRET) + return false; + return e->op == TOKthrownexception; +} + + +// Used for debugging only +void showCtfeExpr(Expression *e, int level = 0) +{ + for (int i = level; i>0; --i) printf(" "); + Expressions *elements = NULL; + // We need the struct definition to detect block assignment + StructDeclaration *sd = NULL; + ClassDeclaration *cd = NULL; + if (e->op == TOKstructliteral) + { elements = ((StructLiteralExp *)e)->elements; + sd = ((StructLiteralExp *)e)->sd; + printf("STRUCT type = %s %p:\n", e->type->toChars(), + e); + } + else if (e->op == TOKclassreference) + { elements = ((ClassReferenceExp *)e)->value->elements; + cd = ((ClassReferenceExp *)e)->originalClass(); + printf("CLASS type = %s %p:\n", e->type->toChars(), + ((ClassReferenceExp *)e)->value); + } + else if (e->op == TOKarrayliteral) + { + elements = ((ArrayLiteralExp *)e)->elements; + printf("ARRAY LITERAL type=%s %p:\n", e->type->toChars(), + e); + } + else if (e->op == TOKassocarrayliteral) + { + printf("AA LITERAL type=%s %p:\n", e->type->toChars(), + e); + } + else if (e->op == TOKstring) + { + printf("STRING %s %p\n", e->toChars(), + ((StringExp *)e)->string); + } + else if (e->op == TOKslice) + { + printf("SLICE %p: %s\n", e, e->toChars()); + showCtfeExpr(((SliceExp *)e)->e1, level + 1); + } + else if (e->op == TOKvar) + { + printf("VAR %p %s\n", e, e->toChars()); + VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); + if (v && v->getValue()) + showCtfeExpr(v->getValue(), level + 1); + } + else if (isPointer(e->type)) + { + // This is potentially recursive. We mustn't try to print the thing we're pointing to. + if (e->op == TOKindex) + printf("POINTER %p into %p [%s]\n", e, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2->toChars()); + else if (e->op == TOKdotvar) + printf("POINTER %p to %p .%s\n", e, ((DotVarExp *)e)->e1, ((DotVarExp *)e)->var->toChars()); + else + printf("POINTER %p: %s\n", e, e->toChars()); + } + else + printf("VALUE %p: %s\n", e, e->toChars()); + + if (elements) + { + size_t fieldsSoFar = 0; + for (size_t i = 0; i < elements->dim; i++) + { Expression *z = NULL; + Dsymbol *s = NULL; + if (i > 15) { + printf("...(total %d elements)\n", elements->dim); + return; + } + if (sd) + { s = sd->fields.tdata()[i]; + z = elements->tdata()[i]; + } + else if (cd) + { while (i - fieldsSoFar >= cd->fields.dim) + { fieldsSoFar += cd->fields.dim; + cd = cd->baseClass; + for (int j = level; j>0; --j) printf(" "); + printf(" BASE CLASS: %s\n", cd->toChars()); + } + s = cd->fields.tdata()[i - fieldsSoFar]; + size_t indx = (elements->dim - fieldsSoFar)- cd->fields.dim + i; + assert(indx >= 0); + assert(indx < elements->dim); + z = elements->tdata()[indx]; + } + if (!z) { + for (int j = level; j>0; --j) printf(" "); + printf(" void\n"); + continue; + } + + if (s) + { + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + // If it is a void assignment, use the default initializer + if ((v->type->ty != z->type->ty) && v->type->ty == Tsarray) + { + for (int j = level; --j;) printf(" "); + printf(" field: block initalized static array\n"); + continue; + } + } + showCtfeExpr(z, level + 1); + } + } +} + +/************************************* + * Attempt to interpret a function given the arguments. + * Input: + * istate state for calling function (NULL if none) + * arguments function arguments + * thisarg 'this', if a needThis() function, NULL if not. + * + * Return result expression if successful, EXP_CANT_INTERPRET if not. + */ + +Expression *FuncDeclaration::interpret(InterState *istate, Expressions *arguments, Expression *thisarg) +{ +#if LOG + printf("\n********\nFuncDeclaration::interpret(istate = %p) %s\n", istate, toChars()); +#endif + if (semanticRun == PASSsemantic3) + return EXP_CANT_INTERPRET; + + if (semanticRun < PASSsemantic3 && scope) + { + /* Forward reference - we need to run semantic3 on this function. + * If errors are gagged, and it's not part of a speculative + * template instance, we need to temporarily ungag errors. + */ + int olderrors = global.errors; + int oldgag = global.gag; + TemplateInstance *spec = isSpeculativeFunction(this); + if (global.gag && !spec) + global.gag = 0; + semantic3(scope); + global.gag = oldgag; // regag errors + + // If it is a speculatively-instantiated template, and errors occur, + // we need to mark the template as having errors. + if (spec && global.errors != olderrors) + spec->errors = global.errors - olderrors; + if (olderrors != global.errors) // if errors compiling this function + return EXP_CANT_INTERPRET; + } + if (semanticRun < PASSsemantic3done) + return EXP_CANT_INTERPRET; + + Type *tb = type->toBasetype(); + assert(tb->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)tb; + Type *tret = tf->next->toBasetype(); + if (tf->varargs && arguments && + ((parameters && arguments->dim != parameters->dim) || (!parameters && arguments->dim))) + { + error("C-style variadic functions are not yet implemented in CTFE"); + return EXP_CANT_INTERPRET; + } + + // Nested functions always inherit the 'this' pointer from the parent, + // except for delegates. (Note that the 'this' pointer may be null). + // Func literals report isNested() even if they are in global scope, + // so we need to check that the parent is a function. + if (isNested() && toParent2()->isFuncDeclaration() && !thisarg && istate) + thisarg = istate->localThis; + + InterState istatex; + istatex.caller = istate; + istatex.fd = this; + istatex.localThis = thisarg; + istatex.framepointer = ctfeStack.startFrame(); + + Expressions vsave; // place to save previous parameter values + size_t dim = 0; + if (needThis() && !thisarg) + { // error, no this. Prevent segfault. + error("need 'this' to access member %s", toChars()); + return EXP_CANT_INTERPRET; + } + if (thisarg && !istate) + { // Check that 'this' aleady has a value + if (thisarg->interpret(istate) == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + } + static int evaluatingArgs = 0; + if (arguments) + { + dim = arguments->dim; + assert(!dim || (parameters && (parameters->dim == dim))); + vsave.setDim(dim); + + /* Evaluate all the arguments to the function, + * store the results in eargs[] + */ + Expressions eargs; + eargs.setDim(dim); + for (size_t i = 0; i < dim; i++) + { Expression *earg = arguments->tdata()[i]; + Parameter *arg = Parameter::getNth(tf->parameters, i); + + if (arg->storageClass & (STCout | STCref)) + { + if (!istate && (arg->storageClass & STCout)) + { // initializing an out parameter involves writing to it. + earg->error("global %s cannot be passed as an 'out' parameter at compile time", earg->toChars()); + return EXP_CANT_INTERPRET; + } + // Convert all reference arguments into lvalue references + ++evaluatingArgs; + earg = earg->interpret(istate, ctfeNeedLvalueRef); + --evaluatingArgs; + if (earg == EXP_CANT_INTERPRET) + return earg; + } + else if (arg->storageClass & STClazy) + { + } + else + { /* Value parameters + */ + Type *ta = arg->type->toBasetype(); + if (ta->ty == Tsarray && earg->op == TOKaddress) + { + /* Static arrays are passed by a simple pointer. + * Skip past this to get at the actual arg. + */ + earg = ((AddrExp *)earg)->e1; + } + ++evaluatingArgs; + earg = earg->interpret(istate); + --evaluatingArgs; + if (earg == EXP_CANT_INTERPRET) + return earg; + } + if (earg->op == TOKthrownexception) + { + if (istate) + return earg; + ((ThrownExceptionExp *)earg)->generateUncaughtError(); + return EXP_CANT_INTERPRET; + } + eargs.tdata()[i] = earg; + } + + for (size_t i = 0; i < dim; i++) + { Expression *earg = eargs.tdata()[i]; + Parameter *arg = Parameter::getNth(tf->parameters, i); + VarDeclaration *v = parameters->tdata()[i]; +#if LOG + printf("arg[%d] = %s\n", i, earg->toChars()); +#endif + if (arg->storageClass & (STCout | STCref) && earg->op == TOKvar) + { + VarExp *ve = (VarExp *)earg; + VarDeclaration *v2 = ve->var->isVarDeclaration(); + if (!v2) + { + error("cannot interpret %s as a ref parameter", ve->toChars()); + return EXP_CANT_INTERPRET; + } + /* The push() isn't a variable we'll use, it's just a place + * to save the old value of v. + * Note that v might be v2! So we need to save v2's index + * before pushing. + */ + size_t oldadr = v2->ctfeAdrOnStack; + ctfeStack.push(v); + v->ctfeAdrOnStack = oldadr; + assert(v2->hasValue()); + } + else + { // Value parameters and non-trivial references + ctfeStack.push(v); + v->setValueWithoutChecking(earg); + } +#if LOG || LOGASSIGN + printf("interpreted arg[%d] = %s\n", i, earg->toChars()); + showCtfeExpr(earg); +#endif + } + } + + if (vresult) + ctfeStack.push(vresult); + + // Enter the function + ++CtfeStatus::callDepth; + if (CtfeStatus::callDepth > CtfeStatus::maxCallDepth) + CtfeStatus::maxCallDepth = CtfeStatus::callDepth; + + Expression *e = NULL; + while (1) + { + if (CtfeStatus::callDepth > CTFE_RECURSION_LIMIT) + { // This is a compiler error. It must not be suppressed. + global.gag = 0; + error("CTFE recursion limit exceeded"); + e = EXP_CANT_INTERPRET; + break; + } + e = fbody->interpret(&istatex); + if (e == EXP_CANT_INTERPRET) + { +#if LOG + printf("function body failed to interpret\n"); +#endif + } + + /* This is how we deal with a recursive statement AST + * that has arbitrary goto statements in it. + * Bubble up a 'result' which is the target of the goto + * statement, then go recursively down the AST looking + * for that statement, then execute starting there. + */ + if (e == EXP_GOTO_INTERPRET) + { + istatex.start = istatex.gotoTarget; // set starting statement + istatex.gotoTarget = NULL; + } + else + break; + } + assert(e != EXP_CONTINUE_INTERPRET && e != EXP_BREAK_INTERPRET); + + // Leave the function + --CtfeStatus::callDepth; + + ctfeStack.endFrame(istatex.framepointer); + + // If fell off the end of a void function, return void + if (!e && type->toBasetype()->nextOf()->ty == Tvoid) + return EXP_VOID_INTERPRET; + // If it generated an exception, return it + if (exceptionOrCantInterpret(e)) + { + if (istate || e == EXP_CANT_INTERPRET) + return e; + ((ThrownExceptionExp *)e)->generateUncaughtError(); + return EXP_CANT_INTERPRET; + } + if (!istate && !evaluatingArgs) + { + e = scrubReturnValue(loc, e); + } + return e; +} + +/******************************** Statement ***************************/ + +#define START() \ + if (istate->start) \ + { if (istate->start != this) \ + return NULL; \ + istate->start = NULL; \ + } + +/*********************************** + * Interpret the statement. + * Returns: + * NULL continue to next statement + * EXP_CANT_INTERPRET cannot interpret statement at compile time + * !NULL expression from return statement, or thrown exception + */ + +Expression *Statement::interpret(InterState *istate) +{ +#if LOG + printf("Statement::interpret()\n"); +#endif + START() + error("Statement %s cannot be interpreted at compile time", this->toChars()); + return EXP_CANT_INTERPRET; +} + +Expression *ExpStatement::interpret(InterState *istate) +{ +#if LOG + printf("ExpStatement::interpret(%s)\n", exp ? exp->toChars() : ""); +#endif + START() + if (exp) + { + Expression *e = exp->interpret(istate, ctfeNeedNothing); + if (e == EXP_CANT_INTERPRET) + { + //printf("-ExpStatement::interpret(): %p\n", e); + return EXP_CANT_INTERPRET; + } + if (e && e!= EXP_VOID_INTERPRET && e->op == TOKthrownexception) + return e; + } + return NULL; +} + +Expression *CompoundStatement::interpret(InterState *istate) +{ Expression *e = NULL; + +#if LOG + printf("CompoundStatement::interpret()\n"); +#endif + if (istate->start == this) + istate->start = NULL; + if (statements) + { + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = statements->tdata()[i]; + + if (s) + { + e = s->interpret(istate); + if (e) + break; + } + } + } +#if LOG + printf("-CompoundStatement::interpret() %p\n", e); +#endif + return e; +} + +Expression *UnrolledLoopStatement::interpret(InterState *istate) +{ Expression *e = NULL; + +#if LOG + printf("UnrolledLoopStatement::interpret()\n"); +#endif + if (istate->start == this) + istate->start = NULL; + if (statements) + { + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = statements->tdata()[i]; + + e = s->interpret(istate); + if (e == EXP_CANT_INTERPRET) + break; + if (e == EXP_CONTINUE_INTERPRET) + { + if (istate->gotoTarget && istate->gotoTarget != this) + break; // continue at higher level + istate->gotoTarget = NULL; + e = NULL; + continue; + } + if (e == EXP_BREAK_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level + break; + } + if (e) + break; + } + } + return e; +} + +// For CTFE only. Returns true if 'e' is TRUE or a non-null pointer. +int isTrueBool(Expression *e) +{ + return e->isBool(TRUE) || ((e->type->ty == Tpointer || e->type->ty == Tclass) + && e->op != TOKnull); +} + +Expression *IfStatement::interpret(InterState *istate) +{ +#if LOG + printf("IfStatement::interpret(%s)\n", condition->toChars()); +#endif + + if (istate->start == this) + istate->start = NULL; + if (istate->start) + { + Expression *e = NULL; + if (ifbody) + e = ifbody->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + if (istate->start && elsebody) + e = elsebody->interpret(istate); + return e; + } + + Expression *e = condition->interpret(istate); + assert(e); + //if (e == EXP_CANT_INTERPRET) printf("cannot interpret\n"); + if (e != EXP_CANT_INTERPRET && (e && e->op != TOKthrownexception)) + { + if (isTrueBool(e)) + e = ifbody ? ifbody->interpret(istate) : NULL; + else if (e->isBool(FALSE)) + e = elsebody ? elsebody->interpret(istate) : NULL; + else + { + e = EXP_CANT_INTERPRET; + } + } + return e; +} + +Expression *ScopeStatement::interpret(InterState *istate) +{ +#if LOG + printf("ScopeStatement::interpret()\n"); +#endif + if (istate->start == this) + istate->start = NULL; + return statement ? statement->interpret(istate) : NULL; +} + +Expression *resolveSlice(Expression *e) +{ + if ( ((SliceExp *)e)->e1->op == TOKnull) + return ((SliceExp *)e)->e1; + return Slice(e->type, ((SliceExp *)e)->e1, + ((SliceExp *)e)->lwr, ((SliceExp *)e)->upr); +} + +/* Determine the array length, without interpreting it. + * e must be an array literal, or a slice + * It's very wasteful to resolve the slice when we only + * need the length. + */ +uinteger_t resolveArrayLength(Expression *e) +{ + if (e->op == TOKnull) + return 0; + if (e->op == TOKslice) + { uinteger_t ilo = ((SliceExp *)e)->lwr->toInteger(); + uinteger_t iup = ((SliceExp *)e)->upr->toInteger(); + return iup - ilo; + } + if (e->op == TOKstring) + { return ((StringExp *)e)->len; + } + if (e->op == TOKarrayliteral) + { ArrayLiteralExp *ale = (ArrayLiteralExp *)e; + return ale->elements ? ale->elements->dim : 0; + } + if (e->op == TOKassocarrayliteral) + { AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e; + return ale->keys->dim; + } + assert(0); + return 0; +} + +// As Equal, but resolves slices before comparing +Expression *ctfeEqual(enum TOK op, Type *type, Expression *e1, Expression *e2) +{ + if (e1->op == TOKslice) + e1 = resolveSlice(e1); + if (e2->op == TOKslice) + e2 = resolveSlice(e2); + return Equal(op, type, e1, e2); +} + +Expression *ctfeCat(Type *type, Expression *e1, Expression *e2) +{ + Loc loc = e1->loc; + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + Expression *e; + if (e2->op == TOKstring && e1->op == TOKarrayliteral && + t1->nextOf()->isintegral()) + { + // [chars] ~ string => string (only valid for CTFE) + StringExp *es1 = (StringExp *)e2; + ArrayLiteralExp *es2 = (ArrayLiteralExp *)e1; + size_t len = es1->len + es2->elements->dim; + int sz = es1->sz; + + void *s = mem.malloc((len + 1) * sz); + memcpy((char *)s + sz * es2->elements->dim, es1->string, es1->len * sz); + for (size_t i = 0; i < es2->elements->dim; i++) + { Expression *es2e = es2->elements->tdata()[i]; + if (es2e->op != TOKint64) + return EXP_CANT_INTERPRET; + dinteger_t v = es2e->toInteger(); + memcpy((unsigned char *)s + i * sz, &v, sz); + } + + // Add terminating 0 + memset((unsigned char *)s + len * sz, 0, sz); + + StringExp *es = new StringExp(loc, s, len); + es->sz = sz; + es->committed = 0; + es->type = type; + e = es; + return e; + } + else if (e1->op == TOKstring && e2->op == TOKarrayliteral && + t2->nextOf()->isintegral()) + { + // string ~ [chars] => string (only valid for CTFE) + // Concatenate the strings + StringExp *es1 = (StringExp *)e1; + ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; + size_t len = es1->len + es2->elements->dim; + int sz = es1->sz; + + void *s = mem.malloc((len + 1) * sz); + memcpy(s, es1->string, es1->len * sz); + for (size_t i = 0; i < es2->elements->dim; i++) + { Expression *es2e = es2->elements->tdata()[i]; + if (es2e->op != TOKint64) + return EXP_CANT_INTERPRET; + dinteger_t v = es2e->toInteger(); + memcpy((unsigned char *)s + (es1->len + i) * sz, &v, sz); + } + + // Add terminating 0 + memset((unsigned char *)s + len * sz, 0, sz); + + StringExp *es = new StringExp(loc, s, len); + es->sz = sz; + es->committed = 0; //es1->committed; + es->type = type; + e = es; + return e; + } + return Cat(type, e1, e2); +} + +void scrubArray(Loc loc, Expressions *elems); + +/* All results destined for use outside of CTFE need to have their CTFE-specific + * features removed. + * In particular, all slices must be resolved. + */ +Expression *scrubReturnValue(Loc loc, Expression *e) +{ + if (e->op == TOKclassreference) + { + error(loc, "%s class literals cannot be returned from CTFE", ((ClassReferenceExp*)e)->originalClass()->toChars()); + return EXP_CANT_INTERPRET; + } + if (e->op == TOKslice) + { + e = resolveSlice(e); + } + if (e->op == TOKstructliteral) + { + StructLiteralExp *se = (StructLiteralExp *)e; + se->ownedByCtfe = false; + scrubArray(loc, se->elements); + } + if (e->op == TOKstring) + { + ((StringExp *)e)->ownedByCtfe = false; + } + if (e->op == TOKarrayliteral) + { + ((ArrayLiteralExp *)e)->ownedByCtfe = false; + scrubArray(loc, ((ArrayLiteralExp *)e)->elements); + } + if (e->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; + aae->ownedByCtfe = false; + scrubArray(loc, aae->keys); + scrubArray(loc, aae->values); + } + return e; +} + +// Scrub all members of an array +void scrubArray(Loc loc, Expressions *elems) +{ + for (size_t i = 0; i < elems->dim; i++) + { + Expression *m = elems->tdata()[i]; + if (!m) + continue; + m = scrubReturnValue(loc, m); + elems->tdata()[i] = m; + } +} + + +Expression *ReturnStatement::interpret(InterState *istate) +{ +#if LOG + printf("ReturnStatement::interpret(%s)\n", exp ? exp->toChars() : ""); +#endif + START() + if (!exp) + return EXP_VOID_INTERPRET; + assert(istate && istate->fd && istate->fd->type); +#if DMDV2 + /* If the function returns a ref AND it's been called from an assignment, + * we need to return an lvalue. Otherwise, just do an (rvalue) interpret. + */ + if (istate->fd->type && istate->fd->type->ty==Tfunction) + { + TypeFunction *tf = (TypeFunction *)istate->fd->type; + if (tf->isref && istate->caller && istate->caller->awaitingLvalueReturn) + { // We need to return an lvalue + Expression *e = exp->interpret(istate, ctfeNeedLvalue); + if (e == EXP_CANT_INTERPRET) + error("ref return %s is not yet supported in CTFE", exp->toChars()); + return e; + } + if (tf->next && (tf->next->ty == Tdelegate) && istate->fd->closureVars.dim > 0) + { + // To support this, we need to copy all the closure vars + // into the delegate literal. + error("closures are not yet supported in CTFE"); + return EXP_CANT_INTERPRET; + } + } +#endif + // We need to treat pointers specially, because TOKsymoff can be used to + // return a value OR a pointer + Expression *e; + if ( isPointer(exp->type) ) + e = exp->interpret(istate, ctfeNeedLvalue); + else + e = exp->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + if (needToCopyLiteral(e)) + e = copyLiteral(e); +#if LOGASSIGN + printf("RETURN %s\n", loc.toChars()); + showCtfeExpr(e); +#endif + return e; +} + +Expression *BreakStatement::interpret(InterState *istate) +{ +#if LOG + printf("BreakStatement::interpret()\n"); +#endif + START() + if (ident) + { LabelDsymbol *label = istate->fd->searchLabel(ident); + assert(label && label->statement); + Statement *s = label->statement; + if (s->isLabelStatement()) + s = s->isLabelStatement()->statement; + if (s->isScopeStatement()) + s = s->isScopeStatement()->statement; + istate->gotoTarget = s; + return EXP_BREAK_INTERPRET; + } + else + { + istate->gotoTarget = NULL; + return EXP_BREAK_INTERPRET; + } +} + +Expression *ContinueStatement::interpret(InterState *istate) +{ +#if LOG + printf("ContinueStatement::interpret()\n"); +#endif + START() + if (ident) + { LabelDsymbol *label = istate->fd->searchLabel(ident); + assert(label && label->statement); + Statement *s = label->statement; + if (s->isLabelStatement()) + s = s->isLabelStatement()->statement; + if (s->isScopeStatement()) + s = s->isScopeStatement()->statement; + istate->gotoTarget = s; + return EXP_CONTINUE_INTERPRET; + } + else + return EXP_CONTINUE_INTERPRET; +} + +Expression *WhileStatement::interpret(InterState *istate) +{ +#if LOG + printf("WhileStatement::interpret()\n"); +#endif + assert(0); // rewritten to ForStatement + return NULL; +} + +Expression *DoStatement::interpret(InterState *istate) +{ +#if LOG + printf("DoStatement::interpret()\n"); +#endif + if (istate->start == this) + istate->start = NULL; + Expression *e; + + if (istate->start) + { + e = body ? body->interpret(istate) : NULL; + if (istate->start) + return NULL; + if (e == EXP_CANT_INTERPRET) + return e; + if (e == EXP_BREAK_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level + return e; + } + if (e == EXP_CONTINUE_INTERPRET) + if (!istate->gotoTarget || istate->gotoTarget == this) + { + goto Lcontinue; + } + else // else continue at a higher level + return e; + if (e) + return e; + } + + while (1) + { + e = body ? body->interpret(istate) : NULL; + if (e == EXP_CANT_INTERPRET) + break; + if (e == EXP_BREAK_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level + break; + } + if (e && e != EXP_CONTINUE_INTERPRET) + break; + if (istate->gotoTarget && istate->gotoTarget != this) + break; // continue at a higher level + + Lcontinue: + istate->gotoTarget = NULL; + e = condition->interpret(istate); + if (exceptionOrCantInterpret(e)) + break; + if (!e->isConst()) + { e = EXP_CANT_INTERPRET; + break; + } + if (isTrueBool(e)) + { + } + else if (e->isBool(FALSE)) + { e = NULL; + break; + } + else + assert(0); + } + return e; +} + +Expression *ForStatement::interpret(InterState *istate) +{ +#if LOG + printf("ForStatement::interpret()\n"); +#endif + if (istate->start == this) + istate->start = NULL; + Expression *e; + + if (init) + { + e = init->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + assert(!e); + } + + if (istate->start) + { + e = body ? body->interpret(istate) : NULL; + if (istate->start) + return NULL; + if (e == EXP_CANT_INTERPRET) + return e; + if (e == EXP_BREAK_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + return NULL; + } // else break at a higher level + } + if (e == EXP_CONTINUE_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + goto Lcontinue; + } // else continue at a higher level + } + if (e) + return e; + } + + while (1) + { + if (!condition) + goto Lhead; + e = condition->interpret(istate); + if (exceptionOrCantInterpret(e)) + break; + if (!e->isConst()) + { e = EXP_CANT_INTERPRET; + break; + } + if (isTrueBool(e)) + { + Lhead: + e = body ? body->interpret(istate) : NULL; + if (e == EXP_CANT_INTERPRET) + break; + if (e == EXP_BREAK_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level + break; + } + if (e && e != EXP_CONTINUE_INTERPRET) + break; + if (istate->gotoTarget && istate->gotoTarget != this) + break; // continue at a higher level + Lcontinue: + istate->gotoTarget = NULL; + if (increment) + { + e = increment->interpret(istate); + if (e == EXP_CANT_INTERPRET) + break; + } + } + else if (e->isBool(FALSE)) + { e = NULL; + break; + } + else + assert(0); + } + return e; +} + +Expression *ForeachStatement::interpret(InterState *istate) +{ + assert(0); // rewritten to ForStatement + return NULL; +} + +#if DMDV2 +Expression *ForeachRangeStatement::interpret(InterState *istate) +{ + assert(0); // rewritten to ForStatement + return NULL; +} +#endif + +Expression *SwitchStatement::interpret(InterState *istate) +{ +#if LOG + printf("SwitchStatement::interpret()\n"); +#endif + if (istate->start == this) + istate->start = NULL; + Expression *e = NULL; + + if (istate->start) + { + e = body ? body->interpret(istate) : NULL; + if (istate->start) + return NULL; + if (e == EXP_CANT_INTERPRET) + return e; + if (e == EXP_BREAK_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + return NULL; + } // else break at a higher level + } + return e; + } + + + Expression *econdition = condition->interpret(istate); + if (exceptionOrCantInterpret(econdition)) + return econdition; + if (econdition->op == TOKslice) + econdition = resolveSlice(econdition); + + Statement *s = NULL; + if (cases) + { + for (size_t i = 0; i < cases->dim; i++) + { + CaseStatement *cs = cases->tdata()[i]; + Expression * caseExp = cs->exp->interpret(istate); + if (exceptionOrCantInterpret(caseExp)) + return caseExp; + e = ctfeEqual(TOKequal, Type::tint32, econdition, caseExp); + if (exceptionOrCantInterpret(e)) + return e; + if (e->isBool(TRUE)) + { s = cs; + break; + } + } + } + if (!s) + { if (hasNoDefault) + error("no default or case for %s in switch statement", econdition->toChars()); + s = sdefault; + } + + assert(s); + istate->start = s; + e = body ? body->interpret(istate) : NULL; + assert(!istate->start); + if (e == EXP_BREAK_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level + } + return e; +} + +Expression *CaseStatement::interpret(InterState *istate) +{ +#if LOG + printf("CaseStatement::interpret(%s) this = %p\n", exp->toChars(), this); +#endif + if (istate->start == this) + istate->start = NULL; + if (statement) + return statement->interpret(istate); + else + return NULL; +} + +Expression *DefaultStatement::interpret(InterState *istate) +{ +#if LOG + printf("DefaultStatement::interpret()\n"); +#endif + if (istate->start == this) + istate->start = NULL; + if (statement) + return statement->interpret(istate); + else + return NULL; +} + +Expression *GotoStatement::interpret(InterState *istate) +{ +#if LOG + printf("GotoStatement::interpret()\n"); +#endif + START() + assert(label && label->statement); + istate->gotoTarget = label->statement; + return EXP_GOTO_INTERPRET; +} + +Expression *GotoCaseStatement::interpret(InterState *istate) +{ +#if LOG + printf("GotoCaseStatement::interpret()\n"); +#endif + START() + assert(cs); + istate->gotoTarget = cs; + return EXP_GOTO_INTERPRET; +} + +Expression *GotoDefaultStatement::interpret(InterState *istate) +{ +#if LOG + printf("GotoDefaultStatement::interpret()\n"); +#endif + START() + assert(sw && sw->sdefault); + istate->gotoTarget = sw->sdefault; + return EXP_GOTO_INTERPRET; +} + +Expression *LabelStatement::interpret(InterState *istate) +{ +#if LOG + printf("LabelStatement::interpret()\n"); +#endif + if (istate->start == this) + istate->start = NULL; + return statement ? statement->interpret(istate) : NULL; +} + + +Expression *TryCatchStatement::interpret(InterState *istate) +{ +#if LOG + printf("TryCatchStatement::interpret()\n"); +#endif + START() + Expression *e = body ? body->interpret(istate) : NULL; + if (e == EXP_CANT_INTERPRET) + return e; + if (!exceptionOrCantInterpret(e)) + return e; + // An exception was thrown + ThrownExceptionExp *ex = (ThrownExceptionExp *)e; + Type *extype = ex->thrown->originalClass()->type; + // Search for an appropriate catch clause. + for (size_t i = 0; i < catches->dim; i++) + { +#if DMDV1 + Catch *ca = (Catch *)catches->data[i]; +#else + Catch *ca = catches->tdata()[i]; +#endif + Type *catype = ca->type; + + if (catype->equals(extype) || catype->isBaseOf(extype, NULL)) + { // Execute the handler + if (ca->var) + { + ctfeStack.push(ca->var); + ca->var->setValue(ex->thrown); + } + return ca->handler->interpret(istate); + } + } + return e; +} + +bool isAnErrorException(ClassDeclaration *cd) +{ + return cd == ClassDeclaration::errorException || ClassDeclaration::errorException->isBaseOf(cd, NULL); +} + +ThrownExceptionExp *chainExceptions(ThrownExceptionExp *oldest, ThrownExceptionExp *newest) +{ +#if LOG + printf("Collided exceptions %s %s\n", oldest->thrown->toChars(), newest->thrown->toChars()); +#endif +#if DMDV2 + // Little sanity check to make sure it's really a Throwable + ClassReferenceExp *boss = oldest->thrown; + assert(boss->value->elements->tdata()[4]->type->ty == Tclass); + ClassReferenceExp *collateral = newest->thrown; + if (isAnErrorException(collateral->originalClass()) + && !isAnErrorException(boss->originalClass())) + { // The new exception bypass the existing chain + assert(collateral->value->elements->tdata()[5]->type->ty == Tclass); + collateral->value->elements->tdata()[5] = boss; + return newest; + } + while (boss->value->elements->tdata()[4]->op == TOKclassreference) + { + boss = (ClassReferenceExp *)(boss->value->elements->tdata()[4]); + } + boss->value->elements->tdata()[4] = collateral; + return oldest; +#else + // for D1, the newest exception just clobbers the older one + return newest; +#endif +} + + +Expression *TryFinallyStatement::interpret(InterState *istate) +{ +#if LOG + printf("TryFinallyStatement::interpret()\n"); +#endif + START() + Expression *e = body ? body->interpret(istate) : NULL; + if (e == EXP_CANT_INTERPRET) + return e; + Expression *second = finalbody ? finalbody->interpret(istate) : NULL; + if (second == EXP_CANT_INTERPRET) + return second; + if (exceptionOrCantInterpret(second)) + { // Check for collided exceptions + if (exceptionOrCantInterpret(e)) + e = chainExceptions((ThrownExceptionExp *)e, (ThrownExceptionExp *)second); + else + e = second; + } + return e; +} + +Expression *ThrowStatement::interpret(InterState *istate) +{ +#if LOG + printf("ThrowStatement::interpret()\n"); +#endif + START() + Expression *e = exp->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + assert(e->op == TOKclassreference); + return new ThrownExceptionExp(loc, (ClassReferenceExp *)e); +} + +Expression *OnScopeStatement::interpret(InterState *istate) +{ + assert(0); + return EXP_CANT_INTERPRET; +} + +Expression *WithStatement::interpret(InterState *istate) +{ +#if LOG + printf("WithStatement::interpret()\n"); +#endif + START() + Expression *e = exp->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + if (wthis->type->ty == Tpointer && exp->type->ty != Tpointer) + { + e = new AddrExp(loc, e); + e->type = wthis->type; + } + ctfeStack.push(wthis); + wthis->setValue(e); + e = body ? body->interpret(istate) : EXP_VOID_INTERPRET; + ctfeStack.pop(wthis); + return e; +} + +Expression *AsmStatement::interpret(InterState *istate) +{ +#if LOG + printf("AsmStatement::interpret()\n"); +#endif + START() + error("asm statements cannot be interpreted at compile time"); + return EXP_CANT_INTERPRET; +} + +#if DMDV2 +Expression *ImportStatement::interpret(InterState *istate) +{ +#if LOG + printf("ImportStatement::interpret()\n"); +#endif + START(); + return NULL; +} +#endif + +/******************************** Expression ***************************/ + +Expression *Expression::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("Expression::interpret() %s\n", toChars()); + printf("type = %s\n", type->toChars()); + dump(0); +#endif + error("Cannot interpret %s at compile time", toChars()); + return EXP_CANT_INTERPRET; +} + +Expression *ThisExp::interpret(InterState *istate, CtfeGoal goal) +{ + while (istate && !istate->localThis) + istate = istate->caller; + if (istate && istate->localThis && istate->localThis->op == TOKstructliteral) + return istate->localThis; + if (istate && istate->localThis) + return istate->localThis->interpret(istate, goal); + error("value of 'this' is not known at compile time"); + return EXP_CANT_INTERPRET; +} + +Expression *NullExp::interpret(InterState *istate, CtfeGoal goal) +{ + return this; +} + +Expression *IntegerExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("IntegerExp::interpret() %s\n", toChars()); +#endif + return this; +} + +Expression *RealExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("RealExp::interpret() %s\n", toChars()); +#endif + return this; +} + +Expression *ComplexExp::interpret(InterState *istate, CtfeGoal goal) +{ + return this; +} + +Expression *StringExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("StringExp::interpret() %s\n", toChars()); +#endif + /* In both D1 and D2, attempts to modify string literals are prevented + * in BinExp::interpretAssignCommon. + * In D2, we also disallow casts of read-only literals to mutable, + * though it isn't strictly necessary. + */ +#if DMDV2 + // Fixed-length char arrays always get duped later anyway. + if (type->ty == Tsarray) + return this; + if (!(((TypeNext *)type)->next->mod & (MODconst | MODimmutable))) + { // It seems this happens only when there has been an explicit cast + error("cannot cast a read-only string literal to mutable in CTFE"); + return EXP_CANT_INTERPRET; + } +#endif + return this; +} + +Expression *FuncExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("FuncExp::interpret() %s\n", toChars()); +#endif + return this; +} + +/* Is it safe to convert from srcPointee* to destPointee* ? + * srcPointee is the genuine type (never void). + * destPointee may be void. + */ +bool isSafePointerCast(Type *srcPointee, Type *destPointee) +{ // It's OK if both are the same (modulo const) +#if DMDV2 + if (srcPointee->castMod(0) == destPointee->castMod(0)) + return true; +#else + if (srcPointee == destPointee) + return true; +#endif + // it's OK to cast to void* + if (destPointee->ty == Tvoid) + return true; + // It's OK if they are the same size integers, eg int* and uint* + return srcPointee->isintegral() && destPointee->isintegral() + && srcPointee->size() == destPointee->size(); +} + +Expression *SymOffExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("SymOffExp::interpret() %s\n", toChars()); +#endif + if (var->isFuncDeclaration() && offset == 0) + { + return this; + } + if (type->ty != Tpointer) + { // Probably impossible + error("Cannot interpret %s at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + Type *pointee = ((TypePointer *)type)->next; + Expression *val = getVarExp(loc, istate, var, goal); + if (val->type->ty == Tarray || val->type->ty == Tsarray) + { + // Check for unsupported type painting operations + Type *elemtype = ((TypeArray *)(val->type))->next; + + // It's OK to cast from fixed length to dynamic array, eg &int[3] to int[]* + if (val->type->ty == Tsarray && pointee->ty == Tarray + && elemtype->size() == pointee->nextOf()->size()) + { + Expression *e = new AddrExp(loc, val); + e->type = type; + return e; + } + if ( !isSafePointerCast(elemtype, pointee) ) + { // It's also OK to cast from &string to string*. + if ( offset == 0 && isSafePointerCast(var->type, pointee) ) + { + VarExp *ve = new VarExp(loc, var); + ve->type = type; + return ve; + } + error("reinterpreting cast from %s to %s is not supported in CTFE", + val->type->toChars(), type->toChars()); + return EXP_CANT_INTERPRET; + } + + TypeArray *tar = (TypeArray *)val->type; + dinteger_t sz = pointee->size(); + dinteger_t indx = offset/sz; + assert(sz * indx == offset); + Expression *aggregate = NULL; + if (val->op == TOKarrayliteral || val->op == TOKstring) + aggregate = val; + else if (val->op == TOKslice) + { + aggregate = ((SliceExp *)val)->e1; + Expression *lwr = ((SliceExp *)val)->lwr->interpret(istate); + indx += lwr->toInteger(); + } + if (aggregate) + { + IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t); + IndexExp *ie = new IndexExp(loc, aggregate, ofs); + ie->type = type; + return ie; + } + } + else if ( offset == 0 && isSafePointerCast(var->type, pointee) ) + { + VarExp *ve = new VarExp(loc, var); + ve->type = type; + return ve; + } + + error("Cannot convert &%s to %s at compile time", var->type->toChars(), type->toChars()); + return EXP_CANT_INTERPRET; +} + +Expression *AddrExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("AddrExp::interpret() %s\n", toChars()); +#endif + // For reference types, we need to return an lvalue ref. + TY tb = e1->type->toBasetype()->ty; + bool needRef = (tb == Tarray || tb == Taarray || tb == Tclass); + Expression *e = e1->interpret(istate, needRef ? ctfeNeedLvalueRef : ctfeNeedLvalue); + if (exceptionOrCantInterpret(e)) + return e; + // Return a simplified address expression + e = new AddrExp(loc, e); + e->type = type; + return e; +} + +Expression *DelegateExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("DelegateExp::interpret() %s\n", toChars()); +#endif + return this; +} + + +// ------------------------------------------------------------- +// Remove out, ref, and this +// ------------------------------------------------------------- +// The variable used in a dotvar, index, or slice expression, +// after 'out', 'ref', and 'this' have been removed. +Expression * resolveReferences(Expression *e, Expression *thisval) +{ + for(;;) + { + if (e->op == TOKthis) + { + assert(thisval); + assert(e != thisval); + e = thisval; + continue; + } + if (e->op == TOKvar) + { + VarExp *ve = (VarExp *)e; + VarDeclaration *v = ve->var->isVarDeclaration(); + if (v->type->ty == Tpointer) + break; + if (v->ctfeAdrOnStack == (size_t)-1) // If not on the stack, can't possibly be a ref. + break; + if (v && v->getValue() && (v->getValue()->op == TOKslice)) + { + SliceExp *se = (SliceExp *)v->getValue(); + if (se->e1->op == TOKarrayliteral || se->e1->op == TOKassocarrayliteral || se->e1->op == TOKstring) + break; + e = v->getValue(); + continue; + } + else if (v && v->getValue() && (v->getValue()->op==TOKindex || v->getValue()->op == TOKdotvar + || v->getValue()->op == TOKthis )) + { + e = v->getValue(); + continue; + } + } + break; + } + return e; +} + +Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal) +{ + Expression *e = EXP_CANT_INTERPRET; + VarDeclaration *v = d->isVarDeclaration(); + SymbolDeclaration *s = d->isSymbolDeclaration(); + if (v) + { +#if DMDV2 + /* Magic variable __ctfe always returns true when interpreting + */ + if (v->ident == Id::ctfe) + return new IntegerExp(loc, 1, Type::tbool); + if ((v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) && v->init && !v->hasValue()) +#else + if (v->isConst() && v->init) +#endif + { e = v->init->toExpression(); + if (e && (e->op == TOKconstruct || e->op == TOKblit)) + { AssignExp *ae = (AssignExp *)e; + e = ae->e2; + v->inuse++; + e = e->interpret(istate, ctfeNeedAnyValue); + v->inuse--; + if (e == EXP_CANT_INTERPRET && !global.gag && !CtfeStatus::stackTraceCallsToSuppress) + errorSupplemental(loc, "while evaluating %s.init", v->toChars()); + if (exceptionOrCantInterpret(e)) + return e; + e->type = v->type; + } + else + { + if (e && !e->type) + e->type = v->type; + if (e) + e = e->interpret(istate, ctfeNeedAnyValue); + if (e == EXP_CANT_INTERPRET && !global.gag && !CtfeStatus::stackTraceCallsToSuppress) + errorSupplemental(loc, "while evaluating %s.init", v->toChars()); + } + if (e && e != EXP_CANT_INTERPRET && e->op != TOKthrownexception) + { + e = copyLiteral(e); + if (v->isDataseg()) + ctfeStack.saveGlobalConstant(v, e); + else + v->setValueWithoutChecking(e); + } + } + else if (v->isCTFE() && !v->hasValue()) + { + if (v->init && v->type->size() != 0) + { + if (v->init->isVoidInitializer()) + { + error(loc, "variable %s is used before initialization", v->toChars()); + return EXP_CANT_INTERPRET; + } + e = v->init->toExpression(); + e = e->interpret(istate); + } + else + e = v->type->defaultInitLiteral(loc); + } + else if (!v->isDataseg() && !v->isCTFE() && !istate) + { error(loc, "variable %s cannot be read at compile time", v->toChars()); + return EXP_CANT_INTERPRET; + } + else + { e = v->hasValue() ? v->getValue() : NULL; + if (!e && !v->isCTFE() && v->isDataseg()) + { error(loc, "static variable %s cannot be read at compile time", v->toChars()); + e = EXP_CANT_INTERPRET; + } + else if (!e) + error(loc, "variable %s is used before initialization", v->toChars()); + else if (exceptionOrCantInterpret(e)) + return e; + else if (goal == ctfeNeedLvalue && v->isRef() && e->op == TOKindex) + { // If it is a foreach ref, resolve the index into a constant + IndexExp *ie = (IndexExp *)e; + Expression *w = ie->e2->interpret(istate); + if (w != ie->e2) + { + e = new IndexExp(ie->loc, ie->e1, w); + e->type = ie->type; + } + return e; + } + else if ((goal == ctfeNeedLvalue) + || e->op == TOKstring || e->op == TOKstructliteral || e->op == TOKarrayliteral + || e->op == TOKassocarrayliteral || e->op == TOKslice + || e->type->toBasetype()->ty == Tpointer) + return e; // it's already an Lvalue + else + e = e->interpret(istate, goal); + } + if (!e) + e = EXP_CANT_INTERPRET; + } + else if (s) + { // Struct static initializers, for example + if (s->dsym->toInitializer() == s->sym) + { e = s->dsym->type->defaultInitLiteral(); + e = e->semantic(NULL); + if (e->op == TOKerror) + e = EXP_CANT_INTERPRET; + } + else + error(loc, "cannot interpret symbol %s at compile time", v->toChars()); + } + else + error(loc, "cannot interpret declaration %s at compile time", d->toChars()); + return e; +} + +Expression *VarExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("VarExp::interpret() %s\n", toChars()); +#endif + if (goal == ctfeNeedLvalueRef) + { + VarDeclaration *v = var->isVarDeclaration(); + if (v && !v->isDataseg() && !v->isCTFE() && !istate) + { error("variable %s cannot be referenced at compile time", v->toChars()); + return EXP_CANT_INTERPRET; + } + else if (v && !v->hasValue() && !v->isCTFE() && v->isDataseg()) + { error("static variable %s cannot be referenced at compile time", v->toChars()); + return EXP_CANT_INTERPRET; + } + return this; + } + Expression *e = getVarExp(loc, istate, var, goal); + // A VarExp may include an implicit cast. It must be done explicitly. + if (e != EXP_CANT_INTERPRET && e->op != TOKthrownexception) + e = paintTypeOntoLiteral(type, e); + return e; +} + +Expression *DeclarationExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("DeclarationExp::interpret() %s\n", toChars()); +#endif + Expression *e; + VarDeclaration *v = declaration->isVarDeclaration(); + if (v) + { + if (v->toAlias()->isTupleDeclaration()) + { // Reserve stack space for all tuple members + TupleDeclaration *td =v->toAlias()->isTupleDeclaration(); + if (!td->objects) + return NULL; + for(int i= 0; i < td->objects->dim; ++i) + { + Object * o = td->objects->tdata()[i]; + Expression *ex = isExpression(o); + DsymbolExp *s = (ex && ex->op == TOKdsymbol) ? (DsymbolExp *)ex : NULL; + VarDeclaration *v2 = s ? s->s->isVarDeclaration() : NULL; + assert(v2); + if (!v2->isDataseg() || v2->isCTFE()) + ctfeStack.push(v2); + } + } + if (!v->isDataseg() || v->isCTFE()) + ctfeStack.push(v); + Dsymbol *s = v->toAlias(); + if (s == v && !v->isStatic() && v->init) + { + ExpInitializer *ie = v->init->isExpInitializer(); + if (ie) + e = ie->exp->interpret(istate); + else if (v->init->isVoidInitializer()) + e = NULL; + else + { + error("Declaration %s is not yet implemented in CTFE", toChars()); + e = EXP_CANT_INTERPRET; + } + } + else if (s == v && !v->init && v->type->size()==0) + { // Zero-length arrays don't need an initializer + e = v->type->defaultInitLiteral(loc); + } +#if DMDV2 + else if (s == v && (v->isConst() || v->isImmutable()) && v->init) +#else + else if (s == v && v->isConst() && v->init) +#endif + { e = v->init->toExpression(); + if (!e) + e = EXP_CANT_INTERPRET; + else if (!e->type) + e->type = v->type; + } + else if (s->isTupleDeclaration() && !v->init) + e = NULL; + else if (v->isStatic() && !v->init) + e = NULL; // Just ignore static variables which aren't read or written yet + else + { + error("Static variable %s cannot be modified at compile time", v->toChars()); + e = EXP_CANT_INTERPRET; + } + } + else if (declaration->isAttribDeclaration() || + declaration->isTemplateMixin() || + declaration->isTupleDeclaration()) + { // Check for static struct declarations, which aren't executable + AttribDeclaration *ad = declaration->isAttribDeclaration(); + if (ad && ad->decl && ad->decl->dim == 1 + && ad->decl->tdata()[0]->isAggregateDeclaration()) + return NULL; // static struct declaration. Nothing to do. + + // These can be made to work, too lazy now + error("Declaration %s is not yet implemented in CTFE", toChars()); + e = EXP_CANT_INTERPRET; + } + else + { // Others should not contain executable code, so are trivial to evaluate + e = NULL; + } +#if LOG + printf("-DeclarationExp::interpret(%s): %p\n", toChars(), e); +#endif + return e; +} + +Expression *TupleExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("TupleExp::interpret() %s\n", toChars()); +#endif + Expressions *expsx = NULL; + + for (size_t i = 0; i < exps->dim; i++) + { Expression *e = exps->tdata()[i]; + Expression *ex; + + ex = e->interpret(istate); + if (exceptionOrCantInterpret(ex)) + { delete expsx; + return ex; + } + + // A tuple of assignments can contain void (Bug 5676). + if (goal == ctfeNeedNothing) + continue; + if (ex == EXP_VOID_INTERPRET) + { + error("ICE: void element %s in tuple", e->toChars()); + assert(0); + } + + /* If any changes, do Copy On Write + */ + if (ex != e) + { + if (!expsx) + { expsx = new Expressions(); + ++CtfeStatus::numArrayAllocs; + expsx->setDim(exps->dim); + for (size_t j = 0; j < i; j++) + { + expsx->tdata()[j] = exps->tdata()[j]; + } + } + expsx->tdata()[i] = ex; + } + } + if (expsx) + { TupleExp *te = new TupleExp(loc, expsx); + expandTuples(te->exps); + te->type = new TypeTuple(te->exps); + return te; + } + return this; +} + +Expression *ArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) +{ Expressions *expsx = NULL; + +#if LOG + printf("ArrayLiteralExp::interpret() %s\n", toChars()); +#endif + if (ownedByCtfe) // We've already interpreted all the elements + return copyLiteral(this); + if (elements) + { + for (size_t i = 0; i < elements->dim; i++) + { Expression *e = elements->tdata()[i]; + Expression *ex; + + if (e->op == TOKindex) // segfault bug 6250 + assert( ((IndexExp*)e)->e1 != this); + ex = e->interpret(istate); + if (ex == EXP_CANT_INTERPRET) + goto Lerror; + if (ex->op == TOKthrownexception) + return ex; + + /* If any changes, do Copy On Write + */ + if (ex != e) + { + if (!expsx) + { expsx = new Expressions(); + ++CtfeStatus::numArrayAllocs; + expsx->setDim(elements->dim); + for (size_t j = 0; j < elements->dim; j++) + { + expsx->tdata()[j] = elements->tdata()[j]; + } + } + expsx->tdata()[i] = ex; + } + } + } + if (elements && expsx) + { + expandTuples(expsx); + if (expsx->dim != elements->dim) + goto Lerror; + ArrayLiteralExp *ae = new ArrayLiteralExp(loc, expsx); + ae->type = type; + return copyLiteral(ae); + } +#if DMDV2 + if (((TypeNext *)type)->next->mod & (MODconst | MODimmutable)) + { // If it's immutable, we don't need to dup it + return this; + } +#endif + return copyLiteral(this); + +Lerror: + if (expsx) + delete expsx; + error("cannot interpret array literal"); + return EXP_CANT_INTERPRET; +} + +Expression *AssocArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) +{ Expressions *keysx = keys; + Expressions *valuesx = values; + +#if LOG + printf("AssocArrayLiteralExp::interpret() %s\n", toChars()); +#endif + if (ownedByCtfe) // We've already interpreted all the elements + return copyLiteral(this); + for (size_t i = 0; i < keys->dim; i++) + { Expression *ekey = keys->tdata()[i]; + Expression *evalue = values->tdata()[i]; + Expression *ex; + + ex = ekey->interpret(istate); + if (ex == EXP_CANT_INTERPRET) + goto Lerr; + if (ex->op == TOKthrownexception) + return ex; + + + /* If any changes, do Copy On Write + */ + if (ex != ekey) + { + if (keysx == keys) + keysx = (Expressions *)keys->copy(); + keysx->tdata()[i] = ex; + } + + ex = evalue->interpret(istate); + if (ex == EXP_CANT_INTERPRET) + goto Lerr; + if (ex->op == TOKthrownexception) + return ex; + + /* If any changes, do Copy On Write + */ + if (ex != evalue) + { + if (valuesx == values) + valuesx = (Expressions *)values->copy(); + valuesx->tdata()[i] = ex; + } + } + if (keysx != keys) + expandTuples(keysx); + if (valuesx != values) + expandTuples(valuesx); + if (keysx->dim != valuesx->dim) + goto Lerr; + + /* Remove duplicate keys + */ + for (size_t i = 1; i < keysx->dim; i++) + { Expression *ekey = keysx->tdata()[i - 1]; + if (ekey->op == TOKslice) + ekey = resolveSlice(ekey); + for (size_t j = i; j < keysx->dim; j++) + { Expression *ekey2 = keysx->tdata()[j]; + Expression *ex = ctfeEqual(TOKequal, Type::tbool, ekey, ekey2); + if (ex == EXP_CANT_INTERPRET) + goto Lerr; + if (ex->isBool(TRUE)) // if a match + { + // Remove ekey + if (keysx == keys) + keysx = (Expressions *)keys->copy(); + if (valuesx == values) + valuesx = (Expressions *)values->copy(); + keysx->remove(i - 1); + valuesx->remove(i - 1); + i -= 1; // redo the i'th iteration + break; + } + } + } + + if (keysx != keys || valuesx != values) + { + AssocArrayLiteralExp *ae; + ae = new AssocArrayLiteralExp(loc, keysx, valuesx); + ae->type = type; + ae->ownedByCtfe = true; + return ae; + } + return this; + +Lerr: + if (keysx != keys) + delete keysx; + if (valuesx != values) + delete values; + return EXP_CANT_INTERPRET; +} + +Expression *StructLiteralExp::interpret(InterState *istate, CtfeGoal goal) +{ Expressions *expsx = NULL; + +#if LOG + printf("StructLiteralExp::interpret() %s\n", toChars()); +#endif + /* We don't know how to deal with overlapping fields + */ + if (sd->hasUnions) + { error("Unions with overlapping fields are not yet supported in CTFE"); + return EXP_CANT_INTERPRET; + } + if (ownedByCtfe) + return copyLiteral(this); + + if (elements) + { + for (size_t i = 0; i < elements->dim; i++) + { Expression *e = elements->tdata()[i]; + if (!e) + continue; + + Expression *ex = e->interpret(istate); + if (exceptionOrCantInterpret(ex)) + { delete expsx; + return ex; + } + + /* If any changes, do Copy On Write + */ + if (ex != e) + { + if (!expsx) + { expsx = new Expressions(); + ++CtfeStatus::numArrayAllocs; + expsx->setDim(elements->dim); + for (size_t j = 0; j < elements->dim; j++) + { + expsx->tdata()[j] = elements->tdata()[j]; + } + } + expsx->tdata()[i] = ex; + } + } + } + if (elements && expsx) + { + expandTuples(expsx); + if (expsx->dim != elements->dim) + { delete expsx; + return EXP_CANT_INTERPRET; + } + StructLiteralExp *se = new StructLiteralExp(loc, sd, expsx); + se->type = type; + se->ownedByCtfe = true; + return se; + } + return copyLiteral(this); +} + +/****************************** + * Helper for NewExp + * Create an array literal consisting of 'elem' duplicated 'dim' times. + */ +ArrayLiteralExp *createBlockDuplicatedArrayLiteral(Loc loc, Type *type, + Expression *elem, size_t dim) +{ + Expressions *elements = new Expressions(); + elements->setDim(dim); + bool mustCopy = needToCopyLiteral(elem); + for (size_t i = 0; i < dim; i++) + { if (mustCopy) + elem = copyLiteral(elem); + elements->tdata()[i] = elem; + } + ArrayLiteralExp *ae = new ArrayLiteralExp(loc, elements); + ae->type = type; + ae->ownedByCtfe = true; + return ae; +} + +/****************************** + * Helper for NewExp + * Create a string literal consisting of 'value' duplicated 'dim' times. + */ +StringExp *createBlockDuplicatedStringLiteral(Loc loc, Type *type, + unsigned value, size_t dim, int sz) +{ + unsigned char *s; + s = (unsigned char *)mem.calloc(dim + 1, sz); + for (size_t elemi=0; elemitype = type; + se->sz = sz; + se->committed = true; + se->ownedByCtfe = true; + return se; +} + +// Create an array literal of type 'newtype' with dimensions given by +// 'arguments'[argnum..$] +Expression *recursivelyCreateArrayLiteral(Loc loc, Type *newtype, InterState *istate, + Expressions *arguments, int argnum) +{ + Expression *lenExpr = ((arguments->tdata()[argnum]))->interpret(istate); + if (exceptionOrCantInterpret(lenExpr)) + return lenExpr; + size_t len = (size_t)(lenExpr->toInteger()); + Type *elemType = ((TypeArray *)newtype)->next; + if (elemType->ty == Tarray && argnum < arguments->dim - 1) + { + Expression *elem = recursivelyCreateArrayLiteral(loc, elemType, istate, + arguments, argnum + 1); + if (exceptionOrCantInterpret(elem)) + return elem; + + Expressions *elements = new Expressions(); + elements->setDim(len); + for (size_t i = 0; i < len; i++) + elements->tdata()[i] = copyLiteral(elem); + ArrayLiteralExp *ae = new ArrayLiteralExp(loc, elements); + ae->type = newtype; + ae->ownedByCtfe = true; + return ae; + } + assert(argnum == arguments->dim - 1); + if (elemType->ty == Tchar || elemType->ty == Twchar + || elemType->ty == Tdchar) + return createBlockDuplicatedStringLiteral(loc, newtype, + (unsigned)(elemType->defaultInitLiteral()->toInteger()), + len, elemType->size()); + return createBlockDuplicatedArrayLiteral(loc, newtype, + elemType->defaultInitLiteral(), + len); +} + +Expression *NewExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("NewExp::interpret() %s\n", toChars()); +#endif + if (newtype->ty == Tarray && arguments) + return recursivelyCreateArrayLiteral(loc, newtype, istate, arguments, 0); + + if (newtype->toBasetype()->ty == Tstruct) + { + Expression *se = newtype->defaultInitLiteral(); +#if DMDV2 + if (member) + { + int olderrors = global.errors; + member->interpret(istate, arguments, se); + if (olderrors != global.errors) + { + error("cannot evaluate %s at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + } +#else // The above code would fail on D1 because it doesn't use STRUCTTHISREF, + // but that's OK because D1 doesn't have struct constructors anyway. + assert(!member); +#endif + Expression *e = new AddrExp(loc, copyLiteral(se)); + e->type = type; + return e; + } + if (newtype->toBasetype()->ty == Tclass) + { + ClassDeclaration *cd = ((TypeClass *)newtype->toBasetype())->sym; + size_t totalFieldCount = 0; + for (ClassDeclaration *c = cd; c; c = c->baseClass) + totalFieldCount += c->fields.dim; + Expressions *elems = new Expressions; + elems->setDim(totalFieldCount); + size_t fieldsSoFar = totalFieldCount; + for (ClassDeclaration *c = cd; c; c = c->baseClass) + { + fieldsSoFar -= c->fields.dim; + for (size_t i = 0; i < c->fields.dim; i++) + { + Dsymbol *s = c->fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + Expression *m = v->init ? v->init->toExpression() : v->type->defaultInitLiteral(); + if (exceptionOrCantInterpret(m)) + return m; + elems->tdata()[fieldsSoFar+i] = copyLiteral(m); + } + } + // Hack: we store a ClassDeclaration instead of a StructDeclaration. + // We probably won't get away with this. + StructLiteralExp *se = new StructLiteralExp(loc, (StructDeclaration *)cd, elems, newtype); + se->ownedByCtfe = true; + Expression *e = new ClassReferenceExp(loc, se, type); + if (member) + { // Call constructor + if (!member->fbody) + { + Expression *ctorfail = evaluateIfBuiltin(istate, loc, member, arguments, e); + if (ctorfail && exceptionOrCantInterpret(ctorfail)) + return ctorfail; + if (ctorfail) + return e; + member->error("%s cannot be constructed at compile time, because the constructor has no available source code", newtype->toChars()); + return EXP_CANT_INTERPRET; + } + Expression * ctorfail = member->interpret(istate, arguments, e); + if (exceptionOrCantInterpret(ctorfail)) + return ctorfail; + } + return e; + } + error("Cannot interpret %s at compile time", toChars()); + return EXP_CANT_INTERPRET; +} + +Expression *UnaExp::interpretCommon(InterState *istate, CtfeGoal goal, Expression *(*fp)(Type *, Expression *)) +{ Expression *e; + Expression *e1; + +#if LOG + printf("UnaExp::interpretCommon() %s\n", toChars()); +#endif + e1 = this->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + return e1; + e = (*fp)(type, e1); + return e; +} + +#define UNA_INTERPRET(op) \ +Expression *op##Exp::interpret(InterState *istate, CtfeGoal goal) \ +{ \ + return interpretCommon(istate, goal, &op); \ +} + +UNA_INTERPRET(Neg) +UNA_INTERPRET(Com) +UNA_INTERPRET(Not) +UNA_INTERPRET(Bool) + +Expression *getAggregateFromPointer(Expression *e, dinteger_t *ofs) +{ + *ofs = 0; + if (e->op == TOKaddress) + e = ((AddrExp *)e)->e1; + if (e->op == TOKdotvar) + { + Expression *ex = ((DotVarExp *)e)->e1; + VarDeclaration *v = ((DotVarExp *)e)->var->isVarDeclaration(); + assert(v); + StructLiteralExp *se = ex->op == TOKclassreference ? ((ClassReferenceExp *)ex)->value : (StructLiteralExp *)ex; + // We can't use getField, because it makes a copy + int i = -1; + if (ex->op == TOKclassreference) + i = ((ClassReferenceExp *)ex)->getFieldIndex(e->type, v->offset); + else + i = se->getFieldIndex(e->type, v->offset); + assert(i != -1); + e = se->elements->tdata()[i]; + } + if (e->op == TOKindex) + { + IndexExp *ie = (IndexExp *)e; + // Note that each AA element is part of its own memory block + if ((ie->e1->type->ty == Tarray || ie->e1->type->ty == Tsarray + || ie->e1->op == TOKstring || ie->e1->op==TOKarrayliteral) && + ie->e2->op == TOKint64) + { + *ofs = ie->e2->toInteger(); + return ie->e1; + } + } + return e; +} + +// return e1 - e2 as an integer, or error if not possible +Expression *pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e2) +{ + dinteger_t ofs1, ofs2; + Expression *agg1 = getAggregateFromPointer(e1, &ofs1); + Expression *agg2 = getAggregateFromPointer(e2, &ofs2); + if (agg1 == agg2) + { + Type *pointee = ((TypePointer *)agg1->type)->next; + dinteger_t sz = pointee->size(); + return new IntegerExp(loc, (ofs1-ofs2)*sz, type); + } + else if (agg1->op == TOKstring && agg2->op == TOKstring) + { + if (((StringExp *)agg1)->string == ((StringExp *)agg2)->string) + { + Type *pointee = ((TypePointer *)agg1->type)->next; + dinteger_t sz = pointee->size(); + return new IntegerExp(loc, (ofs1-ofs2)*sz, type); + } + } +#if LOGASSIGN + printf("FAILED POINTER DIFF\n"); + showCtfeExpr(agg1); + showCtfeExpr(agg2); +#endif + error(loc, "%s - %s cannot be interpreted at compile time: cannot subtract " + "pointers to two different memory blocks", + e1->toChars(), e2->toChars()); + return EXP_CANT_INTERPRET; +} + +// Return eptr op e2, where eptr is a pointer, e2 is an integer, +// and op is TOKadd or TOKmin +Expression *pointerArithmetic(Loc loc, enum TOK op, Type *type, + Expression *eptr, Expression *e2) +{ + if (eptr->type->nextOf()->ty == Tvoid) + { + error(loc, "cannot perform arithmetic on void* pointers at compile time"); + return EXP_CANT_INTERPRET; + } + dinteger_t ofs1, ofs2; + if (eptr->op == TOKaddress) + eptr = ((AddrExp *)eptr)->e1; + Expression *agg1 = getAggregateFromPointer(eptr, &ofs1); + if (agg1->op != TOKstring && agg1->op != TOKarrayliteral) + { + error(loc, "cannot perform pointer arithmetic on non-arrays at compile time"); + return EXP_CANT_INTERPRET; + } + ofs2 = e2->toInteger(); + Type *pointee = ((TypePointer *)agg1->type)->next; + dinteger_t sz = pointee->size(); + Expression *dollar = ArrayLength(Type::tsize_t, agg1); + assert(dollar != EXP_CANT_INTERPRET); + dinteger_t len = dollar->toInteger(); + + Expression *val = agg1; + TypeArray *tar = (TypeArray *)val->type; + dinteger_t indx = ofs1; + if (op == TOKadd || op == TOKaddass || op == TOKplusplus) + indx = indx + ofs2/sz; + else if (op == TOKmin || op == TOKminass || op == TOKminusminus) + indx -= ofs2/sz; + else + { + error(loc, "CTFE Internal compiler error: bad pointer operation"); + return EXP_CANT_INTERPRET; + } + if (val->op != TOKarrayliteral && val->op != TOKstring) + { + error(loc, "CTFE Internal compiler error: pointer arithmetic %s", val->toChars()); + return EXP_CANT_INTERPRET; + } + if (indx < 0 || indx > len) + { + error(loc, "cannot assign pointer to index %jd inside memory block [0..%jd]", indx, len); + return EXP_CANT_INTERPRET; + } + + IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t); + IndexExp *ie = new IndexExp(loc, val, ofs); + ie->type = type; + return ie; +} + +typedef Expression *(*fp_t)(Type *, Expression *, Expression *); + +Expression *BinExp::interpretCommon(InterState *istate, CtfeGoal goal, fp_t fp) +{ Expression *e; + Expression *e1; + Expression *e2; + +#if LOG + printf("BinExp::interpretCommon() %s\n", toChars()); +#endif + if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer && op == TOKmin) + { + e1 = this->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + return e1; + e2 = this->e2->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e2)) + return e2; + return pointerDifference(loc, type, e1, e2); + } + if (this->e1->type->ty == Tpointer && this->e2->type->isintegral()) + { + e1 = this->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + return e1; + e2 = this->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + return e2; + return pointerArithmetic(loc, op, type, e1, e2); + } + if (this->e2->type->ty == Tpointer && this->e1->type->isintegral() && op==TOKadd) + { + e1 = this->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + return e1; + e2 = this->e2->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e2)) + return e1; + return pointerArithmetic(loc, op, type, e2, e1); + } + if (this->e1->type->ty == Tpointer || this->e2->type->ty == Tpointer) + { + error("pointer expression %s cannot be interpreted at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + e1 = this->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + return e1; + if (e1->isConst() != 1) + goto Lcant; + + e2 = this->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + return e2; + if (e2->isConst() != 1) + goto Lcant; + + e = (*fp)(type, e1, e2); + if (e == EXP_CANT_INTERPRET) + error("%s cannot be interpreted at compile time", toChars()); + return e; + +Lcant: + return EXP_CANT_INTERPRET; +} + +#define BIN_INTERPRET(op) \ +Expression *op##Exp::interpret(InterState *istate, CtfeGoal goal) \ +{ \ + return interpretCommon(istate, goal, &op); \ +} + +BIN_INTERPRET(Add) +BIN_INTERPRET(Min) +BIN_INTERPRET(Mul) +BIN_INTERPRET(Div) +BIN_INTERPRET(Mod) +BIN_INTERPRET(Shl) +BIN_INTERPRET(Shr) +BIN_INTERPRET(Ushr) +BIN_INTERPRET(And) +BIN_INTERPRET(Or) +BIN_INTERPRET(Xor) +#if DMDV2 +BIN_INTERPRET(Pow) +#endif + + +typedef Expression *(*fp2_t)(enum TOK, Type *, Expression *, Expression *); + +// Return EXP_CANT_INTERPRET if they point to independent memory blocks +Expression *comparePointers(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2) +{ + dinteger_t ofs1, ofs2; + Expression *agg1 = getAggregateFromPointer(e1, &ofs1); + Expression *agg2 = getAggregateFromPointer(e2, &ofs2); + // Note that type painting can occur with VarExp, so we + // must compare the variables being pointed to. + if (agg1 == agg2 || + (agg1->op == TOKvar && agg2->op == TOKvar && + ((VarExp *)agg1)->var == ((VarExp *)agg2)->var) + ) + { + dinteger_t cm = ofs1 - ofs2; + dinteger_t n; + dinteger_t zero = 0; + switch(op) + { + case TOKlt: n = (ofs1 < ofs2); break; + case TOKle: n = (ofs1 <= ofs2); break; + case TOKgt: n = (ofs1 > ofs2); break; + case TOKge: n = (ofs1 >= ofs2); break; + case TOKidentity: + case TOKequal: n = (ofs1 == ofs2); break; + case TOKnotidentity: + case TOKnotequal: n = (ofs1 != ofs2); break; + default: + assert(0); + } + return new IntegerExp(loc, n, type); + } + int cmp; + if (agg1->op == TOKnull) + { + cmp = (agg2->op == TOKnull); + } + else if (agg2->op == TOKnull) + { + cmp = 0; + } + else + { + switch(op) + { + case TOKidentity: + case TOKequal: + case TOKnotidentity: // 'cmp' gets inverted below + case TOKnotequal: + cmp = 0; + break; + default: + return EXP_CANT_INTERPRET; + } + } + if (op == TOKnotidentity || op == TOKnotequal) + cmp ^= 1; + return new IntegerExp(loc, cmp, type); +} + +Expression *ctfeIdentity(enum TOK op, Type *type, Expression *e1, Expression *e2) +{ + if (e1->op == TOKclassreference || e2->op == TOKclassreference) + { + int cmp = 0; + if (e1->op == TOKclassreference && e2->op == TOKclassreference && + ((ClassReferenceExp *)e1)->value == ((ClassReferenceExp *)e2)->value) + cmp = 1; + if (op == TOKnotidentity || op == TOKnotequal) + cmp ^= 1; + return new IntegerExp(e1->loc, cmp, type); + } + return Identity(op, type, e1, e2); +} + + +Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp) +{ Expression *e; + Expression *e1; + Expression *e2; + +#if LOG + printf("BinExp::interpretCommon2() %s\n", toChars()); +#endif + if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer) + { + e1 = this->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + return e1; + e2 = this->e2->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e2)) + return e2; + e = comparePointers(loc, op, type, e1, e2); + if (e == EXP_CANT_INTERPRET) + { + error("%s and %s point to independent memory blocks and " + "cannot be compared at compile time", this->e1->toChars(), + this->e2->toChars()); + } + return e; + } + e1 = this->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + return e1; + if (e1->op == TOKslice) + e1 = resolveSlice(e1); + + if (e1->isConst() != 1 && + e1->op != TOKnull && + e1->op != TOKstring && + e1->op != TOKarrayliteral && + e1->op != TOKstructliteral && + e1->op != TOKclassreference) + { + error("cannot compare %s at compile time", e1->toChars()); + goto Lcant; + } + + e2 = this->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + return e2; + if (e2->op == TOKslice) + e2 = resolveSlice(e2); + if (e2->isConst() != 1 && + e2->op != TOKnull && + e2->op != TOKstring && + e2->op != TOKarrayliteral && + e2->op != TOKstructliteral && + e2->op != TOKclassreference) + { + error("cannot compare %s at compile time", e2->toChars()); + goto Lcant; + } + e = (*fp)(op, type, e1, e2); + if (e == EXP_CANT_INTERPRET) + error("%s cannot be interpreted at compile time", toChars()); + return e; + +Lcant: + return EXP_CANT_INTERPRET; +} + +#define BIN_INTERPRET2(op, opfunc) \ +Expression *op##Exp::interpret(InterState *istate, CtfeGoal goal) \ +{ \ + return interpretCommon2(istate, goal, &opfunc); \ +} + +BIN_INTERPRET2(Equal, Equal) +BIN_INTERPRET2(Identity, ctfeIdentity) +BIN_INTERPRET2(Cmp, Cmp) + +/* Helper functions for BinExp::interpretAssignCommon + */ + +/*************************************** + * Duplicate the elements array, then set field 'indexToChange' = newelem. + */ +Expressions *changeOneElement(Expressions *oldelems, size_t indexToChange, Expression *newelem) +{ + Expressions *expsx = new Expressions(); + ++CtfeStatus::numArrayAllocs; + expsx->setDim(oldelems->dim); + for (size_t j = 0; j < expsx->dim; j++) + { + if (j == indexToChange) + expsx->tdata()[j] = newelem; + else + expsx->tdata()[j] = oldelems->tdata()[j]; + } + return expsx; +} + +// Create a new struct literal, which is the same as se except that se.field[offset] = elem +Expression * modifyStructField(Type *type, StructLiteralExp *se, size_t offset, Expression *newval) +{ + int fieldi = se->getFieldIndex(newval->type, offset); + if (fieldi == -1) + return EXP_CANT_INTERPRET; + /* Create new struct literal reflecting updated fieldi + */ + Expressions *expsx = changeOneElement(se->elements, fieldi, newval); + StructLiteralExp * ee = new StructLiteralExp(se->loc, se->sd, expsx); + ee->type = se->type; + ee->ownedByCtfe = 1; + return ee; +} + +/******************************** + * Given an array literal arr (either arrayliteral, stringliteral, or assocArrayLiteral), + * set arr[index] = newval and return the new array. + * + */ +Expression *assignAssocArrayElement(Loc loc, AssocArrayLiteralExp *aae, Expression *index, Expression *newval) +{ + /* Create new associative array literal reflecting updated key/value + */ + Expressions *keysx = aae->keys; + Expressions *valuesx = aae->values; + int updated = 0; + for (size_t j = valuesx->dim; j; ) + { j--; + Expression *ekey = aae->keys->tdata()[j]; + Expression *ex = ctfeEqual(TOKequal, Type::tbool, ekey, index); + if (exceptionOrCantInterpret(ex)) + return ex; + if (ex->isBool(TRUE)) + { valuesx->tdata()[j] = newval; + updated = 1; + } + } + if (!updated) + { // Append index/newval to keysx[]/valuesx[] + valuesx->push(newval); + keysx->push(index); + } + return newval; +} + +// Return true if e is derived from UnaryExp. +// Consider moving this function into Expression. +UnaExp *isUnaExp(Expression *e) +{ + switch (e->op) + { + case TOKdotvar: + case TOKindex: + case TOKslice: + case TOKcall: + case TOKdot: + case TOKdotti: + case TOKdottype: + case TOKcast: + return (UnaExp *)e; + default: + break; + } + return NULL; +} + +// Returns the variable which is eventually modified, or NULL if an rvalue. +// thisval is the current value of 'this'. +VarDeclaration * findParentVar(Expression *e, Expression *thisval) +{ + for (;;) + { + e = resolveReferences(e, thisval); + if (e->op == TOKvar) + break; + if (e->op == TOKindex) + e = ((IndexExp*)e)->e1; + else if (e->op == TOKdotvar) + e = ((DotVarExp *)e)->e1; + else if (e->op == TOKdotti) + e = ((DotTemplateInstanceExp *)e)->e1; + else if (e->op == TOKslice) + e = ((SliceExp*)e)->e1; + else + return NULL; + } + VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); + assert(v); + return v; +} + +// Given expr, which evaluates to an array/AA/string literal, +// return true if it needs to be copied +bool needToCopyLiteral(Expression *expr) +{ + for (;;) + { + switch (expr->op) + { + case TOKarrayliteral: + return !((ArrayLiteralExp *)expr)->ownedByCtfe; + case TOKassocarrayliteral: + return !((AssocArrayLiteralExp *)expr)->ownedByCtfe; + case TOKstructliteral: + return !((StructLiteralExp *)expr)->ownedByCtfe; + case TOKstring: + case TOKthis: + case TOKvar: + return false; + case TOKassign: + return false; + case TOKindex: + case TOKdotvar: + case TOKslice: + case TOKcast: + expr = ((UnaExp *)expr)->e1; + continue; + case TOKcat: + return needToCopyLiteral(((BinExp *)expr)->e1) || + needToCopyLiteral(((BinExp *)expr)->e2); + case TOKcatass: + expr = ((BinExp *)expr)->e2; + continue; + default: + return false; + } + } +} + +Expressions *copyLiteralArray(Expressions *oldelems) +{ + if (!oldelems) + return oldelems; + CtfeStatus::numArrayAllocs++; + Expressions *newelems = new Expressions(); + newelems->setDim(oldelems->dim); + for (size_t i = 0; i < oldelems->dim; i++) + newelems->tdata()[i] = copyLiteral(oldelems->tdata()[i]); + return newelems; +} + + + +// Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral. +// This value will be used for in-place modification. +Expression *copyLiteral(Expression *e) +{ + if (e->op == TOKstring) // syntaxCopy doesn't make a copy for StringExp! + { + StringExp *se = (StringExp *)e; + unsigned char *s; + s = (unsigned char *)mem.calloc(se->len + 1, se->sz); + memcpy(s, se->string, se->len * se->sz); + StringExp *se2 = new StringExp(se->loc, s, se->len); + se2->committed = se->committed; + se2->postfix = se->postfix; + se2->type = se->type; + se2->sz = se->sz; + se2->ownedByCtfe = true; + return se2; + } + else if (e->op == TOKarrayliteral) + { + ArrayLiteralExp *ae = (ArrayLiteralExp *)e; + ArrayLiteralExp *r = new ArrayLiteralExp(e->loc, + copyLiteralArray(ae->elements)); + r->type = e->type; + r->ownedByCtfe = true; + return r; + } + else if (e->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; + AssocArrayLiteralExp *r = new AssocArrayLiteralExp(e->loc, + copyLiteralArray(aae->keys), copyLiteralArray(aae->values)); + r->type = e->type; + r->ownedByCtfe = true; + return r; + } + /* syntaxCopy doesn't work for struct literals, because of a nasty special + * case: block assignment is permitted inside struct literals, eg, + * an int[4] array can be initialized with a single int. + */ + else if (e->op == TOKstructliteral) + { + StructLiteralExp *se = (StructLiteralExp *)e; + Expressions *oldelems = se->elements; + Expressions * newelems = new Expressions(); + newelems->setDim(oldelems->dim); + for (size_t i = 0; i < newelems->dim; i++) + { + Expression *m = oldelems->tdata()[i]; + // We need the struct definition to detect block assignment + AggregateDeclaration *sd = se->sd; + Dsymbol *s = sd->fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + // If it is a void assignment, use the default initializer + if (!m) + m = v->type->defaultInitLiteral(e->loc); + if (m->op == TOKslice) + m = resolveSlice(m); + if ((v->type->ty != m->type->ty) && v->type->ty == Tsarray) + { + // Block assignment from inside struct literals + TypeSArray *tsa = (TypeSArray *)v->type; + uinteger_t length = tsa->dim->toInteger(); + m = createBlockDuplicatedArrayLiteral(e->loc, v->type, m, (size_t)length); + } + else if (v->type->ty != Tarray && v->type->ty!=Taarray) // NOTE: do not copy array references + m = copyLiteral(m); + newelems->tdata()[i] = m; + } +#if DMDV2 + StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems, se->stype); +#else + StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems); +#endif + r->type = e->type; + r->ownedByCtfe = true; + return r; + } + else if (e->op == TOKfunction || e->op == TOKdelegate + || e->op == TOKsymoff || e->op == TOKnull + || e->op == TOKvar + || e->op == TOKint64 || e->op == TOKfloat64 + || e->op == TOKchar || e->op == TOKcomplex80) + { // Simple value types + Expression *r = e->syntaxCopy(); + r->type = e->type; + return r; + } + else if ( isPointer(e->type) ) + { // For pointers, we only do a shallow copy. + Expression *r; + if (e->op == TOKaddress) + r = new AddrExp(e->loc, ((AddrExp *)e)->e1); + else if (e->op == TOKindex) + r = new IndexExp(e->loc, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2); + else if (e->op == TOKdotvar) + r = new DotVarExp(e->loc, ((DotVarExp *)e)->e1, + ((DotVarExp *)e)->var +#if DMDV2 + , ((DotVarExp *)e)->hasOverloads +#endif + ); + else + assert(0); + r->type = e->type; + return r; + } + else if (e->op == TOKslice) + { // Array slices only do a shallow copy + Expression *r = new SliceExp(e->loc, ((SliceExp *)e)->e1, + ((SliceExp *)e)->lwr, ((SliceExp *)e)->upr); + r->type = e->type; + return r; + } + else if (e->op == TOKclassreference) + return new ClassReferenceExp(e->loc, ((ClassReferenceExp *)e)->value, e->type); + else + { + e->error("Internal Compiler Error: CTFE literal %s", e->toChars()); + assert(0); + return e; + } +} + +/* Deal with type painting. + * Type painting is a major nuisance: we can't just set + * e->type = type, because that would change the original literal. + * But, we can't simply copy the literal either, because that would change + * the values of any pointers. + */ +Expression *paintTypeOntoLiteral(Type *type, Expression *lit) +{ + if (lit->type == type) + return lit; + Expression *e; + if (lit->op == TOKslice) + { + SliceExp *se = (SliceExp *)lit; + e = new SliceExp(lit->loc, se->e1, se->lwr, se->upr); + } + else if (lit->op == TOKindex) + { + IndexExp *ie = (IndexExp *)lit; + e = new IndexExp(lit->loc, ie->e1, ie->e2); + } + else if (lit->op == TOKarrayliteral) + { + e = new SliceExp(lit->loc, lit, + new IntegerExp(0, 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit)); + } + else if (lit->op == TOKstring) + { + // For strings, we need to introduce another level of indirection + e = new SliceExp(lit->loc, lit, + new IntegerExp(0, 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit)); + } + else if (lit->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)lit; + // TODO: we should be creating a reference to this AAExp, not + // just a ref to the keys and values. + bool wasOwned = aae->ownedByCtfe; + aae = new AssocArrayLiteralExp(lit->loc, aae->keys, aae->values); + aae->ownedByCtfe = wasOwned; + e = aae; + } + else + { // Can't type paint from struct to struct*; this needs another + // level of indirection + if (lit->op == TOKstructliteral && isPointer(type) ) + lit->error("CTFE internal error painting %s", type->toChars()); + e = copyLiteral(lit); + } + e->type = type; + return e; +} + + +Expression *ctfeCast(Loc loc, Type *type, Type *to, Expression *e) +{ + if (e->op == TOKnull) + return paintTypeOntoLiteral(to, e); + if (e->op == TOKclassreference) + { // Disallow reinterpreting class casts. Do this by ensuring that + // the original class can implicitly convert to the target class + ClassDeclaration *originalClass = ((ClassReferenceExp *)e)->originalClass(); + if (originalClass->type->implicitConvTo(to)) + return paintTypeOntoLiteral(to, e); + else + return new NullExp(loc, to); + } + Expression *r = Cast(type, to, e); + if (r == EXP_CANT_INTERPRET) + error(loc, "cannot cast %s to %s at compile time", e->toChars(), to->toChars()); + if (e->op == TOKarrayliteral) + ((ArrayLiteralExp *)e)->ownedByCtfe = true; + if (e->op == TOKstring) + ((StringExp *)e)->ownedByCtfe = true; + return r; +} + +/* Set dest = src, where both dest and src are container value literals + * (ie, struct literals, or static arrays (can be an array literal or a string) + * Assignment is recursively in-place. + * Purpose: any reference to a member of 'dest' will remain valid after the + * assignment. + */ +void assignInPlace(Expression *dest, Expression *src) +{ + assert(dest->op == TOKstructliteral || dest->op == TOKarrayliteral || + dest->op == TOKstring); + Expressions *oldelems; + Expressions *newelems; + if (dest->op == TOKstructliteral) + { + assert(dest->op == src->op); + oldelems = ((StructLiteralExp *)dest)->elements; + newelems = ((StructLiteralExp *)src)->elements; + } + else if (dest->op == TOKarrayliteral && src->op==TOKarrayliteral) + { + oldelems = ((ArrayLiteralExp *)dest)->elements; + newelems = ((ArrayLiteralExp *)src)->elements; + } + else if (dest->op == TOKstring && src->op == TOKstring) + { + sliceAssignStringFromString((StringExp *)dest, (StringExp *)src, 0); + return; + } + else if (dest->op == TOKarrayliteral && src->op == TOKstring) + { + sliceAssignArrayLiteralFromString((ArrayLiteralExp *)dest, (StringExp *)src, 0); + return; + } + else if (src->op == TOKarrayliteral && dest->op == TOKstring) + { + sliceAssignStringFromArrayLiteral((StringExp *)dest, (ArrayLiteralExp *)src, 0); + return; + } + else assert(0); + + assert(oldelems->dim == newelems->dim); + + for (size_t i= 0; i < oldelems->dim; ++i) + { + Expression *e = newelems->tdata()[i]; + Expression *o = oldelems->tdata()[i]; + if (e->op == TOKstructliteral) + { + assert(o->op == e->op); + assignInPlace(o, e); + } + else if (e->type->ty == Tsarray && o->type->ty == Tsarray) + { + assignInPlace(o, e); + } + else + { + oldelems->tdata()[i] = newelems->tdata()[i]; + } + } +} + +void recursiveBlockAssign(ArrayLiteralExp *ae, Expression *val, bool wantRef) +{ + assert( ae->type->ty == Tsarray || ae->type->ty == Tarray); +#if DMDV2 + Type *desttype = ((TypeArray *)ae->type)->next->castMod(0); + bool directblk = (val->type->toBasetype()->castMod(0)) == desttype; +#else + Type *desttype = ((TypeArray *)ae->type)->next; + bool directblk = (val->type->toBasetype()) == desttype; +#endif + + bool cow = !(val->op == TOKstructliteral || val->op == TOKarrayliteral + || val->op == TOKstring); + + for (size_t k = 0; k < ae->elements->dim; k++) + { + if (!directblk && ae->elements->tdata()[k]->op == TOKarrayliteral) + { + recursiveBlockAssign((ArrayLiteralExp *)ae->elements->tdata()[k], val, wantRef); + } + else + { + if (wantRef || cow) + ae->elements->tdata()[k] = val; + else + assignInPlace(ae->elements->tdata()[k], val); + } + } +} + + +Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_t fp, int post) +{ +#if LOG + printf("BinExp::interpretAssignCommon() %s\n", toChars()); +#endif + Expression *returnValue = EXP_CANT_INTERPRET; + Expression *e1 = this->e1; + if (!istate) + { + error("value of %s is not known at compile time", e1->toChars()); + return returnValue; + } + ++CtfeStatus::numAssignments; + /* Before we begin, we need to know if this is a reference assignment + * (dynamic array, AA, or class) or a value assignment. + * Determining this for slice assignments are tricky: we need to know + * if it is a block assignment (a[] = e) rather than a direct slice + * assignment (a[] = b[]). Note that initializers of multi-dimensional + * static arrays can have 2D block assignments (eg, int[7][7] x = 6;). + * So we need to recurse to determine if it is a block assignment. + */ + bool isBlockAssignment = false; + if (e1->op == TOKslice) + { + // a[] = e can have const e. So we compare the naked types. + Type *desttype = e1->type->toBasetype(); +#if DMDV2 + Type *srctype = e2->type->toBasetype()->castMod(0); +#else + Type *srctype = e2->type->toBasetype(); +#endif + while ( desttype->ty == Tsarray || desttype->ty == Tarray) + { + desttype = ((TypeArray *)desttype)->next; +#if DMDV2 + if (srctype == desttype->castMod(0)) +#else + if (srctype == desttype) +#endif + { + isBlockAssignment = true; + break; + } + } + } + bool wantRef = false; + if (!fp && this->e1->type->toBasetype() == this->e2->type->toBasetype() && + (e1->type->toBasetype()->ty == Tarray || isAssocArray(e1->type)) + // e = *x is never a reference, because *x is always a value + && this->e2->op != TOKstar + ) + { +#if DMDV2 + wantRef = true; +#else + /* D1 doesn't have const in the type system. But there is still a + * vestigal const in the form of static const variables. + * Problematic code like: + * const int [] x = [1,2,3]; + * int [] y = x; + * can be dealt with by making this a non-ref assign (y = x.dup). + * Otherwise it's a big mess. + */ + VarDeclaration * targetVar = findParentVar(e2, istate->localThis); + if (!(targetVar && targetVar->isConst())) + wantRef = true; + // slice assignment of static arrays is not reference assignment + if ((e1->op==TOKslice) && ((SliceExp *)e1)->e1->type->ty == Tsarray) + wantRef = false; +#endif + // If it is assignment from a ref parameter, it's not a ref assignment + if (this->e2->op == TOKvar) + { + VarDeclaration *v = ((VarExp *)this->e2)->var->isVarDeclaration(); + if (v && (v->storage_class & (STCref | STCout))) + wantRef = false; + } + } + if (isBlockAssignment && (e2->type->toBasetype()->ty == Tarray || e2->type->toBasetype()->ty == Tsarray)) + { + wantRef = true; + } + // If it is a construction of a ref variable, it is a ref assignment + if (op == TOKconstruct && this->e1->op==TOKvar + && ((VarExp*)this->e1)->var->storage_class & STCref) + { + wantRef = true; + } + + if (fp) + { + while (e1->op == TOKcast) + { CastExp *ce = (CastExp *)e1; + e1 = ce->e1; + } + } + if (exceptionOrCantInterpret(e1)) + return e1; + + // First, deal with this = e; and call() = e; + if (e1->op == TOKthis) + { + e1 = istate->localThis; + } + if (e1->op == TOKcall) + { + bool oldWaiting = istate->awaitingLvalueReturn; + istate->awaitingLvalueReturn = true; + e1 = e1->interpret(istate); + istate->awaitingLvalueReturn = oldWaiting; + if (exceptionOrCantInterpret(e1)) + return e1; + if (e1->op == TOKarrayliteral || e1->op == TOKstring) + { + // f() = e2, when f returns an array, is always a slice assignment. + // Convert into arr[0..arr.length] = e2 + e1 = new SliceExp(loc, e1, + new IntegerExp(0, 0, Type::tsize_t), + ArrayLength(Type::tsize_t, e1)); + e1->type = type; + } + } + if (e1->op == TOKstar) + { + e1 = e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + return e1; + if (!(e1->op == TOKvar || e1->op == TOKdotvar || e1->op == TOKindex + || e1->op == TOKslice)) + { + error("cannot dereference invalid pointer %s", + this->e1->toChars()); + return EXP_CANT_INTERPRET; + } + } + + if (!(e1->op == TOKarraylength || e1->op == TOKvar || e1->op == TOKdotvar + || e1->op == TOKindex || e1->op == TOKslice)) + { + error("CTFE internal error: unsupported assignment %s", toChars()); + return EXP_CANT_INTERPRET; + } + + Expression * newval = NULL; + + if (!wantRef) + { // We need to treat pointers specially, because TOKsymoff can be used to + // return a value OR a pointer + assert(e1); + assert(e1->type); + if ( isPointer(e1->type) && (e2->op == TOKsymoff || e2->op==TOKaddress || e2->op==TOKvar)) + newval = this->e2->interpret(istate, ctfeNeedLvalue); + else + newval = this->e2->interpret(istate); + if (exceptionOrCantInterpret(newval)) + return newval; + } + // ---------------------------------------------------- + // Deal with read-modify-write assignments. + // Set 'newval' to the final assignment value + // Also determine the return value (except for slice + // assignments, which are more complicated) + // ---------------------------------------------------- + + if (fp || e1->op == TOKarraylength) + { + // If it isn't a simple assignment, we need the existing value + Expression * oldval = e1->interpret(istate); + if (exceptionOrCantInterpret(oldval)) + return oldval; + while (oldval->op == TOKvar) + { + oldval = resolveReferences(oldval, istate->localThis); + oldval = oldval->interpret(istate); + if (exceptionOrCantInterpret(oldval)) + return oldval; + } + + if (fp) + { + // ~= can create new values (see bug 6052) + if (op == TOKcatass) + { + // We need to dup it. We can skip this if it's a dynamic array, + // because it gets copied later anyway + if (newval->type->ty != Tarray) + newval = copyLiteral(newval); + if (newval->op == TOKslice) + newval = resolveSlice(newval); + // It becomes a reference assignment + wantRef = true; + } + if (oldval->op == TOKslice) + oldval = resolveSlice(oldval); + if (this->e1->type->ty == Tpointer && this->e2->type->isintegral() + && (op==TOKaddass || op == TOKminass || + op == TOKplusplus || op == TOKminusminus)) + { + oldval = this->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(oldval)) + return oldval; + newval = this->e2->interpret(istate); + if (exceptionOrCantInterpret(newval)) + return newval; + newval = pointerArithmetic(loc, op, type, oldval, newval); + } + else if (this->e1->type->ty == Tpointer) + { + error("pointer expression %s cannot be interpreted at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + else + { + newval = (*fp)(type, oldval, newval); + } + if (newval == EXP_CANT_INTERPRET) + { + error("Cannot interpret %s at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + if (exceptionOrCantInterpret(newval)) + return newval; + // Determine the return value + returnValue = ctfeCast(loc, type, type, post ? oldval : newval); + if (exceptionOrCantInterpret(returnValue)) + return returnValue; + } + else + returnValue = newval; + if (e1->op == TOKarraylength) + { + size_t oldlen = oldval->toInteger(); + size_t newlen = newval->toInteger(); + if (oldlen == newlen) // no change required -- we're done! + return returnValue; + // Now change the assignment from arr.length = n into arr = newval + e1 = ((ArrayLengthExp *)e1)->e1; + if (oldlen != 0) + { // Get the old array literal. + oldval = e1->interpret(istate); + while (oldval->op == TOKvar) + { oldval = resolveReferences(oldval, istate->localThis); + oldval = oldval->interpret(istate); + } + } + if (oldval->op == TOKslice) + oldval = resolveSlice(oldval); + Type *t = e1->type->toBasetype(); + if (t->ty == Tarray) + { + Type *elemType= NULL; + elemType = ((TypeArray *)t)->next; + assert(elemType); + Expression *defaultElem = elemType->defaultInitLiteral(); + + Expressions *elements = new Expressions(); + elements->setDim(newlen); + size_t copylen = oldlen < newlen ? oldlen : newlen; + if (oldval->op == TOKstring) + { + StringExp *oldse = (StringExp *)oldval; + unsigned char *s = (unsigned char *)mem.calloc(newlen + 1, oldse->sz); + memcpy(s, oldse->string, copylen * oldse->sz); + unsigned defaultValue = (unsigned)(defaultElem->toInteger()); + for (size_t elemi = copylen; elemi < newlen; ++elemi) + { + switch (oldse->sz) + { + case 1: s[elemi] = defaultValue; break; + case 2: ((unsigned short *)s)[elemi] = defaultValue; break; + case 4: ((unsigned *)s)[elemi] = defaultValue; break; + default: assert(0); + } + } + StringExp *se = new StringExp(loc, s, newlen); + se->type = t; + se->sz = oldse->sz; + se->committed = oldse->committed; + se->ownedByCtfe = true; + newval = se; + } + else + { + if (oldlen !=0) + assert(oldval->op == TOKarrayliteral); + ArrayLiteralExp *ae = (ArrayLiteralExp *)oldval; + for (size_t i = 0; i < copylen; i++) + elements->tdata()[i] = ae->elements->tdata()[i]; + if (elemType->ty == Tstruct || elemType->ty == Tsarray) + { /* If it is an aggregate literal representing a value type, + * we need to create a unique copy for each element + */ + for (size_t i = copylen; i < newlen; i++) + elements->tdata()[i] = copyLiteral(defaultElem); + } + else + { + for (size_t i = copylen; i < newlen; i++) + elements->tdata()[i] = defaultElem; + } + ArrayLiteralExp *aae = new ArrayLiteralExp(0, elements); + aae->type = t; + newval = aae; + aae->ownedByCtfe = true; + } + // We have changed it into a reference assignment + // Note that returnValue is still the new length. + wantRef = true; + if (e1->op == TOKstar) + { // arr.length+=n becomes (t=&arr, *(t).length=*(t).length+n); + e1 = e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + return e1; + } + } + else + { + error("%s is not yet supported at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + + } + } + else if (!wantRef && e1->op != TOKslice) + { /* Look for special case of struct being initialized with 0. + */ + if (type->toBasetype()->ty == Tstruct && newval->op == TOKint64) + { + newval = type->defaultInitLiteral(loc); + if (newval->op != TOKstructliteral) + { + error("nested structs with constructors are not yet supported in CTFE (Bug 6419)"); + return EXP_CANT_INTERPRET; + } + } + newval = ctfeCast(loc, type, type, newval); + if (exceptionOrCantInterpret(newval)) + return newval; + returnValue = newval; + } + if (exceptionOrCantInterpret(newval)) + return newval; + + // ------------------------------------------------- + // Make sure destination can be modified + // ------------------------------------------------- + // Make sure we're not trying to modify a global or static variable + // We do this by locating the ultimate parent variable which gets modified. + VarDeclaration * ultimateVar = findParentVar(e1, istate->localThis); + if (ultimateVar && ultimateVar->isDataseg() && !ultimateVar->isCTFE()) + { // Can't modify global or static data + error("%s cannot be modified at compile time", ultimateVar->toChars()); + return EXP_CANT_INTERPRET; + } + + e1 = resolveReferences(e1, istate->localThis); + + // Unless we have a simple var assignment, we're + // only modifying part of the variable. So we need to make sure + // that the parent variable exists. + if (e1->op != TOKvar && ultimateVar && !ultimateVar->getValue()) + ultimateVar->setValue(copyLiteral(ultimateVar->type->defaultInitLiteral())); + + // --------------------------------------- + // Deal with reference assignment + // (We already have 'newval' for arraylength operations) + // --------------------------------------- + if (wantRef && !fp && this->e1->op != TOKarraylength) + { + newval = this->e2->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(newval)) + return newval; + // If it is an assignment from a array function parameter passed by + // reference, resolve the reference. (This should NOT happen for + // non-reference types). + if (newval->op == TOKvar && (newval->type->ty == Tarray || + newval->type->ty == Tclass)) + { + newval = newval->interpret(istate); + } + + if (newval->op == TOKassocarrayliteral || newval->op == TOKstring || + newval->op==TOKarrayliteral) + { + if (needToCopyLiteral(newval)) + newval = copyLiteral(newval); + } + returnValue = newval; + } + + // --------------------------------------- + // Deal with AA index assignment + // --------------------------------------- + /* This needs special treatment if the AA doesn't exist yet. + * There are two special cases: + * (1) If the AA is itself an index of another AA, we may need to create + * multiple nested AA literals before we can insert the new value. + * (2) If the ultimate AA is null, no insertion happens at all. Instead, we + * create nested AA literals, and change it into a assignment. + */ + if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) + { + IndexExp *ie = (IndexExp *)e1; + int depth = 0; // how many nested AA indices are there? + while (ie->e1->op == TOKindex && ((IndexExp *)ie->e1)->e1->type->toBasetype()->ty == Taarray) + { + ie = (IndexExp *)ie->e1; + ++depth; + } + Expression *aggregate = resolveReferences(ie->e1, istate->localThis); + Expression *oldagg = aggregate; + // Get the AA to be modified. (We do an LvalueRef interpret, unless it + // is a simple ref parameter -- in which case, we just want the value) + aggregate = aggregate->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(aggregate)) + return aggregate; + if (aggregate->op == TOKassocarrayliteral) + { // Normal case, ultimate parent AA already exists + // We need to walk from the deepest index up, checking that an AA literal + // already exists on each level. + Expression *index = ((IndexExp *)e1)->e2->interpret(istate); + if (exceptionOrCantInterpret(index)) + return index; + if (index->op == TOKslice) // only happens with AA assignment + index = resolveSlice(index); + AssocArrayLiteralExp *existingAA = (AssocArrayLiteralExp *)aggregate; + while (depth > 0) + { // Walk the syntax tree to find the indexExp at this depth + IndexExp *xe = (IndexExp *)e1; + for (int d= 0; d < depth; ++d) + xe = (IndexExp *)xe->e1; + + Expression *indx = xe->e2->interpret(istate); + if (exceptionOrCantInterpret(indx)) + return indx; + if (indx->op == TOKslice) // only happens with AA assignment + indx = resolveSlice(indx); + + // Look up this index in it up in the existing AA, to get the next level of AA. + AssocArrayLiteralExp *newAA = (AssocArrayLiteralExp *)findKeyInAA(existingAA, indx); + if (exceptionOrCantInterpret(newAA)) + return newAA; + if (!newAA) + { // Doesn't exist yet, create an empty AA... + Expressions *valuesx = new Expressions(); + Expressions *keysx = new Expressions(); + newAA = new AssocArrayLiteralExp(loc, keysx, valuesx); + newAA->type = xe->type; + newAA->ownedByCtfe = true; + //... and insert it into the existing AA. + existingAA->keys->push(indx); + existingAA->values->push(newAA); + } + existingAA = newAA; + --depth; + } + if (assignAssocArrayElement(loc, existingAA, index, newval) == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + return returnValue; + } + else + { /* The AA is currently null. 'aggregate' is actually a reference to + * whatever contains it. It could be anything: var, dotvarexp, ... + * We rewrite the assignment from: aggregate[i][j] = newval; + * into: aggregate = [i:[j: newval]]; + */ + while (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) + { + Expression *index = ((IndexExp *)e1)->e2->interpret(istate); + if (exceptionOrCantInterpret(index)) + return index; + if (index->op == TOKslice) // only happens with AA assignment + index = resolveSlice(index); + Expressions *valuesx = new Expressions(); + Expressions *keysx = new Expressions(); + valuesx->push(newval); + keysx->push(index); + AssocArrayLiteralExp *newaae = new AssocArrayLiteralExp(loc, keysx, valuesx); + newaae->ownedByCtfe = true; + newaae->type = e1->type; + newval = newaae; + e1 = ((IndexExp *)e1)->e1; + } + // We must return to the original aggregate, in case it was a reference + wantRef = true; + e1 = oldagg; + // fall through -- let the normal assignment logic take care of it + } + } + + // --------------------------------------- + // Deal with dotvar expressions + // --------------------------------------- + // Because structs are not reference types, dotvar expressions can be + // collapsed into a single assignment. + if (!wantRef && e1->op == TOKdotvar) + { + // Strip of all of the leading dotvars, unless we started with a call + // or a ref parameter + // (in which case, we already have the lvalue). + if (this->e1->op != TOKcall && !(this->e1->op==TOKvar + && ((VarExp*)this->e1)->var->storage_class & (STCref | STCout))) + e1 = e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + return e1; + if (e1->op == TOKstructliteral && newval->op == TOKstructliteral) + { + assignInPlace(e1, newval); + return returnValue; + } + } +#if LOGASSIGN + if (wantRef) + printf("REF ASSIGN: %s=%s\n", e1->toChars(), newval->toChars()); + else + printf("ASSIGN: %s=%s\n", e1->toChars(), newval->toChars()); + showCtfeExpr(newval); +#endif + + /* Assignment to variable of the form: + * v = newval + */ + if (e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e1; + VarDeclaration *v = ve->var->isVarDeclaration(); + if (wantRef) + { + v->setValueNull(); + v->setValue(newval); + } + else if (e1->type->toBasetype()->ty == Tstruct) + { + // In-place modification + if (newval->op != TOKstructliteral) + { + error("CTFE internal error assigning struct"); + return EXP_CANT_INTERPRET; + } + newval = copyLiteral(newval); + if (v->getValue()) + assignInPlace(v->getValue(), newval); + else + v->setValue(newval); + } + else + { + TY tyE1 = e1->type->toBasetype()->ty; + if (tyE1 == Tarray || tyE1 == Taarray) + { // arr op= arr + v->setValue(newval); + } + else + { + v->setValue(newval); + } + } + } + else if (e1->op == TOKdotvar) + { + /* Assignment to member variable of the form: + * e.v = newval + */ + Expression *exx = ((DotVarExp *)e1)->e1; + if (wantRef && exx->op != TOKstructliteral) + { + exx = exx->interpret(istate); + if (exceptionOrCantInterpret(exx)) + return exx; + } + if (exx->op != TOKstructliteral && exx->op != TOKclassreference) + { + error("CTFE internal error: Dotvar assignment"); + return EXP_CANT_INTERPRET; + } + VarDeclaration *member = ((DotVarExp *)e1)->var->isVarDeclaration(); + if (!member) + { + error("CTFE internal error: Dotvar assignment"); + return EXP_CANT_INTERPRET; + } + StructLiteralExp *se = exx->op == TOKstructliteral + ? (StructLiteralExp *)exx + : ((ClassReferenceExp *)exx)->value; + int fieldi = exx->op == TOKstructliteral + ? se->getFieldIndex(member->type, member->offset) + : ((ClassReferenceExp *)exx)->getFieldIndex(member->type, member->offset); + if (fieldi == -1) + { + error("CTFE internal error: cannot find field %s in %s", member->toChars(), exx->toChars()); + return EXP_CANT_INTERPRET; + } + assert(fieldi>=0 && fieldi < se->elements->dim); + if (newval->op == TOKstructliteral) + assignInPlace(se->elements->tdata()[fieldi], newval); + else + se->elements->tdata()[fieldi] = newval; + return returnValue; + } + else if (e1->op == TOKindex) + { + /* Assignment to array element of the form: + * aggregate[i] = newval + * aggregate is not AA (AAs were dealt with already). + */ + IndexExp *ie = (IndexExp *)e1; + assert(ie->e1->type->toBasetype()->ty != Taarray); + uinteger_t destarraylen = 0; + + // Set the $ variable, and find the array literal to modify + if (ie->e1->type->toBasetype()->ty != Tpointer) + { + Expression *oldval = ie->e1->interpret(istate); + if (oldval->op == TOKnull) + { + error("cannot index null array %s", ie->e1->toChars()); + return EXP_CANT_INTERPRET; + } + if (oldval->op != TOKarrayliteral && oldval->op != TOKstring + && oldval->op != TOKslice) + { + error("cannot determine length of %s at compile time", + ie->e1->toChars()); + return EXP_CANT_INTERPRET; + } + destarraylen = resolveArrayLength(oldval); + if (ie->lengthVar) + { + IntegerExp *dollarExp = new IntegerExp(loc, destarraylen, Type::tsize_t); + ctfeStack.push(ie->lengthVar); + ie->lengthVar->setValue(dollarExp); + } + } + Expression *index = ie->e2->interpret(istate); + if (ie->lengthVar) + ctfeStack.pop(ie->lengthVar); // $ is defined only inside [] + if (exceptionOrCantInterpret(index)) + return index; + + assert (index->op != TOKslice); // only happens with AA assignment + + ArrayLiteralExp *existingAE = NULL; + StringExp *existingSE = NULL; + + Expression *aggregate = resolveReferences(ie->e1, istate->localThis); + + // Set the index to modify, and check that it is in range + dinteger_t indexToModify = index->toInteger(); + if (ie->e1->type->toBasetype()->ty == Tpointer) + { + dinteger_t ofs; + aggregate = aggregate->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(aggregate)) + return aggregate; + if (aggregate->op == TOKnull) + { + error("cannot index through null pointer %s", ie->e1->toChars()); + return EXP_CANT_INTERPRET; + } + if (aggregate->op == TOKint64) + { + error("cannot index through invalid pointer %s of value %s", + ie->e1->toChars(), aggregate->toChars()); + return EXP_CANT_INTERPRET; + } + aggregate = getAggregateFromPointer(aggregate, &ofs); + indexToModify += ofs; + destarraylen = resolveArrayLength(aggregate); + } + if (indexToModify >= destarraylen) + { + error("array index %lld is out of bounds [0..%lld]", indexToModify, + destarraylen); + return EXP_CANT_INTERPRET; + } + + /* The only possible indexable LValue aggregates are array literals, and + * slices of array literals. + */ + if (aggregate->op == TOKindex || aggregate->op == TOKdotvar || + aggregate->op == TOKslice || aggregate->op == TOKcall || + aggregate->op == TOKstar) + { + Expression *origagg = aggregate; + aggregate = aggregate->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(aggregate)) + return aggregate; + // The array could be an index of an AA. Resolve it if so. + if (aggregate->op == TOKindex && + ((IndexExp *)aggregate)->e1->op == TOKassocarrayliteral) + { + IndexExp *ix = (IndexExp *)aggregate; + aggregate = findKeyInAA((AssocArrayLiteralExp *)ix->e1, ix->e2); + if (!aggregate) + { + error("key %s not found in associative array %s", + ix->e2->toChars(), ix->e1->toChars()); + return EXP_CANT_INTERPRET; + } + if (exceptionOrCantInterpret(aggregate)) + return aggregate; + } + } + if (aggregate->op == TOKvar) + { + VarExp *ve = (VarExp *)aggregate; + VarDeclaration *v = ve->var->isVarDeclaration(); + aggregate = v->getValue(); + if (aggregate->op == TOKnull) + { + // This would be a runtime segfault + error("cannot index null array %s", v->toChars()); + return EXP_CANT_INTERPRET; + } + } + if (aggregate->op == TOKslice) + { + SliceExp *sexp = (SliceExp *)aggregate; + aggregate = sexp->e1; + Expression *lwr = sexp->lwr->interpret(istate); + indexToModify += lwr->toInteger(); + } + if (aggregate->op == TOKarrayliteral) + existingAE = (ArrayLiteralExp *)aggregate; + else if (aggregate->op == TOKstring) + existingSE = (StringExp *)aggregate; + else + { + error("CTFE internal compiler error %s", aggregate->toChars()); + return EXP_CANT_INTERPRET; + } + if (!wantRef && newval->op == TOKslice) + { + newval = resolveSlice(newval); + if (newval == EXP_CANT_INTERPRET) + { + error("Compiler error: CTFE index assign %s", toChars()); + assert(0); + } + } + if (wantRef && newval->op == TOKindex + && ((IndexExp *)newval)->e1 == aggregate) + { // It's a circular reference, resolve it now + newval = newval->interpret(istate); + } + + if (existingAE) + { + if (newval->op == TOKstructliteral) + assignInPlace((Expression *)(existingAE->elements->tdata()[indexToModify]), newval); + else + existingAE->elements->tdata()[indexToModify] = newval; + return returnValue; + } + if (existingSE) + { + unsigned char *s = (unsigned char *)existingSE->string; + if (!existingSE->ownedByCtfe) + { + error("cannot modify read-only string literal %s", ie->e1->toChars()); + return EXP_CANT_INTERPRET; + } + unsigned value = newval->toInteger(); + switch (existingSE->sz) + { + case 1: s[indexToModify] = value; break; + case 2: ((unsigned short *)s)[indexToModify] = value; break; + case 4: ((unsigned *)s)[indexToModify] = value; break; + default: + assert(0); + break; + } + return returnValue; + } + else + { + error("Index assignment %s is not yet supported in CTFE ", toChars()); + return EXP_CANT_INTERPRET; + } + return returnValue; + } + else if (e1->op == TOKslice) + { + // ------------------------------ + // aggregate[] = newval + // aggregate[low..upp] = newval + // ------------------------------ + SliceExp * sexp = (SliceExp *)e1; + // Set the $ variable + Expression *oldval = sexp->e1; + bool assignmentToSlicedPointer = false; + if (isPointer(oldval->type)) + { // Slicing a pointer + oldval = oldval->interpret(istate, ctfeNeedLvalue); + dinteger_t ofs; + oldval = getAggregateFromPointer(oldval, &ofs); + assignmentToSlicedPointer = true; + } else + oldval = oldval->interpret(istate); + + if (oldval->op != TOKarrayliteral && oldval->op != TOKstring + && oldval->op != TOKslice && oldval->op != TOKnull) + { + error("CTFE ICE: cannot resolve array length"); + return EXP_CANT_INTERPRET; + } + uinteger_t dollar = resolveArrayLength(oldval); + if (sexp->lengthVar) + { + Expression *arraylen = new IntegerExp(loc, dollar, Type::tsize_t); + ctfeStack.push(sexp->lengthVar); + sexp->lengthVar->setValue(arraylen); + } + + Expression *upper = NULL; + Expression *lower = NULL; + if (sexp->upr) + upper = sexp->upr->interpret(istate); + if (exceptionOrCantInterpret(upper)) + { + if (sexp->lengthVar) + ctfeStack.pop(sexp->lengthVar); // $ is defined only in [L..U] + return upper; + } + if (sexp->lwr) + lower = sexp->lwr->interpret(istate); + if (sexp->lengthVar) + ctfeStack.pop(sexp->lengthVar); // $ is defined only in [L..U] + if (exceptionOrCantInterpret(lower)) + return lower; + + size_t dim = dollar; + size_t upperbound = upper ? upper->toInteger() : dim; + int lowerbound = lower ? lower->toInteger() : 0; + + if (!assignmentToSlicedPointer && (((int)lowerbound < 0) || (upperbound > dim))) + { + error("Array bounds [0..%d] exceeded in slice [%d..%d]", + dim, lowerbound, upperbound); + return EXP_CANT_INTERPRET; + } + if (upperbound == lowerbound) + return newval; + + Expression *aggregate = resolveReferences(((SliceExp *)e1)->e1, istate->localThis); + dinteger_t firstIndex = lowerbound; + + ArrayLiteralExp *existingAE = NULL; + StringExp *existingSE = NULL; + + /* The only possible slicable LValue aggregates are array literals, + * and slices of array literals. + */ + + if (aggregate->op == TOKindex || aggregate->op == TOKdotvar || + aggregate->op == TOKslice || + aggregate->op == TOKstar || aggregate->op == TOKcall) + { + aggregate = aggregate->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(aggregate)) + return aggregate; + // The array could be an index of an AA. Resolve it if so. + if (aggregate->op == TOKindex && + ((IndexExp *)aggregate)->e1->op == TOKassocarrayliteral) + { + IndexExp *ix = (IndexExp *)aggregate; + aggregate = findKeyInAA((AssocArrayLiteralExp *)ix->e1, ix->e2); + if (!aggregate) + { + error("key %s not found in associative array %s", + ix->e2->toChars(), ix->e1->toChars()); + return EXP_CANT_INTERPRET; + } + if (exceptionOrCantInterpret(aggregate)) + return aggregate; + } + } + if (aggregate->op == TOKvar) + { + VarExp *ve = (VarExp *)(aggregate); + VarDeclaration *v = ve->var->isVarDeclaration(); + aggregate = v->getValue(); + } + if (aggregate->op == TOKslice) + { // Slice of a slice --> change the bounds + SliceExp *sexpold = (SliceExp *)aggregate; + dinteger_t hi = upperbound + sexpold->lwr->toInteger(); + firstIndex = lowerbound + sexpold->lwr->toInteger(); + if (hi > sexpold->upr->toInteger()) + { + error("slice [%d..%d] exceeds array bounds [0..%jd]", + lowerbound, upperbound, + sexpold->upr->toInteger() - sexpold->lwr->toInteger()); + return EXP_CANT_INTERPRET; + } + aggregate = sexpold->e1; + } + if ( isPointer(aggregate->type) ) + { // Slicing a pointer --> change the bounds + aggregate = sexp->e1->interpret(istate, ctfeNeedLvalue); + dinteger_t ofs; + aggregate = getAggregateFromPointer(aggregate, &ofs); + dinteger_t hi = upperbound + ofs; + firstIndex = lowerbound + ofs; + if (firstIndex < 0 || hi > dim) + { + error("slice [%d..%jd] exceeds memory block bounds [0..%jd]", + firstIndex, hi, dim); + return EXP_CANT_INTERPRET; + } + } + if (aggregate->op == TOKarrayliteral) + existingAE = (ArrayLiteralExp *)aggregate; + else if (aggregate->op == TOKstring) + existingSE = (StringExp *)aggregate; + if (existingSE && !existingSE->ownedByCtfe) + { error("cannot modify read-only string literal %s", sexp->e1->toChars()); + return EXP_CANT_INTERPRET; + } + + if (!wantRef && newval->op == TOKslice) + { + newval = resolveSlice(newval); + if (newval == EXP_CANT_INTERPRET) + { + error("Compiler error: CTFE slice %s", toChars()); + assert(0); + } + } + if (wantRef && newval->op == TOKindex + && ((IndexExp *)newval)->e1 == aggregate) + { // It's a circular reference, resolve it now + newval = newval->interpret(istate); + } + + // For slice assignment, we check that the lengths match. + size_t srclen = 0; + if (newval->op == TOKarrayliteral) + srclen = ((ArrayLiteralExp *)newval)->elements->dim; + else if (newval->op == TOKstring) + srclen = ((StringExp *)newval)->len; + if (!isBlockAssignment && srclen != (upperbound - lowerbound)) + { + error("Array length mismatch assigning [0..%d] to [%d..%d]", srclen, lowerbound, upperbound); + return EXP_CANT_INTERPRET; + } + + if (!isBlockAssignment && newval->op == TOKarrayliteral && existingAE) + { + Expressions *oldelems = existingAE->elements; + Expressions *newelems = ((ArrayLiteralExp *)newval)->elements; + for (size_t j = 0; j < newelems->dim; j++) + { + oldelems->tdata()[j + firstIndex] = newelems->tdata()[j]; + } + return newval; + } + else if (newval->op == TOKstring && existingSE) + { + sliceAssignStringFromString((StringExp *)existingSE, (StringExp *)newval, firstIndex); + return newval; + } + else if (newval->op == TOKstring && existingAE + && existingAE->type->isString()) + { /* Mixed slice: it was initialized as an array literal of chars. + * Now a slice of it is being set with a string. + */ + sliceAssignArrayLiteralFromString(existingAE, (StringExp *)newval, firstIndex); + return newval; + } + else if (newval->op == TOKarrayliteral && existingSE) + { /* Mixed slice: it was initialized as a string literal. + * Now a slice of it is being set with an array literal. + */ + sliceAssignStringFromArrayLiteral(existingSE, (ArrayLiteralExp *)newval, firstIndex); + return newval; + } + else if (existingSE) + { // String literal block slice assign + unsigned value = newval->toInteger(); + unsigned char *s = (unsigned char *)existingSE->string; + for (size_t j = 0; j < upperbound-lowerbound; j++) + { + switch (existingSE->sz) + { + case 1: s[j+firstIndex] = value; break; + case 2: ((unsigned short *)s)[j+firstIndex] = value; break; + case 4: ((unsigned *)s)[j+firstIndex] = value; break; + default: + assert(0); + break; + } + } + if (goal == ctfeNeedNothing) + return NULL; // avoid creating an unused literal + SliceExp *retslice = new SliceExp(loc, existingSE, + new IntegerExp(loc, firstIndex, Type::tsize_t), + new IntegerExp(loc, firstIndex + upperbound-lowerbound, Type::tsize_t)); + retslice->type = this->type; + return retslice->interpret(istate); + } + else if (existingAE) + { + /* Block assignment, initialization of static arrays + * x[] = e + * x may be a multidimensional static array. (Note that this + * only happens with array literals, never with strings). + */ + Expressions * w = existingAE->elements; + assert( existingAE->type->ty == Tsarray || + existingAE->type->ty == Tarray); +#if DMDV2 + Type *desttype = ((TypeArray *)existingAE->type)->next->castMod(0); + bool directblk = (e2->type->toBasetype()->castMod(0)) == desttype; +#else + Type *desttype = ((TypeArray *)existingAE->type)->next; + bool directblk = (e2->type->toBasetype()) == desttype; +#endif + bool cow = !(newval->op == TOKstructliteral || newval->op == TOKarrayliteral + || newval->op == TOKstring); + for (size_t j = 0; j < upperbound-lowerbound; j++) + { + if (!directblk) + // Multidimensional array block assign + recursiveBlockAssign((ArrayLiteralExp *)w->tdata()[j+firstIndex], newval, wantRef); + else + { + if (wantRef || cow) + existingAE->elements->tdata()[j+firstIndex] = newval; + else + assignInPlace(existingAE->elements->tdata()[j+firstIndex], newval); + } + } + if (goal == ctfeNeedNothing) + return NULL; // avoid creating an unused literal + SliceExp *retslice = new SliceExp(loc, existingAE, + new IntegerExp(loc, firstIndex, Type::tsize_t), + new IntegerExp(loc, firstIndex + upperbound-lowerbound, Type::tsize_t)); + retslice->type = this->type; + return retslice->interpret(istate); + } + else + error("Slice operation %s cannot be evaluated at compile time", toChars()); + } + else + { + error("%s cannot be evaluated at compile time", toChars()); +#ifdef DEBUG + dump(0); +#endif + } + return returnValue; +} + +Expression *AssignExp::interpret(InterState *istate, CtfeGoal goal) +{ + return interpretAssignCommon(istate, goal, NULL); +} + +#define BIN_ASSIGN_INTERPRET_CTFE(op, ctfeOp) \ +Expression *op##AssignExp::interpret(InterState *istate, CtfeGoal goal) \ +{ \ + return interpretAssignCommon(istate, goal, &ctfeOp); \ +} + +#define BIN_ASSIGN_INTERPRET(op) BIN_ASSIGN_INTERPRET_CTFE(op, op) + +BIN_ASSIGN_INTERPRET(Add) +BIN_ASSIGN_INTERPRET(Min) +BIN_ASSIGN_INTERPRET_CTFE(Cat, ctfeCat) +BIN_ASSIGN_INTERPRET(Mul) +BIN_ASSIGN_INTERPRET(Div) +BIN_ASSIGN_INTERPRET(Mod) +BIN_ASSIGN_INTERPRET(Shl) +BIN_ASSIGN_INTERPRET(Shr) +BIN_ASSIGN_INTERPRET(Ushr) +BIN_ASSIGN_INTERPRET(And) +BIN_ASSIGN_INTERPRET(Or) +BIN_ASSIGN_INTERPRET(Xor) +#if DMDV2 +BIN_ASSIGN_INTERPRET(Pow) +#endif + +Expression *PostExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("PostExp::interpret() %s\n", toChars()); +#endif + Expression *e; + if (op == TOKplusplus) + e = interpretAssignCommon(istate, goal, &Add, 1); + else + e = interpretAssignCommon(istate, goal, &Min, 1); +#if LOG + if (e == EXP_CANT_INTERPRET) + printf("PostExp::interpret() CANT\n"); +#endif + return e; +} + +Expression *AndAndExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("AndAndExp::interpret() %s\n", toChars()); +#endif + Expression *e = e1->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + + int result; + if (e != EXP_CANT_INTERPRET) + { + if (e->isBool(FALSE)) + result = 0; + else if (isTrueBool(e)) + { + e = e2->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + if (e == EXP_VOID_INTERPRET) + { + assert(type->ty == Tvoid); + return NULL; + } + if (e != EXP_CANT_INTERPRET) + { + if (e->isBool(FALSE)) + result = 0; + else if (isTrueBool(e)) + result = 1; + else + { + error("%s does not evaluate to a boolean", e->toChars()); + e = EXP_CANT_INTERPRET; + } + } + } + else + { + error("%s cannot be interpreted as a boolean", e->toChars()); + e = EXP_CANT_INTERPRET; + } + } + if (e != EXP_CANT_INTERPRET && goal != ctfeNeedNothing) + e = new IntegerExp(loc, result, type); + return e; +} + +Expression *OrOrExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("OrOrExp::interpret() %s\n", toChars()); +#endif + Expression *e = e1->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + + int result; + if (e != EXP_CANT_INTERPRET) + { + if (isTrueBool(e)) + result = 1; + else if (e->isBool(FALSE)) + { + e = e2->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + + if (e == EXP_VOID_INTERPRET) + { + assert(type->ty == Tvoid); + return NULL; + } + if (e != EXP_CANT_INTERPRET) + { + if (e->isBool(FALSE)) + result = 0; + else if (isTrueBool(e)) + result = 1; + else + { + error("%s cannot be interpreted as a boolean", e->toChars()); + e = EXP_CANT_INTERPRET; + } + } + } + else + { + error("%s cannot be interpreted as a boolean", e->toChars()); + e = EXP_CANT_INTERPRET; + } + } + if (e != EXP_CANT_INTERPRET && goal != ctfeNeedNothing) + e = new IntegerExp(loc, result, type); + return e; +} + +// Print a stack trace, starting from callingExp which called fd. +// To shorten the stack trace, try to detect recursion. +void showCtfeBackTrace(InterState *istate, CallExp * callingExp, FuncDeclaration *fd) +{ + if (CtfeStatus::stackTraceCallsToSuppress > 0) + { + --CtfeStatus::stackTraceCallsToSuppress; + return; + } + errorSupplemental(callingExp->loc, "called from here: %s", callingExp->toChars()); + // Quit if it's not worth trying to compress the stack trace + if (CtfeStatus::callDepth < 6 || global.params.verbose) + return; + // Recursion happens if the current function already exists in the call stack. + int numToSuppress = 0; + int recurseCount = 0; + int depthSoFar = 0; + InterState *lastRecurse = istate; + for (InterState * cur = istate; cur; cur = cur->caller) + { + if (cur->fd == fd) + { ++recurseCount; + numToSuppress = depthSoFar; + lastRecurse = cur; + } + ++depthSoFar; + } + // We need at least three calls to the same function, to make compression worthwhile + if (recurseCount < 2) + return; + // We found a useful recursion. Print all the calls involved in the recursion + errorSupplemental(fd->loc, "%d recursive calls to function %s", recurseCount, fd->toChars()); + for (InterState *cur = istate; cur->fd != fd; cur = cur->caller) + { + errorSupplemental(cur->fd->loc, "recursively called from function %s", cur->fd->toChars()); + } + // We probably didn't enter the recursion in this function. + // Go deeper to find the real beginning. + InterState * cur = istate; + while (lastRecurse->caller && cur->fd == lastRecurse->caller->fd) + { + cur = cur->caller; + lastRecurse = lastRecurse->caller; + ++numToSuppress; + } + CtfeStatus::stackTraceCallsToSuppress = numToSuppress; +} + +Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) +{ Expression *e = EXP_CANT_INTERPRET; + +#if LOG + printf("CallExp::interpret() %s\n", toChars()); +#endif + + Expression * pthis = NULL; + FuncDeclaration *fd = NULL; + Expression *ecall = e1; + if (ecall->op == TOKcall) + { + ecall = e1->interpret(istate); + if (exceptionOrCantInterpret(ecall)) + return ecall; + } + if (ecall->op == TOKstar) + { // Calling a function pointer + Expression * pe = ((PtrExp*)ecall)->e1; + if (pe->op == TOKvar) { + VarDeclaration *vd = ((VarExp *)((PtrExp*)ecall)->e1)->var->isVarDeclaration(); + if (vd && vd->getValue() && vd->getValue()->op == TOKsymoff) + fd = ((SymOffExp *)vd->getValue())->var->isFuncDeclaration(); + else + { + ecall = getVarExp(loc, istate, vd, goal); + if (exceptionOrCantInterpret(ecall)) + return ecall; + + if (ecall->op == TOKsymoff) + fd = ((SymOffExp *)ecall)->var->isFuncDeclaration(); + } + } + else if (pe->op == TOKsymoff) + fd = ((SymOffExp *)pe)->var->isFuncDeclaration(); + else + ecall = ((PtrExp*)ecall)->e1->interpret(istate); + + } + if (exceptionOrCantInterpret(ecall)) + return ecall; + + if (ecall->op == TOKindex) + { ecall = e1->interpret(istate); + if (exceptionOrCantInterpret(ecall)) + return ecall; + } + + if (ecall->op == TOKdotvar && !((DotVarExp*)ecall)->var->isFuncDeclaration()) + { ecall = e1->interpret(istate); + if (exceptionOrCantInterpret(ecall)) + return ecall; + } + + if (ecall->op == TOKdotvar) + { // Calling a member function + pthis = ((DotVarExp*)e1)->e1; + fd = ((DotVarExp*)e1)->var->isFuncDeclaration(); + } + else if (ecall->op == TOKvar) + { + VarDeclaration *vd = ((VarExp *)ecall)->var->isVarDeclaration(); + if (vd && vd->getValue()) + ecall = vd->getValue(); + else // Calling a function + fd = ((VarExp *)e1)->var->isFuncDeclaration(); + } + if (ecall->op == TOKdelegate) + { // Calling a delegate + fd = ((DelegateExp *)ecall)->func; + pthis = ((DelegateExp *)ecall)->e1; + } + else if (ecall->op == TOKfunction) + { // Calling a delegate literal + fd = ((FuncExp*)ecall)->fd; + } + else if (ecall->op == TOKstar && ((PtrExp*)ecall)->e1->op==TOKfunction) + { // Calling a function literal + fd = ((FuncExp*)((PtrExp*)ecall)->e1)->fd; + } + + TypeFunction *tf = fd ? (TypeFunction *)(fd->type) : NULL; + if (!tf) + { // DAC: This should never happen, it's an internal compiler error. + //printf("ecall=%s %d %d\n", ecall->toChars(), ecall->op, TOKcall); + if (ecall->op == TOKidentifier) + error("cannot evaluate %s at compile time. Circular reference?", toChars()); + else + error("CTFE internal error: cannot evaluate %s at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + if (!fd) + { + error("cannot evaluate %s at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + if (pthis) + { // Member function call + Expression *oldpthis; + if (pthis->op == TOKthis) + { + pthis = istate ? istate->localThis : NULL; + oldpthis = pthis; + } + else + { + if (pthis->op == TOKcomma) + pthis = pthis->interpret(istate); + if (exceptionOrCantInterpret(pthis)) + return pthis; + // Evaluate 'this' + oldpthis = pthis; + if (pthis->op != TOKvar) + pthis = pthis->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(pthis)) + return pthis; + } + if (fd->isVirtual()) + { // Make a virtual function call. + Expression *thisval = pthis; + if (pthis->op == TOKvar) + { assert(((VarExp*)thisval)->var->isVarDeclaration()); + thisval = ((VarExp*)thisval)->var->isVarDeclaration()->getValue(); + } + // Get the function from the vtable of the original class + ClassDeclaration *cd; + if (thisval && thisval->op == TOKnull) + { + error("function call through null class reference %s", pthis->toChars()); + return EXP_CANT_INTERPRET; + } + if (oldpthis->op == TOKsuper) + { assert(oldpthis->type->ty == Tclass); + cd = ((TypeClass *)oldpthis->type)->sym; + } + else + { + assert(thisval && thisval->op == TOKclassreference); + cd = ((ClassReferenceExp *)thisval)->originalClass(); + } + // We can't just use the vtable index to look it up, because + // vtables for interfaces don't get populated until the glue layer. + fd = cd->findFunc(fd->ident, (TypeFunction *)fd->type); + + assert(fd); + } + } + if (fd && fd->semanticRun >= PASSsemantic3done && fd->semantic3Errors) + { + error("CTFE failed because of previous errors in %s", fd->toChars()); + return EXP_CANT_INTERPRET; + } + // Check for built-in functions + Expression *eresult = evaluateIfBuiltin(istate, loc, fd, arguments, pthis); + if (eresult) + return eresult; + + // Inline .dup. Special case because it needs the return type. + if (!pthis && fd->ident == Id::adDup && arguments && arguments->dim == 2) + { + e = arguments->tdata()[1]; + e = e->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + if (e != EXP_CANT_INTERPRET) + { + if (e->op == TOKslice) + e= resolveSlice(e); + e = paintTypeOntoLiteral(type, copyLiteral(e)); + } + return e; + } + if (!fd->fbody) + { + error("%s cannot be interpreted at compile time," + " because it has no available source code", fd->toChars()); + return EXP_CANT_INTERPRET; + } + eresult = fd->interpret(istate, arguments, pthis); + if (eresult == EXP_CANT_INTERPRET) + { // Print a stack trace. + if (!global.gag) + showCtfeBackTrace(istate, this, fd); + } + return eresult; +} + +Expression *CommaExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("CommaExp::interpret() %s\n", toChars()); +#endif + + CommaExp * firstComma = this; + while (firstComma->e1->op == TOKcomma) + firstComma = (CommaExp *)firstComma->e1; + + // If it creates a variable, and there's no context for + // the variable to be created in, we need to create one now. + InterState istateComma; + if (!istate && firstComma->e1->op == TOKdeclaration) + { + assert(ctfeStack.stackPointer() == 0); + ctfeStack.startFrame(); + istate = &istateComma; + } + + Expression *e = EXP_CANT_INTERPRET; + + // If the comma returns a temporary variable, it needs to be an lvalue + // (this is particularly important for struct constructors) + if (e1->op == TOKdeclaration && e2->op == TOKvar + && ((DeclarationExp *)e1)->declaration == ((VarExp*)e2)->var + && ((VarExp*)e2)->var->storage_class & STCctfe) // same as Expression::isTemp + { + VarExp* ve = (VarExp *)e2; + VarDeclaration *v = ve->var->isVarDeclaration(); + ctfeStack.push(v); + if (!v->init && !v->getValue()) + { + v->setValue(copyLiteral(v->type->defaultInitLiteral())); + } + if (!v->getValue()) { + Expression *newval = v->init->toExpression(); + // Bug 4027. Copy constructors are a weird case where the + // initializer is a void function (the variable is modified + // through a reference parameter instead). + newval = newval->interpret(istate); + if (exceptionOrCantInterpret(newval)) + { + if (istate == &istateComma) + ctfeStack.endFrame(0); + return newval; + } + if (newval != EXP_VOID_INTERPRET) + { + // v isn't necessarily null. + v->setValueWithoutChecking(copyLiteral(newval)); + } + } + if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) + e = e2; + else + e = e2->interpret(istate, goal); + } + else + { + e = e1->interpret(istate, ctfeNeedNothing); + if (!exceptionOrCantInterpret(e)) + e = e2->interpret(istate, goal); + } + // If we created a temporary stack frame, end it now. + if (istate == &istateComma) + ctfeStack.endFrame(0); + return e; +} + +Expression *CondExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("CondExp::interpret() %s\n", toChars()); +#endif + Expression *e; + if ( isPointer(econd->type) ) + { + e = econd->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e)) + return e; + if (e->op != TOKnull) + e = new IntegerExp(loc, 1, Type::tbool); + } + else + e = econd->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + if (isTrueBool(e)) + e = e1->interpret(istate, goal); + else if (e->isBool(FALSE)) + e = e2->interpret(istate, goal); + else + { + error("%s does not evaluate to boolean result at compile time", + econd->toChars()); + e = EXP_CANT_INTERPRET; + } + return e; +} + +Expression *ArrayLengthExp::interpret(InterState *istate, CtfeGoal goal) +{ Expression *e; + Expression *e1; + +#if LOG + printf("ArrayLengthExp::interpret() %s\n", toChars()); +#endif + e1 = this->e1->interpret(istate); + assert(e1); + if (exceptionOrCantInterpret(e1)) + return e1; + if (e1->op == TOKstring || e1->op == TOKarrayliteral || e1->op == TOKslice + || e1->op == TOKassocarrayliteral || e1->op == TOKnull) + { + e = new IntegerExp(loc, resolveArrayLength(e1), type); + } + else + { + error("%s cannot be evaluated at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + return e; +} + +/* Given an AA literal 'ae', and a key 'e2': + * Return ae[e2] if present, or NULL if not found. + * Return EXP_CANT_INTERPRET on error. + */ +Expression *findKeyInAA(AssocArrayLiteralExp *ae, Expression *e2) +{ + /* Search the keys backwards, in case there are duplicate keys + */ + for (size_t i = ae->keys->dim; i;) + { + i--; + Expression *ekey = ae->keys->tdata()[i]; + Expression *ex = ctfeEqual(TOKequal, Type::tbool, ekey, e2); + if (ex == EXP_CANT_INTERPRET) + { + error("cannot evaluate %s==%s at compile time", + ekey->toChars(), e2->toChars()); + return ex; + } + if (ex->isBool(TRUE)) + { + return ae->values->tdata()[i]; + } + } + return NULL; +} + +/* Same as for constfold.Index, except that it only works for static arrays, + * dynamic arrays, and strings. We know that e1 is an + * interpreted CTFE expression, so it cannot have side-effects. + */ +Expression *ctfeIndex(Loc loc, Type *type, Expression *e1, uinteger_t indx) +{ //printf("ctfeIndex(e1 = %s)\n", e1->toChars()); + assert(e1->type); + if (e1->op == TOKstring) + { StringExp *es1 = (StringExp *)e1; + if (indx >= es1->len) + { + error(loc, "string index %ju is out of bounds [0 .. %zu]", indx, es1->len); + return EXP_CANT_INTERPRET; + } + else + return new IntegerExp(loc, es1->charAt(indx), type); + } + assert(e1->op == TOKarrayliteral); + ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; + if (indx >= ale->elements->dim) + { + error(loc, "array index %ju is out of bounds %s[0 .. %u]", indx, e1->toChars(), ale->elements->dim); + return EXP_CANT_INTERPRET; + } + Expression *e = ale->elements->tdata()[indx]; + return paintTypeOntoLiteral(type, e); +} + +Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) +{ + Expression *e1 = NULL; + Expression *e2; + +#if LOG + printf("IndexExp::interpret() %s\n", toChars()); +#endif + if (this->e1->type->toBasetype()->ty == Tpointer) + { + // Indexing a pointer. Note that there is no $ in this case. + e1 = this->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + return e1; + e2 = this->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + return e2; + dinteger_t indx = e2->toInteger(); + dinteger_t ofs; + Expression *agg = getAggregateFromPointer(e1, &ofs); + if (agg->op == TOKnull) + { + error("cannot index null pointer %s", this->e1->toChars()); + return EXP_CANT_INTERPRET; + } + assert(agg->op == TOKarrayliteral || agg->op == TOKstring); + dinteger_t len = ArrayLength(Type::tsize_t, agg)->toInteger(); + Type *pointee = ((TypePointer *)agg->type)->next; + if ((indx + ofs) < 0 || (indx+ofs) > len) + { + error("pointer index [%jd] exceeds allocated memory block [0..%jd]", + indx+ofs, len); + return EXP_CANT_INTERPRET; + } + return ctfeIndex(loc, type, agg, indx+ofs); + } + e1 = this->e1; + if (!(e1->op == TOKarrayliteral && ((ArrayLiteralExp *)e1)->ownedByCtfe)) + e1 = e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + return e1; + + if (e1->op == TOKnull) + { + if (goal == ctfeNeedLvalue && e1->type->ty == Taarray) + return paintTypeOntoLiteral(type, e1); + error("cannot index null array %s", this->e1->toChars()); + return EXP_CANT_INTERPRET; + } + /* Set the $ variable. + * Note that foreach uses indexing but doesn't need $ + */ + if (lengthVar && (e1->op == TOKstring || e1->op == TOKarrayliteral + || e1->op == TOKslice)) + { + uinteger_t dollar = resolveArrayLength(e1); + Expression *dollarExp = new IntegerExp(loc, dollar, Type::tsize_t); + ctfeStack.push(lengthVar); + lengthVar->setValue(dollarExp); + } + + e2 = this->e2->interpret(istate); + if (lengthVar) + ctfeStack.pop(lengthVar); // $ is defined only inside [] + if (exceptionOrCantInterpret(e2)) + return e2; + if (e1->op == TOKslice && e2->op == TOKint64) + { + // Simplify index of slice: agg[lwr..upr][indx] --> agg[indx'] + uinteger_t indx = e2->toInteger(); + uinteger_t ilo = ((SliceExp *)e1)->lwr->toInteger(); + uinteger_t iup = ((SliceExp *)e1)->upr->toInteger(); + + if (indx > iup - ilo) + { + error("index %ju exceeds array length %ju", indx, iup - ilo); + return EXP_CANT_INTERPRET; + } + indx += ilo; + e1 = ((SliceExp *)e1)->e1; + e2 = new IntegerExp(e2->loc, indx, e2->type); + } + Expression *e = NULL; + if ((goal == ctfeNeedLvalue && type->ty != Taarray && type->ty != Tarray + && type->ty != Tsarray && type->ty != Tstruct && type->ty != Tclass) + || (goal == ctfeNeedLvalueRef && type->ty != Tsarray && type->ty != Tstruct) + ) + { // Pointer or reference of a scalar type + e = new IndexExp(loc, e1, e2); + e->type = type; + return e; + } + if (e1->op == TOKassocarrayliteral) + { + if (e2->op == TOKslice) + e2 = resolveSlice(e2); + e = findKeyInAA((AssocArrayLiteralExp *)e1, e2); + if (!e) + { + error("key %s not found in associative array %s", + e2->toChars(), this->e1->toChars()); + return EXP_CANT_INTERPRET; + } + } + else + { + if (e2->op != TOKint64) + { + e1->error("CTFE internal error: non-integral index [%s]", this->e2->toChars()); + return EXP_CANT_INTERPRET; + } + e = ctfeIndex(loc, type, e1, e2->toInteger()); + } + if (exceptionOrCantInterpret(e)) + return e; + if (goal == ctfeNeedRvalue && (e->op == TOKslice || e->op == TOKdotvar)) + e = e->interpret(istate); + e = paintTypeOntoLiteral(type, e); + return e; +} + + +Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) +{ + Expression *e1; + Expression *lwr; + Expression *upr; + +#if LOG + printf("SliceExp::interpret() %s\n", toChars()); +#endif + + if (this->e1->type->toBasetype()->ty == Tpointer) + { + // Slicing a pointer. Note that there is no $ in this case. + e1 = this->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + return e1; + if (e1->op == TOKint64) + { + error("cannot slice invalid pointer %s of value %s", + this->e1->toChars(), e1->toChars()); + return EXP_CANT_INTERPRET; + } + + /* Evaluate lower and upper bounds of slice + */ + lwr = this->lwr->interpret(istate); + if (exceptionOrCantInterpret(lwr)) + return lwr; + upr = this->upr->interpret(istate); + if (exceptionOrCantInterpret(upr)) + return upr; + uinteger_t ilwr; + uinteger_t iupr; + ilwr = lwr->toInteger(); + iupr = upr->toInteger(); + Expression *e; + dinteger_t ofs; + Expression *agg = getAggregateFromPointer(e1, &ofs); + if (agg->op == TOKnull) + { + if (iupr == ilwr) + { + e = new NullExp(loc); + e->type = type; + return e; + } + error("cannot slice null pointer %s", this->e1->toChars()); + return EXP_CANT_INTERPRET; + } + assert(agg->op == TOKarrayliteral || agg->op == TOKstring); + dinteger_t len = ArrayLength(Type::tsize_t, agg)->toInteger(); + Type *pointee = ((TypePointer *)agg->type)->next; + if ((ilwr + ofs) < 0 || (iupr+ofs) > (len + 1) || iupr < ilwr) + { + error("pointer slice [%jd..%jd] exceeds allocated memory block [0..%jd]", + ilwr+ofs, iupr+ofs, len); + return EXP_CANT_INTERPRET; + } + e = new SliceExp(loc, agg, lwr, upr); + e->type = type; + return e; + } + if (goal == ctfeNeedRvalue && this->e1->op == TOKstring) + e1 = this->e1; // Will get duplicated anyway + else + e1 = this->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + return e1; + if (e1->op == TOKvar) + e1 = e1->interpret(istate); + + if (!this->lwr) + { + if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) + return e1; + return paintTypeOntoLiteral(type, e1); + } + + /* Set the $ variable + */ + if (e1->op != TOKarrayliteral && e1->op != TOKstring && + e1->op != TOKnull && e1->op != TOKslice) + { + error("Cannot determine length of %s at compile time", e1->toChars()); + return EXP_CANT_INTERPRET; + } + uinteger_t dollar = resolveArrayLength(e1); + if (lengthVar) + { + IntegerExp *dollarExp = new IntegerExp(loc, dollar, Type::tsize_t); + ctfeStack.push(lengthVar); + lengthVar->setValue(dollarExp); + } + + /* Evaluate lower and upper bounds of slice + */ + lwr = this->lwr->interpret(istate); + if (exceptionOrCantInterpret(lwr)) + { + if (lengthVar) + ctfeStack.pop(lengthVar);; // $ is defined only inside [L..U] + return lwr; + } + upr = this->upr->interpret(istate); + if (lengthVar) + ctfeStack.pop(lengthVar); // $ is defined only inside [L..U] + if (exceptionOrCantInterpret(upr)) + return upr; + + Expression *e; + uinteger_t ilwr; + uinteger_t iupr; + ilwr = lwr->toInteger(); + iupr = upr->toInteger(); + if (e1->op == TOKnull) + { + if (ilwr== 0 && iupr == 0) + return e1; + e1->error("slice [%ju..%ju] is out of bounds", ilwr, iupr); + return EXP_CANT_INTERPRET; + } + if (e1->op == TOKslice) + { + SliceExp *se = (SliceExp *)e1; + // Simplify slice of slice: + // aggregate[lo1..up1][lwr..upr] ---> aggregate[lwr'..upr'] + uinteger_t lo1 = se->lwr->toInteger(); + uinteger_t up1 = se->upr->toInteger(); + if (ilwr > iupr || iupr > up1 - lo1) + { + error("slice[%ju..%ju] exceeds array bounds[%ju..%ju]", + ilwr, iupr, lo1, up1); + return EXP_CANT_INTERPRET; + } + ilwr += lo1; + iupr += lo1; + e = new SliceExp(loc, se->e1, + new IntegerExp(loc, ilwr, lwr->type), + new IntegerExp(loc, iupr, upr->type)); + e->type = type; + return e; + } + if (e1->op == TOKarrayliteral + || e1->op == TOKstring) + { + if (iupr < ilwr || ilwr < 0 || iupr > dollar) + { + error("slice [%jd..%jd] exceeds array bounds [0..%jd]", + ilwr, iupr, dollar); + return EXP_CANT_INTERPRET; + } + } + e = new SliceExp(loc, e1, lwr, upr); + e->type = type; + return e; +} + +Expression *InExp::interpret(InterState *istate, CtfeGoal goal) +{ Expression *e = EXP_CANT_INTERPRET; + +#if LOG + printf("InExp::interpret() %s\n", toChars()); +#endif + Expression *e1 = this->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + return e1; + Expression *e2 = this->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + return e2; + if (e2->op == TOKnull) + return new NullExp(loc, type); + if (e2->op != TOKassocarrayliteral) + { + error(" %s cannot be interpreted at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + if (e1->op == TOKslice) + e1 = resolveSlice(e1); + e = findKeyInAA((AssocArrayLiteralExp *)e2, e1); + if (exceptionOrCantInterpret(e)) + return e; + if (!e) + return new NullExp(loc, type); + e = new IndexExp(loc, e2, e1); + e->type = type; + return e; +} + +Expression *CatExp::interpret(InterState *istate, CtfeGoal goal) +{ Expression *e; + Expression *e1; + Expression *e2; + +#if LOG + printf("CatExp::interpret() %s\n", toChars()); +#endif + e1 = this->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + return e1; + if (e1->op == TOKslice) + { + e1 = resolveSlice(e1); + } + e2 = this->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + return e2; + if (e2->op == TOKslice) + e2 = resolveSlice(e2); + e = ctfeCat(type, e1, e2); + if (e == EXP_CANT_INTERPRET) + error("%s cannot be interpreted at compile time", toChars()); + // We know we still own it, because we interpreted both e1 and e2 + if (e->op == TOKarrayliteral) + ((ArrayLiteralExp *)e)->ownedByCtfe = true; + if (e->op == TOKstring) + ((StringExp *)e)->ownedByCtfe = true; + return e; +} + + +// Return true if t is a pointer (not a function pointer) +bool isPointer(Type *t) +{ + Type * tb = t->toBasetype(); + return tb->ty == Tpointer && tb->nextOf()->ty != Tfunction; +} + +// Return true if t is an AA, or AssociativeArray!(key, value) +bool isAssocArray(Type *t) +{ + t = t->toBasetype(); + if (t->ty == Taarray) + return true; +#if DMDV2 + if (t->ty != Tstruct) + return false; + StructDeclaration *sym = ((TypeStruct *)t)->sym; + if (sym->ident == Id::AssociativeArray) + return true; +#endif + return false; +} + +// Given a template AA type, extract the corresponding built-in AA type +TypeAArray *toBuiltinAAType(Type *t) +{ + t = t->toBasetype(); + if (t->ty == Taarray) + return (TypeAArray *)t; +#if DMDV2 + assert(t->ty == Tstruct); + StructDeclaration *sym = ((TypeStruct *)t)->sym; + assert(sym->ident == Id::AssociativeArray); + TemplateInstance *tinst = sym->parent->isTemplateInstance(); + assert(tinst); + return new TypeAArray((Type *)(tinst->tiargs->tdata()[1]), (Type *)(tinst->tiargs->tdata()[0])); +#else + assert(0); + return NULL; +#endif +} + +Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) +{ Expression *e; + Expression *e1; + +#if LOG + printf("CastExp::interpret() %s\n", toChars()); +#endif + e1 = this->e1->interpret(istate, goal); + if (exceptionOrCantInterpret(e1)) + return e1; + // If the expression has been cast to void, do nothing. + if (to->ty == Tvoid && goal == ctfeNeedNothing) + return e1; + if (to->ty == Tpointer && e1->op != TOKnull) + { + Type *pointee = ((TypePointer *)type)->next; + // Implement special cases of normally-unsafe casts +#if DMDV2 + if (pointee->ty == Taarray && e1->op == TOKaddress + && isAssocArray(((AddrExp*)e1)->e1->type)) + { // cast from template AA pointer to true AA pointer is OK. + return paintTypeOntoLiteral(to, e1); + } +#endif + if (e1->op == TOKint64) + { // Happens with Windows HANDLEs, for example. + return paintTypeOntoLiteral(to, e1); + } + bool castBackFromVoid = false; + if (e1->type->ty == Tarray || e1->type->ty == Tsarray || e1->type->ty == Tpointer) + { + // Check for unsupported type painting operations + // For slices, we need the type being sliced, + // since it may have already been type painted + Type *elemtype = e1->type->nextOf(); + if (e1->op == TOKslice) + elemtype = ((SliceExp *)e1)->e1->type->nextOf(); + // Allow casts from X* to void *, and X** to void** for any X. + // But don't allow cast from X* to void**. + // So, we strip all matching * from source and target to find X. + // Allow casts to X* from void* only if the 'void' was originally an X; + // we check this later on. + Type *ultimatePointee = pointee; + Type *ultimateSrc = elemtype; + while (ultimatePointee->ty == Tpointer && ultimateSrc->ty == Tpointer) + { + ultimatePointee = ultimatePointee->nextOf(); + ultimateSrc = ultimateSrc->nextOf(); + } + if (ultimatePointee->ty != Tvoid && ultimateSrc->ty != Tvoid + && !isSafePointerCast(elemtype, pointee)) + { + error("reinterpreting cast from %s* to %s* is not supported in CTFE", + elemtype->toChars(), pointee->toChars()); + return EXP_CANT_INTERPRET; + } + if (ultimateSrc->ty == Tvoid) + castBackFromVoid = true; + } + + if (e1->op == TOKslice) + { + if ( ((SliceExp *)e1)->e1->op == TOKnull) + { + return paintTypeOntoLiteral(type, ((SliceExp *)e1)->e1); + } + e = new IndexExp(loc, ((SliceExp *)e1)->e1, ((SliceExp *)e1)->lwr); + e->type = type; + return e; + } + if (e1->op == TOKarrayliteral || e1->op == TOKstring) + { + e = new IndexExp(loc, e1, new IntegerExp(loc, 0, Type::tsize_t)); + e->type = type; + return e; + } + if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type != e1->type) + { // type painting operation + IndexExp *ie = (IndexExp *)e1; + e = new IndexExp(e1->loc, ie->e1, ie->e2); + if (castBackFromVoid) + { + // get the original type. For strings, it's just the type... + Type *origType = ie->e1->type->nextOf(); + // ..but for arrays of type void*, it's the type of the element + Expression *xx = NULL; + if (ie->e1->op == TOKarrayliteral && ie->e2->op == TOKint64) + { ArrayLiteralExp *ale = (ArrayLiteralExp *)ie->e1; + uinteger_t indx = ie->e2->toInteger(); + if (indx < ale->elements->dim) + xx = ale->elements->tdata()[indx]; + } + if (xx && xx->op == TOKindex) + origType = ((IndexExp *)xx)->e1->type->nextOf(); + else if (xx && xx->op == TOKaddress) + origType= ((AddrExp *)xx)->e1->type; + else if (xx && xx->op == TOKvar) + origType = ((VarExp *)xx)->var->type; + if (!isSafePointerCast(origType, pointee)) + { + error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", + origType->toChars(), pointee->toChars()); + return EXP_CANT_INTERPRET; + } + } + e->type = type; + return e; + } + if (e1->op == TOKaddress) + { + Type *origType = ((AddrExp *)e1)->type; + if (isSafePointerCast(origType, pointee)) + { + e = new AddrExp(loc, ((AddrExp *)e1)->e1); + e->type = type; + return e; + } + } + if (e1->op == TOKvar) + { // type painting operation + Type *origType = ((VarExp *)e1)->var->type; + if (castBackFromVoid && !isSafePointerCast(origType, pointee)) + { + error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", + origType->toChars(), pointee->toChars()); + return EXP_CANT_INTERPRET; + } + e = new VarExp(loc, ((VarExp *)e1)->var); + e->type = type; + return e; + } + error("pointer cast from %s to %s is not supported at compile time", + e1->type->toChars(), to->toChars()); + return EXP_CANT_INTERPRET; + } + if (to->ty == Tarray && e1->op == TOKslice) + { + e1 = new SliceExp(e1->loc, ((SliceExp *)e1)->e1, ((SliceExp *)e1)->lwr, + ((SliceExp *)e1)->upr); + e1->type = to; + return e1; + } + // Disallow array type painting, except for conversions between built-in + // types of identical size. + if ((to->ty == Tsarray || to->ty == Tarray) && + (e1->type->ty == Tsarray || e1->type->ty == Tarray) && + !isSafePointerCast(e1->type->nextOf(), to->nextOf()) ) + { + error("array cast from %s to %s is not supported at compile time", e1->type->toChars(), to->toChars()); + return EXP_CANT_INTERPRET; + } + if (to->ty == Tsarray && e1->op == TOKslice) + e1 = resolveSlice(e1); + if (to->toBasetype()->ty == Tbool && e1->type->ty==Tpointer) + { + return new IntegerExp(loc, e1->op != TOKnull, to); + } + return ctfeCast(loc, type, to, e1); +} + +Expression *AssertExp::interpret(InterState *istate, CtfeGoal goal) +{ Expression *e; + Expression *e1; + +#if LOG + printf("AssertExp::interpret() %s\n", toChars()); +#endif +#if DMDV2 + e1 = this->e1->interpret(istate); +#else + // Deal with pointers (including compiler-inserted assert(&this, "null this")) + if ( isPointer(this->e1->type) ) + { + e1 = this->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + return e1; + if (e1->op != TOKnull) + return new IntegerExp(loc, 1, Type::tbool); + } + else + e1 = this->e1->interpret(istate); +#endif + if (exceptionOrCantInterpret(e1)) + return e1; + if (isTrueBool(e1)) + { + } + else if (e1->isBool(FALSE)) + { + if (msg) + { + e = msg->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + error("%s", e->toChars()); + } + else + error("%s failed", toChars()); + goto Lcant; + } + else + { + error("%s is not a compile-time boolean expression", e1->toChars()); + goto Lcant; + } + return e1; + +Lcant: + return EXP_CANT_INTERPRET; +} + +Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) +{ Expression *e = EXP_CANT_INTERPRET; + +#if LOG + printf("PtrExp::interpret() %s\n", toChars()); +#endif + // Constant fold *(&structliteral + offset) + if (e1->op == TOKadd) + { AddExp *ae = (AddExp *)e1; + if (ae->e1->op == TOKaddress && ae->e2->op == TOKint64) + { AddrExp *ade = (AddrExp *)ae->e1; + Expression *ex = ade->e1; + ex = ex->interpret(istate); + if (exceptionOrCantInterpret(ex)) + return ex; + if (ex->op == TOKstructliteral) + { StructLiteralExp *se = (StructLiteralExp *)ex; + dinteger_t offset = ae->e2->toInteger(); + e = se->getField(type, offset); + if (!e) + e = EXP_CANT_INTERPRET; + return e; + } + } + e = Ptr(type, e1); + } +#if DMDV2 +#else // this is required for D1, where structs return *this instead of 'this'. + else if (e1->op == TOKthis) + { + if(istate->localThis) + return istate->localThis->interpret(istate); + } +#endif + else + { // It's possible we have an array bounds error. We need to make sure it + // errors with this line number, not the one where the pointer was set. + e = e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e)) + return e; + if (!(e->op == TOKvar || e->op == TOKdotvar || e->op == TOKindex + || e->op == TOKslice || e->op == TOKaddress)) + { + error("dereference of invalid pointer '%s'", e->toChars()); + return EXP_CANT_INTERPRET; + } + if (goal != ctfeNeedLvalue) + { + if (e->op == TOKindex && e->type->ty == Tpointer) + { + IndexExp *ie = (IndexExp *)e; + // Is this a real index to an array of pointers, or just a CTFE pointer? + // If the index has the same levels of indirection, it's an index + int srcLevels = 0; + int destLevels = 0; + for(Type *xx = ie->e1->type; xx->ty == Tpointer; xx = xx->nextOf()) + ++srcLevels; + for(Type *xx = e->type->nextOf(); xx->ty == Tpointer; xx = xx->nextOf()) + ++destLevels; + bool isGenuineIndex = (srcLevels == destLevels); + + if ((ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring) + && ie->e2->op == TOKint64) + { + Expression *dollar = ArrayLength(Type::tsize_t, ie->e1); + dinteger_t len = dollar->toInteger(); + dinteger_t indx = ie->e2->toInteger(); + assert(indx >=0 && indx <= len); // invalid pointer + if (indx == len) + { + error("dereference of pointer %s one past end of memory block limits [0..%jd]", + toChars(), len); + return EXP_CANT_INTERPRET; + } + e = ctfeIndex(loc, type, ie->e1, indx); + if (isGenuineIndex) + { + if (e->op == TOKindex) + e = e->interpret(istate, goal); + else if (e->op == TOKaddress) + e = paintTypeOntoLiteral(type, ((AddrExp *)e)->e1); + } + return e; + } + if (ie->e1->op == TOKassocarrayliteral) + { + e = Index(type, ie->e1, ie->e2); + if (isGenuineIndex) + { + if (e->op == TOKindex) + e = e->interpret(istate, goal); + else if (e->op == TOKaddress) + e = paintTypeOntoLiteral(type, ((AddrExp *)e)->e1); + } + return e; + } + } + if (e->op == TOKstructliteral) + return e; + e = e1->interpret(istate, goal); + if (e->op == TOKaddress) + { + e = ((AddrExp*)e)->e1; + if (e->op == TOKdotvar || e->op == TOKindex) + e = e->interpret(istate, goal); + } + else if (e->op == TOKvar) + { + e = e->interpret(istate, goal); + } + if (exceptionOrCantInterpret(e)) + return e; + } + else if (e->op == TOKaddress) + e = ((AddrExp*)e)->e1; // *(&x) ==> x + if (e->op == TOKnull) + { + error("dereference of null pointer '%s'", e1->toChars()); + return EXP_CANT_INTERPRET; + } + e->type = type; + } + +#if LOG + if (e == EXP_CANT_INTERPRET) + printf("PtrExp::interpret() %s = EXP_CANT_INTERPRET\n", toChars()); +#endif + return e; +} + +Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) +{ Expression *e = EXP_CANT_INTERPRET; + +#if LOG + printf("DotVarExp::interpret() %s\n", toChars()); +#endif + + Expression *ex = e1->interpret(istate); + if (exceptionOrCantInterpret(ex)) + return ex; + if (ex != EXP_CANT_INTERPRET) + { + #if DMDV2 + // Special case for template AAs: AA.var returns the AA itself. + // ie AA.p ----> AA. This is a hack, to get around the + // corresponding hack in the AA druntime implementation. + if (isAssocArray(ex->type)) + return ex; + #endif + if (ex->op == TOKaddress) + ex = ((AddrExp *)ex)->e1; + VarDeclaration *v = var->isVarDeclaration(); + if (!v) + error("CTFE internal error: %s", toChars()); + if (ex->op == TOKnull && ex->type->toBasetype()->ty == Tclass) + { error("class '%s' is null and cannot be dereferenced", e1->toChars()); + return EXP_CANT_INTERPRET; + } + if (ex->op == TOKstructliteral || ex->op == TOKclassreference) + { + StructLiteralExp *se = ex->op == TOKclassreference ? ((ClassReferenceExp *)ex)->value : (StructLiteralExp *)ex; + // We can't use getField, because it makes a copy + int i = -1; + if (ex->op == TOKclassreference) + i = ((ClassReferenceExp *)ex)->findFieldIndexByName(v); + else + i = findFieldIndexByName(se->sd, v); + if (i == -1) + { + error("couldn't find field %s of type %s in %s", v->toChars(), type->toChars(), se->toChars()); + return EXP_CANT_INTERPRET; + } + e = se->elements->tdata()[i]; + if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) + { + // If it is an lvalue literal, return it... + if (e->op == TOKstructliteral) + return e; + if ((type->ty == Tsarray || goal == ctfeNeedLvalue) && ( + e->op == TOKarrayliteral || + e->op == TOKassocarrayliteral || e->op == TOKstring || + e->op == TOKclassreference || e->op == TOKslice)) + return e; + /* Element is an allocated pointer, which was created in + * CastExp. + */ + if (goal == ctfeNeedLvalue && e->op == TOKindex && + e->type == type && + isPointer(type) ) + return e; + // ...Otherwise, just return the (simplified) dotvar expression + e = new DotVarExp(loc, ex, v); + e->type = type; + return e; + } + if (!e) + { + error("couldn't find field %s in %s", v->toChars(), type->toChars()); + e = EXP_CANT_INTERPRET; + } + // If it is an rvalue literal, return it... + if (e->op == TOKstructliteral || e->op == TOKarrayliteral || + e->op == TOKassocarrayliteral || e->op == TOKstring) + return e; + if ( isPointer(type) ) + { + return paintTypeOntoLiteral(type, e); + } + if (e->op == TOKvar) + { // Don't typepaint twice, since that might cause an erroneous copy + e = getVarExp(loc, istate, ((VarExp *)e)->var, goal); + if (e != EXP_CANT_INTERPRET && e->op != TOKthrownexception) + e = paintTypeOntoLiteral(type, e); + return e; + } + return e->interpret(istate, goal); + } + else + error("%s.%s is not yet implemented at compile time", e1->toChars(), var->toChars()); + } + +#if LOG + if (e == EXP_CANT_INTERPRET) + printf("DotVarExp::interpret() %s = EXP_CANT_INTERPRET\n", toChars()); +#endif + return e; +} + +Expression *RemoveExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("RemoveExp::interpret() %s\n", toChars()); +#endif + Expression *agg = e1->interpret(istate); + if (exceptionOrCantInterpret(agg)) + return agg; + Expression *index = e2->interpret(istate); + if (exceptionOrCantInterpret(index)) + return index; + if (agg->op == TOKnull) + return EXP_VOID_INTERPRET; + assert(agg->op == TOKassocarrayliteral); + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)agg; + Expressions *keysx = aae->keys; + Expressions *valuesx = aae->values; + size_t removed = 0; + for (size_t j = 0; j < valuesx->dim; ++j) + { Expression *ekey = keysx->tdata()[j]; + Expression *ex = ctfeEqual(TOKequal, Type::tbool, ekey, index); + if (exceptionOrCantInterpret(ex)) + return ex; + if (ex->isBool(TRUE)) + ++removed; + else if (removed != 0) + { keysx->tdata()[j - removed] = ekey; + valuesx->tdata()[j - removed] = valuesx->tdata()[j]; + } + } + valuesx->dim = valuesx->dim - removed; + keysx->dim = keysx->dim - removed; + return new IntegerExp(loc, removed?1:0, Type::tbool); +} + + +/******************************* Special Functions ***************************/ + +Expression *interpret_length(InterState *istate, Expression *earg) +{ + //printf("interpret_length()\n"); + earg = earg->interpret(istate); + if (earg == EXP_CANT_INTERPRET) + return NULL; + if (exceptionOrCantInterpret(earg)) + return earg; + dinteger_t len = 0; + if (earg->op == TOKassocarrayliteral) + len = ((AssocArrayLiteralExp *)earg)->keys->dim; + else assert(earg->op == TOKnull); + Expression *e = new IntegerExp(earg->loc, len, Type::tsize_t); + return e; +} + +Expression *interpret_keys(InterState *istate, Expression *earg, Type *elemType) +{ +#if LOG + printf("interpret_keys()\n"); +#endif + earg = earg->interpret(istate); + if (earg == EXP_CANT_INTERPRET) + return NULL; + if (exceptionOrCantInterpret(earg)) + return earg; + if (earg->op == TOKnull) + return new NullExp(earg->loc); + if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) + return NULL; + assert(earg->op == TOKassocarrayliteral); + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; + ArrayLiteralExp *ae = new ArrayLiteralExp(aae->loc, aae->keys); + ae->ownedByCtfe = aae->ownedByCtfe; + ae->type = new TypeSArray(elemType, new IntegerExp(aae->keys->dim)); + return copyLiteral(ae); +} + +Expression *interpret_values(InterState *istate, Expression *earg, Type *elemType) +{ +#if LOG + printf("interpret_values()\n"); +#endif + earg = earg->interpret(istate); + if (earg == EXP_CANT_INTERPRET) + return NULL; + if (exceptionOrCantInterpret(earg)) + return earg; + if (earg->op == TOKnull) + return new NullExp(earg->loc); + if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) + return NULL; + assert(earg->op == TOKassocarrayliteral); + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; + ArrayLiteralExp *ae = new ArrayLiteralExp(aae->loc, aae->values); + ae->ownedByCtfe = aae->ownedByCtfe; + ae->type = new TypeSArray(elemType, new IntegerExp(aae->values->dim)); + //printf("result is %s\n", e->toChars()); + return copyLiteral(ae); +} + +// signature is int delegate(ref Value) OR int delegate(ref Key, ref Value) +Expression *interpret_aaApply(InterState *istate, Expression *aa, Expression *deleg) +{ aa = aa->interpret(istate); + if (exceptionOrCantInterpret(aa)) + return aa; + if (aa->op != TOKassocarrayliteral) + return new IntegerExp(deleg->loc, 0, Type::tsize_t); + + FuncDeclaration *fd = NULL; + Expression *pthis = NULL; + if (deleg->op == TOKdelegate) + { + fd = ((DelegateExp *)deleg)->func; + pthis = ((DelegateExp *)deleg)->e1; + } + else if (deleg->op == TOKfunction) + fd = ((FuncExp*)deleg)->fd; + + assert(fd && fd->fbody); + assert(fd->parameters); + int numParams = fd->parameters->dim; + assert(numParams == 1 || numParams==2); + + Type *valueType = fd->parameters->tdata()[numParams-1]->type; + Type *keyType = numParams == 2 ? fd->parameters->tdata()[0]->type + : Type::tsize_t; + Expressions args; + args.setDim(numParams); + + AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)aa; + if (!ae->keys || ae->keys->dim == 0) + return new IntegerExp(deleg->loc, 0, Type::tsize_t); + Expression *eresult; + + for (size_t i = 0; i < ae->keys->dim; ++i) + { + Expression *ekey = ae->keys->tdata()[i]; + Expression *evalue = ae->values->tdata()[i]; + args.tdata()[numParams - 1] = evalue; + if (numParams == 2) args.tdata()[0] = ekey; + + eresult = fd->interpret(istate, &args, pthis); + if (exceptionOrCantInterpret(eresult)) + return eresult; + + assert(eresult->op == TOKint64); + if (((IntegerExp *)eresult)->value != 0) + return eresult; + } + return eresult; +} + +// Helper function: given a function of type A[] f(...), +// return A. +Type *returnedArrayElementType(FuncDeclaration *fd) +{ + assert(fd->type->ty == Tfunction); + assert(fd->type->nextOf()->ty == Tarray); + return ((TypeFunction *)fd->type)->nextOf()->nextOf(); +} + +/* Decoding UTF strings for foreach loops. Duplicates the functionality of + * the twelve _aApplyXXn functions in aApply.d in the runtime. + */ +Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *deleg, bool rvs) +{ +#if LOG + printf("foreachApplyUtf(%s, %s)\n", str->toChars(), deleg->toChars()); +#endif + FuncDeclaration *fd = NULL; + Expression *pthis = NULL; + if (deleg->op == TOKdelegate) + { + fd = ((DelegateExp *)deleg)->func; + pthis = ((DelegateExp *)deleg)->e1; + } + else if (deleg->op == TOKfunction) + fd = ((FuncExp*)deleg)->fd; + + assert(fd && fd->fbody); + assert(fd->parameters); + int numParams = fd->parameters->dim; + assert(numParams == 1 || numParams==2); + Type *charType = fd->parameters->tdata()[numParams-1]->type; + Type *indexType = numParams == 2 ? fd->parameters->tdata()[0]->type + : Type::tsize_t; + uinteger_t len = resolveArrayLength(str); + if (len == 0) + return new IntegerExp(deleg->loc, 0, indexType); + + if (str->op == TOKslice) + str = resolveSlice(str); + + StringExp *se = NULL; + ArrayLiteralExp *ale = NULL; + if (str->op == TOKstring) + se = (StringExp *) str; + else if (str->op == TOKarrayliteral) + ale = (ArrayLiteralExp *)str; + else + { error("CTFE internal error: cannot foreach %s", str->toChars()); + return EXP_CANT_INTERPRET; + } + Expressions args; + args.setDim(numParams); + + Expression *eresult; + + // Buffers for encoding; also used for decoding array literals + unsigned char utf8buf[4]; + unsigned short utf16buf[2]; + + size_t start = rvs ? len : 0; + size_t end = rvs ? 0: len; + for (size_t indx = start; indx != end;) + { + // Step 1: Decode the next dchar from the string. + + const char *errmsg = NULL; // Used for reporting decoding errors + dchar_t rawvalue; // Holds the decoded dchar + size_t currentIndex = indx; // The index of the decoded character + + if (ale) + { // If it is an array literal, copy the code points into the buffer + int buflen = 1; // #code points in the buffer + size_t n = 1; // #code points in this char + size_t sz = ale->type->nextOf()->size(); + + switch(sz) + { + case 1: + if (rvs) + { // find the start of the string + --indx; + buflen = 1; + while (indx > 0 && buflen < 4) + { Expression * r = ale->elements->tdata()[indx]; + assert(r->op == TOKint64); + unsigned char x = (unsigned char)(((IntegerExp *)r)->value); + if ( (x & 0xC0) != 0x80) + break; + ++buflen; + } + } + else + buflen = (indx + 4 > len) ? len - indx : 4; + for (int i=0; i < buflen; ++i) + { + Expression * r = ale->elements->tdata()[indx + i]; + assert(r->op == TOKint64); + utf8buf[i] = (unsigned char)(((IntegerExp *)r)->value); + } + n = 0; + errmsg = utf_decodeChar(&utf8buf[0], buflen, &n, &rawvalue); + break; + case 2: + if (rvs) + { // find the start of the string + --indx; + buflen = 1; + Expression * r = ale->elements->tdata()[indx]; + assert(r->op == TOKint64); + unsigned short x = (unsigned short)(((IntegerExp *)r)->value); + if (indx > 0 && x >= 0xDC00 && x <= 0xDFFF) + { + --indx; + ++buflen; + } + } + else + buflen = (indx + 2 > len) ? len - indx : 2; + for (int i=0; i < buflen; ++i) + { + Expression * r = ale->elements->tdata()[indx + i]; + assert(r->op == TOKint64); + utf16buf[i] = (unsigned short)(((IntegerExp *)r)->value); + } + n = 0; + errmsg = utf_decodeWchar(&utf16buf[0], buflen, &n, &rawvalue); + break; + case 4: + { + if (rvs) + --indx; + + Expression * r = ale->elements->tdata()[indx]; + assert(r->op == TOKint64); + rawvalue = ((IntegerExp *)r)->value; + n = 1; + } + break; + default: + assert(0); + } + if (!rvs) + indx += n; + } + else + { // String literals + size_t saveindx; // used for reverse iteration + + switch (se->sz) + { + case 1: + if (rvs) + { // find the start of the string + unsigned char *s = (unsigned char *)se->string; + --indx; + while (indx > 0 && ((s[indx]&0xC0)==0x80)) + --indx; + saveindx = indx; + } + errmsg = utf_decodeChar((unsigned char *)se->string, se->len, &indx, &rawvalue); + if (rvs) + indx = saveindx; + break; + case 2: + if (rvs) + { // find the start + unsigned short *s = (unsigned short *)se->string; + --indx; + if (s[indx] >= 0xDC00 && s[indx]<= 0xDFFF) + --indx; + saveindx = indx; + } + errmsg = utf_decodeWchar((unsigned short *)se->string, se->len, &indx, &rawvalue); + if (rvs) + indx = saveindx; + break; + case 4: + if (rvs) + --indx; + rawvalue = ((unsigned *)(se->string))[indx]; + if (!rvs) + ++indx; + break; + default: + assert(0); + } + } + if (errmsg) + { deleg->error("%s", errmsg); + return EXP_CANT_INTERPRET; + } + + // Step 2: encode the dchar in the target encoding + + int charlen = 1; // How many codepoints are involved? + switch(charType->size()) + { + case 1: + charlen = utf_codeLengthChar(rawvalue); + utf_encodeChar(&utf8buf[0], rawvalue); + break; + case 2: + charlen = utf_codeLengthWchar(rawvalue); + utf_encodeWchar(&utf16buf[0], rawvalue); + break; + case 4: + break; + default: + assert(0); + } + if (rvs) + currentIndex = indx; + + // Step 3: call the delegate once for each code point + + // The index only needs to be set once + if (numParams == 2) + args.tdata()[0] = new IntegerExp(deleg->loc, currentIndex, indexType); + + Expression *val = NULL; + + for (int k= 0; k < charlen; ++k) + { + dchar_t codepoint; + switch(charType->size()) + { + case 1: + codepoint = utf8buf[k]; + break; + case 2: + codepoint = utf16buf[k]; + break; + case 4: + codepoint = rawvalue; + break; + default: + assert(0); + } + val = new IntegerExp(str->loc, codepoint, charType); + + args.tdata()[numParams - 1] = val; + + eresult = fd->interpret(istate, &args, pthis); + if (exceptionOrCantInterpret(eresult)) + return eresult; + assert(eresult->op == TOKint64); + if (((IntegerExp *)eresult)->value != 0) + return eresult; + } + } + return eresult; +} + +/* If this is a built-in function, return the interpreted result, + * Otherwise, return NULL. + */ +Expression *evaluateIfBuiltin(InterState *istate, Loc loc, + FuncDeclaration *fd, Expressions *arguments, Expression *pthis) +{ + Expression *e = NULL; + int nargs = arguments ? arguments->dim : 0; +#if DMDV2 + if (pthis && isAssocArray(pthis->type)) + { + if (fd->ident == Id::length && nargs==0) + return interpret_length(istate, pthis); + else if (fd->ident == Id::keys && nargs==0) + return interpret_keys(istate, pthis, returnedArrayElementType(fd)); + else if (fd->ident == Id::values && nargs==0) + return interpret_values(istate, pthis, returnedArrayElementType(fd)); + else if (fd->ident == Id::rehash && nargs==0) + return pthis->interpret(istate, ctfeNeedLvalue); // rehash is a no-op + } + if (!pthis) + { + enum BUILTIN b = fd->isBuiltin(); + if (b) + { Expressions args; + args.setDim(nargs); + for (size_t i = 0; i < args.dim; i++) + { + Expression *earg = arguments->tdata()[i]; + earg = earg->interpret(istate); + if (exceptionOrCantInterpret(earg)) + return earg; + args.tdata()[i] = earg; + } + e = eval_builtin(loc, b, &args); + if (!e) + e = EXP_CANT_INTERPRET; + } + } + /* Horrid hack to retrieve the builtin AA functions after they've been + * mashed by the inliner. + */ + if (!pthis) + { + Expression *firstarg = nargs > 0 ? (Expression *)(arguments->data[0]) : NULL; + // Check for the first parameter being a templatized AA. Hack: we assume that + // template AA.var is always the AA data itself. + Expression *firstdotvar = (firstarg && firstarg->op == TOKdotvar) + ? ((DotVarExp *)firstarg)->e1 : NULL; + if (nargs==3 && isAssocArray(firstarg->type) && !strcmp(fd->ident->string, "_aaApply")) + return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); + if (nargs==3 && isAssocArray(firstarg->type) &&!strcmp(fd->ident->string, "_aaApply2")) + return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); + if (firstdotvar && isAssocArray(firstdotvar->type)) + { if (fd->ident == Id::aaLen && nargs == 1) + return interpret_length(istate, firstdotvar->interpret(istate)); + else if (fd->ident == Id::aaKeys && nargs == 2) + { + Expression *trueAA = firstdotvar->interpret(istate); + return interpret_keys(istate, trueAA, toBuiltinAAType(trueAA->type)->index); + } + else if (fd->ident == Id::aaValues && nargs == 3) + { + Expression *trueAA = firstdotvar->interpret(istate); + return interpret_values(istate, trueAA, toBuiltinAAType(trueAA->type)->nextOf()); + } + else if (fd->ident == Id::aaRehash && nargs == 2) + { + return firstdotvar->interpret(istate, ctfeNeedLvalue); + } + } + } +#endif +#if DMDV1 + if (!pthis) + { + Expression *firstarg = nargs > 0 ? (Expression *)(arguments->data[0]) : NULL; + if (firstarg && firstarg->type->toBasetype()->ty == Taarray) + { + TypeAArray *firstAAtype = (TypeAArray *)firstarg->type; + if (fd->ident == Id::aaLen && nargs == 1) + return interpret_length(istate, firstarg); + else if (fd->ident == Id::aaKeys) + return interpret_keys(istate, firstarg, firstAAtype->index); + else if (fd->ident == Id::aaValues) + return interpret_values(istate, firstarg, firstAAtype->nextOf()); + else if (nargs==2 && fd->ident == Id::aaRehash) + return firstarg->interpret(istate, ctfeNeedLvalue); //no-op + else if (nargs==3 && !strcmp(fd->ident->string, "_aaApply")) + return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); + else if (nargs==3 && !strcmp(fd->ident->string, "_aaApply2")) + return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); + } + } +#endif +#if DMDV2 + if (pthis && !fd->fbody && fd->isCtorDeclaration() && fd->parent && fd->parent->parent && fd->parent->parent->ident == Id::object) + { + if (pthis->op == TOKclassreference && fd->parent->ident == Id::Throwable) + { // At present, the constructors just copy their arguments into the struct. + // But we might need some magic if stack tracing gets added to druntime. + StructLiteralExp *se = ((ClassReferenceExp *)pthis)->value; + assert(arguments->dim <= se->elements->dim); + for (int i = 0; i < arguments->dim; ++i) + { + Expression *e = arguments->tdata()[i]->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + se->elements->tdata()[i] = e; + } + return EXP_VOID_INTERPRET; + } + } +#endif + if (nargs == 1 && !pthis && + (fd->ident == Id::criticalenter || fd->ident == Id::criticalexit)) + { // Support synchronized{} as a no-op + return EXP_VOID_INTERPRET; + } + if (!pthis) + { + size_t idlen = strlen(fd->ident->string); + if (nargs == 2 && (idlen == 10 || idlen == 11) + && !strncmp(fd->ident->string, "_aApply", 7)) + { // Functions from aApply.d and aApplyR.d in the runtime + bool rvs = (idlen == 11); // true if foreach_reverse + char c = fd->ident->string[idlen-3]; // char width: 'c', 'w', or 'd' + char s = fd->ident->string[idlen-2]; // string width: 'c', 'w', or 'd' + char n = fd->ident->string[idlen-1]; // numParams: 1 or 2. + // There are 12 combinations + if ( (n == '1' || n == '2') && + (c == 'c' || c == 'w' || c == 'd') && + (s == 'c' || s == 'w' || s == 'd') && c != s) + { Expression *str = arguments->tdata()[0]; + str = str->interpret(istate); + if (exceptionOrCantInterpret(str)) + return str; + return foreachApplyUtf(istate, str, arguments->tdata()[1], rvs); + } + } + } + return e; +} + +/*************************** CTFE Sanity Checks ***************************/ + +/* Setter functions for CTFE variable values. + * These functions exist to check for compiler CTFE bugs. + */ + +bool isCtfeValueValid(Expression *newval) +{ + if ( +#if DMDV2 + newval->type->ty == Tnull || +#endif + isPointer(newval->type) ) + { + if (newval->op == TOKaddress || newval->op == TOKnull || + newval->op == TOKstring) + return true; + if (newval->op == TOKindex) + { + Expression *g = ((IndexExp *)newval)->e1; + if (g->op == TOKarrayliteral || g->op == TOKstring || + g->op == TOKassocarrayliteral) + return true; + } + if (newval->op == TOKvar) + return true; + if (newval->type->nextOf()->ty == Tarray && newval->op == TOKslice) + return true; + if (newval->op == TOKint64) + return true; // Result of a cast, but cannot be dereferenced + // else it must be a reference + } + if (newval->op == TOKclassreference || (newval->op == TOKnull && newval->type->ty == Tclass)) + return true; + if (newval->op == TOKvar) + { + VarExp *ve = (VarExp *)newval; + VarDeclaration *vv = ve->var->isVarDeclaration(); + // Must not be a reference to a reference + if (!(vv && vv->getValue() && vv->getValue()->op == TOKvar)) + return true; + } + if (newval->op == TOKdotvar) + { + if (((DotVarExp *)newval)->e1->op == TOKstructliteral) + { + assert(((StructLiteralExp *)((DotVarExp *)newval)->e1)->ownedByCtfe); + return true; + } + } + if (newval->op == TOKindex) + { + IndexExp *ie = (IndexExp *)newval; + if (ie->e2->op == TOKint64) + { + if (ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring) + return true; + } + if (ie->e1->op == TOKassocarrayliteral) + return true; + // BUG: Happens ONLY in ref foreach. Should tighten this. + if (ie->e2->op == TOKvar) + return true; + } + if (newval->op == TOKfunction) return true; // function/delegate literal + if (newval->op == TOKdelegate) return true; + if (newval->op == TOKsymoff) // function pointer + { + if (((SymOffExp *)newval)->var->isFuncDeclaration()) + return true; + } + if (newval->op == TOKint64 || newval->op == TOKfloat64 || + newval->op == TOKchar || newval->op == TOKcomplex80) + return true; + + // References + + if (newval->op == TOKstructliteral) + assert(((StructLiteralExp *)newval)->ownedByCtfe); + if (newval->op == TOKarrayliteral) + assert(((ArrayLiteralExp *)newval)->ownedByCtfe); + if (newval->op == TOKassocarrayliteral) + assert(((AssocArrayLiteralExp *)newval)->ownedByCtfe); + + if ((newval->op ==TOKarrayliteral) || ( newval->op==TOKstructliteral) || + (newval->op==TOKstring) || (newval->op == TOKassocarrayliteral) || + (newval->op == TOKnull)) + { return true; + } + // Dynamic arrays passed by ref may be null. When this happens + // they may originate from an index or dotvar expression. + if (newval->type->ty == Tarray || newval->type->ty == Taarray) + if (newval->op == TOKdotvar || newval->op == TOKindex) + return true; // actually must be null + + if (newval->op == TOKslice) + { + SliceExp *se = (SliceExp *)newval; + assert(se->lwr && se->lwr != EXP_CANT_INTERPRET && se->lwr->op == TOKint64); + assert(se->upr && se->upr != EXP_CANT_INTERPRET && se->upr->op == TOKint64); + assert(se->e1->op == TOKarrayliteral || se->e1->op == TOKstring); + if (se->e1->op == TOKarrayliteral) + assert(((ArrayLiteralExp *)se->e1)->ownedByCtfe); + return true; + } + newval->error("CTFE internal error: illegal value %s\n", newval->toChars()); + return false; +} + +bool VarDeclaration::hasValue() +{ + if (ctfeAdrOnStack == (size_t)-1) + return false; + return NULL != getValue(); +} + +Expression *VarDeclaration::getValue() +{ + return ctfeStack.getValue(this); +} + +void VarDeclaration::setValueNull() +{ + ctfeStack.setValue(this, NULL); +} + +// Don't check for validity +void VarDeclaration::setValueWithoutChecking(Expression *newval) +{ + ctfeStack.setValue(this, newval); +} + +void VarDeclaration::setValue(Expression *newval) +{ + assert(isCtfeValueValid(newval)); + ctfeStack.setValue(this, newval); +} diff --git a/intrange.c b/intrange.c new file mode 100644 index 00000000..baf099c0 --- /dev/null +++ b/intrange.c @@ -0,0 +1,1105 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by KennyTM +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include "intrange.h" +#include "mars.h" +#include "mtype.h" +#include "expression.h" + +#ifndef PERFORM_UNITTEST +#define PERFORM_UNITTEST 0 +#endif + +// Copy the sign to the value *x*. Equivalent to `sign ? -x : x`. +static uinteger_t copySign(uinteger_t x, bool sign) +{ + // return sign ? -x : x; + return (x - (uinteger_t)sign) ^ -(uinteger_t)sign; +} + +#ifndef UINT64_MAX +#define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL +#endif + +//==================== SignExtendedNumber ====================================== + +SignExtendedNumber SignExtendedNumber::fromInteger(uinteger_t value_) +{ + return SignExtendedNumber(value_, value_ >> 63); +} + +bool SignExtendedNumber::operator==(const SignExtendedNumber& a) const +{ + return value == a.value && negative == a.negative; +} + +bool SignExtendedNumber::operator<(const SignExtendedNumber& a) const +{ + return (negative && !a.negative) + || (negative == a.negative && value < a.value); +} + +SignExtendedNumber SignExtendedNumber::extreme(bool minimum) +{ + return SignExtendedNumber(minimum-1, minimum); +} + +SignExtendedNumber SignExtendedNumber::max() +{ + return SignExtendedNumber(UINT64_MAX, false); +} + +SignExtendedNumber SignExtendedNumber::operator-() const +{ + if (value == 0) + return SignExtendedNumber(-negative); + else + return SignExtendedNumber(-value, !negative); +} + +SignExtendedNumber SignExtendedNumber::operator+(const SignExtendedNumber& a) const +{ + uinteger_t sum = value + a.value; + bool carry = sum < value && sum < a.value; + if (negative != a.negative) + return SignExtendedNumber(sum, !carry); + else if (negative) + return SignExtendedNumber(carry ? sum : 0, true); + else + return SignExtendedNumber(carry ? UINT64_MAX : sum, false); +} + +SignExtendedNumber SignExtendedNumber::operator-(const SignExtendedNumber& a) const +{ + if (a.isMinimum()) + return negative ? SignExtendedNumber(value, false) : max(); + else + return *this + (-a); +} + + +SignExtendedNumber SignExtendedNumber::operator*(const SignExtendedNumber& a) const +{ + // perform *saturated* multiplication, otherwise we may get bogus ranges + // like 0x10 * 0x10 == 0x100 == 0. + + /* Special handling for zeros: + INT65_MIN * 0 = 0 + INT65_MIN * + = INT65_MIN + INT65_MIN * - = INT65_MAX + 0 * anything = 0 + */ + if (value == 0) + { + if (!negative) + return *this; + else if (a.negative) + return max(); + else + return a.value == 0 ? a : *this; + } + else if (a.value == 0) + return a * *this; // don't duplicate the symmetric case. + + SignExtendedNumber rv; + // these are != 0 now surely. + uinteger_t tAbs = copySign(value, negative); + uinteger_t aAbs = copySign(a.value, a.negative); + rv.negative = negative != a.negative; + if (UINT64_MAX / tAbs < aAbs) + rv.value = rv.negative-1; + else + rv.value = copySign(tAbs * aAbs, rv.negative); + return rv; +} + +SignExtendedNumber SignExtendedNumber::operator/(const SignExtendedNumber& a) const +{ + /* special handling for zeros: + INT65_MIN / INT65_MIN = 1 + anything / INT65_MIN = 0 + + / 0 = INT65_MAX (eh?) + - / 0 = INT65_MIN (eh?) + */ + if (a.value == 0) + { + if (a.negative) + return SignExtendedNumber(value == 0 && negative); + else + return extreme(negative); + } + + uinteger_t aAbs = copySign(a.value, a.negative); + uinteger_t rvVal; + + if (!isMinimum()) + rvVal = copySign(value, negative) / aAbs; + // Special handling for INT65_MIN + // if the denominator is not a power of 2, it is same as UINT64_MAX / x. + else if (aAbs & (aAbs-1)) + rvVal = UINT64_MAX / aAbs; + // otherwise, it's the same as reversing the bits of x. + else + { + if (aAbs == 1) + return extreme(!a.negative); + rvVal = 1ULL << 63; + aAbs >>= 1; + if (aAbs & 0xAAAAAAAAAAAAAAAAULL) rvVal >>= 1; + if (aAbs & 0xCCCCCCCCCCCCCCCCULL) rvVal >>= 2; + if (aAbs & 0xF0F0F0F0F0F0F0F0ULL) rvVal >>= 4; + if (aAbs & 0xFF00FF00FF00FF00ULL) rvVal >>= 8; + if (aAbs & 0xFFFF0000FFFF0000ULL) rvVal >>= 16; + if (aAbs & 0xFFFFFFFF00000000ULL) rvVal >>= 32; + } + bool rvNeg = negative != a.negative; + rvVal = copySign(rvVal, rvNeg); + + return SignExtendedNumber(rvVal, rvVal != 0 && rvNeg); +} + +SignExtendedNumber SignExtendedNumber::operator%(const SignExtendedNumber& a) const +{ + if (a.value == 0) + return !a.negative ? a : isMinimum() ? SignExtendedNumber(0) : *this; + + uinteger_t aAbs = copySign(a.value, a.negative); + uinteger_t rvVal; + + // a % b == sgn(a) * abs(a) % abs(b). + if (!isMinimum()) + rvVal = copySign(value, negative) % aAbs; + // Special handling for INT65_MIN + // if the denominator is not a power of 2, it is same as UINT64_MAX%x + 1. + else if (aAbs & (aAbs - 1)) + rvVal = UINT64_MAX % aAbs + 1; + // otherwise, the modulus is trivially zero. + else + rvVal = 0; + + rvVal = copySign(rvVal, negative); + return SignExtendedNumber(rvVal, rvVal != 0 && negative); +} + +SignExtendedNumber& SignExtendedNumber::operator++() +{ + if (value != UINT64_MAX) + ++ value; + else if (negative) + { + value = 0; + negative = false; + } + return *this; +} + +SignExtendedNumber SignExtendedNumber::operator<<(const SignExtendedNumber& a) const +{ + // assume left-shift the shift-amount is always unsigned. Thus negative + // shifts will give huge result. + if (value == 0) + return *this; + else if (a.negative) + return extreme(negative); + + uinteger_t v = copySign(value, negative); + + // compute base-2 log of 'v' to determine the maximum allowed bits to shift. + // Ref: http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog + + size_t r, s; + + r = (v > 0xFFFFFFFFULL) << 5; v >>= r; + s = (v > 0xFFFFULL ) << 4; v >>= s; r |= s; + s = (v > 0xFFULL ) << 3; v >>= s; r |= s; + s = (v > 0xFULL ) << 2; v >>= s; r |= s; + s = (v > 0x3ULL ) << 1; v >>= s; r |= s; + r |= (v >> 1); + + uinteger_t allowableShift = 63 - r; + if (a.value > allowableShift) + return extreme(negative); + else + return SignExtendedNumber(value << a.value, negative); +} + +SignExtendedNumber SignExtendedNumber::operator>>(const SignExtendedNumber& a) const +{ + if (a.negative || a.value > 64) + return negative ? SignExtendedNumber(-1, true) : SignExtendedNumber(0); + else if (isMinimum()) + return a.value == 0 ? *this : SignExtendedNumber(-1ULL << (64-a.value), true); + + uinteger_t x = value ^ -negative; + x >>= a.value; + return SignExtendedNumber(x ^ -negative, negative); +} + + +//==================== IntRange ================================================ + +IntRange IntRange::widest() +{ + return IntRange(SignExtendedNumber::min(), SignExtendedNumber::max()); +} + +#if !PERFORM_UNITTEST +IntRange IntRange::fromType(Type *type) +{ + return fromType(type, type->isunsigned()); +} + +IntRange IntRange::fromType(Type *type, bool isUnsigned) +{ + if (!type->isintegral()) + return widest(); + + uinteger_t mask = type->sizemask(); + SignExtendedNumber lower(0), upper(mask); + if (type->toBasetype()->ty == Tdchar) + upper.value = 0x10FFFFULL; + else if (!isUnsigned) + { + lower.value = ~(mask >> 1); + lower.negative = true; + upper.value = (mask >> 1); + } + return IntRange(lower, upper); +} +#endif + +IntRange IntRange::fromNumbers2(const SignExtendedNumber numbers[2]) +{ + if (numbers[0] < numbers[1]) + return IntRange(numbers[0], numbers[1]); + else + return IntRange(numbers[1], numbers[0]); +} +IntRange IntRange::fromNumbers4(const SignExtendedNumber numbers[4]) +{ + IntRange ab = fromNumbers2(numbers); + IntRange cd = fromNumbers2(numbers + 2); + if (cd.imin < ab.imin) + ab.imin = cd.imin; + if (cd.imax > ab.imax) + ab.imax = cd.imax; + return ab; +} + +bool IntRange::contains(const IntRange& a) const +{ + return imin <= a.imin && imax >= a.imax; +} + +bool IntRange::containsZero() const +{ + return (imin.negative && !imax.negative) + || (!imin.negative && imin.value == 0); +} + +IntRange& IntRange::castUnsigned(uinteger_t mask) +{ + // .... 0x1eff ] [0x1f00 .. 0x1fff] [0 .. 0xff] [0x100 .. 0x1ff] [0x200 .... + // + // regular unsigned type. We just need to see if ir steps across the + // boundary of validRange. If yes, ir will represent the whole validRange, + // otherwise, we just take the modulus. + // e.g. [0x105, 0x107] & 0xff == [5, 7] + // [0x105, 0x207] & 0xff == [0, 0xff] + uinteger_t minChunk = imin.value & ~mask; + uinteger_t maxChunk = imax.value & ~mask; + if (minChunk == maxChunk && imin.negative == imax.negative) + { + imin.value &= mask; + imax.value &= mask; + } + else + { + imin.value = 0; + imax.value = mask; + } + imin.negative = imax.negative = false; + return *this; +} + +IntRange& IntRange::castSigned(uinteger_t mask) +{ + // .... 0x1e7f ] [0x1e80 .. 0x1f7f] [0x1f80 .. 0x7f] [0x80 .. 0x17f] [0x180 .... + // + // regular signed type. We use a technique similar to the unsigned version, + // but the chunk has to be offset by 1/2 of the range. + uinteger_t halfChunkMask = mask >> 1; + uinteger_t minHalfChunk = imin.value & ~halfChunkMask; + uinteger_t maxHalfChunk = imax.value & ~halfChunkMask; + int minHalfChunkNegativity = imin.negative; // 1 = neg, 0 = nonneg, -1 = chunk containing ::max + int maxHalfChunkNegativity = imax.negative; + if (minHalfChunk & mask) + { + minHalfChunk += halfChunkMask+1; + if (minHalfChunk == 0) + -- minHalfChunkNegativity; + } + if (maxHalfChunk & mask) + { + maxHalfChunk += halfChunkMask+1; + if (maxHalfChunk == 0) + -- maxHalfChunkNegativity; + } + if (minHalfChunk == maxHalfChunk && minHalfChunkNegativity == maxHalfChunkNegativity) + { + imin.value &= mask; + imax.value &= mask; + // sign extend if necessary. + imin.negative = imin.value & ~halfChunkMask; + imax.negative = imax.value & ~halfChunkMask; + halfChunkMask += 1; + imin.value = (imin.value ^ halfChunkMask) - halfChunkMask; + imax.value = (imax.value ^ halfChunkMask) - halfChunkMask; + } + else + { + imin = SignExtendedNumber(~halfChunkMask, true); + imax = SignExtendedNumber(halfChunkMask, false); + } + return *this; +} + +IntRange& IntRange::castDchar() +{ + // special case for dchar. Casting to dchar means "I'll ignore all + // invalid characters." + castUnsigned(0xFFFFFFFFULL); + if (imin.value > 0x10FFFFULL) // ?? + imin.value = 0x10FFFFULL; // ?? + if (imax.value > 0x10FFFFULL) + imax.value = 0x10FFFFULL; + return *this; +} + +#if !PERFORM_UNITTEST +IntRange& IntRange::cast(Type *type) +{ + if (!type->isintegral()) + return *this; + else if (!type->isunsigned()) + return castSigned(type->sizemask()); + else if (type->toBasetype()->ty == Tdchar) + return castDchar(); + else + return castUnsigned(type->sizemask()); +} + +IntRange& IntRange::castUnsigned(Type *type) +{ + if (!type->isintegral()) + return castUnsigned(UINT64_MAX); + else if (type->toBasetype()->ty == Tdchar) + return castDchar(); + else + return castUnsigned(type->sizemask()); +} +#endif + +IntRange IntRange::absNeg() const +{ + if (imax.negative) + return *this; + else if (!imin.negative) + return IntRange(-imax, -imin); + else + { + SignExtendedNumber imaxAbsNeg = -imax; + return IntRange(imaxAbsNeg < imin ? imaxAbsNeg : imin, + SignExtendedNumber(0)); + } +} + +IntRange IntRange::unionWith(const IntRange& other) const +{ + return IntRange(imin < other.imin ? imin : other.imin, + imax > other.imax ? imax : other.imax); +} + +void IntRange::unionOrAssign(const IntRange& other, bool& union_) +{ + if (!union_ || imin > other.imin) + imin = other.imin; + if (!union_ || imax < other.imax) + imax = other.imax; + union_ = true; +} + +void IntRange::splitBySign(IntRange& negRange, bool& hasNegRange, + IntRange& nonNegRange, bool& hasNonNegRange) const +{ + hasNegRange = imin.negative; + if (hasNegRange) + { + negRange.imin = imin; + negRange.imax = imax.negative ? imax : SignExtendedNumber(-1, true); + } + hasNonNegRange = !imax.negative; + if (hasNonNegRange) + { + nonNegRange.imin = imin.negative ? SignExtendedNumber(0) : imin; + nonNegRange.imax = imax; + } +} + + +#if !PERFORM_UNITTEST +const IntRange& IntRange::dump(const char* funcName, Expression *e) const +{ + printf("[(%c)%#018llx, (%c)%#018llx] @ %s ::: %s\n", + imin.negative?'-':'+', (unsigned long long)imin.value, + imax.negative?'-':'+', (unsigned long long)imax.value, + funcName, e->toChars()); + return *this; +} +#endif + +//------------------------------------------------------------------------------ + +#if PERFORM_UNITTEST +#include +#include + +class AssertionError : public std::exception { +public: + AssertionError() : std::exception() {} +}; + +void _assertPred(uinteger_t x, uinteger_t y, int line) { + if (x != y) { + printf("Line %d: %#018llx != %#018llx\n", line, x, y); + throw AssertionError(); + } +} +void _assertPred(const SignExtendedNumber& x, const SignExtendedNumber& y, int line) { + if (x != y) { + printf("Line %d: (%c)%#018llx != (%c)%#018llx\n", line, + x.negative?'-':'+', x.value, + y.negative?'-':'+', y.value); + throw AssertionError(); + } +} +void _assertPred(bool x, bool y, int line) { + if (x != y) { + static const char* const names[] = {"false", "true"}; + printf("Line %d: %s != %s\n", line, names[x], names[y]); + throw AssertionError(); + } +} +#define assertPred(x, y) _assertPred(x, y, __LINE__) +#define RUN(testName) \ + try { \ + testName(); \ + } catch (const AssertionError&) { \ + printf("********" #testName " failed\n"); \ + } + +void testAssertSanity() { + int saneCount = 0; + + printf("Testing 'assert' sanity. You should see 3 assertion failures below\n"); + + assertPred(true, true); + try { + assertPred(true, false); + } catch (const AssertionError&) { + ++ saneCount; + } + + assertPred(4ULL, 4ULL); + try { + assertPred(3ULL, -3ULL); + } catch (const AssertionError&) { + ++ saneCount; + } + + assertPred(SignExtendedNumber(5, false), SignExtendedNumber(5, false)); + try { + assertPred(SignExtendedNumber(4, false), SignExtendedNumber(4, true)); + } catch (const AssertionError&) { + ++ saneCount; + } + + printf("--------------\n"); + + if (saneCount != 3) throw AssertionError(); +} + +void testNegation() { + SignExtendedNumber s (4); + SignExtendedNumber t = -s; + assertPred(t.value, -4ULL); + assertPred(t.negative, true); + + s = SignExtendedNumber::max(); + t = -s; + assertPred(t.value, 1); + assertPred(t.negative, true); + + s = SignExtendedNumber::fromInteger(-4); + assertPred(s.value, -4ULL); + assertPred(s.negative, true); + + t = -s; + assertPred(t.value, 4); + assertPred(t.negative, false); + + s = SignExtendedNumber::min(); + t = -s; + assertPred(t.value, UINT64_MAX); + assertPred(t.negative, false); + + s = SignExtendedNumber(0); + t = -s; + assertPred(t.value, 0); + assertPred(t.negative, false); +} + +void testCompare() { + SignExtendedNumber a = SignExtendedNumber::min(); + SignExtendedNumber b = SignExtendedNumber(-5, true); + SignExtendedNumber c = SignExtendedNumber(0, false); + SignExtendedNumber d = SignExtendedNumber(5, false); + SignExtendedNumber e = SignExtendedNumber::max(); + + assertPred(a == a, true); + assertPred(a != a, false); + assertPred(a < b, true); + assertPred(b < c, true); + assertPred(c < d, true); + assertPred(d < e, true); + assertPred(a < c, true); + assertPred(c < e, true); + assertPred(b < d, true); + assertPred(b < a, false); + assertPred(c < b, false); + assertPred(d < c, false); + assertPred(e < d, false); + assertPred(e < c, false); + assertPred(d < b, false); + assertPred(c < a, false); + + assertPred(a, a); + assertPred(SignExtendedNumber::extreme(false), SignExtendedNumber::max()); + assertPred(SignExtendedNumber::extreme(true), SignExtendedNumber::min()); +} + +void testAddition() { + assertPred(SignExtendedNumber(4, false) + SignExtendedNumber(8, false), + SignExtendedNumber(12, false)); + assertPred(SignExtendedNumber(4, false) + SignExtendedNumber(-9, true), + SignExtendedNumber(-5, true)); + assertPred(SignExtendedNumber(-9, true) + SignExtendedNumber(4, false), + SignExtendedNumber(-5, true)); + assertPred(SignExtendedNumber(-4, true) + SignExtendedNumber(9, false), + SignExtendedNumber(5, false)); + assertPred(SignExtendedNumber(9, false) + SignExtendedNumber(-4, true), + SignExtendedNumber(5, false)); + assertPred(SignExtendedNumber(9, true) + SignExtendedNumber(-4, false), + SignExtendedNumber(5, false)); + assertPred(SignExtendedNumber(-4, true) + SignExtendedNumber(-6, true), + SignExtendedNumber(-10, true)); + assertPred(SignExtendedNumber::max() + SignExtendedNumber(1, false), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(UINT64_MAX/2+1, false) + SignExtendedNumber(UINT64_MAX/2+1, false), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::max() + SignExtendedNumber::min(), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber::min() + SignExtendedNumber(-1, true), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::max() + SignExtendedNumber::max(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::min() + SignExtendedNumber::min(), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(1, true) + SignExtendedNumber(1, true), + SignExtendedNumber::min()); + + SignExtendedNumber x(0); + assertPred(++x, SignExtendedNumber(1)); + x = SignExtendedNumber(-1, true); + assertPred(++x, SignExtendedNumber(0)); + x = SignExtendedNumber::min(); + assertPred(++x, SignExtendedNumber(1, true)); + x = SignExtendedNumber::max(); + assertPred(++x, SignExtendedNumber::max()); +} + +void testSubtraction() { + assertPred(SignExtendedNumber(4, false) - SignExtendedNumber(8, false), + SignExtendedNumber(-4, true)); + assertPred(SignExtendedNumber(4, false) - SignExtendedNumber(-9, true), + SignExtendedNumber(13, false)); + assertPred(SignExtendedNumber(-9, true) - SignExtendedNumber(4, false), + SignExtendedNumber(-13, true)); + assertPred(SignExtendedNumber(-4, true) - SignExtendedNumber(9, false), + SignExtendedNumber(-13, true)); + assertPred(SignExtendedNumber(9, false) - SignExtendedNumber(-4, true), + SignExtendedNumber(13, false)); + assertPred(SignExtendedNumber(9, true) - SignExtendedNumber(-4, false), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(-4, true) - SignExtendedNumber(-6, true), + SignExtendedNumber(2, false)); + assertPred(SignExtendedNumber::max() - SignExtendedNumber(-1, true), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::max() - SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() - SignExtendedNumber::min(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::min() - SignExtendedNumber(1, false), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(1, false) - SignExtendedNumber::min(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::min() - SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(1, true) - SignExtendedNumber::min(), + SignExtendedNumber(1, false)); +} + +void testMultiplication() { + assertPred(SignExtendedNumber(4, false) * SignExtendedNumber(8, false), + SignExtendedNumber(32, false)); + assertPred(SignExtendedNumber(4, false) * SignExtendedNumber(-9, true), + SignExtendedNumber(-36, true)); + assertPred(SignExtendedNumber(-9, true) * SignExtendedNumber(4, false), + SignExtendedNumber(-36, true)); + assertPred(SignExtendedNumber(-4, true) * SignExtendedNumber(9, false), + SignExtendedNumber(-36, true)); + assertPred(SignExtendedNumber(9, false) * SignExtendedNumber(-4, true), + SignExtendedNumber(-36, true)); + assertPred(SignExtendedNumber(9, true) * SignExtendedNumber(-4, false), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(-4, true) * SignExtendedNumber(-6, true), + SignExtendedNumber(24, false)); + assertPred(SignExtendedNumber::max() * SignExtendedNumber::max(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::max() * SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() * SignExtendedNumber::min(), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(0) * SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) * SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) * SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() * SignExtendedNumber::max(), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::min() * SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() * SignExtendedNumber::min(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(-6, false) * SignExtendedNumber(2, false), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(-6, false) * SignExtendedNumber(-2, true), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::max() * SignExtendedNumber(-1, true), + SignExtendedNumber(1, true)); + assertPred(SignExtendedNumber::max() * SignExtendedNumber(-2, true), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::max() * SignExtendedNumber(2, false), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::min() * SignExtendedNumber(2, false), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::min() * SignExtendedNumber(-1, true), + SignExtendedNumber::max()); +} + +void testDivision() { + assertPred(SignExtendedNumber(4, false) / SignExtendedNumber(8, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(8, false) / SignExtendedNumber(4, false), + SignExtendedNumber(2, false)); + assertPred(SignExtendedNumber(4, false) / SignExtendedNumber(-9, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(-9, true) / SignExtendedNumber(4, false), + SignExtendedNumber(-2, true)); + assertPred(SignExtendedNumber(-4, true) / SignExtendedNumber(9, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(9, false) / SignExtendedNumber(-4, true), + SignExtendedNumber(-2, true)); + assertPred(SignExtendedNumber(4, true) / SignExtendedNumber(-9, false), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(-6, true) / SignExtendedNumber(-4, true), + SignExtendedNumber(1, false)); + assertPred(SignExtendedNumber::max() / SignExtendedNumber::max(), + SignExtendedNumber(1)); + assertPred(SignExtendedNumber::max() / SignExtendedNumber(0), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::max() / SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) / SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) / SignExtendedNumber(0), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(0) / SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() / SignExtendedNumber::max(), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber::min() / SignExtendedNumber(0), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::min() / SignExtendedNumber::min(), + SignExtendedNumber(1)); + assertPred(SignExtendedNumber(-6, false) / SignExtendedNumber(2, false), + SignExtendedNumber((~5ULL)>>1)); + assertPred(SignExtendedNumber(-6, false) / SignExtendedNumber(-2, true), + SignExtendedNumber(3 | 1ULL<<63, true)); + assertPred(SignExtendedNumber::max() / SignExtendedNumber(-1, true), + SignExtendedNumber(1, true)); + assertPred(SignExtendedNumber::min() / SignExtendedNumber(-1, true), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::max() / SignExtendedNumber(1, false), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::min() / SignExtendedNumber(1, false), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::min() / SignExtendedNumber(2, false), + SignExtendedNumber(-(1ULL << 63), true)); + assertPred(SignExtendedNumber::min() / SignExtendedNumber(-1024, true), + SignExtendedNumber(1ULL << 54)); +} + +void testModulus() { + assertPred(SignExtendedNumber(4, false) % SignExtendedNumber(8, false), + SignExtendedNumber(4, false)); + assertPred(SignExtendedNumber(8, false) % SignExtendedNumber(4, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(4, false) % SignExtendedNumber(-9, true), + SignExtendedNumber(4, false)); + assertPred(SignExtendedNumber(-9, true) % SignExtendedNumber(4, false), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(-4, true) % SignExtendedNumber(9, false), + SignExtendedNumber(-4, true)); + assertPred(SignExtendedNumber(9, false) % SignExtendedNumber(-4, true), + SignExtendedNumber(1, false)); + assertPred(SignExtendedNumber(4, true) % SignExtendedNumber(-9, false), + SignExtendedNumber(-5, true)); + assertPred(SignExtendedNumber(-6, true) % SignExtendedNumber(-4, true), + SignExtendedNumber(-2, true)); + assertPred(SignExtendedNumber::max() % SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() % SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() % SignExtendedNumber::min(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(0) % SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) % SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) % SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber::max(), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(-6, false) % SignExtendedNumber(2, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(-6, false) % SignExtendedNumber(-2, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() % SignExtendedNumber(-1, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber(-1, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() % SignExtendedNumber(1, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber(1, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber(2, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber(999, false), + SignExtendedNumber(-160, true)); +} + +void testShift() { + assertPred(SignExtendedNumber(0) << SignExtendedNumber(4), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) << SignExtendedNumber(74), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) << SignExtendedNumber(-5, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) << SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) << SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(1) << SignExtendedNumber(4), + SignExtendedNumber(16)); + assertPred(SignExtendedNumber(1) << SignExtendedNumber(74), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(1) << SignExtendedNumber(-5, true), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(1) << SignExtendedNumber::max(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(1) << SignExtendedNumber::min(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(-1, true) << SignExtendedNumber(4), + SignExtendedNumber(-16, true)); + assertPred(SignExtendedNumber(-1, true) << SignExtendedNumber(74), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(-1, true) << SignExtendedNumber(-5, true), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(-1, true) << SignExtendedNumber::max(), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(-1, true) << SignExtendedNumber::min(), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(0xabcdef) << SignExtendedNumber(12, false), + SignExtendedNumber(0xabcdef000ULL)); + assertPred(SignExtendedNumber(0xabcdef) << SignExtendedNumber(40, false), + SignExtendedNumber(0xabcdef0000000000ULL)); + assertPred(SignExtendedNumber(0xabcdef) << SignExtendedNumber(41, false), + SignExtendedNumber::max()); + + + assertPred(SignExtendedNumber(0) >> SignExtendedNumber(4), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) >> SignExtendedNumber(74), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) >> SignExtendedNumber(-5, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) >> SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) >> SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(16) >> SignExtendedNumber(4), + SignExtendedNumber(1)); + assertPred(SignExtendedNumber(16) >> SignExtendedNumber(74), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(16) >> SignExtendedNumber(-5, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(16) >> SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(16) >> SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(-32, true) >> SignExtendedNumber(4), + SignExtendedNumber(-2, true)); + assertPred(SignExtendedNumber(-32, true) >> SignExtendedNumber(74), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(-32, true) >> SignExtendedNumber(-5, true), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(-32, true) >> SignExtendedNumber::max(), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(-32, true) >> SignExtendedNumber::min(), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(0xabcdef, false) >> SignExtendedNumber(12, false), + SignExtendedNumber(0xabcULL)); + assertPred(SignExtendedNumber(0xabcdef, true) >> SignExtendedNumber(12, false), + SignExtendedNumber(0xFFF0000000000ABCULL, true)); + assertPred(SignExtendedNumber::min() >> SignExtendedNumber(1, false), + SignExtendedNumber(0x8000000000000000ULL, true)); + assertPred(SignExtendedNumber::min() >> SignExtendedNumber(63, false), + SignExtendedNumber(-2, true)); + assertPred(SignExtendedNumber::min() >> SignExtendedNumber(65, false), + SignExtendedNumber(-1, true)); +} + +void testFromNumbers() { + SignExtendedNumber a[] = { + SignExtendedNumber(12, false), + SignExtendedNumber(-35, true), + SignExtendedNumber(40, false), + SignExtendedNumber(-21, true), + SignExtendedNumber::min() + }; + + IntRange ir1 = IntRange::fromNumbers2(a); + assertPred(ir1.imin, SignExtendedNumber(-35, true)); + assertPred(ir1.imax, SignExtendedNumber(12, false)); + + IntRange ir2 = IntRange::fromNumbers2(a+1); + assertPred(ir2.imin, SignExtendedNumber(-35, true)); + assertPred(ir2.imax, SignExtendedNumber(40, false)); + + IntRange ir3 = IntRange::fromNumbers4(a); + assertPred(ir3.imin, SignExtendedNumber(-35, true)); + assertPred(ir3.imax, SignExtendedNumber(40, false)); + + IntRange ir4 = IntRange::fromNumbers4(a+1); + assertPred(ir4.imin, SignExtendedNumber::min()); + assertPred(ir4.imax, SignExtendedNumber(40, false)); + + assertPred(ir4.contains(ir3), true); + assertPred(ir1.contains(ir2), false); + + IntRange ir5 = IntRange::widest(); + assertPred(ir5.imin, SignExtendedNumber::min()); + assertPred(ir5.imax, SignExtendedNumber::max()); + assertPred(ir5.contains(ir4), true); +} + +void testContainsZero() { + IntRange ir1 (SignExtendedNumber(0), SignExtendedNumber(4)); + assertPred(ir1.containsZero(), true); + + IntRange ir2 (SignExtendedNumber(-4, true), SignExtendedNumber(0)); + assertPred(ir2.containsZero(), true); + + IntRange ir3 (SignExtendedNumber(-5, true), SignExtendedNumber(5)); + assertPred(ir3.containsZero(), true); + + assertPred(IntRange::widest().containsZero(), true); + + IntRange ir4 (SignExtendedNumber(8), SignExtendedNumber(9)); + assertPred(ir4.containsZero(), false); + + IntRange ir5 (SignExtendedNumber(-5, true), SignExtendedNumber(-2, true)); + assertPred(ir5.containsZero(), false); + + IntRange ir6 (SignExtendedNumber(0), SignExtendedNumber(0)); + assertPred(ir6.containsZero(), true); +} + +void testCast() { + { + IntRange ir1 (SignExtendedNumber(0), SignExtendedNumber(0xFFFF)); + ir1.castUnsigned(0xFF); + assertPred(ir1.imin, SignExtendedNumber(0)); + assertPred(ir1.imax, SignExtendedNumber(0xFF)); + + IntRange ir2 (SignExtendedNumber(0x101), SignExtendedNumber(0x105)); + ir2.castUnsigned(0xFF); + assertPred(ir2.imin, SignExtendedNumber(1)); + assertPred(ir2.imax, SignExtendedNumber(5)); + + IntRange ir3 (SignExtendedNumber(-7, true), SignExtendedNumber(7, false)); + ir3.castUnsigned(0xFF); + assertPred(ir3.imin, SignExtendedNumber(0)); + assertPred(ir3.imax, SignExtendedNumber(0xFF)); + + IntRange ir4 (SignExtendedNumber(0x997F), SignExtendedNumber(0x9999)); + ir4.castUnsigned(0xFF); + assertPred(ir4.imin, SignExtendedNumber(0x7F)); + assertPred(ir4.imax, SignExtendedNumber(0x99)); + + IntRange ir5 (SignExtendedNumber(-1, true), SignExtendedNumber(1, false)); + ir5.castUnsigned(UINT64_MAX); + assertPred(ir5.imin, SignExtendedNumber(0)); + assertPred(ir5.imax, SignExtendedNumber::max()); + + IntRange ir6 (SignExtendedNumber::min(), SignExtendedNumber(0)); + ir6.castUnsigned(UINT64_MAX); + assertPred(ir6.imin, SignExtendedNumber(0)); + assertPred(ir6.imax, SignExtendedNumber::max()); + + IntRange ir7 (SignExtendedNumber::min(), SignExtendedNumber(-0x80, true)); + ir7.castUnsigned(UINT64_MAX); + assertPred(ir7.imin, SignExtendedNumber(0)); + assertPred(ir7.imax, SignExtendedNumber(-0x80, false)); + + IntRange ir8 = IntRange::widest(); + ir8.castUnsigned(0xFF); + assertPred(ir8.imin, SignExtendedNumber(0)); + assertPred(ir8.imax, SignExtendedNumber(0xFF)); + } + + { + IntRange ir1 (SignExtendedNumber(0), SignExtendedNumber(0xFFFF)); + ir1.castSigned(0xFF); + assertPred(ir1.imin, SignExtendedNumber(-0x80, true)); + assertPred(ir1.imax, SignExtendedNumber(0x7F, false)); + + IntRange ir2 (SignExtendedNumber(0x101), SignExtendedNumber(0x105)); + ir2.castSigned(0xFF); + assertPred(ir2.imin, SignExtendedNumber(1)); + assertPred(ir2.imax, SignExtendedNumber(5)); + + IntRange ir3 (SignExtendedNumber(-7, true), SignExtendedNumber(7, false)); + ir3.castSigned(0xFF); + assertPred(ir3.imin, SignExtendedNumber(-7, true)); + assertPred(ir3.imax, SignExtendedNumber(7, false)); + + IntRange ir4 (SignExtendedNumber(0x997F), SignExtendedNumber(0x9999)); + ir4.castSigned(0xFF); + assertPred(ir4.imin, SignExtendedNumber(-0x80, true)); + assertPred(ir4.imax, SignExtendedNumber(0x7F, false)); + + IntRange ir5 (SignExtendedNumber(-0xFF, true), SignExtendedNumber(-0x80, true)); + ir5.castSigned(0xFF); + assertPred(ir5.imin, SignExtendedNumber(-0x80, true)); + assertPred(ir5.imax, SignExtendedNumber(0x7F, false)); + + IntRange ir6 (SignExtendedNumber(-0x80, true), SignExtendedNumber(-0x80, true)); + ir6.castSigned(0xFF); + assertPred(ir6.imin, SignExtendedNumber(-0x80, true)); + assertPred(ir6.imax, SignExtendedNumber(-0x80, true)); + + IntRange ir7 = IntRange::widest(); + ir7.castSigned(0xFFFFFFFFULL); + assertPred(ir7.imin, SignExtendedNumber(-0x80000000ULL, true)); + assertPred(ir7.imax, SignExtendedNumber( 0x7FFFFFFFULL, false)); + } + + { + IntRange ir1 (SignExtendedNumber(0), SignExtendedNumber(0x9999)); + ir1.castDchar(); + assertPred(ir1.imin, SignExtendedNumber(0)); + assertPred(ir1.imax, SignExtendedNumber(0x9999)); + + IntRange ir2 (SignExtendedNumber(0xFFFF), SignExtendedNumber(0x7FFFFFFF)); + ir2.castDchar(); + assertPred(ir2.imin, SignExtendedNumber(0xFFFF)); + assertPred(ir2.imax, SignExtendedNumber(0x10FFFF)); + + IntRange ir3 = IntRange::widest(); + ir3.castDchar(); + assertPred(ir3.imin, SignExtendedNumber(0)); + assertPred(ir3.imax, SignExtendedNumber(0x10FFFF)); + } +} + +void testAbsNeg() { + IntRange ir1 = IntRange(SignExtendedNumber(5), SignExtendedNumber(104)).absNeg(); + assertPred(ir1.imin, SignExtendedNumber(-104, true)); + assertPred(ir1.imax, SignExtendedNumber(-5, true)); + + IntRange ir2 = IntRange(SignExtendedNumber(-46, true), SignExtendedNumber(-3, true)).absNeg(); + assertPred(ir2.imin, SignExtendedNumber(-46, true)); + assertPred(ir2.imax, SignExtendedNumber(-3, true)); + + IntRange ir3 = IntRange(SignExtendedNumber(-7, true), SignExtendedNumber(9)).absNeg(); + assertPred(ir3.imin, SignExtendedNumber(-9, true)); + assertPred(ir3.imax, SignExtendedNumber(0)); + + IntRange ir4 = IntRange(SignExtendedNumber(-12, true), SignExtendedNumber(2)).absNeg(); + assertPred(ir4.imin, SignExtendedNumber(-12, true)); + assertPred(ir4.imax, SignExtendedNumber(0)); + + IntRange ir5 = IntRange::widest().absNeg(); + assertPred(ir5.imin, SignExtendedNumber::min()); + assertPred(ir5.imax, SignExtendedNumber(0)); + + IntRange ir6 = IntRange(SignExtendedNumber(0), SignExtendedNumber::max()).absNeg(); + assertPred(ir6.imin, SignExtendedNumber(1, true)); + assertPred(ir6.imax, SignExtendedNumber(0)); +} + +int main() { + RUN(testAssertSanity); + RUN(testNegation); + RUN(testCompare); + RUN(testAddition); + RUN(testSubtraction); + RUN(testMultiplication); + RUN(testDivision); + RUN(testModulus); + RUN(testShift); + RUN(testFromNumbers); + RUN(testContainsZero); + RUN(testCast); + RUN(testAbsNeg); + printf("Finished all tests.\n"); +} + + +#endif + + diff --git a/intrange.h b/intrange.h new file mode 100644 index 00000000..77685b02 --- /dev/null +++ b/intrange.h @@ -0,0 +1,149 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by KennyTM +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#ifndef DMD_SXNUM_H +#define DMD_SXNUM_H + +#include "mars.h" // for uinteger_t +struct Type; +struct Expression; + +/** +This class represents a "sign-extended number", i.e. a 65-bit number, which can +represent all built-in integer types in D. This class is mainly used for +performing value-range propagation only, therefore all arithmetic are done with +saturation, not wrapping as usual. +*/ +struct SignExtendedNumber +{ + /// The lower 64-bit of the number. + uinteger_t value; + /// The sign (i.e. the most significant bit) of the number. + bool negative; + + /// Create an uninitialized sign-extended number. + SignExtendedNumber() {} + + /// Create a sign-extended number from an unsigned 64-bit number. + SignExtendedNumber(uinteger_t value_) + : value(value_), negative(false) {} + /// Create a sign-extended number from the lower 64-bit and the sign bit. + SignExtendedNumber(uinteger_t value_, bool negative_) + : value(value_), negative(negative_) {} + + /// Create a sign-extended number from a signed 64-bit number. + static SignExtendedNumber fromInteger(uinteger_t value_); + + /// Get the minimum or maximum value of a sign-extended number. + static SignExtendedNumber extreme(bool minimum); + static SignExtendedNumber max(); + static SignExtendedNumber min() { return SignExtendedNumber(0, true); } + + /// Check if the sign-extended number is minimum or zero. + bool isMinimum() const { return negative && value == 0; } + + /// Compare two sign-extended number. + bool operator==(const SignExtendedNumber&) const; + bool operator!=(const SignExtendedNumber& a) const { return !(*this == a); } + bool operator<(const SignExtendedNumber&) const; + bool operator>(const SignExtendedNumber& a) const { return a < *this; } + bool operator<=(const SignExtendedNumber& a) const { return !(a < *this); } + bool operator>=(const SignExtendedNumber& a) const { return !(*this < a); } + + /// Compute the saturated negation of a sign-extended number. + SignExtendedNumber operator-() const; + + /// Compute the saturated sum of two sign-extended number. + SignExtendedNumber operator+(const SignExtendedNumber&) const; + /// Compute the saturated difference of two sign-extended number. + SignExtendedNumber operator-(const SignExtendedNumber& a) const; + /// Compute the saturated product of two sign-extended number. + SignExtendedNumber operator*(const SignExtendedNumber&) const; + /// Compute the saturated quotient of two sign-extended number. + SignExtendedNumber operator/(const SignExtendedNumber&) const; + /// Compute the saturated modulus of two sign-extended number. + SignExtendedNumber operator%(const SignExtendedNumber&) const; + + /// Increase the sign-extended number by 1 (saturated). + SignExtendedNumber& operator++(); + + /// Compute the saturated shifts of two sign-extended number. + SignExtendedNumber operator<<(const SignExtendedNumber&) const; + SignExtendedNumber operator>>(const SignExtendedNumber&) const; +}; + +/** +This class represents a range of integers, denoted by its lower and upper bounds +(inclusive). +*/ +struct IntRange +{ + SignExtendedNumber imin, imax; + + /// Create an uninitialized range. + IntRange() {} + + /// Create a range consisting of a single number. + IntRange(const SignExtendedNumber& a) + : imin(a), imax(a) {} + /// Create a range with the lower and upper bounds. + IntRange(const SignExtendedNumber& lower, const SignExtendedNumber& upper) + : imin(lower), imax(upper) {} + + /// Create the tightest range containing all valid integers in the specified + /// type. + static IntRange fromType(Type *type); + /// Create the tightest range containing all valid integers in the type with + /// a forced signedness. + static IntRange fromType(Type *type, bool isUnsigned); + + + /// Create the tightest range containing all specified numbers. + static IntRange fromNumbers2(const SignExtendedNumber numbers[2]); + static IntRange fromNumbers4(const SignExtendedNumber numbers[4]); + + /// Create the widest range possible. + static IntRange widest(); + + /// Cast the integer range to a signed type with the given size mask. + IntRange& castSigned(uinteger_t mask); + /// Cast the integer range to an unsigned type with the given size mask. + IntRange& castUnsigned(uinteger_t mask); + /// Cast the integer range to the dchar type. + IntRange& castDchar(); + + /// Cast the integer range to a specific type. + IntRange& cast(Type *type); + /// Cast the integer range to a specific type, forcing it to be unsigned. + IntRange& castUnsigned(Type *type); + + /// Check if this range contains another range. + bool contains(const IntRange& a) const; + + /// Check if this range contains 0. + bool containsZero() const; + + /// Compute the range of the negated absolute values of the original range. + IntRange absNeg() const; + + /// Compute the union of two ranges. + IntRange unionWith(const IntRange& other) const; + void unionOrAssign(const IntRange& other, bool& union_); + + /// Dump the content of the integer range to the console. + const IntRange& dump(const char* funcName, Expression *e) const; + + /// Split the range into two nonnegative- and negative-only subintervals. + void splitBySign(IntRange& negRange, bool& hasNegRange, + IntRange& nonNegRange, bool& hasNonNegRange) const; +}; + +#endif diff --git a/irstate.c b/irstate.c new file mode 100644 index 00000000..42649bb0 --- /dev/null +++ b/irstate.c @@ -0,0 +1,191 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com + +#include + +#include "mars.h" +#include "mtype.h" +#include "declaration.h" +#include "irstate.h" + +IRState::IRState(IRState *irs, Statement *s) +{ + prev = irs; + statement = s; + symbol = NULL; + breakBlock = NULL; + contBlock = NULL; + switchBlock = NULL; + defaultBlock = NULL; + ident = NULL; + ehidden = NULL; + startaddress = NULL; + if (irs) + { + m = irs->m; + shidden = irs->shidden; + sclosure = irs->sclosure; + sthis = irs->sthis; + blx = irs->blx; + deferToObj = irs->deferToObj; + varsInScope = irs->varsInScope; + } + else + { + m = NULL; + shidden = NULL; + sclosure = NULL; + sthis = NULL; + blx = NULL; + deferToObj = NULL; + varsInScope = NULL; + } +} + +IRState::IRState(IRState *irs, Dsymbol *s) +{ + prev = irs; + statement = NULL; + symbol = s; + breakBlock = NULL; + contBlock = NULL; + switchBlock = NULL; + defaultBlock = NULL; + ident = NULL; + ehidden = NULL; + startaddress = NULL; + if (irs) + { + m = irs->m; + shidden = irs->shidden; + sclosure = irs->sclosure; + sthis = irs->sthis; + blx = irs->blx; + deferToObj = irs->deferToObj; + varsInScope = irs->varsInScope; + } + else + { + m = NULL; + shidden = NULL; + sclosure = NULL; + sthis = NULL; + blx = NULL; + deferToObj = NULL; + varsInScope = NULL; + } +} + +IRState::IRState(Module *m, Dsymbol *s) +{ + prev = NULL; + statement = NULL; + this->m = m; + symbol = s; + breakBlock = NULL; + contBlock = NULL; + switchBlock = NULL; + defaultBlock = NULL; + ident = NULL; + ehidden = NULL; + shidden = NULL; + sclosure = NULL; + sthis = NULL; + blx = NULL; + deferToObj = NULL; + startaddress = NULL; + varsInScope = NULL; +} + +block *IRState::getBreakBlock(Identifier *ident) +{ + IRState *bc; + + for (bc = this; bc; bc = bc->prev) + { + if (ident) + { + if (bc->prev && bc->prev->ident == ident) + return bc->breakBlock; + } + else if (bc->breakBlock) + return bc->breakBlock; + } + return NULL; +} + +block *IRState::getContBlock(Identifier *ident) +{ + IRState *bc; + + for (bc = this; bc; bc = bc->prev) + { + if (ident) + { + if (bc->prev && bc->prev->ident == ident) + return bc->contBlock; + } + else if (bc->contBlock) + return bc->contBlock; + } + return NULL; +} + +block *IRState::getSwitchBlock() +{ + IRState *bc; + + for (bc = this; bc; bc = bc->prev) + { + if (bc->switchBlock) + return bc->switchBlock; + } + return NULL; +} + +block *IRState::getDefaultBlock() +{ + IRState *bc; + + for (bc = this; bc; bc = bc->prev) + { + if (bc->defaultBlock) + return bc->defaultBlock; + } + return NULL; +} + +FuncDeclaration *IRState::getFunc() +{ + IRState *bc; + + for (bc = this; bc->prev; bc = bc->prev) + { + } + return (FuncDeclaration *)(bc->symbol); +} + + +/********************** + * Return !=0 if do array bounds checking + */ +int IRState::arrayBoundsCheck() +{ + int result = global.params.useArrayBounds; + + if (result == 1) + { // For safe functions only + result = 0; + FuncDeclaration *fd = getFunc(); + if (fd) + { Type *t = fd->type; + if (t->ty == Tfunction && ((TypeFunction *)t)->trust == TRUSTsafe) + result = 1; + } + } + return result; +} diff --git a/irstate.h b/irstate.h new file mode 100644 index 00000000..75194b6f --- /dev/null +++ b/irstate.h @@ -0,0 +1,59 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com + +#ifndef DMD_CONTEXT_H +#define DMD_CONTEXT_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +struct Module; +struct Statement; +struct block; +struct Dsymbol; +struct Identifier; +struct Symbol; +struct FuncDeclaration; +struct Blockx; +struct elem; +#include "arraytypes.h" + +struct IRState +{ + IRState *prev; + Statement *statement; + Module *m; // module + Dsymbol *symbol; + Identifier *ident; + Symbol *shidden; // hidden parameter to function + Symbol *sthis; // 'this' parameter to function (member and nested) + Symbol *sclosure; // pointer to closure instance + Blockx *blx; + Dsymbols *deferToObj; // array of Dsymbol's to run toObjFile(int multiobj) on later + elem *ehidden; // transmit hidden pointer to CallExp::toElem() + Symbol *startaddress; + VarDeclarations *varsInScope; // variables that are in scope that will need destruction later + + block *breakBlock; + block *contBlock; + block *switchBlock; + block *defaultBlock; + + IRState(IRState *irs, Statement *s); + IRState(IRState *irs, Dsymbol *s); + IRState(Module *m, Dsymbol *s); + + block *getBreakBlock(Identifier *ident); + block *getContBlock(Identifier *ident); + block *getSwitchBlock(); + block *getDefaultBlock(); + FuncDeclaration *getFunc(); + int arrayBoundsCheck(); +}; + +#endif /* DMD_CONTEXT_H */ diff --git a/json.c b/json.c new file mode 100644 index 00000000..d3efacf5 --- /dev/null +++ b/json.c @@ -0,0 +1,462 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +// This implements the JSON capability. + +#include +#include +#include +#include +#include + +#include "rmem.h" +#include "root.h" + +#include "mars.h" +#include "dsymbol.h" +#include "macro.h" +#include "template.h" +#include "lexer.h" +#include "aggregate.h" +#include "declaration.h" +#include "enum.h" +#include "id.h" +#include "module.h" +#include "scope.h" +#include "hdrgen.h" +#include "json.h" +#include "mtype.h" +#include "attrib.h" +#include "cond.h" + +const char Pname[] = "name"; +const char Pkind[] = "kind"; +const char Pfile[] = "file"; +const char Pline[] = "line"; +const char Ptype[] = "type"; +const char Pcomment[] = "comment"; +const char Pmembers[] = "members"; +const char Pprotection[] = "protection"; +const char* Pprotectionnames[] = {NULL, "none", "private", "package", "protected", "public", "export"}; + +void JsonRemoveComma(OutBuffer *buf); + +void json_generate(Modules *modules) +{ OutBuffer buf; + + buf.writestring("[\n"); + for (size_t i = 0; i < modules->dim; i++) + { Module *m = modules->tdata()[i]; + if (global.params.verbose) + printf("json gen %s\n", m->toChars()); + m->toJsonBuffer(&buf); + buf.writestring(",\n"); + } + JsonRemoveComma(&buf); + buf.writestring("]\n"); + + // Write buf to file + char *arg = global.params.xfilename; + if (!arg || !*arg) + { // Generate lib file name from first obj name + char *n = global.params.objfiles->tdata()[0]; + + n = FileName::name(n); + FileName *fn = FileName::forceExt(n, global.json_ext); + arg = fn->toChars(); + } + else if (arg[0] == '-' && arg[1] == 0) + { // Write to stdout; assume it succeeds + int n = fwrite(buf.data, 1, buf.offset, stdout); + assert(n == buf.offset); // keep gcc happy about return values + return; + } +// if (!FileName::absolute(arg)) +// arg = FileName::combine(dir, arg); + FileName *jsonfilename = FileName::defaultExt(arg, global.json_ext); + File *jsonfile = new File(jsonfilename); + assert(jsonfile); + jsonfile->setbuffer(buf.data, buf.offset); + jsonfile->ref = 1; + char *pt = FileName::path(jsonfile->toChars()); + if (*pt) + FileName::ensurePathExists(pt); + mem.free(pt); + jsonfile->writev(); +} + + +/********************************* + * Encode string into buf, and wrap it in double quotes. + */ +void JsonString(OutBuffer *buf, const char *s) +{ + buf->writeByte('\"'); + for (; *s; s++) + { + unsigned char c = (unsigned char) *s; + switch (c) + { + case '\n': + buf->writestring("\\n"); + break; + + case '\r': + buf->writestring("\\r"); + break; + + case '\t': + buf->writestring("\\t"); + break; + + case '\"': + buf->writestring("\\\""); + break; + + case '\\': + buf->writestring("\\\\"); + break; + + case '/': + buf->writestring("\\/"); + break; + + case '\b': + buf->writestring("\\b"); + break; + + case '\f': + buf->writestring("\\f"); + break; + + default: + if (c < 0x20) + buf->printf("\\u%04x", c); + else + // Note that UTF-8 chars pass through here just fine + buf->writeByte(c); + break; + } + } + buf->writeByte('\"'); +} + +void JsonProperty(OutBuffer *buf, const char *name, const char *value) +{ + JsonString(buf, name); + buf->writestring(" : "); + JsonString(buf, value); + buf->writestring(",\n"); +} + +void JsonProperty(OutBuffer *buf, const char *name, int value) +{ + JsonString(buf, name); + buf->writestring(" : "); + buf->printf("%d", value); + buf->writestring(",\n"); +} + +void JsonRemoveComma(OutBuffer *buf) +{ + if (buf->offset >= 2 && + buf->data[buf->offset - 2] == ',' && + buf->data[buf->offset - 1] == '\n') + buf->offset -= 2; +} + +void Dsymbol::toJsonBuffer(OutBuffer *buf) +{ +} + +void Module::toJsonBuffer(OutBuffer *buf) +{ + buf->writestring("{\n"); + + if (md) + JsonProperty(buf, Pname, md->toChars()); + + JsonProperty(buf, Pkind, kind()); + + JsonProperty(buf, Pfile, srcfile->toChars()); + + if (comment) + JsonProperty(buf, Pcomment, (const char *)comment); + + JsonString(buf, Pmembers); + buf->writestring(" : [\n"); + + size_t offset = buf->offset; + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + if (offset != buf->offset) + { buf->writestring(",\n"); + offset = buf->offset; + } + s->toJsonBuffer(buf); + } + + JsonRemoveComma(buf); + buf->writestring("]\n"); + + buf->writestring("}\n"); +} + +void AttribDeclaration::toJsonBuffer(OutBuffer *buf) +{ + //printf("AttribDeclaration::toJsonBuffer()\n"); + + Dsymbols *d = include(NULL, NULL); + + if (d) + { + size_t offset = buf->offset; + for (unsigned i = 0; i < d->dim; i++) + { Dsymbol *s = d->tdata()[i]; + //printf("AttribDeclaration::toJsonBuffer %s\n", s->toChars()); + if (offset != buf->offset) + { buf->writestring(",\n"); + offset = buf->offset; + } + s->toJsonBuffer(buf); + } + JsonRemoveComma(buf); + } +} + + +void ConditionalDeclaration::toJsonBuffer(OutBuffer *buf) +{ + //printf("ConditionalDeclaration::toJsonBuffer()\n"); + if (condition->inc) + { + AttribDeclaration::toJsonBuffer(buf); + } +} + + +void InvariantDeclaration::toJsonBuffer(OutBuffer *buf) { } +void DtorDeclaration::toJsonBuffer(OutBuffer *buf) { } +void StaticCtorDeclaration::toJsonBuffer(OutBuffer *buf) { } +void StaticDtorDeclaration::toJsonBuffer(OutBuffer *buf) { } +void ClassInfoDeclaration::toJsonBuffer(OutBuffer *buf) { } +void ModuleInfoDeclaration::toJsonBuffer(OutBuffer *buf) { } +void TypeInfoDeclaration::toJsonBuffer(OutBuffer *buf) { } +void UnitTestDeclaration::toJsonBuffer(OutBuffer *buf) { } +#if DMDV2 +void PostBlitDeclaration::toJsonBuffer(OutBuffer *buf) { } +#endif + +void Declaration::toJsonBuffer(OutBuffer *buf) +{ + //printf("Declaration::toJsonBuffer()\n"); + buf->writestring("{\n"); + + JsonProperty(buf, Pname, toChars()); + JsonProperty(buf, Pkind, kind()); + + if (prot()) + JsonProperty(buf, Pprotection, Pprotectionnames[prot()]); + + if (type) + JsonProperty(buf, Ptype, type->toChars()); + + if (comment) + JsonProperty(buf, Pcomment, (const char *)comment); + + if (loc.linnum) + JsonProperty(buf, Pline, loc.linnum); + + TypedefDeclaration *td = isTypedefDeclaration(); + if (td) + { + JsonProperty(buf, "base", td->basetype->toChars()); + } + + JsonRemoveComma(buf); + buf->writestring("}\n"); +} + +void AggregateDeclaration::toJsonBuffer(OutBuffer *buf) +{ + //printf("AggregateDeclaration::toJsonBuffer()\n"); + buf->writestring("{\n"); + + JsonProperty(buf, Pname, toChars()); + JsonProperty(buf, Pkind, kind()); + + if (prot()) + JsonProperty(buf, Pprotection, Pprotectionnames[prot()]); + + if (comment) + JsonProperty(buf, Pcomment, (const char *)comment); + + if (loc.linnum) + JsonProperty(buf, Pline, loc.linnum); + + ClassDeclaration *cd = isClassDeclaration(); + if (cd) + { + if (cd->baseClass) + { + JsonProperty(buf, "base", cd->baseClass->toChars()); + } + if (cd->interfaces_dim) + { + JsonString(buf, "interfaces"); + buf->writestring(" : [\n"); + size_t offset = buf->offset; + for (size_t i = 0; i < cd->interfaces_dim; i++) + { BaseClass *b = cd->interfaces[i]; + if (offset != buf->offset) + { buf->writestring(",\n"); + offset = buf->offset; + } + JsonString(buf, b->base->toChars()); + } + JsonRemoveComma(buf); + buf->writestring("],\n"); + } + } + + if (members) + { + JsonString(buf, Pmembers); + buf->writestring(" : [\n"); + size_t offset = buf->offset; + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + if (offset != buf->offset) + { buf->writestring(",\n"); + offset = buf->offset; + } + s->toJsonBuffer(buf); + } + JsonRemoveComma(buf); + buf->writestring("]\n"); + } + JsonRemoveComma(buf); + + buf->writestring("}\n"); +} + +void TemplateDeclaration::toJsonBuffer(OutBuffer *buf) +{ + //printf("TemplateDeclaration::toJsonBuffer()\n"); + + buf->writestring("{\n"); + + JsonProperty(buf, Pname, toChars()); + JsonProperty(buf, Pkind, kind()); + + if (prot()) + JsonProperty(buf, Pprotection, Pprotectionnames[prot()]); + + if (comment) + JsonProperty(buf, Pcomment, (const char *)comment); + + if (loc.linnum) + JsonProperty(buf, Pline, loc.linnum); + + JsonString(buf, Pmembers); + buf->writestring(" : [\n"); + size_t offset = buf->offset; + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + if (offset != buf->offset) + { buf->writestring(",\n"); + offset = buf->offset; + } + s->toJsonBuffer(buf); + } + JsonRemoveComma(buf); + buf->writestring("]\n"); + + buf->writestring("}\n"); +} + +void EnumDeclaration::toJsonBuffer(OutBuffer *buf) +{ + //printf("EnumDeclaration::toJsonBuffer()\n"); + if (isAnonymous()) + { + if (members) + { + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->toJsonBuffer(buf); + buf->writestring(",\n"); + } + JsonRemoveComma(buf); + } + return; + } + + buf->writestring("{\n"); + + JsonProperty(buf, Pname, toChars()); + JsonProperty(buf, Pkind, kind()); + + if (prot()) + JsonProperty(buf, Pprotection, Pprotectionnames[prot()]); + + if (comment) + JsonProperty(buf, Pcomment, (const char *)comment); + + if (loc.linnum) + JsonProperty(buf, Pline, loc.linnum); + + if (memtype) + JsonProperty(buf, "base", memtype->toChars()); + + if (members) + { + JsonString(buf, Pmembers); + buf->writestring(" : [\n"); + size_t offset = buf->offset; + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + if (offset != buf->offset) + { buf->writestring(",\n"); + offset = buf->offset; + } + s->toJsonBuffer(buf); + } + JsonRemoveComma(buf); + buf->writestring("]\n"); + } + JsonRemoveComma(buf); + + buf->writestring("}\n"); +} + +void EnumMember::toJsonBuffer(OutBuffer *buf) +{ + //printf("EnumMember::toJsonBuffer()\n"); + buf->writestring("{\n"); + + JsonProperty(buf, Pname, toChars()); + JsonProperty(buf, Pkind, kind()); + + if (prot()) + JsonProperty(buf, Pprotection, Pprotectionnames[prot()]); + + if (comment) + JsonProperty(buf, Pcomment, (const char *)comment); + + if (loc.linnum) + JsonProperty(buf, Pline, loc.linnum); + + JsonRemoveComma(buf); + buf->writestring("}\n"); +} + + diff --git a/json.h b/json.h new file mode 100644 index 00000000..2c7e2e60 --- /dev/null +++ b/json.h @@ -0,0 +1,24 @@ + + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2008 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_JSON_H +#define DMD_JSON_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "arraytypes.h" + +void json_generate(Modules *); + +#endif /* DMD_JSON_H */ + diff --git a/lexer.c b/lexer.c new file mode 100644 index 00000000..8070f820 --- /dev/null +++ b/lexer.c @@ -0,0 +1,3211 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +/* Lexical Analyzer */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include // for time() and ctime() + +#include "rmem.h" + +#include "stringtable.h" + +#include "lexer.h" +#include "utf.h" +#include "identifier.h" +#include "id.h" +#include "module.h" + +#if _WIN32 && __DMC__ +// from \dm\src\include\setlocal.h +extern "C" char * __cdecl __locale_decpoint; +#endif + +extern int HtmlNamedEntity(unsigned char *p, int length); + +#define LS 0x2028 // UTF line separator +#define PS 0x2029 // UTF paragraph separator + +void unittest_lexer(); + +/******************************************** + * Do our own char maps + */ + +static unsigned char cmtable[256]; + +const int CMoctal = 0x1; +const int CMhex = 0x2; +const int CMidchar = 0x4; + +inline unsigned char isoctal (unsigned char c) { return cmtable[c] & CMoctal; } +inline unsigned char ishex (unsigned char c) { return cmtable[c] & CMhex; } +inline unsigned char isidchar(unsigned char c) { return cmtable[c] & CMidchar; } + +static void cmtable_init() +{ + for (unsigned c = 0; c < sizeof(cmtable) / sizeof(cmtable[0]); c++) + { + if ('0' <= c && c <= '7') + cmtable[c] |= CMoctal; + if (isdigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')) + cmtable[c] |= CMhex; + if (isalnum(c) || c == '_') + cmtable[c] |= CMidchar; + } +} + + +/************************* Token **********************************************/ + +const char *Token::tochars[TOKMAX]; + +void *Token::operator new(size_t size) +{ Token *t; + + if (Lexer::freelist) + { + t = Lexer::freelist; + Lexer::freelist = t->next; + return t; + } + + return ::operator new(size); +} + +#ifdef DEBUG +void Token::print() +{ + fprintf(stdmsg, "%s\n", toChars()); +} +#endif + +const char *Token::toChars() +{ const char *p; + static char buffer[3 + 3 * sizeof(float80value) + 1]; + + p = buffer; + switch (value) + { + case TOKint32v: +#if IN_GCC + sprintf(buffer,"%d",(d_int32)int64value); +#else + sprintf(buffer,"%d",int32value); +#endif + break; + + case TOKuns32v: + case TOKcharv: + case TOKwcharv: + case TOKdcharv: +#if IN_GCC + sprintf(buffer,"%uU",(d_uns32)uns64value); +#else + sprintf(buffer,"%uU",uns32value); +#endif + break; + + case TOKint64v: + sprintf(buffer,"%jdL",(intmax_t)int64value); + break; + + case TOKuns64v: + sprintf(buffer,"%juUL",(uintmax_t)uns64value); + break; + +#if IN_GCC + case TOKfloat32v: + case TOKfloat64v: + case TOKfloat80v: + float80value.format(buffer, sizeof(buffer)); + break; + case TOKimaginary32v: + case TOKimaginary64v: + case TOKimaginary80v: + float80value.format(buffer, sizeof(buffer)); + // %% buffer + strcat(buffer, "i"); + break; +#else + case TOKfloat32v: + sprintf(buffer,"%Lgf", float80value); + break; + + case TOKfloat64v: + sprintf(buffer,"%Lg", float80value); + break; + + case TOKfloat80v: + sprintf(buffer,"%LgL", float80value); + break; + + case TOKimaginary32v: + sprintf(buffer,"%Lgfi", float80value); + break; + + case TOKimaginary64v: + sprintf(buffer,"%Lgi", float80value); + break; + + case TOKimaginary80v: + sprintf(buffer,"%LgLi", float80value); + break; +#endif + + case TOKstring: +#if CSTRINGS + p = string; +#else + { OutBuffer buf; + + buf.writeByte('"'); + for (size_t i = 0; i < len; ) + { unsigned c; + + utf_decodeChar((unsigned char *)ustring, len, &i, &c); + switch (c) + { + case 0: + break; + + case '"': + case '\\': + buf.writeByte('\\'); + default: + if (isprint(c)) + buf.writeByte(c); + else if (c <= 0x7F) + buf.printf("\\x%02x", c); + else if (c <= 0xFFFF) + buf.printf("\\u%04x", c); + else + buf.printf("\\U%08x", c); + continue; + } + break; + } + buf.writeByte('"'); + if (postfix) + buf.writeByte('"'); + buf.writeByte(0); + p = (char *)buf.extractData(); + } +#endif + break; + + case TOKidentifier: + case TOKenum: + case TOKstruct: + case TOKimport: + case BASIC_TYPES: + p = ident->toChars(); + break; + + default: + p = toChars(value); + break; + } + return p; +} + +const char *Token::toChars(enum TOK value) +{ const char *p; + static char buffer[3 + 3 * sizeof(value) + 1]; + + p = tochars[value]; + if (!p) + { sprintf(buffer,"TOK%d",value); + p = buffer; + } + return p; +} + +/*************************** Lexer ********************************************/ + +Token *Lexer::freelist = NULL; +StringTable Lexer::stringtable; +OutBuffer Lexer::stringbuffer; + +Lexer::Lexer(Module *mod, + unsigned char *base, unsigned begoffset, unsigned endoffset, + int doDocComment, int commentToken) + : loc(mod, 1) +{ + //printf("Lexer::Lexer(%p,%d)\n",base,length); + //printf("lexer.mod = %p, %p\n", mod, this->loc.mod); + memset(&token,0,sizeof(token)); + this->base = base; + this->end = base + endoffset; + p = base + begoffset; + this->mod = mod; + this->doDocComment = doDocComment; + this->anyToken = 0; + this->commentToken = commentToken; + //initKeywords(); + + /* If first line starts with '#!', ignore the line + */ + + if (p[0] == '#' && p[1] =='!') + { + p += 2; + while (1) + { unsigned char c = *p; + switch (c) + { + case '\n': + p++; + break; + + case '\r': + p++; + if (*p == '\n') + p++; + break; + + case 0: + case 0x1A: + break; + + default: + if (c & 0x80) + { unsigned u = decodeUTF(); + if (u == PS || u == LS) + break; + } + p++; + continue; + } + break; + } + loc.linnum = 2; + } +} + + +void Lexer::error(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); +} + +void Lexer::error(Loc loc, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); +} + +void Lexer::verror(Loc loc, const char *format, va_list ap) +{ + if (mod && !global.gag) + { + char *p = loc.toChars(); + if (*p) + fprintf(stdmsg, "%s: ", p); + mem.free(p); + + vfprintf(stdmsg, format, ap); + + fprintf(stdmsg, "\n"); + fflush(stdmsg); + + if (global.errors >= 20) // moderate blizzard of cascading messages + fatal(); + } + else + { + global.gaggedErrors++; + } + global.errors++; +} + +TOK Lexer::nextToken() +{ Token *t; + + if (token.next) + { + t = token.next; + memcpy(&token,t,sizeof(Token)); + t->next = freelist; + freelist = t; + } + else + { + scan(&token); + } + //token.print(); + return token.value; +} + +Token *Lexer::peek(Token *ct) +{ Token *t; + + if (ct->next) + t = ct->next; + else + { + t = new Token(); + scan(t); + t->next = NULL; + ct->next = t; + } + return t; +} + +/*********************** + * Look ahead at next token's value. + */ + +TOK Lexer::peekNext() +{ + return peek(&token)->value; +} + +/*********************** + * Look 2 tokens ahead at value. + */ + +TOK Lexer::peekNext2() +{ + Token *t = peek(&token); + return peek(t)->value; +} + +/********************************* + * tk is on the opening (. + * Look ahead and return token that is past the closing ). + */ + +Token *Lexer::peekPastParen(Token *tk) +{ + //printf("peekPastParen()\n"); + int parens = 1; + int curlynest = 0; + while (1) + { + tk = peek(tk); + //tk->print(); + switch (tk->value) + { + case TOKlparen: + parens++; + continue; + + case TOKrparen: + --parens; + if (parens) + continue; + tk = peek(tk); + break; + + case TOKlcurly: + curlynest++; + continue; + + case TOKrcurly: + if (--curlynest >= 0) + continue; + break; + + case TOKsemicolon: + if (curlynest) + continue; + break; + + case TOKeof: + break; + + default: + continue; + } + return tk; + } +} + +/********************************** + * Determine if string is a valid Identifier. + * Placed here because of commonality with Lexer functionality. + * Returns: + * 0 invalid + */ + +int Lexer::isValidIdentifier(char *p) +{ + size_t len; + size_t idx; + + if (!p || !*p) + goto Linvalid; + + if (*p >= '0' && *p <= '9') // beware of isdigit() on signed chars + goto Linvalid; + + len = strlen(p); + idx = 0; + while (p[idx]) + { dchar_t dc; + + const char *q = utf_decodeChar((unsigned char *)p, len, &idx, &dc); + if (q) + goto Linvalid; + + if (!((dc >= 0x80 && isUniAlpha(dc)) || isalnum(dc) || dc == '_')) + goto Linvalid; + } + return 1; + +Linvalid: + return 0; +} + +/**************************** + * Turn next token in buffer into a token. + */ + +void Lexer::scan(Token *t) +{ + unsigned lastLine = loc.linnum; + unsigned linnum; + + t->blockComment = NULL; + t->lineComment = NULL; + while (1) + { + t->ptr = p; + //printf("p = %p, *p = '%c'\n",p,*p); + switch (*p) + { + case 0: + case 0x1A: + t->value = TOKeof; // end of file + return; + + case ' ': + case '\t': + case '\v': + case '\f': + p++; + continue; // skip white space + + case '\r': + p++; + if (*p != '\n') // if CR stands by itself + loc.linnum++; + continue; // skip white space + + case '\n': + p++; + loc.linnum++; + continue; // skip white space + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + t->value = number(t); + return; + +#if CSTRINGS + case '\'': + t->value = charConstant(t, 0); + return; + + case '"': + t->value = stringConstant(t,0); + return; + + case 'l': + case 'L': + if (p[1] == '\'') + { + p++; + t->value = charConstant(t, 1); + return; + } + else if (p[1] == '"') + { + p++; + t->value = stringConstant(t, 1); + return; + } +#else + case '\'': + t->value = charConstant(t,0); + return; + + case 'r': + if (p[1] != '"') + goto case_ident; + p++; + case '`': + t->value = wysiwygStringConstant(t, *p); + return; + + case 'x': + if (p[1] != '"') + goto case_ident; + p++; + t->value = hexStringConstant(t); + return; + +#if DMDV2 + case 'q': + if (p[1] == '"') + { + p++; + t->value = delimitedStringConstant(t); + return; + } + else if (p[1] == '{') + { + p++; + t->value = tokenStringConstant(t); + return; + } + else + goto case_ident; +#endif + + case '"': + t->value = escapeStringConstant(t,0); + return; + +#if ! TEXTUAL_ASSEMBLY_OUT + case '\\': // escaped string literal + { unsigned c; + unsigned char *pstart = p; + + stringbuffer.reset(); + do + { + p++; + switch (*p) + { + case 'u': + case 'U': + case '&': + c = escapeSequence(); + stringbuffer.writeUTF8(c); + break; + + default: + c = escapeSequence(); + stringbuffer.writeByte(c); + break; + } + } while (*p == '\\'); + t->len = stringbuffer.offset; + stringbuffer.writeByte(0); + t->ustring = (unsigned char *)mem.malloc(stringbuffer.offset); + memcpy(t->ustring, stringbuffer.data, stringbuffer.offset); + t->postfix = 0; + t->value = TOKstring; +#if DMDV2 + if (!global.params.useDeprecated) + error("Escape String literal %.*s is deprecated, use double quoted string literal \"%.*s\" instead", p - pstart, pstart, p - pstart, pstart); +#endif + return; + } +#endif + + case 'l': + case 'L': +#endif + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'm': case 'n': case 'o': +#if DMDV2 + case 'p': /*case 'q': case 'r':*/ case 's': case 't': +#else + case 'p': case 'q': /*case 'r':*/ case 's': case 't': +#endif + case 'u': case 'v': case 'w': /*case 'x':*/ case 'y': + case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': + case '_': + case_ident: + { unsigned char c; + + while (1) + { + c = *++p; + if (isidchar(c)) + continue; + else if (c & 0x80) + { unsigned char *s = p; + unsigned u = decodeUTF(); + if (isUniAlpha(u)) + continue; + error("char 0x%04x not allowed in identifier", u); + p = s; + } + break; + } + + StringValue *sv = stringtable.update((char *)t->ptr, p - t->ptr); + Identifier *id = (Identifier *) sv->ptrvalue; + if (!id) + { id = new Identifier(sv->lstring.string,TOKidentifier); + sv->ptrvalue = id; + } + t->ident = id; + t->value = (enum TOK) id->value; + anyToken = 1; + if (*t->ptr == '_') // if special identifier token + { + static char date[11+1]; + static char time[8+1]; + static char timestamp[24+1]; + + if (!date[0]) // lazy evaluation + { time_t t; + char *p; + + ::time(&t); + p = ctime(&t); + assert(p); + sprintf(date, "%.6s %.4s", p + 4, p + 20); + sprintf(time, "%.8s", p + 11); + sprintf(timestamp, "%.24s", p); + } + +#if DMDV1 + if (mod && id == Id::FILE) + { + t->ustring = (unsigned char *)(loc.filename ? loc.filename : mod->ident->toChars()); + goto Lstr; + } + else if (mod && id == Id::LINE) + { + t->value = TOKint64v; + t->uns64value = loc.linnum; + } + else +#endif + if (id == Id::DATE) + { + t->ustring = (unsigned char *)date; + goto Lstr; + } + else if (id == Id::TIME) + { + t->ustring = (unsigned char *)time; + goto Lstr; + } + else if (id == Id::VENDOR) + { + t->ustring = (unsigned char *)"Digital Mars D"; + goto Lstr; + } + else if (id == Id::TIMESTAMP) + { + t->ustring = (unsigned char *)timestamp; + Lstr: + t->value = TOKstring; + t->postfix = 0; + t->len = strlen((char *)t->ustring); + } + else if (id == Id::VERSIONX) + { unsigned major = 0; + unsigned minor = 0; + + for (const char *p = global.version + 1; 1; p++) + { + char c = *p; + if (isdigit((unsigned char)c)) + minor = minor * 10 + c - '0'; + else if (c == '.') + { major = minor; + minor = 0; + } + else + break; + } + t->value = TOKint64v; + t->uns64value = major * 1000 + minor; + } +#if DMDV2 + else if (id == Id::EOFX) + { + t->value = TOKeof; + // Advance scanner to end of file + while (!(*p == 0 || *p == 0x1A)) + p++; + } +#endif + } + //printf("t->value = %d\n",t->value); + return; + } + + case '/': + p++; + switch (*p) + { + case '=': + p++; + t->value = TOKdivass; + return; + + case '*': + p++; + linnum = loc.linnum; + while (1) + { + while (1) + { unsigned char c = *p; + switch (c) + { + case '/': + break; + + case '\n': + loc.linnum++; + p++; + continue; + + case '\r': + p++; + if (*p != '\n') + loc.linnum++; + continue; + + case 0: + case 0x1A: + error("unterminated /* */ comment"); + p = end; + t->value = TOKeof; + return; + + default: + if (c & 0x80) + { unsigned u = decodeUTF(); + if (u == PS || u == LS) + loc.linnum++; + } + p++; + continue; + } + break; + } + p++; + if (p[-2] == '*' && p - 3 != t->ptr) + break; + } + if (commentToken) + { + t->value = TOKcomment; + return; + } + else if (doDocComment && t->ptr[2] == '*' && p - 4 != t->ptr) + { // if /** but not /**/ + getDocComment(t, lastLine == linnum); + } + continue; + + case '/': // do // style comments + linnum = loc.linnum; + while (1) + { unsigned char c = *++p; + switch (c) + { + case '\n': + break; + + case '\r': + if (p[1] == '\n') + p++; + break; + + case 0: + case 0x1A: + if (commentToken) + { + p = end; + t->value = TOKcomment; + return; + } + if (doDocComment && t->ptr[2] == '/') + getDocComment(t, lastLine == linnum); + p = end; + t->value = TOKeof; + return; + + default: + if (c & 0x80) + { unsigned u = decodeUTF(); + if (u == PS || u == LS) + break; + } + continue; + } + break; + } + + if (commentToken) + { + p++; + loc.linnum++; + t->value = TOKcomment; + return; + } + if (doDocComment && t->ptr[2] == '/') + getDocComment(t, lastLine == linnum); + + p++; + loc.linnum++; + continue; + + case '+': + { int nest; + + linnum = loc.linnum; + p++; + nest = 1; + while (1) + { unsigned char c = *p; + switch (c) + { + case '/': + p++; + if (*p == '+') + { + p++; + nest++; + } + continue; + + case '+': + p++; + if (*p == '/') + { + p++; + if (--nest == 0) + break; + } + continue; + + case '\r': + p++; + if (*p != '\n') + loc.linnum++; + continue; + + case '\n': + loc.linnum++; + p++; + continue; + + case 0: + case 0x1A: + error("unterminated /+ +/ comment"); + p = end; + t->value = TOKeof; + return; + + default: + if (c & 0x80) + { unsigned u = decodeUTF(); + if (u == PS || u == LS) + loc.linnum++; + } + p++; + continue; + } + break; + } + if (commentToken) + { + t->value = TOKcomment; + return; + } + if (doDocComment && t->ptr[2] == '+' && p - 4 != t->ptr) + { // if /++ but not /++/ + getDocComment(t, lastLine == linnum); + } + continue; + } + } + t->value = TOKdiv; + return; + + case '.': + p++; + if (isdigit(*p)) + { /* Note that we don't allow ._1 and ._ as being + * valid floating point numbers. + */ + p--; + t->value = inreal(t); + } + else if (p[0] == '.') + { + if (p[1] == '.') + { p += 2; + t->value = TOKdotdotdot; + } + else + { p++; + t->value = TOKslice; + } + } + else + t->value = TOKdot; + return; + + case '&': + p++; + if (*p == '=') + { p++; + t->value = TOKandass; + } + else if (*p == '&') + { p++; + t->value = TOKandand; + } + else + t->value = TOKand; + return; + + case '|': + p++; + if (*p == '=') + { p++; + t->value = TOKorass; + } + else if (*p == '|') + { p++; + t->value = TOKoror; + } + else + t->value = TOKor; + return; + + case '-': + p++; + if (*p == '=') + { p++; + t->value = TOKminass; + } +#if 0 + else if (*p == '>') + { p++; + t->value = TOKarrow; + } +#endif + else if (*p == '-') + { p++; + t->value = TOKminusminus; + } + else + t->value = TOKmin; + return; + + case '+': + p++; + if (*p == '=') + { p++; + t->value = TOKaddass; + } + else if (*p == '+') + { p++; + t->value = TOKplusplus; + } + else + t->value = TOKadd; + return; + + case '<': + p++; + if (*p == '=') + { p++; + t->value = TOKle; // <= + } + else if (*p == '<') + { p++; + if (*p == '=') + { p++; + t->value = TOKshlass; // <<= + } + else + t->value = TOKshl; // << + } + else if (*p == '>') + { p++; + if (*p == '=') + { p++; + t->value = TOKleg; // <>= + } + else + t->value = TOKlg; // <> + } + else + t->value = TOKlt; // < + return; + + case '>': + p++; + if (*p == '=') + { p++; + t->value = TOKge; // >= + } + else if (*p == '>') + { p++; + if (*p == '=') + { p++; + t->value = TOKshrass; // >>= + } + else if (*p == '>') + { p++; + if (*p == '=') + { p++; + t->value = TOKushrass; // >>>= + } + else + t->value = TOKushr; // >>> + } + else + t->value = TOKshr; // >> + } + else + t->value = TOKgt; // > + return; + + case '!': + p++; + if (*p == '=') + { p++; + if (*p == '=' && global.params.Dversion == 1) + { p++; + t->value = TOKnotidentity; // !== + } + else + t->value = TOKnotequal; // != + } + else if (*p == '<') + { p++; + if (*p == '>') + { p++; + if (*p == '=') + { p++; + t->value = TOKunord; // !<>= + } + else + t->value = TOKue; // !<> + } + else if (*p == '=') + { p++; + t->value = TOKug; // !<= + } + else + t->value = TOKuge; // !< + } + else if (*p == '>') + { p++; + if (*p == '=') + { p++; + t->value = TOKul; // !>= + } + else + t->value = TOKule; // !> + } + else + t->value = TOKnot; // ! + return; + + case '=': + p++; + if (*p == '=') + { p++; + if (*p == '=' && global.params.Dversion == 1) + { p++; + t->value = TOKidentity; // === + } + else + t->value = TOKequal; // == + } +#if DMDV2 + else if (*p == '>') + { p++; + t->value = TOKgoesto; // => + } +#endif + else + t->value = TOKassign; // = + return; + + case '~': + p++; + if (*p == '=') + { p++; + t->value = TOKcatass; // ~= + } + else + t->value = TOKtilde; // ~ + return; + +#if DMDV2 + case '^': + p++; + if (*p == '^') + { p++; + if (*p == '=') + { p++; + t->value = TOKpowass; // ^^= + } + else + t->value = TOKpow; // ^^ + } + else if (*p == '=') + { p++; + t->value = TOKxorass; // ^= + } + else + t->value = TOKxor; // ^ + return; +#endif + +#define SINGLE(c,tok) case c: p++; t->value = tok; return; + + SINGLE('(', TOKlparen) + SINGLE(')', TOKrparen) + SINGLE('[', TOKlbracket) + SINGLE(']', TOKrbracket) + SINGLE('{', TOKlcurly) + SINGLE('}', TOKrcurly) + SINGLE('?', TOKquestion) + SINGLE(',', TOKcomma) + SINGLE(';', TOKsemicolon) + SINGLE(':', TOKcolon) + SINGLE('$', TOKdollar) +#if DMDV2 + SINGLE('@', TOKat) +#endif +#undef SINGLE + +#define DOUBLE(c1,tok1,c2,tok2) \ + case c1: \ + p++; \ + if (*p == c2) \ + { p++; \ + t->value = tok2; \ + } \ + else \ + t->value = tok1; \ + return; + + DOUBLE('*', TOKmul, '=', TOKmulass) + DOUBLE('%', TOKmod, '=', TOKmodass) +#if DMDV1 + DOUBLE('^', TOKxor, '=', TOKxorass) +#endif +#undef DOUBLE + + case '#': + p++; + pragma(); + continue; + + default: + { unsigned c = *p; + + if (c & 0x80) + { c = decodeUTF(); + + // Check for start of unicode identifier + if (isUniAlpha(c)) + goto case_ident; + + if (c == PS || c == LS) + { + loc.linnum++; + p++; + continue; + } + } + if (c < 0x80 && isprint(c)) + error("unsupported char '%c'", c); + else + error("unsupported char 0x%02x", c); + p++; + continue; + } + } + } +} + +/******************************************* + * Parse escape sequence. + */ + +unsigned Lexer::escapeSequence() +{ unsigned c = *p; + +#ifdef TEXTUAL_ASSEMBLY_OUT + return c; +#endif + int n; + int ndigits; + + switch (c) + { + case '\'': + case '"': + case '?': + case '\\': + Lconsume: + p++; + break; + + case 'a': c = 7; goto Lconsume; + case 'b': c = 8; goto Lconsume; + case 'f': c = 12; goto Lconsume; + case 'n': c = 10; goto Lconsume; + case 'r': c = 13; goto Lconsume; + case 't': c = 9; goto Lconsume; + case 'v': c = 11; goto Lconsume; + + case 'u': + ndigits = 4; + goto Lhex; + case 'U': + ndigits = 8; + goto Lhex; + case 'x': + ndigits = 2; + Lhex: + p++; + c = *p; + if (ishex(c)) + { unsigned v; + + n = 0; + v = 0; + while (1) + { + if (isdigit(c)) + c -= '0'; + else if (islower(c)) + c -= 'a' - 10; + else + c -= 'A' - 10; + v = v * 16 + c; + c = *++p; + if (++n == ndigits) + break; + if (!ishex(c)) + { error("escape hex sequence has %d hex digits instead of %d", n, ndigits); + break; + } + } + if (ndigits != 2 && !utf_isValidDchar(v)) + { error("invalid UTF character \\U%08x", v); + v = '?'; // recover with valid UTF character + } + c = v; + } + else + error("undefined escape hex sequence \\%c\n",c); + break; + + case '&': // named character entity + for (unsigned char *idstart = ++p; 1; p++) + { + switch (*p) + { + case ';': + c = HtmlNamedEntity(idstart, p - idstart); + if (c == ~0) + { error("unnamed character entity &%.*s;", (int)(p - idstart), idstart); + c = ' '; + } + p++; + break; + + default: + if (isalpha(*p) || + (p != idstart + 1 && isdigit(*p))) + continue; + error("unterminated named entity"); + break; + } + break; + } + break; + + case 0: + case 0x1A: // end of file + c = '\\'; + break; + + default: + if (isoctal(c)) + { unsigned v; + + n = 0; + v = 0; + do + { + v = v * 8 + (c - '0'); + c = *++p; + } while (++n < 3 && isoctal(c)); + c = v; + if (c > 0xFF) + error("0%03o is larger than a byte", c); + } + else + error("undefined escape sequence \\%c\n",c); + break; + } + return c; +} + +/************************************** + */ + +TOK Lexer::wysiwygStringConstant(Token *t, int tc) +{ unsigned c; + Loc start = loc; + + p++; + stringbuffer.reset(); + while (1) + { + c = *p++; + switch (c) + { + case '\n': + loc.linnum++; + break; + + case '\r': + if (*p == '\n') + continue; // ignore + c = '\n'; // treat EndOfLine as \n character + loc.linnum++; + break; + + case 0: + case 0x1A: + error("unterminated string constant starting at %s", start.toChars()); + t->ustring = (unsigned char *)""; + t->len = 0; + t->postfix = 0; + return TOKstring; + + case '"': + case '`': + if (c == tc) + { + t->len = stringbuffer.offset; + stringbuffer.writeByte(0); + t->ustring = (unsigned char *)mem.malloc(stringbuffer.offset); + memcpy(t->ustring, stringbuffer.data, stringbuffer.offset); + stringPostfix(t); + return TOKstring; + } + break; + + default: + if (c & 0x80) + { p--; + unsigned u = decodeUTF(); + p++; + if (u == PS || u == LS) + loc.linnum++; + stringbuffer.writeUTF8(u); + continue; + } + break; + } + stringbuffer.writeByte(c); + } +} + +/************************************** + * Lex hex strings: + * x"0A ae 34FE BD" + */ + +TOK Lexer::hexStringConstant(Token *t) +{ unsigned c; + Loc start = loc; + unsigned n = 0; + unsigned v; + + p++; + stringbuffer.reset(); + while (1) + { + c = *p++; + switch (c) + { + case ' ': + case '\t': + case '\v': + case '\f': + continue; // skip white space + + case '\r': + if (*p == '\n') + continue; // ignore + // Treat isolated '\r' as if it were a '\n' + case '\n': + loc.linnum++; + continue; + + case 0: + case 0x1A: + error("unterminated string constant starting at %s", start.toChars()); + t->ustring = (unsigned char *)""; + t->len = 0; + t->postfix = 0; + return TOKstring; + + case '"': + if (n & 1) + { error("odd number (%d) of hex characters in hex string", n); + stringbuffer.writeByte(v); + } + t->len = stringbuffer.offset; + stringbuffer.writeByte(0); + t->ustring = (unsigned char *)mem.malloc(stringbuffer.offset); + memcpy(t->ustring, stringbuffer.data, stringbuffer.offset); + stringPostfix(t); + return TOKstring; + + default: + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'a' && c <= 'f') + c -= 'a' - 10; + else if (c >= 'A' && c <= 'F') + c -= 'A' - 10; + else if (c & 0x80) + { p--; + unsigned u = decodeUTF(); + p++; + if (u == PS || u == LS) + loc.linnum++; + else + error("non-hex character \\u%04x", u); + } + else + error("non-hex character '%c'", c); + if (n & 1) + { v = (v << 4) | c; + stringbuffer.writeByte(v); + } + else + v = c; + n++; + break; + } + } +} + + +#if DMDV2 +/************************************** + * Lex delimited strings: + * q"(foo(xxx))" // "foo(xxx)" + * q"[foo(]" // "foo(" + * q"/foo]/" // "foo]" + * q"HERE + * foo + * HERE" // "foo\n" + * Input: + * p is on the " + */ + +TOK Lexer::delimitedStringConstant(Token *t) +{ unsigned c; + Loc start = loc; + unsigned delimleft = 0; + unsigned delimright = 0; + unsigned nest = 1; + unsigned nestcount; + Identifier *hereid = NULL; + unsigned blankrol = 0; + unsigned startline = 0; + + p++; + stringbuffer.reset(); + while (1) + { + c = *p++; + //printf("c = '%c'\n", c); + switch (c) + { + case '\n': + Lnextline: + loc.linnum++; + startline = 1; + if (blankrol) + { blankrol = 0; + continue; + } + if (hereid) + { + stringbuffer.writeUTF8(c); + continue; + } + break; + + case '\r': + if (*p == '\n') + continue; // ignore + c = '\n'; // treat EndOfLine as \n character + goto Lnextline; + + case 0: + case 0x1A: + goto Lerror; + + default: + if (c & 0x80) + { p--; + c = decodeUTF(); + p++; + if (c == PS || c == LS) + goto Lnextline; + } + break; + } + if (delimleft == 0) + { delimleft = c; + nest = 1; + nestcount = 1; + if (c == '(') + delimright = ')'; + else if (c == '{') + delimright = '}'; + else if (c == '[') + delimright = ']'; + else if (c == '<') + delimright = '>'; + else if (isalpha(c) || c == '_' || (c >= 0x80 && isUniAlpha(c))) + { // Start of identifier; must be a heredoc + Token t; + p--; + scan(&t); // read in heredoc identifier + if (t.value != TOKidentifier) + { error("identifier expected for heredoc, not %s", t.toChars()); + delimright = c; + } + else + { hereid = t.ident; + //printf("hereid = '%s'\n", hereid->toChars()); + blankrol = 1; + } + nest = 0; + } + else + { delimright = c; + nest = 0; +#if DMDV2 + if (isspace(c)) + error("delimiter cannot be whitespace"); +#endif + } + } + else + { + if (blankrol) + { error("heredoc rest of line should be blank"); + blankrol = 0; + continue; + } + if (nest == 1) + { + if (c == delimleft) + nestcount++; + else if (c == delimright) + { nestcount--; + if (nestcount == 0) + goto Ldone; + } + } + else if (c == delimright) + goto Ldone; + if (startline && isalpha(c) +#if DMDV2 + && hereid +#endif + ) + { Token t; + unsigned char *psave = p; + p--; + scan(&t); // read in possible heredoc identifier + //printf("endid = '%s'\n", t.ident->toChars()); + if (t.value == TOKidentifier && t.ident->equals(hereid)) + { /* should check that rest of line is blank + */ + goto Ldone; + } + p = psave; + } + stringbuffer.writeUTF8(c); + startline = 0; + } + } + +Ldone: + if (*p == '"') + p++; + else + error("delimited string must end in %c\"", delimright); + t->len = stringbuffer.offset; + stringbuffer.writeByte(0); + t->ustring = (unsigned char *)mem.malloc(stringbuffer.offset); + memcpy(t->ustring, stringbuffer.data, stringbuffer.offset); + stringPostfix(t); + return TOKstring; + +Lerror: + error("unterminated string constant starting at %s", start.toChars()); + t->ustring = (unsigned char *)""; + t->len = 0; + t->postfix = 0; + return TOKstring; +} + +/************************************** + * Lex delimited strings: + * q{ foo(xxx) } // " foo(xxx) " + * q{foo(} // "foo(" + * q{{foo}"}"} // "{foo}"}"" + * Input: + * p is on the q + */ + +TOK Lexer::tokenStringConstant(Token *t) +{ + unsigned nest = 1; + Loc start = loc; + unsigned char *pstart = ++p; + + while (1) + { Token tok; + + scan(&tok); + switch (tok.value) + { + case TOKlcurly: + nest++; + continue; + + case TOKrcurly: + if (--nest == 0) + goto Ldone; + continue; + + case TOKeof: + goto Lerror; + + default: + continue; + } + } + +Ldone: + t->len = p - 1 - pstart; + t->ustring = (unsigned char *)mem.malloc(t->len + 1); + memcpy(t->ustring, pstart, t->len); + t->ustring[t->len] = 0; + stringPostfix(t); + return TOKstring; + +Lerror: + error("unterminated token string constant starting at %s", start.toChars()); + t->ustring = (unsigned char *)""; + t->len = 0; + t->postfix = 0; + return TOKstring; +} + +#endif + + +/************************************** + */ + +TOK Lexer::escapeStringConstant(Token *t, int wide) +{ unsigned c; + Loc start = loc; + + p++; + stringbuffer.reset(); + while (1) + { + c = *p++; + switch (c) + { +#if !( TEXTUAL_ASSEMBLY_OUT ) + case '\\': + switch (*p) + { + case 'u': + case 'U': + case '&': + c = escapeSequence(); + stringbuffer.writeUTF8(c); + continue; + + default: + c = escapeSequence(); + break; + } + break; +#endif + case '\n': + loc.linnum++; + break; + + case '\r': + if (*p == '\n') + continue; // ignore + c = '\n'; // treat EndOfLine as \n character + loc.linnum++; + break; + + case '"': + t->len = stringbuffer.offset; + stringbuffer.writeByte(0); + t->ustring = (unsigned char *)mem.malloc(stringbuffer.offset); + memcpy(t->ustring, stringbuffer.data, stringbuffer.offset); + stringPostfix(t); + return TOKstring; + + case 0: + case 0x1A: + p--; + error("unterminated string constant starting at %s", start.toChars()); + t->ustring = (unsigned char *)""; + t->len = 0; + t->postfix = 0; + return TOKstring; + + default: + if (c & 0x80) + { + p--; + c = decodeUTF(); + if (c == LS || c == PS) + { c = '\n'; + loc.linnum++; + } + p++; + stringbuffer.writeUTF8(c); + continue; + } + break; + } + stringbuffer.writeByte(c); + } +} + +/************************************** + */ + +TOK Lexer::charConstant(Token *t, int wide) +{ + unsigned c; + TOK tk = TOKcharv; + + //printf("Lexer::charConstant\n"); + p++; + c = *p++; + switch (c) + { +#if ! TEXTUAL_ASSEMBLY_OUT + case '\\': + switch (*p) + { + case 'u': + t->uns64value = escapeSequence(); + tk = TOKwcharv; + break; + + case 'U': + case '&': + t->uns64value = escapeSequence(); + tk = TOKdcharv; + break; + + default: + t->uns64value = escapeSequence(); + break; + } + break; +#endif + case '\n': + L1: + loc.linnum++; + case '\r': + case 0: + case 0x1A: + case '\'': + error("unterminated character constant"); + return tk; + + default: + if (c & 0x80) + { + p--; + c = decodeUTF(); + p++; + if (c == LS || c == PS) + goto L1; + if (c < 0xD800 || (c >= 0xE000 && c < 0xFFFE)) + tk = TOKwcharv; + else + tk = TOKdcharv; + } + t->uns64value = c; + break; + } + + if (*p != '\'') + { error("unterminated character constant"); + return tk; + } + p++; + return tk; +} + +/*************************************** + * Get postfix of string literal. + */ + +void Lexer::stringPostfix(Token *t) +{ + switch (*p) + { + case 'c': + case 'w': + case 'd': + t->postfix = *p; + p++; + break; + + default: + t->postfix = 0; + break; + } +} + +/*************************************** + * Read \u or \U unicode sequence + * Input: + * u 'u' or 'U' + */ + +#if 0 +unsigned Lexer::wchar(unsigned u) +{ + unsigned value; + unsigned n; + unsigned char c; + unsigned nchars; + + nchars = (u == 'U') ? 8 : 4; + value = 0; + for (n = 0; 1; n++) + { + ++p; + if (n == nchars) + break; + c = *p; + if (!ishex(c)) + { error("\\%c sequence must be followed by %d hex characters", u, nchars); + break; + } + if (isdigit(c)) + c -= '0'; + else if (islower(c)) + c -= 'a' - 10; + else + c -= 'A' - 10; + value <<= 4; + value |= c; + } + return value; +} +#endif + +/************************************** + * Read in a number. + * If it's an integer, store it in tok.TKutok.Vlong. + * integers can be decimal, octal or hex + * Handle the suffixes U, UL, LU, L, etc. + * If it's double, store it in tok.TKutok.Vdouble. + * Returns: + * TKnum + * TKdouble,... + */ + +TOK Lexer::number(Token *t) +{ + // We use a state machine to collect numbers + enum STATE { STATE_initial, STATE_0, STATE_decimal, STATE_octal, STATE_octale, + STATE_hex, STATE_binary, STATE_hex0, STATE_binary0, + STATE_hexh, STATE_error }; + enum STATE state; + + enum FLAGS + { FLAGS_decimal = 1, // decimal + FLAGS_unsigned = 2, // u or U suffix + FLAGS_long = 4, // l or L suffix + }; + enum FLAGS flags = FLAGS_decimal; + + int base; + unsigned c; + unsigned char *start; + TOK result; + + //printf("Lexer::number()\n"); + state = STATE_initial; + base = 0; + stringbuffer.reset(); + start = p; + while (1) + { + c = *p; + switch (state) + { + case STATE_initial: // opening state + if (c == '0') + state = STATE_0; + else + state = STATE_decimal; + break; + + case STATE_0: + flags = (FLAGS) (flags & ~FLAGS_decimal); + switch (c) + { +#if ZEROH + case 'H': // 0h + case 'h': + goto hexh; +#endif + case 'X': + case 'x': + state = STATE_hex0; + break; + + case '.': + if (p[1] == '.') // .. is a separate token + goto done; + case 'i': + case 'f': + case 'F': + goto real; +#if ZEROH + case 'E': + case 'e': + goto case_hex; +#endif + case 'B': + case 'b': + state = STATE_binary0; + break; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + state = STATE_octal; + break; + +#if ZEROH + case '8': case '9': case 'A': + case 'C': case 'D': case 'F': + case 'a': case 'c': case 'd': case 'f': + case_hex: + state = STATE_hexh; + break; +#endif + case '_': + state = STATE_octal; + p++; + continue; + + case 'L': + if (p[1] == 'i') + goto real; + goto done; + + default: + goto done; + } + break; + + case STATE_decimal: // reading decimal number + if (!isdigit(c)) + { +#if ZEROH + if (ishex(c) + || c == 'H' || c == 'h' + ) + goto hexh; +#endif + if (c == '_') // ignore embedded _ + { p++; + continue; + } + if (c == '.' && p[1] != '.') + { +#if DMDV2 + if (isalpha(p[1]) || p[1] == '_') + goto done; +#endif + goto real; + } + else if (c == 'i' || c == 'f' || c == 'F' || + c == 'e' || c == 'E') + { + real: // It's a real number. Back up and rescan as a real + p = start; + return inreal(t); + } + else if (c == 'L' && p[1] == 'i') + goto real; + goto done; + } + break; + + case STATE_hex0: // reading hex number + case STATE_hex: + if (!ishex(c)) + { + if (c == '_') // ignore embedded _ + { p++; + continue; + } + if (c == '.' && p[1] != '.') + goto real; + if (c == 'P' || c == 'p' || c == 'i') + goto real; + if (state == STATE_hex0) + error("Hex digit expected, not '%c'", c); + goto done; + } + state = STATE_hex; + break; + +#if ZEROH + hexh: + state = STATE_hexh; + case STATE_hexh: // parse numbers like 0FFh + if (!ishex(c)) + { + if (c == 'H' || c == 'h') + { + p++; + base = 16; + goto done; + } + else + { + // Check for something like 1E3 or 0E24 + if (memchr((char *)stringbuffer.data, 'E', stringbuffer.offset) || + memchr((char *)stringbuffer.data, 'e', stringbuffer.offset)) + goto real; + error("Hex digit expected, not '%c'", c); + goto done; + } + } + break; +#endif + + case STATE_octal: // reading octal number + case STATE_octale: // reading octal number with non-octal digits + if (!isoctal(c)) + { +#if ZEROH + if (ishex(c) + || c == 'H' || c == 'h' + ) + goto hexh; +#endif + if (c == '_') // ignore embedded _ + { p++; + continue; + } + if (c == '.' && p[1] != '.') + goto real; + if (c == 'i') + goto real; + if (isdigit(c)) + { + state = STATE_octale; + } + else + goto done; + } + break; + + case STATE_binary0: // starting binary number + case STATE_binary: // reading binary number + if (c != '0' && c != '1') + { +#if ZEROH + if (ishex(c) + || c == 'H' || c == 'h' + ) + goto hexh; +#endif + if (c == '_') // ignore embedded _ + { p++; + continue; + } + if (state == STATE_binary0) + { error("binary digit expected"); + state = STATE_error; + break; + } + else + goto done; + } + state = STATE_binary; + break; + + case STATE_error: // for error recovery + if (!isdigit(c)) // scan until non-digit + goto done; + break; + + default: + assert(0); + } + stringbuffer.writeByte(c); + p++; + } +done: + stringbuffer.writeByte(0); // terminate string + if (state == STATE_octale) + error("Octal digit expected"); + + uinteger_t n; // unsigned >=64 bit integer type + + if (stringbuffer.offset == 2 && (state == STATE_decimal || state == STATE_0)) + n = stringbuffer.data[0] - '0'; + else + { + // Convert string to integer +#if __DMC__ + errno = 0; + n = strtoull((char *)stringbuffer.data,NULL,base); + if (errno == ERANGE) + error("integer overflow"); +#else + // Not everybody implements strtoull() + char *p = (char *)stringbuffer.data; + int r = 10, d; + + if (*p == '0') + { + if (p[1] == 'x' || p[1] == 'X') + p += 2, r = 16; + else if (p[1] == 'b' || p[1] == 'B') + p += 2, r = 2; + else if (isdigit((unsigned char)p[1])) + p += 1, r = 8; + } + + n = 0; + while (1) + { + if (*p >= '0' && *p <= '9') + d = *p - '0'; + else if (*p >= 'a' && *p <= 'z') + d = *p - 'a' + 10; + else if (*p >= 'A' && *p <= 'Z') + d = *p - 'A' + 10; + else + break; + if (d >= r) + break; + uinteger_t n2 = n * r; + //printf("n2 / r = %llx, n = %llx\n", n2/r, n); + if (n2 / r != n || n2 + d < n) + { + error ("integer overflow"); + break; + } + + n = n2 + d; + p++; + } +#endif + if (sizeof(n) > 8 && + n > 0xFFFFFFFFFFFFFFFFULL) // if n needs more than 64 bits + error("integer overflow"); + } + + // Parse trailing 'u', 'U', 'l' or 'L' in any combination + const unsigned char *psuffix = p; + while (1) + { unsigned char f; + + switch (*p) + { case 'U': + case 'u': + f = FLAGS_unsigned; + goto L1; + + case 'l': + if (1 || !global.params.useDeprecated) + error("'l' suffix is deprecated, use 'L' instead"); + case 'L': + f = FLAGS_long; + L1: + p++; + if (flags & f) + error("unrecognized token"); + flags = (FLAGS) (flags | f); + continue; + default: + break; + } + break; + } + +#if DMDV2 + if (state == STATE_octal && n >= 8 && !global.params.useDeprecated) + error("octal literals 0%llo%.*s are deprecated, use std.conv.octal!%llo%.*s instead", + n, p - psuffix, psuffix, n, p - psuffix, psuffix); +#endif + + switch (flags) + { + case 0: + /* Octal or Hexadecimal constant. + * First that fits: int, uint, long, ulong + */ + if (n & 0x8000000000000000LL) + result = TOKuns64v; + else if (n & 0xFFFFFFFF00000000LL) + result = TOKint64v; + else if (n & 0x80000000) + result = TOKuns32v; + else + result = TOKint32v; + break; + + case FLAGS_decimal: + /* First that fits: int, long, long long + */ + if (n & 0x8000000000000000LL) + { error("signed integer overflow"); + result = TOKuns64v; + } + else if (n & 0xFFFFFFFF80000000LL) + result = TOKint64v; + else + result = TOKint32v; + break; + + case FLAGS_unsigned: + case FLAGS_decimal | FLAGS_unsigned: + /* First that fits: uint, ulong + */ + if (n & 0xFFFFFFFF00000000LL) + result = TOKuns64v; + else + result = TOKuns32v; + break; + + case FLAGS_decimal | FLAGS_long: + if (n & 0x8000000000000000LL) + { error("signed integer overflow"); + result = TOKuns64v; + } + else + result = TOKint64v; + break; + + case FLAGS_long: + if (n & 0x8000000000000000LL) + result = TOKuns64v; + else + result = TOKint64v; + break; + + case FLAGS_unsigned | FLAGS_long: + case FLAGS_decimal | FLAGS_unsigned | FLAGS_long: + result = TOKuns64v; + break; + + default: + #ifdef DEBUG + printf("%x\n",flags); + #endif + assert(0); + } + t->uns64value = n; + return result; +} + +/************************************** + * Read in characters, converting them to real. + * Bugs: + * Exponent overflow not detected. + * Too much requested precision is not detected. + */ + +TOK Lexer::inreal(Token *t) +#ifdef __DMC__ +__in +{ + assert(*p == '.' || isdigit(*p)); +} +__out (result) +{ + switch (result) + { + case TOKfloat32v: + case TOKfloat64v: + case TOKfloat80v: + case TOKimaginary32v: + case TOKimaginary64v: + case TOKimaginary80v: + break; + + default: + assert(0); + } +} +__body +#endif /* __DMC__ */ +{ int dblstate; + unsigned c; + char hex; // is this a hexadecimal-floating-constant? + TOK result; + + //printf("Lexer::inreal()\n"); + stringbuffer.reset(); + dblstate = 0; + hex = 0; +Lnext: + while (1) + { + // Get next char from input + c = *p++; + //printf("dblstate = %d, c = '%c'\n", dblstate, c); + while (1) + { + switch (dblstate) + { + case 0: // opening state + if (c == '0') + dblstate = 9; + else if (c == '.') + dblstate = 3; + else + dblstate = 1; + break; + + case 9: + dblstate = 1; + if (c == 'X' || c == 'x') + { hex++; + break; + } + case 1: // digits to left of . + case 3: // digits to right of . + case 7: // continuing exponent digits + if (!isdigit(c) && !(hex && isxdigit(c))) + { + if (c == '_') + goto Lnext; // ignore embedded '_' + dblstate++; + continue; + } + break; + + case 2: // no more digits to left of . + if (c == '.') + { dblstate++; + break; + } + case 4: // no more digits to right of . + if ((c == 'E' || c == 'e') || + hex && (c == 'P' || c == 'p')) + { dblstate = 5; + hex = 0; // exponent is always decimal + break; + } + if (hex) + error("binary-exponent-part required"); + goto done; + + case 5: // looking immediately to right of E + dblstate++; + if (c == '-' || c == '+') + break; + case 6: // 1st exponent digit expected + if (!isdigit(c)) + error("exponent expected"); + dblstate++; + break; + + case 8: // past end of exponent digits + goto done; + } + break; + } + stringbuffer.writeByte(c); + } +done: + p--; + + stringbuffer.writeByte(0); + +#if _WIN32 && __DMC__ + char *save = __locale_decpoint; + __locale_decpoint = "."; +#endif +#ifdef IN_GCC + t->float80value = real_t::parse((char *)stringbuffer.data, real_t::LongDouble); +#else + t->float80value = strtold((char *)stringbuffer.data, NULL); +#endif + errno = 0; + switch (*p) + { + case 'F': + case 'f': +#ifdef IN_GCC + real_t::parse((char *)stringbuffer.data, real_t::Float); +#else + { // Only interested in errno return + double d = strtof((char *)stringbuffer.data, NULL); + // Assign to f to keep gcc warnings at bay + } +#endif + result = TOKfloat32v; + p++; + break; + + default: +#ifdef IN_GCC + real_t::parse((char *)stringbuffer.data, real_t::Double); +#else + /* Should do our own strtod(), since dmc and linux gcc + * accept 2.22507e-308, while apple gcc will only take + * 2.22508e-308. Not sure who is right. + */ + { // Only interested in errno return + double d = strtod((char *)stringbuffer.data, NULL); + // Assign to d to keep gcc warnings at bay + } +#endif + result = TOKfloat64v; + break; + + case 'l': + if (!global.params.useDeprecated) + error("'l' suffix is deprecated, use 'L' instead"); + case 'L': + result = TOKfloat80v; + p++; + break; + } + if (*p == 'i' || *p == 'I') + { + if (!global.params.useDeprecated && *p == 'I') + error("'I' suffix is deprecated, use 'i' instead"); + p++; + switch (result) + { + case TOKfloat32v: + result = TOKimaginary32v; + break; + case TOKfloat64v: + result = TOKimaginary64v; + break; + case TOKfloat80v: + result = TOKimaginary80v; + break; + } + } +#if _WIN32 && __DMC__ + __locale_decpoint = save; +#endif + if (errno == ERANGE) + error("number is not representable"); + return result; +} + +/********************************************* + * Do pragma. + * Currently, the only pragma supported is: + * #line linnum [filespec] + */ + +void Lexer::pragma() +{ + Token tok; + int linnum; + char *filespec = NULL; + Loc loc = this->loc; + + scan(&tok); + if (tok.value != TOKidentifier || tok.ident != Id::line) + goto Lerr; + + scan(&tok); + if (tok.value == TOKint32v || tok.value == TOKint64v) + { linnum = tok.uns64value - 1; + if (linnum != tok.uns64value - 1) + error("line number out of range"); + } + else + goto Lerr; + + while (1) + { + switch (*p) + { + case 0: + case 0x1A: + case '\n': + Lnewline: + this->loc.linnum = linnum; + if (filespec) + this->loc.filename = filespec; + return; + + case '\r': + p++; + if (*p != '\n') + { p--; + goto Lnewline; + } + continue; + + case ' ': + case '\t': + case '\v': + case '\f': + p++; + continue; // skip white space + + case '_': + if (mod && memcmp(p, "__FILE__", 8) == 0) + { + p += 8; + filespec = mem.strdup(loc.filename ? loc.filename : mod->ident->toChars()); + } + continue; + + case '"': + if (filespec) + goto Lerr; + stringbuffer.reset(); + p++; + while (1) + { unsigned c; + + c = *p; + switch (c) + { + case '\n': + case '\r': + case 0: + case 0x1A: + goto Lerr; + + case '"': + stringbuffer.writeByte(0); + filespec = mem.strdup((char *)stringbuffer.data); + p++; + break; + + default: + if (c & 0x80) + { unsigned u = decodeUTF(); + if (u == PS || u == LS) + goto Lerr; + } + stringbuffer.writeByte(c); + p++; + continue; + } + break; + } + continue; + + default: + if (*p & 0x80) + { unsigned u = decodeUTF(); + if (u == PS || u == LS) + goto Lnewline; + } + goto Lerr; + } + } + +Lerr: + error(loc, "#line integer [\"filespec\"]\\n expected"); +} + + +/******************************************** + * Decode UTF character. + * Issue error messages for invalid sequences. + * Return decoded character, advance p to last character in UTF sequence. + */ + +unsigned Lexer::decodeUTF() +{ + dchar_t u; + unsigned char c; + unsigned char *s = p; + size_t len; + size_t idx; + const char *msg; + + c = *s; + assert(c & 0x80); + + // Check length of remaining string up to 6 UTF-8 characters + for (len = 1; len < 6 && s[len]; len++) + ; + + idx = 0; + msg = utf_decodeChar(s, len, &idx, &u); + p += idx - 1; + if (msg) + { + error("%s", msg); + } + return u; +} + + +/*************************************************** + * Parse doc comment embedded between t->ptr and p. + * Remove trailing blanks and tabs from lines. + * Replace all newlines with \n. + * Remove leading comment character from each line. + * Decide if it's a lineComment or a blockComment. + * Append to previous one for this token. + */ + +void Lexer::getDocComment(Token *t, unsigned lineComment) +{ + /* ct tells us which kind of comment it is: '/', '*', or '+' + */ + unsigned char ct = t->ptr[2]; + + /* Start of comment text skips over / * *, / + +, or / / / + */ + unsigned char *q = t->ptr + 3; // start of comment text + + unsigned char *qend = p; + if (ct == '*' || ct == '+') + qend -= 2; + + /* Scan over initial row of ****'s or ++++'s or ////'s + */ + for (; q < qend; q++) + { + if (*q != ct) + break; + } + + /* Remove trailing row of ****'s or ++++'s + */ + if (ct != '/') + { + for (; q < qend; qend--) + { + if (qend[-1] != ct) + break; + } + } + + /* Comment is now [q .. qend]. + * Canonicalize it into buf[]. + */ + OutBuffer buf; + int linestart = 0; + + for (; q < qend; q++) + { + unsigned char c = *q; + + switch (c) + { + case '*': + case '+': + if (linestart && c == ct) + { linestart = 0; + /* Trim preceding whitespace up to preceding \n + */ + while (buf.offset && (buf.data[buf.offset - 1] == ' ' || buf.data[buf.offset - 1] == '\t')) + buf.offset--; + continue; + } + break; + + case ' ': + case '\t': + break; + + case '\r': + if (q[1] == '\n') + continue; // skip the \r + goto Lnewline; + + default: + if (c == 226) + { + // If LS or PS + if (q[1] == 128 && + (q[2] == 168 || q[2] == 169)) + { + q += 2; + goto Lnewline; + } + } + linestart = 0; + break; + + Lnewline: + c = '\n'; // replace all newlines with \n + case '\n': + linestart = 1; + + /* Trim trailing whitespace + */ + while (buf.offset && (buf.data[buf.offset - 1] == ' ' || buf.data[buf.offset - 1] == '\t')) + buf.offset--; + + break; + } + buf.writeByte(c); + } + + // Always end with a newline + if (!buf.offset || buf.data[buf.offset - 1] != '\n') + buf.writeByte('\n'); + + buf.writeByte(0); + + // It's a line comment if the start of the doc comment comes + // after other non-whitespace on the same line. + unsigned char** dc = (lineComment && anyToken) + ? &t->lineComment + : &t->blockComment; + + // Combine with previous doc comment, if any + if (*dc) + *dc = combineComments(*dc, (unsigned char *)buf.data); + else + *dc = (unsigned char *)buf.extractData(); +} + +/******************************************** + * Combine two document comments into one, + * separated by a newline. + */ + +unsigned char *Lexer::combineComments(unsigned char *c1, unsigned char *c2) +{ + //printf("Lexer::combineComments('%s', '%s')\n", c1, c2); + + unsigned char *c = c2; + + if (c1) + { c = c1; + if (c2) + { size_t len1 = strlen((char *)c1); + size_t len2 = strlen((char *)c2); + + c = (unsigned char *)mem.malloc(len1 + 1 + len2 + 1); + memcpy(c, c1, len1); + if (len1 && c1[len1 - 1] != '\n') + { c[len1] = '\n'; + len1++; + } + memcpy(c + len1, c2, len2); + c[len1 + len2] = 0; + } + } + return c; +} + +/******************************************** + * Create an identifier in the string table. + */ + +Identifier *Lexer::idPool(const char *s) +{ + size_t len = strlen(s); + StringValue *sv = stringtable.update(s, len); + Identifier *id = (Identifier *) sv->ptrvalue; + if (!id) + { + id = new Identifier(sv->lstring.string, TOKidentifier); + sv->ptrvalue = id; + } + return id; +} + +/********************************************* + * Create a unique identifier using the prefix s. + */ + +Identifier *Lexer::uniqueId(const char *s, int num) +{ char buffer[32]; + size_t slen = strlen(s); + + assert(slen + sizeof(num) * 3 + 1 <= sizeof(buffer)); + sprintf(buffer, "%s%d", s, num); + return idPool(buffer); +} + +Identifier *Lexer::uniqueId(const char *s) +{ + static int num; + return uniqueId(s, ++num); +} + +/**************************************** + */ + +struct Keyword +{ const char *name; + enum TOK value; +}; + +static Keyword keywords[] = +{ +// { "", TOK }, + + { "this", TOKthis }, + { "super", TOKsuper }, + { "assert", TOKassert }, + { "null", TOKnull }, + { "true", TOKtrue }, + { "false", TOKfalse }, + { "cast", TOKcast }, + { "new", TOKnew }, + { "delete", TOKdelete }, + { "throw", TOKthrow }, + { "module", TOKmodule }, + { "pragma", TOKpragma }, + { "typeof", TOKtypeof }, + { "typeid", TOKtypeid }, + + { "template", TOKtemplate }, + + { "void", TOKvoid }, + { "byte", TOKint8 }, + { "ubyte", TOKuns8 }, + { "short", TOKint16 }, + { "ushort", TOKuns16 }, + { "int", TOKint32 }, + { "uint", TOKuns32 }, + { "long", TOKint64 }, + { "ulong", TOKuns64 }, + { "cent", TOKcent, }, + { "ucent", TOKucent, }, + { "float", TOKfloat32 }, + { "double", TOKfloat64 }, + { "real", TOKfloat80 }, + + { "bool", TOKbool }, + { "char", TOKchar }, + { "wchar", TOKwchar }, + { "dchar", TOKdchar }, + + { "ifloat", TOKimaginary32 }, + { "idouble", TOKimaginary64 }, + { "ireal", TOKimaginary80 }, + + { "cfloat", TOKcomplex32 }, + { "cdouble", TOKcomplex64 }, + { "creal", TOKcomplex80 }, + + { "delegate", TOKdelegate }, + { "function", TOKfunction }, + + { "is", TOKis }, + { "if", TOKif }, + { "else", TOKelse }, + { "while", TOKwhile }, + { "for", TOKfor }, + { "do", TOKdo }, + { "switch", TOKswitch }, + { "case", TOKcase }, + { "default", TOKdefault }, + { "break", TOKbreak }, + { "continue", TOKcontinue }, + { "synchronized", TOKsynchronized }, + { "return", TOKreturn }, + { "goto", TOKgoto }, + { "try", TOKtry }, + { "catch", TOKcatch }, + { "finally", TOKfinally }, + { "with", TOKwith }, + { "asm", TOKasm }, + { "foreach", TOKforeach }, + { "foreach_reverse", TOKforeach_reverse }, + { "scope", TOKscope }, + + { "struct", TOKstruct }, + { "class", TOKclass }, + { "interface", TOKinterface }, + { "union", TOKunion }, + { "enum", TOKenum }, + { "import", TOKimport }, + { "mixin", TOKmixin }, + { "static", TOKstatic }, + { "final", TOKfinal }, + { "const", TOKconst }, + { "typedef", TOKtypedef }, + { "alias", TOKalias }, + { "override", TOKoverride }, + { "abstract", TOKabstract }, + { "volatile", TOKvolatile }, + { "debug", TOKdebug }, + { "deprecated", TOKdeprecated }, + { "in", TOKin }, + { "out", TOKout }, + { "inout", TOKinout }, + { "lazy", TOKlazy }, + { "auto", TOKauto }, + + { "align", TOKalign }, + { "extern", TOKextern }, + { "private", TOKprivate }, + { "package", TOKpackage }, + { "protected", TOKprotected }, + { "public", TOKpublic }, + { "export", TOKexport }, + + { "body", TOKbody }, + { "invariant", TOKinvariant }, + { "unittest", TOKunittest }, + { "version", TOKversion }, + //{ "manifest", TOKmanifest }, + + // Added after 1.0 + { "__argTypes", TOKargTypes }, + { "ref", TOKref }, + { "macro", TOKmacro }, +#if DMDV2 + { "pure", TOKpure }, + { "nothrow", TOKnothrow }, + { "__thread", TOKtls }, + { "__gshared", TOKgshared }, + { "__traits", TOKtraits }, + { "__vector", TOKvector }, + { "__overloadset", TOKoverloadset }, + { "__FILE__", TOKfile }, + { "__LINE__", TOKline }, + { "shared", TOKshared }, + { "immutable", TOKimmutable }, +#endif +}; + +int Token::isKeyword() +{ + for (unsigned u = 0; u < sizeof(keywords) / sizeof(keywords[0]); u++) + { + if (keywords[u].value == value) + return 1; + } + return 0; +} + +void Lexer::initKeywords() +{ + unsigned nkeywords = sizeof(keywords) / sizeof(keywords[0]); + + stringtable.init(6151); + + if (global.params.Dversion == 1) + nkeywords -= 2; + + cmtable_init(); + + for (unsigned u = 0; u < nkeywords; u++) + { + //printf("keyword[%d] = '%s'\n",u, keywords[u].name); + const char *s = keywords[u].name; + enum TOK v = keywords[u].value; + StringValue *sv = stringtable.insert(s, strlen(s)); + sv->ptrvalue = (void *) new Identifier(sv->lstring.string,v); + + //printf("tochars[%d] = '%s'\n",v, s); + Token::tochars[v] = s; + } + + Token::tochars[TOKeof] = "EOF"; + Token::tochars[TOKlcurly] = "{"; + Token::tochars[TOKrcurly] = "}"; + Token::tochars[TOKlparen] = "("; + Token::tochars[TOKrparen] = ")"; + Token::tochars[TOKlbracket] = "["; + Token::tochars[TOKrbracket] = "]"; + Token::tochars[TOKsemicolon] = ";"; + Token::tochars[TOKcolon] = ":"; + Token::tochars[TOKcomma] = ","; + Token::tochars[TOKdot] = "."; + Token::tochars[TOKxor] = "^"; + Token::tochars[TOKxorass] = "^="; + Token::tochars[TOKassign] = "="; + Token::tochars[TOKconstruct] = "="; +#if DMDV2 + Token::tochars[TOKblit] = "="; +#endif + Token::tochars[TOKlt] = "<"; + Token::tochars[TOKgt] = ">"; + Token::tochars[TOKle] = "<="; + Token::tochars[TOKge] = ">="; + Token::tochars[TOKequal] = "=="; + Token::tochars[TOKnotequal] = "!="; + Token::tochars[TOKnotidentity] = "!is"; + Token::tochars[TOKtobool] = "!!"; + + Token::tochars[TOKunord] = "!<>="; + Token::tochars[TOKue] = "!<>"; + Token::tochars[TOKlg] = "<>"; + Token::tochars[TOKleg] = "<>="; + Token::tochars[TOKule] = "!>"; + Token::tochars[TOKul] = "!>="; + Token::tochars[TOKuge] = "!<"; + Token::tochars[TOKug] = "!<="; + + Token::tochars[TOKnot] = "!"; + Token::tochars[TOKtobool] = "!!"; + Token::tochars[TOKshl] = "<<"; + Token::tochars[TOKshr] = ">>"; + Token::tochars[TOKushr] = ">>>"; + Token::tochars[TOKadd] = "+"; + Token::tochars[TOKmin] = "-"; + Token::tochars[TOKmul] = "*"; + Token::tochars[TOKdiv] = "/"; + Token::tochars[TOKmod] = "%"; + Token::tochars[TOKslice] = ".."; + Token::tochars[TOKdotdotdot] = "..."; + Token::tochars[TOKand] = "&"; + Token::tochars[TOKandand] = "&&"; + Token::tochars[TOKor] = "|"; + Token::tochars[TOKoror] = "||"; + Token::tochars[TOKarray] = "[]"; + Token::tochars[TOKindex] = "[i]"; + Token::tochars[TOKaddress] = "&"; + Token::tochars[TOKstar] = "*"; + Token::tochars[TOKtilde] = "~"; + Token::tochars[TOKdollar] = "$"; + Token::tochars[TOKcast] = "cast"; + Token::tochars[TOKplusplus] = "++"; + Token::tochars[TOKminusminus] = "--"; + Token::tochars[TOKpreplusplus] = "++"; + Token::tochars[TOKpreminusminus] = "--"; + Token::tochars[TOKtype] = "type"; + Token::tochars[TOKquestion] = "?"; + Token::tochars[TOKneg] = "-"; + Token::tochars[TOKuadd] = "+"; + Token::tochars[TOKvar] = "var"; + Token::tochars[TOKaddass] = "+="; + Token::tochars[TOKminass] = "-="; + Token::tochars[TOKmulass] = "*="; + Token::tochars[TOKdivass] = "/="; + Token::tochars[TOKmodass] = "%="; + Token::tochars[TOKshlass] = "<<="; + Token::tochars[TOKshrass] = ">>="; + Token::tochars[TOKushrass] = ">>>="; + Token::tochars[TOKandass] = "&="; + Token::tochars[TOKorass] = "|="; + Token::tochars[TOKcatass] = "~="; + Token::tochars[TOKcat] = "~"; + Token::tochars[TOKcall] = "call"; + Token::tochars[TOKidentity] = "is"; + Token::tochars[TOKnotidentity] = "!is"; + + Token::tochars[TOKorass] = "|="; + Token::tochars[TOKidentifier] = "identifier"; +#if DMDV2 + Token::tochars[TOKat] = "@"; + Token::tochars[TOKpow] = "^^"; + Token::tochars[TOKpowass] = "^^="; + Token::tochars[TOKgoesto] = "=>"; +#endif + + // For debugging + Token::tochars[TOKerror] = "error"; + Token::tochars[TOKdotexp] = "dotexp"; + Token::tochars[TOKdotti] = "dotti"; + Token::tochars[TOKdotvar] = "dotvar"; + Token::tochars[TOKdottype] = "dottype"; + Token::tochars[TOKsymoff] = "symoff"; + Token::tochars[TOKarraylength] = "arraylength"; + Token::tochars[TOKarrayliteral] = "arrayliteral"; + Token::tochars[TOKassocarrayliteral] = "assocarrayliteral"; + Token::tochars[TOKstructliteral] = "structliteral"; + Token::tochars[TOKstring] = "string"; + Token::tochars[TOKdsymbol] = "symbol"; + Token::tochars[TOKtuple] = "tuple"; + Token::tochars[TOKdeclaration] = "declaration"; + Token::tochars[TOKdottd] = "dottd"; + Token::tochars[TOKon_scope_exit] = "scope(exit)"; + Token::tochars[TOKon_scope_success] = "scope(success)"; + Token::tochars[TOKon_scope_failure] = "scope(failure)"; + +#if UNITTEST + unittest_lexer(); +#endif +} + +#if UNITTEST + +void unittest_lexer() +{ + //printf("unittest_lexer()\n"); + + /* Not much here, just trying things out. + */ + const unsigned char text[] = "int"; + Lexer lex1(NULL, (unsigned char *)text, 0, sizeof(text), 0, 0); + TOK tok; + tok = lex1.nextToken(); + //printf("tok == %s, %d, %d\n", Token::toChars(tok), tok, TOKint32); + assert(tok == TOKint32); + tok = lex1.nextToken(); + assert(tok == TOKeof); + tok = lex1.nextToken(); + assert(tok == TOKeof); +} + +#endif + diff --git a/lexer.h b/lexer.h new file mode 100644 index 00000000..9b1e132e --- /dev/null +++ b/lexer.h @@ -0,0 +1,316 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2010 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_LEXER_H +#define DMD_LEXER_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "root.h" +#include "mars.h" + +struct StringTable; +struct Identifier; +struct Module; + +/* Tokens: + ( ) + [ ] + { } + < > <= >= == != === !== + << >> <<= >>= >>> >>>= + + - += -= + * / % *= /= %= + & | ^ &= |= ^= + = ! ~ @ + ^^ ^^= + ++ -- + . -> : , => + ? && || + */ + +enum TOK +{ + TOKreserved, + + // Other + TOKlparen, TOKrparen, + TOKlbracket, TOKrbracket, + TOKlcurly, TOKrcurly, + TOKcolon, TOKneg, + TOKsemicolon, TOKdotdotdot, + TOKeof, TOKcast, + TOKnull, TOKassert, + TOKtrue, TOKfalse, + TOKarray, TOKcall, + TOKaddress, + TOKtype, TOKthrow, + TOKnew, TOKdelete, + TOKstar, TOKsymoff, + TOKvar, TOKdotvar, + TOKdotti, TOKdotexp, + TOKdottype, TOKslice, + TOKarraylength, TOKversion, + TOKmodule, TOKdollar, + TOKtemplate, TOKdottd, + TOKdeclaration, TOKtypeof, + TOKpragma, TOKdsymbol, + TOKtypeid, TOKuadd, + TOKremove, + TOKnewanonclass, TOKcomment, + TOKarrayliteral, TOKassocarrayliteral, + TOKstructliteral, + + // Operators + TOKlt, TOKgt, + TOKle, TOKge, + TOKequal, TOKnotequal, + TOKidentity, TOKnotidentity, + TOKindex, TOKis, + TOKtobool, + +// 60 + // NCEG floating point compares + // !<>= <> <>= !> !>= !< !<= !<> + TOKunord,TOKlg,TOKleg,TOKule,TOKul,TOKuge,TOKug,TOKue, + + TOKshl, TOKshr, + TOKshlass, TOKshrass, + TOKushr, TOKushrass, + TOKcat, TOKcatass, // ~ ~= + TOKadd, TOKmin, TOKaddass, TOKminass, + TOKmul, TOKdiv, TOKmod, + TOKmulass, TOKdivass, TOKmodass, + TOKand, TOKor, TOKxor, + TOKandass, TOKorass, TOKxorass, + TOKassign, TOKnot, TOKtilde, + TOKplusplus, TOKminusminus, TOKconstruct, TOKblit, + TOKdot, TOKarrow, TOKcomma, + TOKquestion, TOKandand, TOKoror, + TOKpreplusplus, TOKpreminusminus, + +// 106 + // Numeric literals + TOKint32v, TOKuns32v, + TOKint64v, TOKuns64v, + TOKfloat32v, TOKfloat64v, TOKfloat80v, + TOKimaginary32v, TOKimaginary64v, TOKimaginary80v, + + // Char constants + TOKcharv, TOKwcharv, TOKdcharv, + + // Leaf operators + TOKidentifier, TOKstring, + TOKthis, TOKsuper, + TOKhalt, TOKtuple, + TOKerror, + + // Basic types + TOKvoid, + TOKint8, TOKuns8, + TOKint16, TOKuns16, + TOKint32, TOKuns32, + TOKint64, TOKuns64, + TOKfloat32, TOKfloat64, TOKfloat80, + TOKimaginary32, TOKimaginary64, TOKimaginary80, + TOKcomplex32, TOKcomplex64, TOKcomplex80, + TOKchar, TOKwchar, TOKdchar, TOKbit, TOKbool, + TOKcent, TOKucent, + +// 152 + // Aggregates + TOKstruct, TOKclass, TOKinterface, TOKunion, TOKenum, TOKimport, + TOKtypedef, TOKalias, TOKoverride, TOKdelegate, TOKfunction, + TOKmixin, + + TOKalign, TOKextern, TOKprivate, TOKprotected, TOKpublic, TOKexport, + TOKstatic, /*TOKvirtual,*/ TOKfinal, TOKconst, TOKabstract, TOKvolatile, + TOKdebug, TOKdeprecated, TOKin, TOKout, TOKinout, TOKlazy, + TOKauto, TOKpackage, TOKmanifest, TOKimmutable, + + // Statements + TOKif, TOKelse, TOKwhile, TOKfor, TOKdo, TOKswitch, + TOKcase, TOKdefault, TOKbreak, TOKcontinue, TOKwith, + TOKsynchronized, TOKreturn, TOKgoto, TOKtry, TOKcatch, TOKfinally, + TOKasm, TOKforeach, TOKforeach_reverse, + TOKscope, + TOKon_scope_exit, TOKon_scope_failure, TOKon_scope_success, + + // Contracts + TOKbody, TOKinvariant, + + // Testing + TOKunittest, + + // Added after 1.0 + TOKargTypes, + TOKref, + TOKmacro, +#if DMDV2 + TOKtraits, + TOKoverloadset, + TOKpure, + TOKnothrow, + TOKtls, + TOKgshared, + TOKline, + TOKfile, + TOKshared, + TOKat, + TOKpow, + TOKpowass, + TOKgoesto, + TOKvector, +#endif + + TOKMAX +}; + +#define TOKwild TOKinout + +#define BASIC_TYPES \ + TOKwchar: case TOKdchar: \ + case TOKbit: case TOKbool: case TOKchar: \ + case TOKint8: case TOKuns8: \ + case TOKint16: case TOKuns16: \ + case TOKint32: case TOKuns32: \ + case TOKint64: case TOKuns64: \ + case TOKfloat32: case TOKfloat64: case TOKfloat80: \ + case TOKimaginary32: case TOKimaginary64: case TOKimaginary80: \ + case TOKcomplex32: case TOKcomplex64: case TOKcomplex80: \ + case TOKvoid + +#define BASIC_TYPES_X(t) \ + TOKvoid: t = Type::tvoid; goto LabelX; \ + case TOKint8: t = Type::tint8; goto LabelX; \ + case TOKuns8: t = Type::tuns8; goto LabelX; \ + case TOKint16: t = Type::tint16; goto LabelX; \ + case TOKuns16: t = Type::tuns16; goto LabelX; \ + case TOKint32: t = Type::tint32; goto LabelX; \ + case TOKuns32: t = Type::tuns32; goto LabelX; \ + case TOKint64: t = Type::tint64; goto LabelX; \ + case TOKuns64: t = Type::tuns64; goto LabelX; \ + case TOKfloat32: t = Type::tfloat32; goto LabelX; \ + case TOKfloat64: t = Type::tfloat64; goto LabelX; \ + case TOKfloat80: t = Type::tfloat80; goto LabelX; \ + case TOKimaginary32: t = Type::timaginary32; goto LabelX; \ + case TOKimaginary64: t = Type::timaginary64; goto LabelX; \ + case TOKimaginary80: t = Type::timaginary80; goto LabelX; \ + case TOKcomplex32: t = Type::tcomplex32; goto LabelX; \ + case TOKcomplex64: t = Type::tcomplex64; goto LabelX; \ + case TOKcomplex80: t = Type::tcomplex80; goto LabelX; \ + case TOKbool: t = Type::tbool; goto LabelX; \ + case TOKchar: t = Type::tchar; goto LabelX; \ + case TOKwchar: t = Type::twchar; goto LabelX; \ + case TOKdchar: t = Type::tdchar; goto LabelX; \ + LabelX + +struct Token +{ + Token *next; + unsigned char *ptr; // pointer to first character of this token within buffer + enum TOK value; + unsigned char *blockComment; // doc comment string prior to this token + unsigned char *lineComment; // doc comment for previous token + union + { + // Integers + d_int32 int32value; + d_uns32 uns32value; + d_int64 int64value; + d_uns64 uns64value; + + // Floats +#ifdef IN_GCC + // real_t float80value; // can't use this in a union! +#else + d_float80 float80value; +#endif + + struct + { unsigned char *ustring; // UTF8 string + unsigned len; + unsigned char postfix; // 'c', 'w', 'd' + }; + + Identifier *ident; + }; +#ifdef IN_GCC + real_t float80value; // can't use this in a union! +#endif + + static const char *tochars[TOKMAX]; + static void *operator new(size_t sz); + + int isKeyword(); + void print(); + const char *toChars(); + static const char *toChars(enum TOK); +}; + +struct Lexer +{ + static StringTable stringtable; + static OutBuffer stringbuffer; + static Token *freelist; + + Loc loc; // for error messages + + unsigned char *base; // pointer to start of buffer + unsigned char *end; // past end of buffer + unsigned char *p; // current character + Token token; + Module *mod; + int doDocComment; // collect doc comment information + int anyToken; // !=0 means seen at least one token + int commentToken; // !=0 means comments are TOKcomment's + + Lexer(Module *mod, + unsigned char *base, unsigned begoffset, unsigned endoffset, + int doDocComment, int commentToken); + + static void initKeywords(); + static Identifier *idPool(const char *s); + static Identifier *uniqueId(const char *s); + static Identifier *uniqueId(const char *s, int num); + + TOK nextToken(); + TOK peekNext(); + TOK peekNext2(); + void scan(Token *t); + Token *peek(Token *t); + Token *peekPastParen(Token *t); + unsigned escapeSequence(); + TOK wysiwygStringConstant(Token *t, int tc); + TOK hexStringConstant(Token *t); +#if DMDV2 + TOK delimitedStringConstant(Token *t); + TOK tokenStringConstant(Token *t); +#endif + TOK escapeStringConstant(Token *t, int wide); + TOK charConstant(Token *t, int wide); + void stringPostfix(Token *t); + unsigned wchar(unsigned u); + TOK number(Token *t); + TOK inreal(Token *t); + void error(const char *format, ...); + void error(Loc loc, const char *format, ...); + void verror(Loc loc, const char *format, va_list ap); + void pragma(); + unsigned decodeUTF(); + void getDocComment(Token *t, unsigned lineComment); + + static int isValidIdentifier(char *p); + static unsigned char *combineComments(unsigned char *c1, unsigned char *c2); +}; + +#endif /* DMD_LEXER_H */ diff --git a/lib.h b/lib.h new file mode 100644 index 00000000..abd31cb2 --- /dev/null +++ b/lib.h @@ -0,0 +1,54 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2008 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_LIB_H +#define DMD_LIB_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +struct ObjModule; + +struct ObjSymbol +{ + char *name; + ObjModule *om; +}; + +#include "arraytypes.h" + +typedef ArrayBase ObjModules; +typedef ArrayBase ObjSymbols; + +struct Library +{ + File *libfile; + ObjModules objmodules; // ObjModule[] + ObjSymbols objsymbols; // ObjSymbol[] + + StringTable tab; + + Library(); + void setFilename(char *dir, char *filename); + void addObject(const char *module_name, void *buf, size_t buflen); + void addLibrary(void *buf, size_t buflen); + void write(); + + private: + void addSymbol(ObjModule *om, char *name, int pickAny = 0); + void scanObjModule(ObjModule *om); + unsigned short numDictPages(unsigned padding); + int FillDict(unsigned char *bucketsP, unsigned short uNumPages); + void WriteLibToBuffer(OutBuffer *libbuf); +}; + +#endif /* DMD_LIB_H */ + diff --git a/libelf.c b/libelf.c new file mode 100644 index 00000000..cf56fd39 --- /dev/null +++ b/libelf.c @@ -0,0 +1,773 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include +#include +#include +#include + +#include "rmem.h" +#include "root.h" +#include "stringtable.h" + +#include "mars.h" +#include "lib.h" +#include "melf.h" + +#define LOG 0 + +Library::Library() +{ + libfile = NULL; + tab.init(); +} + +/*********************************** + * Set the library file name based on the output directory + * and the filename. + * Add default library file name extension. + */ + +void Library::setFilename(char *dir, char *filename) +{ +#if LOG + printf("Library::setFilename(dir = '%s', filename = '%s')\n", + dir ? dir : "", filename ? filename : ""); +#endif + char *arg = filename; + if (!arg || !*arg) + { // Generate lib file name from first obj name + char *n = global.params.objfiles->tdata()[0]; + + n = FileName::name(n); + FileName *fn = FileName::forceExt(n, global.lib_ext); + arg = fn->toChars(); + } + if (!FileName::absolute(arg)) + arg = FileName::combine(dir, arg); + FileName *libfilename = FileName::defaultExt(arg, global.lib_ext); + + libfile = new File(libfilename); +} + +void Library::write() +{ + if (global.params.verbose) + printf("library %s\n", libfile->name->toChars()); + + OutBuffer libbuf; + WriteLibToBuffer(&libbuf); + + // Transfer image to file + libfile->setbuffer(libbuf.data, libbuf.offset); + libbuf.extractData(); + + + char *p = FileName::path(libfile->name->toChars()); + FileName::ensurePathExists(p); + //mem.free(p); + + libfile->writev(); +} + +/*****************************************************************************/ + +void Library::addLibrary(void *buf, size_t buflen) +{ + addObject(NULL, buf, buflen); +} + + +/*****************************************************************************/ +/*****************************************************************************/ + +static char elf[4] = { 0x7F, 'E', 'L', 'F' }; // ELF file signature + +void sputl(int value, void* buffer) +{ + unsigned char *p = (unsigned char*)buffer; + p[0] = (unsigned char)(value >> 24); + p[1] = (unsigned char)(value >> 16); + p[2] = (unsigned char)(value >> 8); + p[3] = (unsigned char)(value); +} + +int sgetl(void* buffer) +{ + unsigned char *p = (unsigned char*)buffer; + return (((((p[0] << 8) | p[1]) << 8) | p[2]) << 8) | p[3]; +} + + +struct ObjModule +{ + unsigned char *base; // where are we holding it in memory + unsigned length; // in bytes + unsigned offset; // offset from start of library + char *name; // module name (file name) + int name_offset; // if not -1, offset into string table of name + time_t file_time; // file time + unsigned user_id; + unsigned group_id; + unsigned file_mode; + int scan; // 1 means scan for symbols +}; + +struct Header +{ + #define OBJECT_NAME_SIZE 16 + char object_name[OBJECT_NAME_SIZE]; + char file_time[12]; + char user_id[6]; + char group_id[6]; + char file_mode[8]; // in octal + char file_size[10]; + char trailer[2]; +}; + +void OmToHeader(Header *h, ObjModule *om) +{ + size_t len; + if (om->name_offset == -1) + { + len = strlen(om->name); + memcpy(h->object_name, om->name, len); + h->object_name[len] = '/'; + } + else + { + len = sprintf(h->object_name, "/%d", om->name_offset); + h->object_name[len] = ' '; + } + assert(len < OBJECT_NAME_SIZE); + memset(h->object_name + len + 1, ' ', OBJECT_NAME_SIZE - (len + 1)); + + /* In the following sprintf's, don't worry if the trailing 0 + * that sprintf writes goes off the end of the field. It will + * write into the next field, which we will promptly overwrite + * anyway. (So make sure to write the fields in ascending order.) + */ + + len = sprintf(h->file_time, "%lu", om->file_time); + assert(len <= 12); + memset(h->file_time + len, ' ', 12 - len); + + if (om->user_id > 999999) + om->user_id = 0; + len = sprintf(h->user_id, "%u", om->user_id); + assert(len <= 6); + memset(h->user_id + len, ' ', 6 - len); + + len = sprintf(h->group_id, "%u", om->group_id); + assert(len <= 6); + memset(h->group_id + len, ' ', 6 - len); + + len = sprintf(h->file_mode, "%o", om->file_mode); + assert(len <= 8); + memset(h->file_mode + len, ' ', 8 - len); + + len = sprintf(h->file_size, "%u", om->length); + assert(len <= 10); + memset(h->file_size + len, ' ', 10 - len); + + h->trailer[0] = '`'; + h->trailer[1] = '\n'; +} + +void Library::addSymbol(ObjModule *om, char *name, int pickAny) +{ +#if LOG + printf("Library::addSymbol(%s, %s, %d)\n", om->name, name, pickAny); +#endif + StringValue *s = tab.insert(name, strlen(name)); + if (!s) + { // already in table + if (!pickAny) + { s = tab.lookup(name, strlen(name)); + assert(s); + ObjSymbol *os = (ObjSymbol *)s->ptrvalue; + error("multiple definition of %s: %s and %s: %s", + om->name, name, os->om->name, os->name); + } + } + else + { + ObjSymbol *os = new ObjSymbol(); + os->name = strdup(name); + os->om = om; + s->ptrvalue = (void *)os; + + objsymbols.push(os); + } +} + +/************************************ + * Scan single object module for dictionary symbols. + * Send those symbols to Library::addSymbol(). + */ + +void Library::scanObjModule(ObjModule *om) +{ +#if LOG + printf("Library::scanObjModule(%s)\n", om->name); +#endif + unsigned char *buf = (unsigned char *)om->base; + size_t buflen = om->length; + int reason = 0; + + if (buflen < EI_NIDENT + sizeof(Elf32_Hdr)) + { + Lcorrupt: + error("corrupt ELF object module %s %d", om->name, reason); + return; + } + + if (memcmp(buf, elf, 4)) + { reason = 1; + goto Lcorrupt; + } + if (buf[EI_VERSION] != EV_CURRENT) + { + error("ELF object module %s has EI_VERSION = %d, should be %d", om->name, buf[EI_VERSION], EV_CURRENT); + return; + } + if (buf[EI_DATA] != ELFDATA2LSB) + { + error("ELF object module %s is byte swapped and unsupported", om->name); + return; + } + if (buf[EI_CLASS] == ELFCLASS32) + { + Elf32_Hdr *eh = (Elf32_Hdr *)(buf + EI_NIDENT); + if (eh->e_type != ET_REL) + { + error("ELF object module %s is not relocatable", om->name); + return; // not relocatable object module + } + if (eh->e_version != EV_CURRENT) + goto Lcorrupt; + + /* For each Section + */ + for (unsigned u = 0; u < eh->e_shnum; u++) + { Elf32_Shdr *section = (Elf32_Shdr *)(buf + eh->e_shoff + eh->e_shentsize * u); + + if (section->sh_type == SHT_SYMTAB) + { /* sh_link gives the particular string table section + * used for the symbol names. + */ + Elf32_Shdr *string_section = (Elf32_Shdr *)(buf + eh->e_shoff + + eh->e_shentsize * section->sh_link); + if (string_section->sh_type != SHT_STRTAB) + { + reason = 3; + goto Lcorrupt; + } + char *string_tab = (char *)(buf + string_section->sh_offset); + + for (unsigned offset = 0; offset < section->sh_size; offset += sizeof(Elf32_Sym)) + { Elf32_Sym *sym = (Elf32_Sym *)(buf + section->sh_offset + offset); + + if (((sym->st_info >> 4) == STB_GLOBAL || + (sym->st_info >> 4) == STB_WEAK) && + sym->st_shndx != SHT_UNDEF) // not extern + { + char *name = string_tab + sym->st_name; + //printf("sym st_name = x%x\n", sym->st_name); + addSymbol(om, name, 1); + } + } + } + } + } + else if (buf[EI_CLASS] == ELFCLASS64) + { + Elf64_Ehdr *eh = (Elf64_Ehdr *)(buf + EI_NIDENT); + if (buflen < EI_NIDENT + sizeof(Elf64_Ehdr)) + goto Lcorrupt; + if (eh->e_type != ET_REL) + { + error("ELF object module %s is not relocatable", om->name); + return; // not relocatable object module + } + if (eh->e_version != EV_CURRENT) + goto Lcorrupt; + + /* For each Section + */ + for (unsigned u = 0; u < eh->e_shnum; u++) + { Elf64_Shdr *section = (Elf64_Shdr *)(buf + eh->e_shoff + eh->e_shentsize * u); + + if (section->sh_type == SHT_SYMTAB) + { /* sh_link gives the particular string table section + * used for the symbol names. + */ + Elf64_Shdr *string_section = (Elf64_Shdr *)(buf + eh->e_shoff + + eh->e_shentsize * section->sh_link); + if (string_section->sh_type != SHT_STRTAB) + { + reason = 3; + goto Lcorrupt; + } + char *string_tab = (char *)(buf + string_section->sh_offset); + + for (unsigned offset = 0; offset < section->sh_size; offset += sizeof(Elf64_Sym)) + { Elf64_Sym *sym = (Elf64_Sym *)(buf + section->sh_offset + offset); + + if (((sym->st_info >> 4) == STB_GLOBAL || + (sym->st_info >> 4) == STB_WEAK) && + sym->st_shndx != SHT_UNDEF) // not extern + { + char *name = string_tab + sym->st_name; + //printf("sym st_name = x%x\n", sym->st_name); + addSymbol(om, name, 1); + } + } + } + } + } + else + { + error("ELF object module %s is unrecognized class %d", om->name, buf[EI_CLASS]); + return; + } + +#if 0 + /* String table section + */ + Elf32_Shdr *string_section = (Elf32_Shdr *)(buf + eh->e_shoff + + eh->e_shentsize * eh->e_shstrndx); + if (string_section->sh_type != SHT_STRTAB) + { + //printf("buf = %p, e_shentsize = %d, e_shstrndx = %d\n", buf, eh->e_shentsize, eh->e_shstrndx); + //printf("sh_type = %d, SHT_STRTAB = %d\n", string_section->sh_type, SHT_STRTAB); + reason = 2; + goto Lcorrupt; + } + printf("strtab sh_offset = x%x\n", string_section->sh_offset); + char *string_tab = (char *)(buf + string_section->sh_offset); +#endif + +} + +/*************************************** + * Add object module or library to the library. + * Examine the buffer to see which it is. + * If the buffer is NULL, use module_name as the file name + * and load the file. + */ + +void Library::addObject(const char *module_name, void *buf, size_t buflen) +{ + if (!module_name) + module_name = ""; +#if LOG + printf("Library::addObject(%s)\n", module_name); +#endif + int fromfile = 0; + if (!buf) + { assert(module_name[0]); + FileName f((char *)module_name, 0); + File file(&f); + file.readv(); + buf = file.buffer; + buflen = file.len; + file.ref = 1; + fromfile = 1; + } + int reason = 0; + + if (buflen < 16) + { +#if LOG + printf("buf = %p, buflen = %d\n", buf, buflen); +#endif + Lcorrupt: + error("corrupt object module %s %d", module_name, reason); + return; + } + + if (memcmp(buf, "!\n", 8) == 0) + { /* Library file. + * Pull each object module out of the library and add it + * to the object module array. + */ +#if LOG + printf("archive, buf = %p, buflen = %d\n", buf, buflen); +#endif + unsigned offset = 8; + char *symtab = NULL; + unsigned symtab_size = 0; + char *filenametab = NULL; + unsigned filenametab_size = 0; + unsigned mstart = objmodules.dim; + while (offset < buflen) + { + if (offset + sizeof(Header) >= buflen) + { reason = 1; + goto Lcorrupt; + } + Header *header = (Header *)((unsigned char *)buf + offset); + offset += sizeof(Header); + char *endptr = NULL; + unsigned long size = strtoul(header->file_size, &endptr, 10); + if (endptr >= &header->file_size[10] || *endptr != ' ') + { reason = 2; + goto Lcorrupt; + } + if (offset + size > buflen) + { reason = 3; + goto Lcorrupt; + } + + if (header->object_name[0] == '/' && + header->object_name[1] == ' ') + { + /* Instead of rescanning the object modules we pull from a + * library, just use the already created symbol table. + */ + if (symtab) + { reason = 4; + goto Lcorrupt; + } + symtab = (char *)buf + offset; + symtab_size = size; + if (size < 4) + { reason = 5; + goto Lcorrupt; + } + } + else if (header->object_name[0] == '/' && + header->object_name[1] == '/') + { + /* This is the file name table, save it for later. + */ + if (filenametab) + { reason = 6; + goto Lcorrupt; + } + filenametab = (char *)buf + offset; + filenametab_size = size; + } + else + { + ObjModule *om = new ObjModule(); + om->base = (unsigned char *)buf + offset /*- sizeof(Header)*/; + om->length = size; + om->offset = 0; + if (header->object_name[0] == '/') + { /* Pick long name out of file name table + */ + unsigned foff = strtoul(header->object_name + 1, &endptr, 10); + unsigned i; + for (i = 0; 1; i++) + { if (foff + i >= filenametab_size) + { reason = 7; + goto Lcorrupt; + } + char c = filenametab[foff + i]; + if (c == '/') + break; + } + om->name = (char *)malloc(i + 1); + assert(om->name); + memcpy(om->name, filenametab + foff, i); + om->name[i] = 0; + } + else + { /* Pick short name out of header + */ + om->name = (char *)malloc(OBJECT_NAME_SIZE); + assert(om->name); + for (int i = 0; 1; i++) + { if (i == OBJECT_NAME_SIZE) + { reason = 8; + goto Lcorrupt; + } + char c = header->object_name[i]; + if (c == '/') + { om->name[i] = 0; + break; + } + om->name[i] = c; + } + } + om->name_offset = -1; + om->file_time = strtoul(header->file_time, &endptr, 10); + om->user_id = strtoul(header->user_id, &endptr, 10); + om->group_id = strtoul(header->group_id, &endptr, 10); + om->file_mode = strtoul(header->file_mode, &endptr, 8); + om->scan = 0; + objmodules.push(om); + } + offset += (size + 1) & ~1; + } + if (offset != buflen) + { reason = 9; + goto Lcorrupt; + } + + /* Scan the library's symbol table, and insert it into our own. + * We use this instead of rescanning the object module, because + * the library's creator may have a different idea of what symbols + * go into the symbol table than we do. + * This is also probably faster. + */ + unsigned nsymbols = sgetl(symtab); + char *s = symtab + 4 + nsymbols * 4; + if (4 + nsymbols * (4 + 1) > symtab_size) + { reason = 10; + goto Lcorrupt; + } + for (unsigned i = 0; i < nsymbols; i++) + { char *name = s; + s += strlen(name) + 1; + if (s - symtab > symtab_size) + { reason = 11; + goto Lcorrupt; + } + unsigned moff = sgetl(symtab + 4 + i * 4); +//printf("symtab[%d] moff = %x %x, name = %s\n", i, moff, moff + sizeof(Header), name); + for (unsigned m = mstart; 1; m++) + { if (m == objmodules.dim) + { reason = 12; + goto Lcorrupt; // didn't find it + } + ObjModule *om = objmodules.tdata()[m]; +//printf("\t%x\n", (char *)om->base - (char *)buf); + if (moff + sizeof(Header) == (char *)om->base - (char *)buf) + { + addSymbol(om, name, 1); +// if (mstart == m) +// mstart++; + break; + } + } + } + + return; + } + + if (memcmp(buf, elf, 4) != 0) + { reason = 13; + goto Lcorrupt; + } + + /* It's an ELF object module + */ + ObjModule *om = new ObjModule(); + om->base = (unsigned char *)buf; + om->length = buflen; + om->offset = 0; + om->name = FileName::name(module_name); // remove path, but not extension + om->name_offset = -1; + om->scan = 1; + if (fromfile) + { struct stat statbuf; + int i = stat(module_name, &statbuf); + if (i == -1) // error, errno is set + { reason = 14; + goto Lcorrupt; + } + om->file_time = statbuf.st_ctime; + om->user_id = statbuf.st_uid; + om->group_id = statbuf.st_gid; + om->file_mode = statbuf.st_mode; + } + else + { /* Mock things up for the object module file that never was + * actually written out. + */ + static uid_t uid; + static gid_t gid; + static int init; + if (!init) + { init = 1; + uid = getuid(); + gid = getgid(); + } + time(&om->file_time); + om->user_id = uid; + om->group_id = gid; + om->file_mode = 0100640; + } + objmodules.push(om); +} + + +/*****************************************************************************/ +/*****************************************************************************/ + +/********************************************** + * Create and write library to libbuf. + * The library consists of: + * !\n + * header + * dictionary + * object modules... + */ + +void Library::WriteLibToBuffer(OutBuffer *libbuf) +{ +#if LOG + printf("Library::WriteLibToBuffer()\n"); +#endif + + /************* Scan Object Modules for Symbols ******************/ + + for (int i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + if (om->scan) + { + scanObjModule(om); + } + } + + /************* Determine string section ******************/ + + /* The string section is where we store long file names. + */ + unsigned noffset = 0; + for (int i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + size_t len = strlen(om->name); + if (len >= OBJECT_NAME_SIZE) + { + om->name_offset = noffset; + noffset += len + 2; + } + else + om->name_offset = -1; + } + +#if LOG + printf("\tnoffset = x%x\n", noffset); +#endif + + /************* Determine module offsets ******************/ + + unsigned moffset = 8 + sizeof(Header) + 4; + + for (int i = 0; i < objsymbols.dim; i++) + { ObjSymbol *os = objsymbols.tdata()[i]; + + moffset += 4 + strlen(os->name) + 1; + } + unsigned hoffset = moffset; + +#if LOG + printf("\tmoffset = x%x\n", moffset); +#endif + + moffset += moffset & 1; + if (noffset) + moffset += sizeof(Header) + noffset; + + for (int i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + + moffset += moffset & 1; + om->offset = moffset; + moffset += sizeof(Header) + om->length; + } + + libbuf->reserve(moffset); + + /************* Write the library ******************/ + libbuf->write("!\n", 8); + + ObjModule om; + om.name_offset = -1; + om.base = NULL; + om.length = hoffset - (8 + sizeof(Header)); + om.offset = 8; + om.name = (char*)""; + ::time(&om.file_time); + om.user_id = 0; + om.group_id = 0; + om.file_mode = 0; + + Header h; + OmToHeader(&h, &om); + libbuf->write(&h, sizeof(h)); + char buf[4]; + sputl(objsymbols.dim, buf); + libbuf->write(buf, 4); + + for (int i = 0; i < objsymbols.dim; i++) + { ObjSymbol *os = objsymbols.tdata()[i]; + + sputl(os->om->offset, buf); + libbuf->write(buf, 4); + } + + for (int i = 0; i < objsymbols.dim; i++) + { ObjSymbol *os = objsymbols.tdata()[i]; + + libbuf->writestring(os->name); + libbuf->writeByte(0); + } + +#if LOG + printf("\tlibbuf->moffset = x%x\n", libbuf->offset); +#endif + + /* Write out the string section + */ + if (noffset) + { + if (libbuf->offset & 1) + libbuf->writeByte('\n'); + + // header + memset(&h, ' ', sizeof(Header)); + h.object_name[0] = '/'; + h.object_name[1] = '/'; + size_t len = sprintf(h.file_size, "%u", noffset); + assert(len < 10); + h.file_size[len] = ' '; + h.trailer[0] = '`'; + h.trailer[1] = '\n'; + libbuf->write(&h, sizeof(h)); + + for (int i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + if (om->name_offset >= 0) + { libbuf->writestring(om->name); + libbuf->writeByte('/'); + libbuf->writeByte('\n'); + } + } + } + + /* Write out each of the object modules + */ + for (int i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + + if (libbuf->offset & 1) + libbuf->writeByte('\n'); // module alignment + + assert(libbuf->offset == om->offset); + + OmToHeader(&h, om); + libbuf->write(&h, sizeof(h)); // module header + + libbuf->write(om->base, om->length); // module contents + } + +#if LOG + printf("moffset = x%x, libbuf->offset = x%x\n", moffset, libbuf->offset); +#endif + assert(libbuf->offset == moffset); +} diff --git a/libmach.c b/libmach.c new file mode 100644 index 00000000..8748a312 --- /dev/null +++ b/libmach.c @@ -0,0 +1,798 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +/* Implements object library reading and writing in the Mach-O object + * module format. While the format is + * equivalent to the Linux arch format, it differs in many details. + * This format is described in the Apple document + * "Mac OS X ABI Mach-O File Format Reference" dated 2007-04-26 + * in the section "Static Archive Libraries". + * That specification is only about half complete and has numerous + * errors, so use the source code here as a better guide. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mach.h" + +#include "rmem.h" +#include "root.h" +#include "stringtable.h" + +#include "mars.h" +#include "lib.h" + +#define LOG 0 + +Library::Library() +{ + libfile = NULL; + tab.init(); +} + +/*********************************** + * Set the library file name based on the output directory + * and the filename. + * Add default library file name extension. + */ + +void Library::setFilename(char *dir, char *filename) +{ +#if LOG + printf("Library::setFilename(dir = '%s', filename = '%s')\n", + dir ? dir : "", filename ? filename : ""); +#endif + char *arg = filename; + if (!arg || !*arg) + { // Generate lib file name from first obj name + char *n = global.params.objfiles->tdata()[0]; + + n = FileName::name(n); + FileName *fn = FileName::forceExt(n, global.lib_ext); + arg = fn->toChars(); + } + if (!FileName::absolute(arg)) + arg = FileName::combine(dir, arg); + FileName *libfilename = FileName::defaultExt(arg, global.lib_ext); + + libfile = new File(libfilename); +} + +void Library::write() +{ + if (global.params.verbose) + printf("library %s\n", libfile->name->toChars()); + + OutBuffer libbuf; + WriteLibToBuffer(&libbuf); + + // Transfer image to file + libfile->setbuffer(libbuf.data, libbuf.offset); + libbuf.extractData(); + + + char *p = FileName::path(libfile->name->toChars()); + FileName::ensurePathExists(p); + //mem.free(p); + + libfile->writev(); +} + +/*****************************************************************************/ + +void Library::addLibrary(void *buf, size_t buflen) +{ + addObject(NULL, buf, buflen); +} + + +/*****************************************************************************/ +/*****************************************************************************/ + +static uint32_t mach_signature = MH_MAGIC; // Mach-O file signature for 32 bit files +static uint32_t mach_signature64 = MH_MAGIC_64; // Mach-O file signature for 64 bit files + +void sputl(int value, void* buffer) +{ + unsigned char *p = (unsigned char*)buffer; + p[3] = (unsigned char)(value >> 24); + p[2] = (unsigned char)(value >> 16); + p[1] = (unsigned char)(value >> 8); + p[0] = (unsigned char)(value); +} + +int sgetl(void* buffer) +{ + unsigned char *p = (unsigned char*)buffer; + return (((((p[3] << 8) | p[2]) << 8) | p[1]) << 8) | p[0]; +} + + +struct ObjModule +{ + unsigned char *base; // where are we holding it in memory + unsigned length; // in bytes + unsigned offset; // offset from start of library + char *name; // module name (file name) + long file_time; // file time + unsigned user_id; + unsigned group_id; + unsigned file_mode; + int scan; // 1 means scan for symbols +}; + +struct Header +{ + #define OBJECT_NAME_SIZE 16 + char object_name[OBJECT_NAME_SIZE]; + char file_time[12]; + char user_id[6]; + char group_id[6]; + char file_mode[8]; // in octal + char file_size[10]; + char trailer[2]; +}; + +void OmToHeader(Header *h, ObjModule *om) +{ + size_t slen = strlen(om->name); + int nzeros = 8 - ((slen + 4) & 7); + if (nzeros < 4) + nzeros += 8; // emulate mysterious behavior of ar + + size_t len = sprintf(h->object_name, "#1/%ld", slen + nzeros); + memset(h->object_name + len, ' ', OBJECT_NAME_SIZE - len); + + /* In the following sprintf's, don't worry if the trailing 0 + * that sprintf writes goes off the end of the field. It will + * write into the next field, which we will promptly overwrite + * anyway. (So make sure to write the fields in ascending order.) + */ + + len = sprintf(h->file_time, "%lu", om->file_time); + assert(len <= 12); + memset(h->file_time + len, ' ', 12 - len); + + if (om->user_id > 999999) // yes, it happens + om->user_id = 0; // don't really know what to do here + len = sprintf(h->user_id, "%u", om->user_id); + assert(len <= 6); + memset(h->user_id + len, ' ', 6 - len); + + if (om->group_id > 999999) // yes, it happens + om->group_id = 0; // don't really know what to do here + len = sprintf(h->group_id, "%u", om->group_id); + assert(len <= 6); + memset(h->group_id + len, ' ', 6 - len); + + len = sprintf(h->file_mode, "%o", om->file_mode); + assert(len <= 8); + memset(h->file_mode + len, ' ', 8 - len); + + int filesize = om->length; + filesize = (filesize + 7) & ~7; + len = sprintf(h->file_size, "%lu", slen + nzeros + filesize); + assert(len <= 10); + memset(h->file_size + len, ' ', 10 - len); + + h->trailer[0] = '`'; + h->trailer[1] = '\n'; +} + +void Library::addSymbol(ObjModule *om, char *name, int pickAny) +{ +#if LOG + printf("Library::addSymbol(%s, %s, %d)\n", om->name, name, pickAny); +#endif +#if 0 // let linker sort out duplicates + StringValue *s = tab.insert(name, strlen(name)); + if (!s) + { // already in table + if (!pickAny) + { s = tab.lookup(name, strlen(name)); + assert(s); + ObjSymbol *os = (ObjSymbol *)s->ptrvalue; + error("multiple definition of %s: %s and %s: %s", + om->name, name, os->om->name, os->name); + } + } + else + { + ObjSymbol *os = new ObjSymbol(); + os->name = strdup(name); + os->om = om; + s->ptrvalue = (void *)os; + + objsymbols.push(os); + } +#else + ObjSymbol *os = new ObjSymbol(); + os->name = strdup(name); + os->om = om; + objsymbols.push(os); +#endif +} + +/************************************ + * Scan single object module for dictionary symbols. + * Send those symbols to Library::addSymbol(). + */ + +void Library::scanObjModule(ObjModule *om) +{ +#if LOG + printf("Library::scanObjModule(%s)\n", om->name); +#endif + unsigned char *buf = (unsigned char *)om->base; + size_t buflen = om->length; + int reason = 0; + uint32_t ncmds; + + struct mach_header *header = (struct mach_header *)buf; + struct mach_header_64 *header64 = NULL; + + /* First do sanity checks on object file + */ + if (buflen < sizeof(struct mach_header)) + { + Lcorrupt: + error("Mach-O object module %s corrupt, %d", om->name, reason); + return; + } + if (header->magic == MH_MAGIC) + { + if (header->cputype != CPU_TYPE_I386) + { + error("Mach-O object module %s has cputype = %d, should be %d", + om->name, header->cputype, CPU_TYPE_I386); + return; + } + if (header->filetype != MH_OBJECT) + { + error("Mach-O object module %s has file type = %d, should be %d", + om->name, header->filetype, MH_OBJECT); + return; + } + if (buflen < sizeof(struct mach_header) + header->sizeofcmds) + { reason = 2; + goto Lcorrupt; + } + ncmds = header->ncmds; + } + else if (header->magic == MH_MAGIC_64) + { + header64 = (struct mach_header_64 *)buf; + if (buflen < sizeof(struct mach_header_64)) + goto Lcorrupt; + if (header64->cputype != CPU_TYPE_X86_64) + { + error("Mach-O object module %s has cputype = %d, should be %d", + om->name, header64->cputype, CPU_TYPE_X86_64); + return; + } + if (header64->filetype != MH_OBJECT) + { + error("Mach-O object module %s has file type = %d, should be %d", + om->name, header64->filetype, MH_OBJECT); + return; + } + if (buflen < sizeof(struct mach_header_64) + header64->sizeofcmds) + { reason = 2; + goto Lcorrupt; + } + ncmds = header64->ncmds; + } + else + { reason = 1; + goto Lcorrupt; + } + + struct segment_command *segment_commands = NULL; + struct segment_command_64 *segment_commands64 = NULL; + struct symtab_command *symtab_commands = NULL; + struct dysymtab_command *dysymtab_commands = NULL; + + // Commands immediately follow mach_header + char *commands = (char *)buf + + (header->magic == MH_MAGIC_64 + ? sizeof(struct mach_header_64) + : sizeof(struct mach_header)); + for (uint32_t i = 0; i < ncmds; i++) + { struct load_command *command = (struct load_command *)commands; + //printf("cmd = 0x%02x, cmdsize = %u\n", command->cmd, command->cmdsize); + switch (command->cmd) + { + case LC_SEGMENT: + segment_commands = (struct segment_command *)command; + break; + case LC_SEGMENT_64: + segment_commands64 = (struct segment_command_64 *)command; + break; + case LC_SYMTAB: + symtab_commands = (struct symtab_command *)command; + break; + case LC_DYSYMTAB: + dysymtab_commands = (struct dysymtab_command *)command; + break; + } + commands += command->cmdsize; + } + + if (symtab_commands) + { + // Get pointer to string table + char *strtab = (char *)buf + symtab_commands->stroff; + if (buflen < symtab_commands->stroff + symtab_commands->strsize) + { reason = 3; + goto Lcorrupt; + } + + if (header->magic == MH_MAGIC_64) + { + // Get pointer to symbol table + struct nlist_64 *symtab = (struct nlist_64 *)((char *)buf + symtab_commands->symoff); + if (buflen < symtab_commands->symoff + symtab_commands->nsyms * sizeof(struct nlist_64)) + { reason = 4; + goto Lcorrupt; + } + + // For each symbol + for (int i = 0; i < symtab_commands->nsyms; i++) + { struct nlist_64 *s = symtab + i; + char *name = strtab + s->n_un.n_strx; + + if (s->n_type & N_STAB) + // values in /usr/include/mach-o/stab.h + ; //printf(" N_STAB"); + else + { + if (s->n_type & N_PEXT) + ; + if (s->n_type & N_EXT) + ; + switch (s->n_type & N_TYPE) + { + case N_UNDF: + break; + case N_ABS: + break; + case N_SECT: + if (s->n_type & N_EXT /*&& !(s->n_desc & N_REF_TO_WEAK)*/) + addSymbol(om, name, 1); + break; + case N_PBUD: + break; + case N_INDR: + break; + } + } + } + } + else + { + // Get pointer to symbol table + struct nlist *symtab = (struct nlist *)((char *)buf + symtab_commands->symoff); + if (buflen < symtab_commands->symoff + symtab_commands->nsyms * sizeof(struct nlist)) + { reason = 4; + goto Lcorrupt; + } + + // For each symbol + for (int i = 0; i < symtab_commands->nsyms; i++) + { struct nlist *s = symtab + i; + char *name = strtab + s->n_un.n_strx; + + if (s->n_type & N_STAB) + // values in /usr/include/mach-o/stab.h + ; //printf(" N_STAB"); + else + { + if (s->n_type & N_PEXT) + ; + if (s->n_type & N_EXT) + ; + switch (s->n_type & N_TYPE) + { + case N_UNDF: + break; + case N_ABS: + break; + case N_SECT: + if (s->n_type & N_EXT /*&& !(s->n_desc & N_REF_TO_WEAK)*/) + addSymbol(om, name, 1); + break; + case N_PBUD: + break; + case N_INDR: + break; + } + } + } + } + } +} + +/*************************************** + * Add object module or library to the library. + * Examine the buffer to see which it is. + * If the buffer is NULL, use module_name as the file name + * and load the file. + */ + +void Library::addObject(const char *module_name, void *buf, size_t buflen) +{ + if (!module_name) + module_name = ""; +#if LOG + printf("Library::addObject(%s)\n", module_name); +#endif + int fromfile = 0; + if (!buf) + { assert(module_name[0]); + FileName f((char *)module_name, 0); + File file(&f); + file.readv(); + buf = file.buffer; + buflen = file.len; + file.ref = 1; + fromfile = 1; + } + int reason = 0; + + if (buflen < 16) + { +#if LOG + printf("buf = %p, buflen = %d\n", buf, buflen); +#endif + Lcorrupt: + error("corrupt object module %s %d", module_name, reason); + return; + } + + if (memcmp(buf, "!\n", 8) == 0) + { /* Library file. + * Pull each object module out of the library and add it + * to the object module array. + */ +#if LOG + printf("archive, buf = %p, buflen = %d\n", buf, buflen); +#endif + unsigned offset = 8; + char *symtab = NULL; + unsigned symtab_size = 0; + char *filenametab = NULL; + unsigned filenametab_size = 0; + unsigned mstart = objmodules.dim; + while (offset < buflen) + { + if (offset + sizeof(Header) >= buflen) + { reason = 1; + goto Lcorrupt; + } + Header *header = (Header *)((unsigned char *)buf + offset); + offset += sizeof(Header); + char *endptr = NULL; + unsigned long size = strtoul(header->file_size, &endptr, 10); + if (endptr >= &header->file_size[10] || *endptr != ' ') + { reason = 2; + goto Lcorrupt; + } + if (offset + size > buflen) + { reason = 3; + goto Lcorrupt; + } + + if (memcmp(header->object_name, "__.SYMDEF ", 16) == 0 || + memcmp(header->object_name, "__.SYMDEF SORTED", 16) == 0) + { + /* Instead of rescanning the object modules we pull from a + * library, just use the already created symbol table. + */ + if (symtab) + { reason = 4; + goto Lcorrupt; + } + symtab = (char *)buf + offset; + symtab_size = size; + if (size < 4) + { reason = 5; + goto Lcorrupt; + } + } + else + { + ObjModule *om = new ObjModule(); + om->base = (unsigned char *)buf + offset - sizeof(Header); + om->length = size + sizeof(Header); + om->offset = 0; + om->name = (char *)(om->base + sizeof(Header)); + om->file_time = strtoul(header->file_time, &endptr, 10); + om->user_id = strtoul(header->user_id, &endptr, 10); + om->group_id = strtoul(header->group_id, &endptr, 10); + om->file_mode = strtoul(header->file_mode, &endptr, 8); + om->scan = 0; + objmodules.push(om); + } + offset += (size + 1) & ~1; + } + if (offset != buflen) + { reason = 9; + goto Lcorrupt; + } + + /* Scan the library's symbol table, and insert it into our own. + * We use this instead of rescanning the object module, because + * the library's creator may have a different idea of what symbols + * go into the symbol table than we do. + * This is also probably faster. + */ + unsigned nsymbols = sgetl(symtab) / 8; + char *s = symtab + 4 + nsymbols * 8 + 4; + if (4 + nsymbols * 8 + 4 > symtab_size) + { reason = 10; + goto Lcorrupt; + } + for (unsigned i = 0; i < nsymbols; i++) + { + unsigned soff = sgetl(symtab + 4 + i * 8); + char *name = s + soff; + //printf("soff = x%x name = %s\n", soff, name); + if (s + strlen(name) + 1 - symtab > symtab_size) + { reason = 11; + goto Lcorrupt; + } + unsigned moff = sgetl(symtab + 4 + i * 8 + 4); + //printf("symtab[%d] moff = x%x x%x, name = %s\n", i, moff, moff + sizeof(Header), name); + for (unsigned m = mstart; 1; m++) + { if (m == objmodules.dim) + { reason = 12; + goto Lcorrupt; // didn't find it + } + ObjModule *om = objmodules.tdata()[m]; + //printf("\tom offset = x%x\n", (char *)om->base - (char *)buf); + if (moff == (char *)om->base - (char *)buf) + { + addSymbol(om, name, 1); +// if (mstart == m) +// mstart++; + break; + } + } + } + + return; + } + + if (memcmp(buf, &mach_signature, sizeof(mach_signature)) != 0 && + memcmp(buf, &mach_signature64, sizeof(mach_signature64)) != 0) + { reason = 13; + goto Lcorrupt; + } + + /* It's a Mach-O object module + */ + ObjModule *om = new ObjModule(); + om->base = (unsigned char *)buf; + om->length = buflen; + om->offset = 0; + om->name = FileName::name(module_name); // remove path, but not extension + om->scan = 1; + if (fromfile) + { struct stat statbuf; + int i = stat(module_name, &statbuf); + if (i == -1) // error, errno is set + { reason = 14; + goto Lcorrupt; + } + om->file_time = statbuf.st_ctime; + om->user_id = statbuf.st_uid; + om->group_id = statbuf.st_gid; + om->file_mode = statbuf.st_mode; + } + else + { /* Mock things up for the object module file that never was + * actually written out. + */ + static uid_t uid; + static gid_t gid; + static int init; + if (!init) + { init = 1; + uid = getuid(); + gid = getgid(); + } + time(&om->file_time); + om->user_id = uid; + om->group_id = gid; + om->file_mode = 0100644; + } + objmodules.push(om); +} + + +/*****************************************************************************/ +/*****************************************************************************/ + +/********************************************** + * Create and write library to libbuf. + * The library consists of: + * !\n + * header + * dictionary + * object modules... + */ + +void Library::WriteLibToBuffer(OutBuffer *libbuf) +{ +#if LOG + printf("Library::WriteLibToBuffer()\n"); +#endif + static char pad[7] = { 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, }; + + /************* Scan Object Modules for Symbols ******************/ + + for (int i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + if (om->scan) + { + scanObjModule(om); + } + } + + /************* Determine module offsets ******************/ + + unsigned moffset = 8 + sizeof(Header) + 4 + 4; + + for (int i = 0; i < objsymbols.dim; i++) + { ObjSymbol *os = objsymbols.tdata()[i]; + + moffset += 8 + strlen(os->name) + 1; + } + moffset = (moffset + 3) & ~3; +// if (moffset & 4) +// moffset += 4; + unsigned hoffset = moffset; + +#if LOG + printf("\tmoffset = x%x\n", moffset); +#endif + + for (int i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + + moffset += moffset & 1; + om->offset = moffset; + if (om->scan) + { + size_t slen = strlen(om->name); + int nzeros = 8 - ((slen + 4) & 7); + if (nzeros < 4) + nzeros += 8; // emulate mysterious behavior of ar + int filesize = om->length; + filesize = (filesize + 7) & ~7; + moffset += sizeof(Header) + slen + nzeros + filesize; + } + else + { + moffset += om->length; + } + } + + libbuf->reserve(moffset); + + /************* Write the library ******************/ + libbuf->write("!\n", 8); + + ObjModule om; + om.base = NULL; + om.length = hoffset - (8 + sizeof(Header)); + om.offset = 8; + om.name = (char*)""; + ::time(&om.file_time); + om.user_id = getuid(); + om.group_id = getgid(); + om.file_mode = 0100644; + + Header h; + OmToHeader(&h, &om); + memcpy(h.object_name, "__.SYMDEF", 9); + int len = sprintf(h.file_size, "%u", om.length); + assert(len <= 10); + memset(h.file_size + len, ' ', 10 - len); + + libbuf->write(&h, sizeof(h)); + + char buf[4]; + + sputl(objsymbols.dim * 8, buf); + libbuf->write(buf, 4); + + int stringoff = 0; + for (int i = 0; i < objsymbols.dim; i++) + { ObjSymbol *os = objsymbols.tdata()[i]; + + sputl(stringoff, buf); + libbuf->write(buf, 4); + + sputl(os->om->offset, buf); + libbuf->write(buf, 4); + + stringoff += strlen(os->name) + 1; + } + + sputl(stringoff, buf); + libbuf->write(buf, 4); + + for (int i = 0; i < objsymbols.dim; i++) + { ObjSymbol *os = objsymbols.tdata()[i]; + + libbuf->writestring(os->name); + libbuf->writeByte(0); + } + while (libbuf->offset & 3) + libbuf->writeByte(0); + +// if (libbuf->offset & 4) +// libbuf->write(pad, 4); + +#if LOG + printf("\tlibbuf->moffset = x%x\n", libbuf->offset); +#endif + assert(libbuf->offset == hoffset); + + /* Write out each of the object modules + */ + for (int i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + + if (libbuf->offset & 1) + libbuf->writeByte('\n'); // module alignment + + assert(libbuf->offset == om->offset); + + if (om->scan) + { + OmToHeader(&h, om); + libbuf->write(&h, sizeof(h)); // module header + + size_t len = strlen(om->name); + libbuf->write(om->name, len); + + int nzeros = 8 - ((len + 4) & 7); + if (nzeros < 4) + nzeros += 8; // emulate mysterious behavior of ar + libbuf->fill0(nzeros); + + libbuf->write(om->base, om->length); // module contents + + // obj modules are padded out to 8 bytes in length with 0x0A + int filealign = om->length & 7; + if (filealign) + { + libbuf->write(pad, 8 - filealign); + } + } + else + { + libbuf->write(om->base, om->length); // module contents + } + } + +#if LOG + printf("moffset = x%x, libbuf->offset = x%x\n", moffset, libbuf->offset); +#endif + assert(libbuf->offset == moffset); +} diff --git a/libomf.c b/libomf.c new file mode 100644 index 00000000..b3703c6a --- /dev/null +++ b/libomf.c @@ -0,0 +1,959 @@ + +/* + * Copyright (c) 1986-1995 by Symantec + * Copyright (c) 2000-2011 by Digital Mars + * All Rights Reserved + * http://www.digitalmars.com + * Written by Walter Bright + * + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +// Compiler implementation of the D programming language + +#include +#include +#include + +#include "rmem.h" +#include "root.h" +#include "stringtable.h" + +#include "mars.h" +#include "lib.h" + +#define LOG 0 + +Library::Library() +{ + libfile = NULL; + tab.init(); +} + +/*********************************** + * Set the library file name based on the output directory + * and the filename. + * Add default library file name extension. + */ + +void Library::setFilename(char *dir, char *filename) +{ + char *arg = filename; + if (!arg || !*arg) + { // Generate lib file name from first obj name + char *n = global.params.objfiles->tdata()[0]; + + n = FileName::name(n); + FileName *fn = FileName::forceExt(n, global.lib_ext); + arg = fn->toChars(); + } + if (!FileName::absolute(arg)) + arg = FileName::combine(dir, arg); + FileName *libfilename = FileName::defaultExt(arg, global.lib_ext); + + libfile = new File(libfilename); +} + +void Library::write() +{ + if (global.params.verbose) + printf("library %s\n", libfile->name->toChars()); + + OutBuffer libbuf; + WriteLibToBuffer(&libbuf); + + // Transfer image to file + libfile->setbuffer(libbuf.data, libbuf.offset); + libbuf.extractData(); + + + char *p = FileName::path(libfile->name->toChars()); + FileName::ensurePathExists(p); + //mem.free(p); + + libfile->writev(); +} + +/*****************************************************************************/ + +void Library::addLibrary(void *buf, size_t buflen) +{ + addObject(NULL, buf, buflen); +} + + +/*****************************************************************************/ +/*****************************************************************************/ + +/************************** + * Record types: + */ + +#define RHEADR 0x6E +#define REGINT 0x70 +#define REDATA 0x72 +#define RIDATA 0x74 +#define OVLDEF 0x76 +#define ENDREC 0x78 +#define BLKDEF 0x7A +#define BLKEND 0x7C +#define DEBSYM 0x7E +#define THEADR 0x80 +#define LHEADR 0x82 +#define PEDATA 0x84 +#define PIDATA 0x86 +#define COMENT 0x88 +#define MODEND 0x8A +#define M386END 0x8B /* 32 bit module end record */ +#define EXTDEF 0x8C +#define TYPDEF 0x8E +#define PUBDEF 0x90 +#define PUB386 0x91 +#define LOCSYM 0x92 +#define LINNUM 0x94 +#define LNAMES 0x96 +#define SEGDEF 0x98 +#define GRPDEF 0x9A +#define FIXUPP 0x9C +/*#define (none) 0x9E */ +#define LEDATA 0xA0 +#define LIDATA 0xA2 +#define LIBHED 0xA4 +#define LIBNAM 0xA6 +#define LIBLOC 0xA8 +#define LIBDIC 0xAA +#define COMDEF 0xB0 +#define LEXTDEF 0xB4 +#define LPUBDEF 0xB6 +#define LCOMDEF 0xB8 +#define CEXTDEF 0xBC +#define COMDAT 0xC2 +#define LINSYM 0xC4 +#define ALIAS 0xC6 +#define LLNAMES 0xCA + + +#define LIBIDMAX (512 - 0x25 - 3 - 4) // max size that will fit in dictionary + + +struct ObjModule +{ + unsigned char *base; // where are we holding it in memory + unsigned length; // in bytes + unsigned short page; // page module starts in output file + unsigned char flags; +#define MFgentheadr 1 // generate THEADR record +#define MFtheadr 2 // module name comes from THEADR record + char *name; // module name +}; + +static void parseName(unsigned char **pp, char *name) +{ + unsigned char *p = *pp; + unsigned len = *p++; + if (len == 0xFF && *p == 0) // if long name + { + len = p[1] & 0xFF; + len |= (unsigned)p[2] << 8; + p += 3; + assert(len <= LIBIDMAX); + } + memcpy(name, p, len); + name[len] = 0; + *pp = p + len; +} + +static unsigned short parseIdx(unsigned char **pp) +{ + unsigned char *p = *pp; + unsigned char c = *p++; + + unsigned short idx = (0x80 & c) ? ((0x7F & c) << 8) + *p++ : c; + *pp = p; + return idx; +} + +void Library::addSymbol(ObjModule *om, char *name, int pickAny) +{ +#if LOG + printf("Library::addSymbol(%s, %s, %d)\n", om->name, name, pickAny); +#endif + StringValue *s = tab.insert(name, strlen(name)); + if (!s) + { // already in table + if (!pickAny) + { s = tab.lookup(name, strlen(name)); + assert(s); + ObjSymbol *os = (ObjSymbol *)s->ptrvalue; + error("multiple definition of %s: %s and %s: %s", + om->name, name, os->om->name, os->name); + } + } + else + { + ObjSymbol *os = new ObjSymbol(); + os->name = strdup(name); + os->om = om; + s->ptrvalue = (void *)os; + + objsymbols.push(os); + } +} + +/************************************ + * Scan single object module for dictionary symbols. + * Send those symbols to Library::addSymbol(). + */ + +void Library::scanObjModule(ObjModule *om) +{ int easyomf; + unsigned u; + unsigned char result = 0; + char name[LIBIDMAX + 1]; + + Strings names; + names.push(NULL); // don't use index 0 + + assert(om); + easyomf = 0; // assume not EASY-OMF + unsigned char *pend = om->base + om->length; + + unsigned char *pnext; + for (unsigned char *p = om->base; 1; p = pnext) + { + assert(p < pend); + unsigned char recTyp = *p++; + unsigned short recLen = *(unsigned short *)p; + p += 2; + pnext = p + recLen; + recLen--; // forget the checksum + + switch (recTyp) + { + case LNAMES: + case LLNAMES: + while (p + 1 < pnext) + { + parseName(&p, name); + names.push(strdup(name)); + } + break; + + case PUBDEF: + if (easyomf) + recTyp = PUB386; // convert to MS format + case PUB386: + if (!(parseIdx(&p) | parseIdx(&p))) + p += 2; // skip seg, grp, frame + while (p + 1 < pnext) + { + parseName(&p, name); + p += (recTyp == PUBDEF) ? 2 : 4; // skip offset + parseIdx(&p); // skip type index + addSymbol(om, name); + } + break; + + case COMDAT: + if (easyomf) + recTyp = COMDAT+1; // convert to MS format + case COMDAT+1: + int pickAny = 0; + + if (*p++ & 5) // if continuation or local comdat + break; + + unsigned char attr = *p++; + if (attr & 0xF0) // attr: if multiple instances allowed + pickAny = 1; + p++; // align + + p += 2; // enum data offset + if (recTyp == COMDAT+1) + p += 2; // enum data offset + + parseIdx(&p); // type index + + if ((attr & 0x0F) == 0) // if explicit allocation + { parseIdx(&p); // base group + parseIdx(&p); // base segment + } + + unsigned idx = parseIdx(&p); // public name index + if( idx == 0 || idx >= names.dim) + { + //debug(printf("[s] name idx=%d, uCntNames=%d\n", idx, uCntNames)); + error("corrupt COMDAT"); + return; + } + + //printf("[s] name='%s'\n",name); + addSymbol(om, names.tdata()[idx],pickAny); + break; + + case ALIAS: + while (p + 1 < pnext) + { + parseName(&p, name); + addSymbol(om, name); + parseName(&p, name); + } + break; + + case MODEND: + case M386END: + result = 1; + goto Ret; + + case COMENT: + // Recognize Phar Lap EASY-OMF format + { static unsigned char omfstr[7] = + {0x80,0xAA,'8','0','3','8','6'}; + + if (recLen == sizeof(omfstr)) + { + for (unsigned i = 0; i < sizeof(omfstr); i++) + if (*p++ != omfstr[i]) + goto L1; + easyomf = 1; + break; + L1: ; + } + } + // Recognize .IMPDEF Import Definition Records + { static unsigned char omfstr[] = + {0,0xA0,1}; + + if (recLen >= 7) + { + p++; + for (unsigned i = 1; i < sizeof(omfstr); i++) + if (*p++ != omfstr[i]) + goto L2; + p++; // skip OrdFlag field + parseName(&p, name); + addSymbol(om, name); + break; + L2: ; + } + } + break; + + default: + // ignore + ; + } + } +Ret: + for (u = 1; u < names.dim; u++) + free(names.tdata()[u]); +} + +/*************************************** + * Add object module or library to the library. + * Examine the buffer to see which it is. + * If the buffer is NULL, use module_name as the file name + * and load the file. + */ + +void Library::addObject(const char *module_name, void *buf, size_t buflen) +{ +#if LOG + printf("Library::addObject(%s)\n", module_name ? module_name : ""); +#endif + if (!buf) + { assert(module_name); + FileName f((char *)module_name, 0); + File file(&f); + file.readv(); + buf = file.buffer; + buflen = file.len; + file.ref = 1; + } + + unsigned g_page_size; + unsigned char *pstart = (unsigned char *)buf; + int islibrary = 0; + + /* See if it's an OMF library. + * Don't go by file extension. + */ + + #pragma pack(1) + struct LibHeader + { + unsigned char recTyp; // 0xF0 + unsigned short pagesize; + unsigned long lSymSeek; + unsigned short ndicpages; + unsigned char flags; + }; + #pragma pack() + + /* Determine if it is an OMF library, an OMF object module, + * or something else. + */ + if (buflen < sizeof(LibHeader)) + { + Lcorrupt: + error("corrupt object module"); + } + LibHeader *lh = (LibHeader *)buf; + if (lh->recTyp == 0xF0) + { /* OMF library + * The modules are all at buf[g_page_size .. lh->lSymSeek] + */ + islibrary = 1; + g_page_size = lh->pagesize + 3; + buf = (void *)(pstart + g_page_size); + if (lh->lSymSeek > buflen || + g_page_size > buflen) + goto Lcorrupt; + buflen = lh->lSymSeek - g_page_size; + } + else if (lh->recTyp == '!' && memcmp(lh, "!\n", 8) == 0) + { + error("COFF libraries not supported"); + return; + } + else + { // Not a library, assume OMF object module + g_page_size = 16; + } + + /* Split up the buffer buf[0..buflen] into multiple object modules, + * each aligned on a g_page_size boundary. + */ + + ObjModule *om = NULL; + int first_module = 1; + + unsigned char *p = (unsigned char *)buf; + unsigned char *pend = p + buflen; + unsigned char *pnext; + for (; p < pend; p = pnext) // for each OMF record + { + if (p + 3 >= pend) + goto Lcorrupt; + unsigned char recTyp = *p; + unsigned short recLen = *(unsigned short *)(p + 1); + pnext = p + 3 + recLen; + if (pnext > pend) + goto Lcorrupt; + recLen--; /* forget the checksum */ + + switch (recTyp) + { + case LHEADR : + case THEADR : + if (!om) + { char name[LIBIDMAX + 1]; + om = new ObjModule(); + om->flags = 0; + om->base = p; + p += 3; + parseName(&p, name); + if (first_module && module_name && !islibrary) + { // Remove path and extension + om->name = strdup(FileName::name(module_name)); + char *ext = FileName::ext(om->name); + if (ext) + ext[-1] = 0; + } + else + { /* Use THEADR name as module name, + * removing path and extension. + */ + om->name = strdup(FileName::name(name)); + char *ext = FileName::ext(om->name); + if (ext) + ext[-1] = 0; + + om->flags |= MFtheadr; + } + if (strcmp(name, "C") == 0) // old C compilers did this + { om->flags |= MFgentheadr; // generate our own THEADR + om->base = pnext; // skip past THEADR + } + objmodules.push(om); + first_module = 0; + } + break; + + case MODEND : + case M386END: + if (om) + { om->page = (om->base - pstart) / g_page_size; + om->length = pnext - om->base; + om = NULL; + } + // Round up to next page + unsigned t = pnext - pstart; + t = (t + g_page_size - 1) & ~(unsigned)(g_page_size - 1); + pnext = pstart + t; + break; + + default: + // ignore + ; + } + } + + if (om) + goto Lcorrupt; // missing MODEND record +} + + +/*****************************************************************************/ +/*****************************************************************************/ + +typedef int (__cdecl * cmpfunc_t)(const void *,const void *); + +extern "C" int NameCompare(ObjSymbol **p1, ObjSymbol **p2) +{ + return strcmp((*p1)->name, (*p2)->name); +} + +#define HASHMOD 0x25 +#define BUCKETPAGE 512 +#define BUCKETSIZE (BUCKETPAGE - HASHMOD - 1) + + +/*********************************** + * Calculates number of pages needed for dictionary + * Returns: + * number of pages + */ + +unsigned short Library::numDictPages(unsigned padding) +{ + unsigned short ndicpages; + unsigned short bucksForHash; + unsigned short bucksForSize; + unsigned symSize = 0; + + for (size_t i = 0; i < objsymbols.dim; i++) + { ObjSymbol *s = objsymbols.tdata()[i]; + + symSize += ( strlen(s->name) + 4 ) & ~1; + } + + for (size_t i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + + size_t len = strlen(om->name); + if (len > 0xFF) + len += 2; // Digital Mars long name extension + symSize += ( len + 4 + 1 ) & ~1; + } + + bucksForHash = (objsymbols.dim + objmodules.dim + HASHMOD - 3) / + (HASHMOD - 2); + bucksForSize = (symSize + BUCKETSIZE - padding - padding - 1) / + (BUCKETSIZE - padding); + + ndicpages = (bucksForHash > bucksForSize ) ? bucksForHash : bucksForSize; + //printf("ndicpages = %u\n",ndicpages); + + // Find prime number greater than ndicpages + static unsigned primes[] = + { 1,2,3,5,7,11,13,17,19,23,29,31,37,41,43, + 47,53,59,61,67,71,73,79,83,89,97,101,103, + 107,109,113,127,131,137,139,149,151,157, + 163,167,173,179,181,191,193,197,199,211, + 223,227,229,233,239,241,251,257,263,269, + 271,277,281,283,293,307,311,313,317,331, + 337,347,349,353,359,367,373,379,383,389, + 397,401,409,419,421,431,433,439,443,449, + 457,461,463,467,479,487,491,499,503,509, + //521,523,541,547, + 0 + }; + + for (size_t i = 0; 1; i++) + { + if ( primes[i] == 0 ) + { // Quick and easy way is out. + // Now try and find first prime number > ndicpages + unsigned prime; + + for (prime = (ndicpages + 1) | 1; 1; prime += 2) + { // Determine if prime is prime + for (unsigned u = 3; u < prime / 2; u += 2) + { + if ((prime / u) * u == prime) + goto L1; + } + break; + + L1: ; + } + ndicpages = prime; + break; + } + + if (primes[i] > ndicpages) + { + ndicpages = primes[i]; + break; + } + } + + return ndicpages; +} + + +/******************************************* + * Write a single entry into dictionary. + * Returns: + * 0 failure + */ + +static int EnterDict( unsigned char *bucketsP, unsigned short ndicpages, unsigned char *entry, unsigned entrylen ) +{ + unsigned short uStartIndex; + unsigned short uStep; + unsigned short uStartPage; + unsigned short uPageStep; + unsigned short uIndex; + unsigned short uPage; + unsigned short n; + unsigned u; + unsigned nbytes; + unsigned char *aP; + unsigned char *zP; + + aP = entry; + zP = aP + entrylen; // point at last char in identifier + + uStartPage = 0; + uPageStep = 0; + uStartIndex = 0; + uStep = 0; + + u = entrylen; + while ( u-- ) + { + uStartPage = _rotl( uStartPage, 2 ) ^ ( *aP | 0x20 ); + uStep = _rotr( uStep, 2 ) ^ ( *aP++ | 0x20 ); + uStartIndex = _rotr( uStartIndex, 2 ) ^ ( *zP | 0x20 ); + uPageStep = _rotl( uPageStep, 2 ) ^ ( *zP-- | 0x20 ); + } + + uStartPage %= ndicpages; + uPageStep %= ndicpages; + if ( uPageStep == 0 ) + uPageStep++; + uStartIndex %= HASHMOD; + uStep %= HASHMOD; + if ( uStep == 0 ) + uStep++; + + uPage = uStartPage; + uIndex = uStartIndex; + + // number of bytes in entry + nbytes = 1 + entrylen + 2; + if (entrylen > 255) + nbytes += 2; + + while (1) + { + aP = &bucketsP[uPage * BUCKETPAGE]; + uStartIndex = uIndex; + while (1) + { + if ( 0 == aP[ uIndex ] ) + { + // n = next available position in this page + n = aP[ HASHMOD ] << 1; + assert(n > HASHMOD); + + // if off end of this page + if (n + nbytes > BUCKETPAGE ) + { aP[ HASHMOD ] = 0xFF; + break; // next page + } + else + { + aP[ uIndex ] = n >> 1; + memcpy( (aP + n), entry, nbytes ); + aP[ HASHMOD ] += (nbytes + 1) >> 1; + if (aP[HASHMOD] == 0) + aP[HASHMOD] = 0xFF; + return 1; + } + } + uIndex += uStep; + uIndex %= 0x25; + /*if (uIndex > 0x25) + uIndex -= 0x25;*/ + if( uIndex == uStartIndex ) + break; + } + uPage += uPageStep; + if (uPage >= ndicpages) + uPage -= ndicpages; + if( uPage == uStartPage ) + break; + } + + return 0; +} + +/******************************************* + * Write the module and symbol names to the dictionary. + * Returns: + * 0 failure + */ + +int Library::FillDict(unsigned char *bucketsP, unsigned short ndicpages) +{ + unsigned char entry[4 + LIBIDMAX + 2 + 1]; + + //printf("FillDict()\n"); + + // Add each of the module names + for (size_t i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + + unsigned short n = strlen( om->name ); + if (n > 255) + { entry[0] = 0xFF; + entry[1] = 0; + *(unsigned short *)(entry + 2) = n + 1; + memcpy(entry + 4, om->name, n); + n += 3; + } + else + { entry[ 0 ] = 1 + n; + memcpy(entry + 1, om->name, n ); + } + entry[ n + 1 ] = '!'; + *((unsigned short *)( n + 2 + entry )) = om->page; + if ( n & 1 ) + entry[ n + 2 + 2 ] = 0; + if ( !EnterDict( bucketsP, ndicpages, entry, n + 1 ) ) + return 0; + } + + // Sort the symbols + qsort( objsymbols.tdata(), objsymbols.dim, 4, (cmpfunc_t)NameCompare ); + + // Add each of the symbols + for (size_t i = 0; i < objsymbols.dim; i++) + { ObjSymbol *os = objsymbols.tdata()[i]; + + unsigned short n = strlen( os->name ); + if (n > 255) + { entry[0] = 0xFF; + entry[1] = 0; + *(unsigned short *)(entry + 2) = n; + memcpy(entry + 4, os->name, n); + n += 3; + } + else + { entry[ 0 ] = n; + memcpy( entry + 1, os->name, n ); + } + *((unsigned short *)( n + 1 + entry )) = os->om->page; + if ( (n & 1) == 0 ) + entry[ n + 3] = 0; + if ( !EnterDict( bucketsP, ndicpages, entry, n ) ) + { + return 0; + } + } + return 1; +} + + +/********************************************** + * Create and write library to libbuf. + * The library consists of: + * library header + * object modules... + * dictionary header + * dictionary pages... + */ + +void Library::WriteLibToBuffer(OutBuffer *libbuf) +{ + /* Scan each of the object modules for symbols + * to go into the dictionary + */ + for (size_t i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + + scanObjModule(om); + } + + unsigned g_page_size = 16; + + /* Calculate page size so that the number of pages + * fits in 16 bits. This is because object modules + * are indexed by page number, stored as an unsigned short. + */ + while (1) + { + Lagain: +#if LOG + printf("g_page_size = %d\n", g_page_size); +#endif + unsigned offset = g_page_size; + + for (size_t i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + + unsigned page = offset / g_page_size; + if (page > 0xFFFF) + { // Page size is too small, double it and try again + g_page_size *= 2; + goto Lagain; + } + + // Write out the object module m + if (om->flags & MFgentheadr) // if generate THEADR record + { + size_t size = strlen(om->name); + assert(size <= LIBIDMAX); + + offset += size + 5; + //offset += om->length - (size + 5); + offset += om->length; + } + else + offset += om->length; + + // Round the size of the file up to the next page size + // by filling with 0s + unsigned n = (g_page_size - 1) & offset; + if (n) + offset += g_page_size - n; + } + break; + } + + + /* Leave one page of 0s at start as a dummy library header. + * Fill it in later with the real data. + */ + libbuf->fill0(g_page_size); + + /* Write each object module into the library + */ + for (size_t i = 0; i < objmodules.dim; i++) + { ObjModule *om = objmodules.tdata()[i]; + + unsigned page = libbuf->offset / g_page_size; + assert(page <= 0xFFFF); + om->page = page; + + // Write out the object module om + if (om->flags & MFgentheadr) // if generate THEADR record + { + unsigned size = strlen(om->name); + unsigned char header[4 + LIBIDMAX + 1]; + + header [0] = THEADR; + header [1] = 2 + size; + header [2] = 0; + header [3] = size; + assert(size <= 0xFF - 2); + + memcpy(4 + header, om->name, size); + + // Compute and store record checksum + unsigned n = size + 4; + unsigned char checksum = 0; + unsigned char *p = header; + while (n--) + { checksum -= *p; + p++; + } + *p = checksum; + + libbuf->write(header, size + 5); + //libbuf->write(om->base, om->length - (size + 5)); + libbuf->write(om->base, om->length); + } + else + libbuf->write(om->base, om->length); + + // Round the size of the file up to the next page size + // by filling with 0s + unsigned n = (g_page_size - 1) & libbuf->offset; + if (n) + libbuf->fill0(g_page_size - n); + } + + // File offset of start of dictionary + unsigned offset = libbuf->offset; + + // Write dictionary header, then round it to a BUCKETPAGE boundary + unsigned short size = (BUCKETPAGE - ((short)offset + 3)) & (BUCKETPAGE - 1); + libbuf->writeByte(0xF1); + libbuf->writeword(size); + libbuf->fill0(size); + + // Create dictionary + unsigned char *bucketsP = NULL; + unsigned short ndicpages; + unsigned short padding = 32; + for (;;) + { + ndicpages = numDictPages(padding); + +#if LOG + printf("ndicpages = %d\n", ndicpages); +#endif + // Allocate dictionary + if (bucketsP) + bucketsP = (unsigned char *)realloc(bucketsP, ndicpages * BUCKETPAGE); + else + bucketsP = (unsigned char *)malloc(ndicpages * BUCKETPAGE); + assert(bucketsP); + memset(bucketsP, 0, ndicpages * BUCKETPAGE); + for (unsigned u = 0; u < ndicpages; u++) + { + // 'next available' slot + bucketsP[u * BUCKETPAGE + HASHMOD] = (HASHMOD + 1) >> 1; + } + + if (FillDict(bucketsP, ndicpages)) + break; + padding += 16; // try again with more margins + } + + // Write dictionary + libbuf->write(bucketsP, ndicpages * BUCKETPAGE); + if (bucketsP) + free(bucketsP); + + // Create library header + #pragma pack(1) + struct Libheader + { + unsigned char recTyp; + unsigned short recLen; + long trailerPosn; + unsigned short ndicpages; + unsigned char flags; + char filler[ 6 ]; + }; + #pragma pack() + + Libheader libHeader; + memset(&libHeader, 0, sizeof(Libheader)); + libHeader.recTyp = 0xF0; + libHeader.recLen = 0x0D; + libHeader.trailerPosn = offset + (3 + size); + libHeader.recLen = g_page_size - 3; + libHeader.ndicpages = ndicpages; + libHeader.flags = 1; // always case sensitive + + // Write library header at start of buffer + memcpy(libbuf->data, &libHeader, sizeof(libHeader)); +} diff --git a/link.c b/link.c new file mode 100644 index 00000000..7e1c86f3 --- /dev/null +++ b/link.c @@ -0,0 +1,636 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#include +#include +#include +#include +#include +#include + +#if _WIN32 +#include +#endif + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#include +#include +#include +#endif + +#if linux || __APPLE__ + #define HAS_POSIX_SPAWN 1 + #include + #if __APPLE__ + #include + #define environ (*(_NSGetEnviron())) + #endif +#else + #define HAS_POSIX_SPAWN 0 +#endif + +#include "root.h" + +#include "mars.h" + +#include "rmem.h" + +#include "arraytypes.h" + +int executecmd(char *cmd, char *args, int useenv); +int executearg0(char *cmd, char *args); + +/**************************************** + * Write filename to cmdbuf, quoting if necessary. + */ + +void writeFilename(OutBuffer *buf, char *filename, size_t len) +{ + /* Loop and see if we need to quote + */ + for (size_t i = 0; i < len; i++) + { char c = filename[i]; + + if (isalnum((unsigned char)c) || c == '_') + continue; + + /* Need to quote + */ + buf->writeByte('"'); + buf->write(filename, len); + buf->writeByte('"'); + return; + } + + /* No quoting necessary + */ + buf->write(filename, len); +} + +void writeFilename(OutBuffer *buf, char *filename) +{ + writeFilename(buf, filename, strlen(filename)); +} + +/***************************** + * Run the linker. Return status of execution. + */ + +int runLINK() +{ +#if _WIN32 + char *p; + int status; + OutBuffer cmdbuf; + + global.params.libfiles->push("user32"); + global.params.libfiles->push("kernel32"); + + for (size_t i = 0; i < global.params.objfiles->dim; i++) + { + if (i) + cmdbuf.writeByte('+'); + p = global.params.objfiles->tdata()[i]; + char *basename = FileName::removeExt(FileName::name(p)); + char *ext = FileName::ext(p); + if (ext && !strchr(basename, '.')) + // Write name sans extension (but not if a double extension) + writeFilename(&cmdbuf, p, ext - p - 1); + else + writeFilename(&cmdbuf, p); + mem.free(basename); + } + cmdbuf.writeByte(','); + if (global.params.exefile) + writeFilename(&cmdbuf, global.params.exefile); + else + { /* Generate exe file name from first obj name. + * No need to add it to cmdbuf because the linker will default to it. + */ + char *n = global.params.objfiles->tdata()[0]; + n = FileName::name(n); + FileName *fn = FileName::forceExt(n, "exe"); + global.params.exefile = fn->toChars(); + } + + // Make sure path to exe file exists + { char *p = FileName::path(global.params.exefile); + FileName::ensurePathExists(p); + mem.free(p); + } + + cmdbuf.writeByte(','); + if (global.params.mapfile) + writeFilename(&cmdbuf, global.params.mapfile); + else if (global.params.map) + { + FileName *fn = FileName::forceExt(global.params.exefile, "map"); + + char *path = FileName::path(global.params.exefile); + char *p; + if (path[0] == '\0') + p = FileName::combine(global.params.objdir, fn->toChars()); + else + p = fn->toChars(); + + writeFilename(&cmdbuf, p); + } + else + cmdbuf.writestring("nul"); + cmdbuf.writeByte(','); + + for (size_t i = 0; i < global.params.libfiles->dim; i++) + { + if (i) + cmdbuf.writeByte('+'); + writeFilename(&cmdbuf, global.params.libfiles->tdata()[i]); + } + + if (global.params.deffile) + { + cmdbuf.writeByte(','); + writeFilename(&cmdbuf, global.params.deffile); + } + + /* Eliminate unnecessary trailing commas */ + while (1) + { size_t i = cmdbuf.offset; + if (!i || cmdbuf.data[i - 1] != ',') + break; + cmdbuf.offset--; + } + + if (global.params.resfile) + { + cmdbuf.writestring("/RC:"); + writeFilename(&cmdbuf, global.params.resfile); + } + + if (global.params.map || global.params.mapfile) + cmdbuf.writestring("/m"); + +#if 0 + if (debuginfo) + cmdbuf.writestring("/li"); + if (codeview) + { + cmdbuf.writestring("/co"); + if (codeview3) + cmdbuf.writestring(":3"); + } +#else + if (global.params.symdebug) + cmdbuf.writestring("/co"); +#endif + + cmdbuf.writestring("/noi"); + for (size_t i = 0; i < global.params.linkswitches->dim; i++) + { + cmdbuf.writestring(global.params.linkswitches->tdata()[i]); + } + cmdbuf.writeByte(';'); + + p = cmdbuf.toChars(); + + FileName *lnkfilename = NULL; + size_t plen = strlen(p); + if (plen > 7000) + { + lnkfilename = FileName::forceExt(global.params.exefile, "lnk"); + File flnk(lnkfilename); + flnk.setbuffer(p, plen); + flnk.ref = 1; + if (flnk.write()) + error("error writing file %s", lnkfilename); + if (lnkfilename->len() < plen) + sprintf(p, "@%s", lnkfilename->toChars()); + } + + char *linkcmd = getenv("LINKCMD"); + if (!linkcmd) + linkcmd = "link"; + status = executecmd(linkcmd, p, 1); + if (lnkfilename) + { + remove(lnkfilename->toChars()); + delete lnkfilename; + } + return status; +#elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + pid_t childpid; + int i; + int status; + + // Build argv[] + Strings argv; + + const char *cc = getenv("CC"); + if (!cc) + cc = "gcc"; + argv.push((char *)cc); + argv.insert(1, global.params.objfiles); + +#if __APPLE__ + // If we are on Mac OS X and linking a dynamic library, + // add the "-dynamiclib" flag + if (global.params.dll) + argv.push((char *) "-dynamiclib"); +#elif linux || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + if (global.params.dll) + argv.push((char *) "-shared"); +#endif + + // None of that a.out stuff. Use explicit exe file name, or + // generate one from name of first source file. + argv.push((char *)"-o"); + if (global.params.exefile) + { + if (global.params.dll) + global.params.exefile = FileName::forceExt(global.params.exefile, global.dll_ext)->toChars(); + argv.push(global.params.exefile); + } + else + { // Generate exe file name from first obj name + char *n = global.params.objfiles->tdata()[0]; + char *e; + char *ex; + + n = FileName::name(n); + e = FileName::ext(n); + if (e) + { + e--; // back up over '.' + ex = (char *)mem.malloc(e - n + 1); + memcpy(ex, n, e - n); + ex[e - n] = 0; + // If generating dll then force dll extension + if (global.params.dll) + ex = FileName::forceExt(ex, global.dll_ext)->toChars(); + } + else + ex = (char *)"a.out"; // no extension, so give up + argv.push(ex); + global.params.exefile = ex; + } + + // Make sure path to exe file exists + { char *p = FileName::path(global.params.exefile); + FileName::ensurePathExists(p); + mem.free(p); + } + + if (global.params.symdebug) + argv.push((char *)"-g"); + + if (global.params.is64bit) + argv.push((char *)"-m64"); + else + argv.push((char *)"-m32"); + + if (global.params.map || global.params.mapfile) + { + argv.push((char *)"-Xlinker"); +#if __APPLE__ + argv.push((char *)"-map"); +#else + argv.push((char *)"-Map"); +#endif + if (!global.params.mapfile) + { + FileName *fn = FileName::forceExt(global.params.exefile, "map"); + + char *path = FileName::path(global.params.exefile); + char *p; + if (path[0] == '\0') + p = FileName::combine(global.params.objdir, fn->toChars()); + else + p = fn->toChars(); + + global.params.mapfile = p; + } + argv.push((char *)"-Xlinker"); + argv.push(global.params.mapfile); + } + + if (0 && global.params.exefile) + { + /* This switch enables what is known as 'smart linking' + * in the Windows world, where unreferenced sections + * are removed from the executable. It eliminates unreferenced + * functions, essentially making a 'library' out of a module. + * Although it is documented to work with ld version 2.13, + * in practice it does not, but just seems to be ignored. + * Thomas Kuehne has verified that it works with ld 2.16.1. + * BUG: disabled because it causes exception handling to fail + * because EH sections are "unreferenced" and elided + */ + argv.push((char *)"-Xlinker"); + argv.push((char *)"--gc-sections"); + } + + for (size_t i = 0; i < global.params.linkswitches->dim; i++) + { char *p = global.params.linkswitches->tdata()[i]; + if (!p || !p[0] || !(p[0] == '-' && p[1] == 'l')) + // Don't need -Xlinker if switch starts with -l + argv.push((char *)"-Xlinker"); + argv.push(p); + } + + /* Add each library, prefixing it with "-l". + * The order of libraries passed is: + * 1. any libraries passed with -L command line switch + * 2. libraries specified on the command line + * 3. libraries specified by pragma(lib), which were appended + * to global.params.libfiles. + * 4. standard libraries. + */ + for (size_t i = 0; i < global.params.libfiles->dim; i++) + { char *p = global.params.libfiles->tdata()[i]; + size_t plen = strlen(p); + if (plen > 2 && p[plen - 2] == '.' && p[plen -1] == 'a') + argv.push(p); + else + { + char *s = (char *)mem.malloc(plen + 3); + s[0] = '-'; + s[1] = 'l'; + memcpy(s + 2, p, plen + 1); + argv.push(s); + } + } + + /* Standard libraries must go after user specified libraries + * passed with -l. + */ + const char *libname = (global.params.symdebug) + ? global.params.debuglibname + : global.params.defaultlibname; + size_t slen = strlen(libname); + if (slen) + { + char *buf = (char *)malloc(2 + slen + 1); + strcpy(buf, "-l"); + strcpy(buf + 2, libname); + argv.push(buf); // turns into /usr/lib/libphobos2.a + } + +// argv.push((void *)"-ldruntime"); + argv.push((char *)"-lpthread"); + argv.push((char *)"-lm"); +#if linux && DMDV2 + // Changes in ld for Ubuntu 11.10 require this to appear after phobos2 + argv.push((char *)"-lrt"); +#endif + + if (!global.params.quiet || global.params.verbose) + { + // Print it + for (size_t i = 0; i < argv.dim; i++) + printf("%s ", argv.tdata()[i]); + printf("\n"); + fflush(stdout); + } + + argv.push(NULL); +#if HAS_POSIX_SPAWN + int spawn_err = posix_spawnp(&childpid, argv.tdata()[0], NULL, NULL, argv.tdata(), environ); + if (spawn_err != 0) + { + perror(argv.tdata()[0]); + return -1; + } +#else + childpid = fork(); + if (childpid == 0) + { + execvp(argv.tdata()[0], argv.tdata()); + perror(argv.tdata()[0]); // failed to execute + return -1; + } + else if (childpid == -1) + { + perror("Unable to fork"); + return -1; + } +#endif + + waitpid(childpid, &status, 0); + + if (WIFEXITED(status)) + { + status = WEXITSTATUS(status); + if (status) + printf("--- errorlevel %d\n", status); + } + else if (WIFSIGNALED(status)) + { + printf("--- killed by signal %d\n", WTERMSIG(status)); + status = 1; + } + return status; +#else + printf ("Linking is not yet supported for this version of DMD.\n"); + return -1; +#endif +} + +/********************************** + * Delete generated EXE file. + */ + +void deleteExeFile() +{ + if (global.params.exefile) + { + //printf("deleteExeFile() %s\n", global.params.exefile); + remove(global.params.exefile); + } +} + +/****************************** + * Execute a rule. Return the status. + * cmd program to run + * args arguments to cmd, as a string + * useenv if cmd knows about _CMDLINE environment variable + */ + +#if _WIN32 +int executecmd(char *cmd, char *args, int useenv) +{ + int status; + size_t len; + + if (!global.params.quiet || global.params.verbose) + { + printf("%s %s\n", cmd, args); + fflush(stdout); + } + + if ((len = strlen(args)) > 255) + { char *q; + static char envname[] = "@_CMDLINE"; + + envname[0] = '@'; + switch (useenv) + { case 0: goto L1; + case 2: envname[0] = '%'; break; + } + q = (char *) alloca(sizeof(envname) + len + 1); + sprintf(q,"%s=%s", envname + 1, args); + status = putenv(q); + if (status == 0) + args = envname; + else + { + L1: + error("command line length of %d is too long",len); + } + } + + status = executearg0(cmd,args); +#if _WIN32 + if (status == -1) + status = spawnlp(0,cmd,cmd,args,NULL); +#endif +// if (global.params.verbose) +// printf("\n"); + if (status) + { + if (status == -1) + printf("Can't run '%s', check PATH\n", cmd); + else + printf("--- errorlevel %d\n", status); + } + return status; +} +#endif + +/************************************** + * Attempt to find command to execute by first looking in the directory + * where DMD was run from. + * Returns: + * -1 did not find command there + * !=-1 exit status from command + */ + +#if _WIN32 +int executearg0(char *cmd, char *args) +{ + const char *file; + char *argv0 = global.params.argv0; + + //printf("argv0='%s', cmd='%s', args='%s'\n",argv0,cmd,args); + + // If cmd is fully qualified, we don't do this + if (FileName::absolute(cmd)) + return -1; + + file = FileName::replaceName(argv0, cmd); + + //printf("spawning '%s'\n",file); +#if _WIN32 + return spawnl(0,file,file,args,NULL); +#elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + char *full; + int cmdl = strlen(cmd); + + full = (char*) mem.malloc(cmdl + strlen(args) + 2); + if (full == NULL) + return 1; + strcpy(full, cmd); + full [cmdl] = ' '; + strcpy(full + cmdl + 1, args); + + int result = system(full); + + mem.free(full); + return result; +#else + assert(0); +#endif +} +#endif + +/*************************************** + * Run the compiled program. + * Return exit status. + */ + +int runProgram() +{ + //printf("runProgram()\n"); + if (global.params.verbose) + { + printf("%s", global.params.exefile); + for (size_t i = 0; i < global.params.runargs_length; i++) + printf(" %s", (char *)global.params.runargs[i]); + printf("\n"); + } + + // Build argv[] + Strings argv; + + argv.push(global.params.exefile); + for (size_t i = 0; i < global.params.runargs_length; i++) + { char *a = global.params.runargs[i]; + +#if _WIN32 + // BUG: what about " appearing in the string? + if (strchr(a, ' ')) + { char *b = (char *)mem.malloc(3 + strlen(a)); + sprintf(b, "\"%s\"", a); + a = b; + } +#endif + argv.push(a); + } + argv.push(NULL); + +#if _WIN32 + char *ex = FileName::name(global.params.exefile); + if (ex == global.params.exefile) + ex = FileName::combine(".", ex); + else + ex = global.params.exefile; + return spawnv(0,ex,argv.tdata()); +#elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + pid_t childpid; + int status; + + childpid = fork(); + if (childpid == 0) + { + char *fn = argv.tdata()[0]; + if (!FileName::absolute(fn)) + { // Make it "./fn" + fn = FileName::combine(".", fn); + } + execv(fn, argv.tdata()); + perror(fn); // failed to execute + return -1; + } + + waitpid(childpid, &status, 0); + + if (WIFEXITED(status)) + { + status = WEXITSTATUS(status); + //printf("--- errorlevel %d\n", status); + } + else if (WIFSIGNALED(status)) + { + printf("--- killed by signal %d\n", WTERMSIG(status)); + status = 1; + } + return status; +#else + assert(0); +#endif +} diff --git a/macro.c b/macro.c new file mode 100644 index 00000000..398ba77f --- /dev/null +++ b/macro.c @@ -0,0 +1,449 @@ + +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +/* Simple macro text processor. + */ + +#include +#include +#include +#include +#include + +#include "rmem.h" +#include "root.h" + +#include "macro.h" + +#define isidstart(c) (isalpha(c) || (c) == '_') +#define isidchar(c) (isalnum(c) || (c) == '_') + +unsigned char *memdup(unsigned char *p, size_t len) +{ + return (unsigned char *)memcpy(mem.malloc(len), p, len); +} + +Macro::Macro(unsigned char *name, size_t namelen, unsigned char *text, size_t textlen) +{ + next = NULL; + +#if 1 + this->name = name; + this->namelen = namelen; + + this->text = text; + this->textlen = textlen; +#else + this->name = name; + this->namelen = namelen; + + this->text = text; + this->textlen = textlen; +#endif + inuse = 0; +} + + +Macro *Macro::search(unsigned char *name, size_t namelen) +{ Macro *table; + + //printf("Macro::search(%.*s)\n", namelen, name); + for (table = this; table; table = table->next) + { + if (table->namelen == namelen && + memcmp(table->name, name, namelen) == 0) + { + //printf("\tfound %d\n", table->textlen); + break; + } + } + return table; +} + +Macro *Macro::define(Macro **ptable, unsigned char *name, size_t namelen, unsigned char *text, size_t textlen) +{ + //printf("Macro::define('%.*s' = '%.*s')\n", namelen, name, textlen, text); + + Macro *table; + + //assert(ptable); + for (table = *ptable; table; table = table->next) + { + if (table->namelen == namelen && + memcmp(table->name, name, namelen) == 0) + { + table->text = text; + table->textlen = textlen; + return table; + } + } + table = new Macro(name, namelen, text, textlen); + table->next = *ptable; + *ptable = table; + return table; +} + +/********************************************************** + * Given buffer p[0..end], extract argument marg[0..marglen]. + * Params: + * n 0: get entire argument + * 1..9: get nth argument + * -1: get 2nd through end + */ + +unsigned extractArgN(unsigned char *p, unsigned end, unsigned char **pmarg, unsigned *pmarglen, int n) +{ + /* Scan forward for matching right parenthesis. + * Nest parentheses. + * Skip over $( and $) + * Skip over "..." and '...' strings inside HTML tags. + * Skip over comments. + * Skip over previous macro insertions + * Set marglen. + */ + unsigned parens = 1; + unsigned char instring = 0; + unsigned incomment = 0; + unsigned intag = 0; + unsigned inexp = 0; + unsigned argn = 0; + + unsigned v = 0; + + Largstart: +#if 1 + // Skip first space, if any, to find the start of the macro argument + if (v < end && isspace(p[v])) + v++; +#else + // Skip past spaces to find the start of the macro argument + for (; v < end && isspace(p[v]); v++) + ; +#endif + *pmarg = p + v; + + for (; v < end; v++) + { unsigned char c = p[v]; + + switch (c) + { + case ',': + if (!inexp && !instring && !incomment && parens == 1) + { + argn++; + if (argn == 1 && n == -1) + { v++; + goto Largstart; + } + if (argn == n) + break; + if (argn + 1 == n) + { v++; + goto Largstart; + } + } + continue; + + case '(': + if (!inexp && !instring && !incomment) + parens++; + continue; + + case ')': + if (!inexp && !instring && !incomment && --parens == 0) + { + break; + } + continue; + + case '"': + case '\'': + if (!inexp && !incomment && intag) + { + if (c == instring) + instring = 0; + else if (!instring) + instring = c; + } + continue; + + case '<': + if (!inexp && !instring && !incomment) + { + if (v + 6 < end && + p[v + 1] == '!' && + p[v + 2] == '-' && + p[v + 3] == '-') + { + incomment = 1; + v += 3; + } + else if (v + 2 < end && + isalpha(p[v + 1])) + intag = 1; + } + continue; + + case '>': + if (!inexp) + intag = 0; + continue; + + case '-': + if (!inexp && + !instring && + incomment && + v + 2 < end && + p[v + 1] == '-' && + p[v + 2] == '>') + { + incomment = 0; + v += 2; + } + continue; + + case 0xFF: + if (v + 1 < end) + { + if (p[v + 1] == '{') + inexp++; + else if (p[v + 1] == '}') + inexp--; + } + continue; + + default: + continue; + } + break; + } + if (argn == 0 && n == -1) + *pmarg = p + v; + *pmarglen = p + v - *pmarg; + //printf("extractArg%d('%.*s') = '%.*s'\n", n, end, p, *pmarglen, *pmarg); + return v; +} + + +/***************************************************** + * Expand macro in place in buf. + * Only look at the text in buf from start to end. + */ + +void Macro::expand(OutBuffer *buf, unsigned start, unsigned *pend, + unsigned char *arg, unsigned arglen) +{ +#if 0 + printf("Macro::expand(buf[%d..%d], arg = '%.*s')\n", start, *pend, arglen, arg); + printf("Buf is: '%.*s'\n", *pend - start, buf->data + start); +#endif + + static int nest; + if (nest > 100) // limit recursive expansion + return; + nest++; + + unsigned end = *pend; + assert(start <= end); + assert(end <= buf->offset); + + /* First pass - replace $0 + */ + arg = memdup(arg, arglen); + for (unsigned u = start; u + 1 < end; ) + { + unsigned char *p = buf->data; // buf->data is not loop invariant + + /* Look for $0, but not $$0, and replace it with arg. + */ + if (p[u] == '$' && (isdigit(p[u + 1]) || p[u + 1] == '+')) + { + if (u > start && p[u - 1] == '$') + { // Don't expand $$0, but replace it with $0 + buf->remove(u - 1, 1); + end--; + u += 1; // now u is one past the closing '1' + continue; + } + + unsigned char c = p[u + 1]; + int n = (c == '+') ? -1 : c - '0'; + + unsigned char *marg; + unsigned marglen; + extractArgN(arg, arglen, &marg, &marglen, n); + if (marglen == 0) + { // Just remove macro invocation + //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg); + buf->remove(u, 2); + end -= 2; + } + else if (c == '+') + { + // Replace '$+' with 'arg' + //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg); + buf->remove(u, 2); + buf->insert(u, marg, marglen); + end += marglen - 2; + + // Scan replaced text for further expansion + unsigned mend = u + marglen; + expand(buf, u, &mend, NULL, 0); + end += mend - (u + marglen); + u = mend; + } + else + { + // Replace '$1' with '\xFF{arg\xFF}' + //printf("Replacing '$%c' with '\xFF{%.*s\xFF}'\n", p[u + 1], marglen, marg); + buf->data[u] = 0xFF; + buf->data[u + 1] = '{'; + buf->insert(u + 2, marg, marglen); + buf->insert(u + 2 + marglen, "\xFF}", 2); + end += -2 + 2 + marglen + 2; + + // Scan replaced text for further expansion + unsigned mend = u + 2 + marglen; + expand(buf, u + 2, &mend, NULL, 0); + end += mend - (u + 2 + marglen); + u = mend; + } + //printf("u = %d, end = %d\n", u, end); + //printf("#%.*s#\n", end, &buf->data[0]); + continue; + } + + u++; + } + + /* Second pass - replace other macros + */ + for (unsigned u = start; u + 4 < end; ) + { + unsigned char *p = buf->data; // buf->data is not loop invariant + + /* A valid start of macro expansion is $(c, where c is + * an id start character, and not $$(c. + */ + if (p[u] == '$' && p[u + 1] == '(' && isidstart(p[u + 2])) + { + //printf("\tfound macro start '%c'\n", p[u + 2]); + unsigned char *name = p + u + 2; + unsigned namelen = 0; + + unsigned char *marg; + unsigned marglen; + + unsigned v; + /* Scan forward to find end of macro name and + * beginning of macro argument (marg). + */ + for (v = u + 2; v < end; v++) + { unsigned char c = p[v]; + + if (!isidchar(c)) + { // We've gone past the end of the macro name. + namelen = v - (u + 2); + break; + } + } + + v += extractArgN(p + v, end - v, &marg, &marglen, 0); + assert(v <= end); + + if (v < end) + { // v is on the closing ')' + if (u > start && p[u - 1] == '$') + { // Don't expand $$(NAME), but replace it with $(NAME) + buf->remove(u - 1, 1); + end--; + u = v; // now u is one past the closing ')' + continue; + } + + Macro *m = search(name, namelen); + if (m) + { +#if 0 + if (m->textlen && m->text[0] == ' ') + { m->text++; + m->textlen--; + } +#endif + if (m->inuse && marglen == 0) + { // Remove macro invocation + buf->remove(u, v + 1 - u); + end -= v + 1 - u; + } + else if (m->inuse && arglen == marglen && memcmp(arg, marg, arglen) == 0) + { // Recursive expansion; just leave in place + + } + else + { + //printf("\tmacro '%.*s'(%.*s) = '%.*s'\n", m->namelen, m->name, marglen, marg, m->textlen, m->text); +#if 1 + marg = memdup(marg, marglen); + // Insert replacement text + buf->spread(v + 1, 2 + m->textlen + 2); + buf->data[v + 1] = 0xFF; + buf->data[v + 2] = '{'; + memcpy(buf->data + v + 3, m->text, m->textlen); + buf->data[v + 3 + m->textlen] = 0xFF; + buf->data[v + 3 + m->textlen + 1] = '}'; + + end += 2 + m->textlen + 2; + + // Scan replaced text for further expansion + m->inuse++; + unsigned mend = v + 1 + 2+m->textlen+2; + expand(buf, v + 1, &mend, marg, marglen); + end += mend - (v + 1 + 2+m->textlen+2); + m->inuse--; + + buf->remove(u, v + 1 - u); + end -= v + 1 - u; + u += mend - (v + 1); +#else + // Insert replacement text + buf->insert(v + 1, m->text, m->textlen); + end += m->textlen; + + // Scan replaced text for further expansion + m->inuse++; + unsigned mend = v + 1 + m->textlen; + expand(buf, v + 1, &mend, marg, marglen); + end += mend - (v + 1 + m->textlen); + m->inuse--; + + buf->remove(u, v + 1 - u); + end -= v + 1 - u; + u += mend - (v + 1); +#endif + mem.free(marg); + //printf("u = %d, end = %d\n", u, end); + //printf("#%.*s#\n", end - u, &buf->data[u]); + continue; + } + } + else + { + // Replace $(NAME) with nothing + buf->remove(u, v + 1 - u); + end -= (v + 1 - u); + continue; + } + } + } + u++; + } + mem.free(arg); + *pend = end; + nest--; +} diff --git a/macro.h b/macro.h new file mode 100644 index 00000000..7c939621 --- /dev/null +++ b/macro.h @@ -0,0 +1,45 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_MACRO_H +#define DMD_MACRO_H 1 + +#include +#include +#include +#include + +#include "root.h" + + +struct Macro +{ + private: + Macro *next; // next in list + + unsigned char *name; // macro name + size_t namelen; // length of macro name + + unsigned char *text; // macro replacement text + size_t textlen; // length of replacement text + + int inuse; // macro is in use (don't expand) + + Macro(unsigned char *name, size_t namelen, unsigned char *text, size_t textlen); + Macro *search(unsigned char *name, size_t namelen); + + public: + static Macro *define(Macro **ptable, unsigned char *name, size_t namelen, unsigned char *text, size_t textlen); + + void expand(OutBuffer *buf, unsigned start, unsigned *pend, + unsigned char *arg, unsigned arglen); +}; + +#endif diff --git a/mangle.c b/mangle.c new file mode 100644 index 00000000..c4baa871 --- /dev/null +++ b/mangle.c @@ -0,0 +1,272 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2010 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include + +#include "root.h" + +#include "init.h" +#include "declaration.h" +#include "aggregate.h" +#include "mtype.h" +#include "attrib.h" +#include "template.h" +#include "id.h" +#include "module.h" + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +char *cpp_mangle(Dsymbol *s); +#endif + +char *mangle(Declaration *sthis) +{ + OutBuffer buf; + char *id; + Dsymbol *s; + + //printf("::mangle(%s)\n", sthis->toChars()); + s = sthis; + do + { + //printf("mangle: s = %p, '%s', parent = %p\n", s, s->toChars(), s->parent); + if (s->ident) + { + FuncDeclaration *fd = s->isFuncDeclaration(); + if (s != sthis && fd) + { + id = mangle(fd); + buf.prependstring(id); + goto L1; + } + else + { + id = s->ident->toChars(); + int len = strlen(id); + char tmp[sizeof(len) * 3 + 1]; + buf.prependstring(id); + sprintf(tmp, "%d", len); + buf.prependstring(tmp); + } + } + else + buf.prependstring("0"); + s = s->parent; + } while (s); + +// buf.prependstring("_D"); +L1: + //printf("deco = '%s'\n", sthis->type->deco ? sthis->type->deco : "null"); + //printf("sthis->type = %s\n", sthis->type->toChars()); + FuncDeclaration *fd = sthis->isFuncDeclaration(); + if (fd && (fd->needThis() || fd->isNested())) + buf.writeByte(Type::needThisPrefix()); + if (sthis->type->deco) + buf.writestring(sthis->type->deco); + else + { +#ifdef DEBUG + if (!fd->inferRetType) + printf("%s\n", fd->toChars()); +#endif + assert(fd && fd->inferRetType); + } + + id = buf.toChars(); + buf.data = NULL; + return id; +} + +char *Declaration::mangle() +#if __DMC__ + __out(result) + { + int len = strlen(result); + + assert(len > 0); + //printf("mangle: '%s' => '%s'\n", toChars(), result); + for (int i = 0; i < len; i++) + { + assert(result[i] == '_' || + result[i] == '@' || + isalnum(result[i]) || result[i] & 0x80); + } + } + __body +#endif + { + //printf("Declaration::mangle(this = %p, '%s', parent = '%s', linkage = %d)\n", this, toChars(), parent ? parent->toChars() : "null", linkage); + if (!parent || parent->isModule() || linkage == LINKcpp) // if at global scope + { + // If it's not a D declaration, no mangling + switch (linkage) + { + case LINKd: + break; + + case LINKc: + case LINKwindows: + case LINKpascal: + return ident->toChars(); + + case LINKcpp: +#if CPP_MANGLE + return cpp_mangle(this); +#else + // Windows C++ mangling is done by C++ back end + return ident->toChars(); +#endif + + case LINKdefault: + error("forward declaration"); + return ident->toChars(); + + default: + fprintf(stdmsg, "'%s', linkage = %d\n", toChars(), linkage); + assert(0); + } + } + char *p = ::mangle(this); + OutBuffer buf; + buf.writestring("_D"); + buf.writestring(p); + p = buf.toChars(); + buf.data = NULL; + //printf("Declaration::mangle(this = %p, '%s', parent = '%s', linkage = %d) = %s\n", this, toChars(), parent ? parent->toChars() : "null", linkage, p); + return p; + } + +char *FuncDeclaration::mangle() +#if __DMC__ + __out(result) + { + assert(strlen(result) > 0); + } + __body +#endif + { + if (isMain()) + return (char *)"_Dmain"; + + if (isWinMain() || isDllMain() || ident == Id::tls_get_addr) + return ident->toChars(); + + assert(this); + return Declaration::mangle(); + } + +char *StructDeclaration::mangle() +{ + //printf("StructDeclaration::mangle() '%s'\n", toChars()); + return Dsymbol::mangle(); +} + + +char *TypedefDeclaration::mangle() +{ + //printf("TypedefDeclaration::mangle() '%s'\n", toChars()); + return Dsymbol::mangle(); +} + + +char *ClassDeclaration::mangle() +{ + Dsymbol *parentsave = parent; + + //printf("ClassDeclaration::mangle() %s.%s\n", parent->toChars(), toChars()); + + /* These are reserved to the compiler, so keep simple + * names for them. + */ + if (ident == Id::Exception) + { if (parent->ident == Id::object) + parent = NULL; + } + else if (ident == Id::TypeInfo || +// ident == Id::Exception || + ident == Id::TypeInfo_Struct || + ident == Id::TypeInfo_Class || + ident == Id::TypeInfo_Typedef || + ident == Id::TypeInfo_Tuple || + this == object || + this == classinfo || + this == Module::moduleinfo || + memcmp(ident->toChars(), "TypeInfo_", 9) == 0 + ) + parent = NULL; + + char *id = Dsymbol::mangle(); + parent = parentsave; + return id; +} + + +char *TemplateInstance::mangle() +{ + OutBuffer buf; + +#if 0 + printf("TemplateInstance::mangle() %p %s", this, toChars()); + if (parent) + printf(" parent = %s %s", parent->kind(), parent->toChars()); + printf("\n"); +#endif + char *id = ident ? ident->toChars() : toChars(); + if (!tempdecl) + error("is not defined"); + else + { + Dsymbol *par = isnested || isTemplateMixin() ? parent : tempdecl->parent; + if (par) + { + char *p = par->mangle(); + if (p[0] == '_' && p[1] == 'D') + p += 2; + buf.writestring(p); + } + } + buf.printf("%zu%s", strlen(id), id); + id = buf.toChars(); + buf.data = NULL; + //printf("TemplateInstance::mangle() %s = %s\n", toChars(), id); + return id; +} + + + +char *Dsymbol::mangle() +{ + OutBuffer buf; + char *id; + +#if 0 + printf("Dsymbol::mangle() '%s'", toChars()); + if (parent) + printf(" parent = %s %s", parent->kind(), parent->toChars()); + printf("\n"); +#endif + id = ident ? ident->toChars() : toChars(); + if (parent) + { + char *p = parent->mangle(); + if (p[0] == '_' && p[1] == 'D') + p += 2; + buf.writestring(p); + } + buf.printf("%zu%s", strlen(id), id); + id = buf.toChars(); + buf.data = NULL; + //printf("Dsymbol::mangle() %s = %s\n", toChars(), id); + return id; +} + + diff --git a/mars.c b/mars.c new file mode 100644 index 00000000..5c7f38eb --- /dev/null +++ b/mars.c @@ -0,0 +1,1594 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2012 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// https://github.com/D-Programming-Language/dmd/blob/master/src/mars.c +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include +#include + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#include +#endif + +#include "rmem.h" +#include "root.h" +#include "async.h" + +#include "mars.h" +#include "module.h" +#include "mtype.h" +#include "id.h" +#include "cond.h" +#include "expression.h" +#include "lexer.h" +#include "lib.h" +#include "json.h" + +#if WINDOWS_SEH +#include +long __cdecl __ehfilter(LPEXCEPTION_POINTERS ep); +#endif + + +int response_expand(int *pargc, char ***pargv); +void browse(const char *url); +void getenv_setargv(const char *envvar, int *pargc, char** *pargv); + +void obj_start(char *srcfile); +void obj_end(Library *library, File *objfile); + +void printCtfePerformanceStats(); + +Global global; + +Global::Global() +{ + mars_ext = "d"; + sym_ext = "d"; + hdr_ext = "di"; + doc_ext = "html"; + ddoc_ext = "ddoc"; + json_ext = "json"; + map_ext = "map"; + +#if TARGET_WINDOS + obj_ext = "obj"; +#elif TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + obj_ext = "o"; +#elif TARGET_NET +#else +#error "fix this" +#endif + +#if TARGET_WINDOS + lib_ext = "lib"; +#elif TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + lib_ext = "a"; +#elif TARGET_NET +#else +#error "fix this" +#endif + +#if TARGET_WINDOS + dll_ext = "dll"; +#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + dll_ext = "so"; +#elif TARGET_OSX + dll_ext = "dylib"; +#else +#error "fix this" +#endif + + copyright = "Copyright (c) 1999-2012 by Digital Mars"; + written = "written by Walter Bright" +#if TARGET_NET + "\nMSIL back-end (alpha release) by Cristian L. Vlasceanu and associates."; +#endif + ; + version = "v2.058"; + global.structalign = 8; + + memset(¶ms, 0, sizeof(Param)); +} + +unsigned Global::startGagging() +{ + ++gag; + return gaggedErrors; +} + +bool Global::endGagging(unsigned oldGagged) +{ + bool anyErrs = (gaggedErrors != oldGagged); + --gag; + // Restore the original state of gagged errors; set total errors + // to be original errors + new ungagged errors. + errors -= (gaggedErrors - oldGagged); + gaggedErrors = oldGagged; + return anyErrs; +} + + +char *Loc::toChars() +{ + OutBuffer buf; + + if (filename) + { + buf.printf("%s", filename); + } + + if (linnum) + buf.printf("(%d)", linnum); + buf.writeByte(0); + return (char *)buf.extractData(); +} + +Loc::Loc(Module *mod, unsigned linnum) +{ + this->linnum = linnum; + this->filename = mod ? mod->srcfile->toChars() : NULL; +} + +bool Loc::equals(const Loc& loc) +{ + return linnum == loc.linnum && FileName::equals(filename, loc.filename); +} + +/************************************** + * Print error message + */ + +void error(Loc loc, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end( ap ); +} + +void error(const char *filename, unsigned linnum, const char *format, ...) +{ Loc loc; + loc.filename = (char *)filename; + loc.linnum = linnum; + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end( ap ); +} + +void warning(Loc loc, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vwarning(loc, format, ap); + va_end( ap ); +} + +/************************************** + * Print supplementary message about the last error + * Used for backtraces, etc + */ +void errorSupplemental(Loc loc, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + verrorSupplemental(loc, format, ap); + va_end( ap ); +} + +void verror(Loc loc, const char *format, va_list ap) +{ + if (!global.gag) + { + char *p = loc.toChars(); + + if (*p) + fprintf(stdmsg, "%s: ", p); + mem.free(p); + + fprintf(stdmsg, "Error: "); +#if _MSC_VER + // MS doesn't recognize %zu format + OutBuffer tmp; + tmp.vprintf(format, ap); + fprintf(stdmsg, "%s", tmp.toChars()); +#else + vfprintf(stdmsg, format, ap); +#endif + fprintf(stdmsg, "\n"); + fflush(stdmsg); +//halt(); + } + else + { + global.gaggedErrors++; + } + global.errors++; +} + +// Doesn't increase error count, doesn't print "Error:". +void verrorSupplemental(Loc loc, const char *format, va_list ap) +{ + if (!global.gag) + { + fprintf(stdmsg, "%s: ", loc.toChars()); +#if _MSC_VER + // MS doesn't recognize %zu format + OutBuffer tmp; + tmp.vprintf(format, ap); + fprintf(stdmsg, "%s", tmp.toChars()); +#else + vfprintf(stdmsg, format, ap); +#endif + fprintf(stdmsg, "\n"); + fflush(stdmsg); + } +} + +void vwarning(Loc loc, const char *format, va_list ap) +{ + if (global.params.warnings && !global.gag) + { + char *p = loc.toChars(); + + if (*p) + fprintf(stdmsg, "%s: ", p); + mem.free(p); + + fprintf(stdmsg, "Warning: "); +#if _MSC_VER + // MS doesn't recognize %zu format + OutBuffer tmp; + tmp.vprintf(format, ap); + fprintf(stdmsg, "%s", tmp.toChars()); +#else + vfprintf(stdmsg, format, ap); +#endif + fprintf(stdmsg, "\n"); + fflush(stdmsg); +//halt(); + if (global.params.warnings == 1) + global.warnings++; // warnings don't count if gagged + } +} + +/*************************************** + * Call this after printing out fatal error messages to clean up and exit + * the compiler. + */ + +void fatal() +{ +#if 0 + halt(); +#endif + exit(EXIT_FAILURE); +} + +/************************************** + * Try to stop forgetting to remove the breakpoints from + * release builds. + */ +void halt() +{ +#ifdef DEBUG + *(volatile char*)0=0; +#endif +} + +extern void backend_init(); +extern void backend_term(); + +void usage() +{ +#if TARGET_LINUX + const char fpic[] ="\ + -fPIC generate position independent code\n\ +"; +#else + const char fpic[] = ""; +#endif + printf("DMD%s D Compiler %s\n%s %s\n", + sizeof(size_t) == 4 ? "32" : "64", + global.version, global.copyright, global.written); + printf("\ +Documentation: http://www.dlang.org/index.html\n\ +Usage:\n\ + dmd files.d ... { -switch }\n\ +\n\ + files.d D source files\n\ + @cmdfile read arguments from cmdfile\n\ + -c do not link\n\ + -cov do code coverage analysis\n\ + -D generate documentation\n\ + -Dddocdir write documentation file to docdir directory\n\ + -Dffilename write documentation file to filename\n\ + -d allow deprecated features\n\ + -debug compile in debug code\n\ + -debug=level compile in debug code <= level\n\ + -debug=ident compile in debug code identified by ident\n\ + -debuglib=name set symbolic debug library to name\n\ + -defaultlib=name set default library to name\n\ + -deps=filename write module dependencies to filename\n%s" +" -g add symbolic debug info\n\ + -gc add symbolic debug info, pretend to be C\n\ + -gs always emit stack frame\n\ + -H generate 'header' file\n\ + -Hddirectory write 'header' file to directory\n\ + -Hffilename write 'header' file to filename\n\ + --help print help\n\ + -Ipath where to look for imports\n\ + -ignore ignore unsupported pragmas\n\ + -inline do function inlining\n\ + -Jpath where to look for string imports\n\ + -Llinkerflag pass linkerflag to link\n\ + -lib generate library rather than object files\n" +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +" -m32 generate 32 bit code\n\ + -m64 generate 64 bit code\n" +#endif +" -man open web browser on manual page\n\ + -map generate linker .map file\n\ + -noboundscheck turns off array bounds checking for all functions\n\ + -nofloat do not emit reference to floating point\n\ + -O optimize\n\ + -o- do not write object file\n\ + -odobjdir write object & library files to directory objdir\n\ + -offilename name output file to filename\n\ + -op do not strip paths from source file\n\ + -profile profile runtime performance of generated code\n\ + -property enforce property syntax\n\ + -quiet suppress unnecessary messages\n\ + -release compile release version\n\ + -run srcfile args... run resulting program, passing args\n" +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +" -shared generate shared library\n" +#endif +" -unittest compile in unit tests\n\ + -v verbose\n\ + -version=level compile in version code >= level\n\ + -version=ident compile in version code identified by ident\n\ + -vtls list all variables going into thread local storage\n\ + -w enable warnings\n\ + -wi enable informational warnings\n\ + -X generate JSON file\n\ + -Xffilename write JSON file to filename\n\ +", fpic); +} + +extern signed char tyalignsize[]; + +#if _WIN32 +extern "C" +{ + extern int _xi_a; + extern int _end; +} +#endif + +int main(int argc, char *argv[]) +{ + mem.init(); // initialize storage allocator + mem.setStackBottom(&argv); +#if _WIN32 + mem.addroots((char *)&_xi_a, (char *)&_end); +#endif + + Strings files; + Strings libmodules; + char *p; + Module *m; + int status = EXIT_SUCCESS; + int argcstart = argc; + int setdebuglib = 0; + char noboundscheck = 0; + const char *inifilename = NULL; + +#ifdef DEBUG + printf("DMD %s DEBUG\n", global.version); +#endif + + unittests(); + + // Check for malformed input + if (argc < 1 || !argv) + { + Largs: + error("missing or null command line arguments"); + fatal(); + } + for (size_t i = 0; i < argc; i++) + { + if (!argv[i]) + goto Largs; + } + + if (response_expand(&argc,&argv)) // expand response files + error("can't open response file"); + + files.reserve(argc - 1); + + // Set default values + global.params.argv0 = argv[0]; + global.params.link = 1; + global.params.useAssert = 1; + global.params.useInvariants = 1; + global.params.useIn = 1; + global.params.useOut = 1; + global.params.useArrayBounds = 2; // default to all functions + global.params.useSwitchError = 1; + global.params.useInline = 0; + global.params.obj = 1; + global.params.Dversion = 2; + global.params.quiet = 1; + + global.params.linkswitches = new Strings(); + global.params.libfiles = new Strings(); + global.params.objfiles = new Strings(); + global.params.ddocfiles = new Strings(); + + // Default to -m32 for 32 bit dmd, -m64 for 64 bit dmd + global.params.is64bit = (sizeof(size_t) == 8); + +#if TARGET_WINDOS + global.params.defaultlibname = "phobos"; +#elif TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + global.params.defaultlibname = "phobos2"; +#elif TARGET_NET +#else +#error "fix this" +#endif + + // Predefine version identifiers + VersionCondition::addPredefinedGlobalIdent("DigitalMars"); + +#if TARGET_WINDOS + VersionCondition::addPredefinedGlobalIdent("Windows"); + global.params.isWindows = 1; +#if TARGET_NET + // TARGET_NET macro is NOT mutually-exclusive with TARGET_WINDOS + VersionCondition::addPredefinedGlobalIdent("D_NET"); +#endif +#elif TARGET_LINUX + VersionCondition::addPredefinedGlobalIdent("Posix"); + VersionCondition::addPredefinedGlobalIdent("linux"); + global.params.isLinux = 1; +#elif TARGET_OSX + VersionCondition::addPredefinedGlobalIdent("Posix"); + VersionCondition::addPredefinedGlobalIdent("OSX"); + global.params.isOSX = 1; + + // For legacy compatibility + VersionCondition::addPredefinedGlobalIdent("darwin"); +#elif TARGET_FREEBSD + VersionCondition::addPredefinedGlobalIdent("Posix"); + VersionCondition::addPredefinedGlobalIdent("FreeBSD"); + global.params.isFreeBSD = 1; +#elif TARGET_OPENBSD + VersionCondition::addPredefinedGlobalIdent("Posix"); + VersionCondition::addPredefinedGlobalIdent("OpenBSD"); + global.params.isFreeBSD = 1; +#elif TARGET_SOLARIS + VersionCondition::addPredefinedGlobalIdent("Posix"); + VersionCondition::addPredefinedGlobalIdent("Solaris"); + global.params.isSolaris = 1; +#else +#error "fix this" +#endif + + VersionCondition::addPredefinedGlobalIdent("LittleEndian"); + //VersionCondition::addPredefinedGlobalIdent("D_Bits"); +#if DMDV2 + VersionCondition::addPredefinedGlobalIdent("D_Version2"); +#endif + VersionCondition::addPredefinedGlobalIdent("all"); + +#if _WIN32 + inifilename = inifile(argv[0], "sc.ini"); +#elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + inifilename = inifile(argv[0], "dmd.conf"); +#else +#error "fix this" +#endif + getenv_setargv("DFLAGS", &argc, &argv); + +#if 0 + for (size_t i = 0; i < argc; i++) + { + printf("argv[%d] = '%s'\n", i, argv[i]); + } +#endif + + for (size_t i = 1; i < argc; i++) + { + p = argv[i]; + if (*p == '-') + { + if (strcmp(p + 1, "d") == 0) + global.params.useDeprecated = 1; + else if (strcmp(p + 1, "c") == 0) + global.params.link = 0; + else if (strcmp(p + 1, "cov") == 0) + global.params.cov = 1; +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + else if (strcmp(p + 1, "shared") == 0 +#if TARGET_OSX + // backwards compatibility with old switch + || strcmp(p + 1, "dylib") == 0 +#endif + ) + global.params.dll = 1; + else if (strcmp(p + 1, "fPIC") == 0) + global.params.pic = 1; +#endif + else if (strcmp(p + 1, "map") == 0) + global.params.map = 1; + else if (strcmp(p + 1, "multiobj") == 0) + global.params.multiobj = 1; + else if (strcmp(p + 1, "g") == 0) + global.params.symdebug = 1; + else if (strcmp(p + 1, "gc") == 0) + global.params.symdebug = 2; + else if (strcmp(p + 1, "gs") == 0) + global.params.alwaysframe = 1; + else if (strcmp(p + 1, "gt") == 0) + { error("use -profile instead of -gt\n"); + global.params.trace = 1; + } + else if (strcmp(p + 1, "m32") == 0) + global.params.is64bit = 0; + else if (strcmp(p + 1, "m64") == 0) + global.params.is64bit = 1; + else if (strcmp(p + 1, "profile") == 0) + global.params.trace = 1; + else if (strcmp(p + 1, "v") == 0) + global.params.verbose = 1; +#if DMDV2 + else if (strcmp(p + 1, "vtls") == 0) + global.params.vtls = 1; +#endif + else if (strcmp(p + 1, "v1") == 0) + { +#if DMDV1 + global.params.Dversion = 1; +#else + error("use DMD 1.0 series compilers for -v1 switch"); + break; +#endif + } + else if (strcmp(p + 1, "w") == 0) + global.params.warnings = 1; + else if (strcmp(p + 1, "wi") == 0) + global.params.warnings = 2; + else if (strcmp(p + 1, "O") == 0) + global.params.optimize = 1; + else if (p[1] == 'o') + { + switch (p[2]) + { + case '-': + global.params.obj = 0; + break; + + case 'd': + if (!p[3]) + goto Lnoarg; + global.params.objdir = p + 3; + break; + + case 'f': + if (!p[3]) + goto Lnoarg; + global.params.objname = p + 3; + break; + + case 'p': + if (p[3]) + goto Lerror; + global.params.preservePaths = 1; + break; + + case 0: + error("-o no longer supported, use -of or -od"); + break; + + default: + goto Lerror; + } + } + else if (p[1] == 'D') + { global.params.doDocComments = 1; + switch (p[2]) + { + case 'd': + if (!p[3]) + goto Lnoarg; + global.params.docdir = p + 3; + break; + case 'f': + if (!p[3]) + goto Lnoarg; + global.params.docname = p + 3; + break; + + case 0: + break; + + default: + goto Lerror; + } + } + else if (p[1] == 'H') + { global.params.doHdrGeneration = 1; + switch (p[2]) + { + case 'd': + if (!p[3]) + goto Lnoarg; + global.params.hdrdir = p + 3; + break; + + case 'f': + if (!p[3]) + goto Lnoarg; + global.params.hdrname = p + 3; + break; + + case 0: + break; + + default: + goto Lerror; + } + } + else if (p[1] == 'X') + { global.params.doXGeneration = 1; + switch (p[2]) + { + case 'f': + if (!p[3]) + goto Lnoarg; + global.params.xfilename = p + 3; + break; + + case 0: + break; + + default: + goto Lerror; + } + } + else if (strcmp(p + 1, "ignore") == 0) + global.params.ignoreUnsupportedPragmas = 1; + else if (strcmp(p + 1, "property") == 0) + global.params.enforcePropertySyntax = 1; + else if (strcmp(p + 1, "inline") == 0) + global.params.useInline = 1; + else if (strcmp(p + 1, "lib") == 0) + global.params.lib = 1; + else if (strcmp(p + 1, "nofloat") == 0) + global.params.nofloat = 1; + else if (strcmp(p + 1, "quiet") == 0) + global.params.quiet = 1; + else if (strcmp(p + 1, "release") == 0) + global.params.release = 1; +#if DMDV2 + else if (strcmp(p + 1, "noboundscheck") == 0) + noboundscheck = 1; +#endif + else if (strcmp(p + 1, "unittest") == 0) + global.params.useUnitTests = 1; + else if (p[1] == 'I') + { + if (!global.params.imppath) + global.params.imppath = new Strings(); + global.params.imppath->push(p + 2); + } + else if (p[1] == 'J') + { + if (!global.params.fileImppath) + global.params.fileImppath = new Strings(); + global.params.fileImppath->push(p + 2); + } + else if (memcmp(p + 1, "debug", 5) == 0 && p[6] != 'l') + { + // Parse: + // -debug + // -debug=number + // -debug=identifier + if (p[6] == '=') + { + if (isdigit((unsigned char)p[7])) + { long level; + + errno = 0; + level = strtol(p + 7, &p, 10); + if (*p || errno || level > INT_MAX) + goto Lerror; + DebugCondition::setGlobalLevel((int)level); + } + else if (Lexer::isValidIdentifier(p + 7)) + DebugCondition::addGlobalIdent(p + 7); + else + goto Lerror; + } + else if (p[6]) + goto Lerror; + else + global.params.debuglevel = 1; + } + else if (memcmp(p + 1, "version", 5) == 0) + { + // Parse: + // -version=number + // -version=identifier + if (p[8] == '=') + { + if (isdigit((unsigned char)p[9])) + { long level; + + errno = 0; + level = strtol(p + 9, &p, 10); + if (*p || errno || level > INT_MAX) + goto Lerror; + VersionCondition::setGlobalLevel((int)level); + } + else if (Lexer::isValidIdentifier(p + 9)) + VersionCondition::addGlobalIdent(p + 9); + else + goto Lerror; + } + else + goto Lerror; + } + else if (strcmp(p + 1, "-b") == 0) + global.params.debugb = 1; + else if (strcmp(p + 1, "-c") == 0) + global.params.debugc = 1; + else if (strcmp(p + 1, "-f") == 0) + global.params.debugf = 1; + else if (strcmp(p + 1, "-help") == 0) + { usage(); + exit(EXIT_SUCCESS); + } + else if (strcmp(p + 1, "-r") == 0) + global.params.debugr = 1; + else if (strcmp(p + 1, "-x") == 0) + global.params.debugx = 1; + else if (strcmp(p + 1, "-y") == 0) + global.params.debugy = 1; + else if (p[1] == 'L') + { + global.params.linkswitches->push(p + 2); + } + else if (memcmp(p + 1, "defaultlib=", 11) == 0) + { + global.params.defaultlibname = p + 1 + 11; + } + else if (memcmp(p + 1, "debuglib=", 9) == 0) + { + setdebuglib = 1; + global.params.debuglibname = p + 1 + 9; + } + else if (memcmp(p + 1, "deps=", 5) == 0) + { + global.params.moduleDepsFile = p + 1 + 5; + if (!global.params.moduleDepsFile[0]) + goto Lnoarg; + global.params.moduleDeps = new OutBuffer; + } + else if (memcmp(p + 1, "man", 3) == 0) + { +#if _WIN32 +#if DMDV1 + browse("http://www.digitalmars.com/d/1.0/dmd-windows.html"); +#else + browse("http://www.dlang.org/dmd-windows.html"); +#endif +#endif +#if linux +#if DMDV1 + browse("http://www.digitalmars.com/d/1.0/dmd-linux.html"); +#else + browse("http://www.dlang.org/dmd-linux.html"); +#endif +#endif +#if __APPLE__ +#if DMDV1 + browse("http://www.digitalmars.com/d/1.0/dmd-osx.html"); +#else + browse("http://www.dlang.org/dmd-osx.html"); +#endif +#endif +#if __FreeBSD__ +#if DMDV1 + browse("http://www.digitalmars.com/d/1.0/dmd-freebsd.html"); +#else + browse("http://www.dlang.org/dmd-freebsd.html"); +#endif +#endif +#if __OpenBSD__ +#if DMDV1 + browse("http://www.digitalmars.com/d/1.0/dmd-openbsd.html"); +#else + browse("http://www.dlang.org/dmd-openbsd.html"); +#endif +#endif + exit(EXIT_SUCCESS); + } + else if (strcmp(p + 1, "run") == 0) + { global.params.run = 1; + global.params.runargs_length = ((i >= argcstart) ? argc : argcstart) - i - 1; + if (global.params.runargs_length) + { + files.push(argv[i + 1]); + global.params.runargs = &argv[i + 2]; + i += global.params.runargs_length; + global.params.runargs_length--; + } + else + { global.params.run = 0; + goto Lnoarg; + } + } + else + { + Lerror: + error("unrecognized switch '%s'", argv[i]); + continue; + + Lnoarg: + error("argument expected for switch '%s'", argv[i]); + continue; + } + } + else + { +#if TARGET_WINDOS + char *ext = FileName::ext(p); + if (ext && FileName::compare(ext, "exe") == 0) + { + global.params.objname = p; + continue; + } +#endif + files.push(p); + } + } + if (global.errors) + { + fatal(); + } + if (files.dim == 0) + { usage(); + return EXIT_FAILURE; + } + + if (!setdebuglib) + global.params.debuglibname = global.params.defaultlibname; + +#if TARGET_OSX + global.params.pic = 1; +#endif + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (global.params.lib && global.params.dll) + error("cannot mix -lib and -shared\n"); +#endif + + if (global.params.release) + { global.params.useInvariants = 0; + global.params.useIn = 0; + global.params.useOut = 0; + global.params.useAssert = 0; + global.params.useArrayBounds = 1; + global.params.useSwitchError = 0; + } + if (noboundscheck) + global.params.useArrayBounds = 0; + + if (global.params.run) + global.params.quiet = 1; + + if (global.params.useUnitTests) + global.params.useAssert = 1; + + if (!global.params.obj || global.params.lib) + global.params.link = 0; + + if (global.params.link) + { + global.params.exefile = global.params.objname; + global.params.oneobj = 1; + if (global.params.objname) + { + /* Use this to name the one object file with the same + * name as the exe file. + */ + global.params.objname = FileName::forceExt(global.params.objname, global.obj_ext)->toChars(); + + /* If output directory is given, use that path rather than + * the exe file path. + */ + if (global.params.objdir) + { char *name = FileName::name(global.params.objname); + global.params.objname = FileName::combine(global.params.objdir, name); + } + } + } + else if (global.params.lib) + { + global.params.libname = global.params.objname; + global.params.objname = NULL; + + // Haven't investigated handling these options with multiobj + if (!global.params.cov && !global.params.trace +#if 0 && TARGET_WINDOS + /* multiobj causes class/struct debug info to be attached to init-data, + * but this will not be linked into the executable, so this info is lost. + * Bugzilla 4014 + */ + && !global.params.symdebug +#endif + ) + global.params.multiobj = 1; + } + else if (global.params.run) + { + error("flags conflict with -run"); + fatal(); + } + else + { + if (global.params.objname && files.dim > 1) + { + global.params.oneobj = 1; + //error("multiple source files, but only one .obj name"); + //fatal(); + } + } + if (global.params.is64bit) + { + VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86_64"); + VersionCondition::addPredefinedGlobalIdent("X86_64"); + VersionCondition::addPredefinedGlobalIdent("D_LP64"); + VersionCondition::addPredefinedGlobalIdent("D_SIMD"); +#if TARGET_WINDOS + VersionCondition::addPredefinedGlobalIdent("Win64"); +#endif + } + else + { + VersionCondition::addPredefinedGlobalIdent("D_InlineAsm"); + VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86"); + VersionCondition::addPredefinedGlobalIdent("X86"); +#if TARGET_OSX + VersionCondition::addPredefinedGlobalIdent("D_SIMD"); +#endif +#if TARGET_WINDOS + VersionCondition::addPredefinedGlobalIdent("Win32"); +#endif + } + if (global.params.doDocComments) + VersionCondition::addPredefinedGlobalIdent("D_Ddoc"); + if (global.params.cov) + VersionCondition::addPredefinedGlobalIdent("D_Coverage"); + if (global.params.pic) + VersionCondition::addPredefinedGlobalIdent("D_PIC"); +#if DMDV2 + if (global.params.useUnitTests) + VersionCondition::addPredefinedGlobalIdent("unittest"); +#endif + + // Initialization + Type::init(); + Id::initialize(); + Module::init(); + initPrecedence(); + + if (global.params.verbose) + { printf("binary %s\n", argv[0]); + printf("version %s\n", global.version); + printf("config %s\n", inifilename ? inifilename : "(none)"); + } + + //printf("%d source files\n",files.dim); + + // Build import search path + if (global.params.imppath) + { + for (size_t i = 0; i < global.params.imppath->dim; i++) + { + char *path = (*global.params.imppath)[i]; + Strings *a = FileName::splitPath(path); + + if (a) + { + if (!global.path) + global.path = new Strings(); + global.path->append(a); + } + } + } + + // Build string import search path + if (global.params.fileImppath) + { + for (size_t i = 0; i < global.params.fileImppath->dim; i++) + { + char *path = global.params.fileImppath->tdata()[i]; + Strings *a = FileName::splitPath(path); + + if (a) + { + if (!global.filePath) + global.filePath = new Strings(); + global.filePath->append(a); + } + } + } + + // Create Modules + Modules modules; + modules.reserve(files.dim); + int firstmodule = 1; + for (size_t i = 0; i < files.dim; i++) + { + char *ext; + char *name; + + p = files.tdata()[i]; + +#if _WIN32 + // Convert / to \ so linker will work + for (size_t i = 0; p[i]; i++) + { + if (p[i] == '/') + p[i] = '\\'; + } +#endif + + p = FileName::name(p); // strip path + ext = FileName::ext(p); + if (ext) + { /* Deduce what to do with a file based on its extension + */ + if (FileName::equals(ext, global.obj_ext)) + { + global.params.objfiles->push(files.tdata()[i]); + libmodules.push(files.tdata()[i]); + continue; + } + + if (FileName::equals(ext, global.lib_ext)) + { + global.params.libfiles->push(files.tdata()[i]); + libmodules.push(files.tdata()[i]); + continue; + } + + if (strcmp(ext, global.ddoc_ext) == 0) + { + global.params.ddocfiles->push(files.tdata()[i]); + continue; + } + + if (FileName::equals(ext, global.json_ext)) + { + global.params.doXGeneration = 1; + global.params.xfilename = files.tdata()[i]; + continue; + } + + if (FileName::equals(ext, global.map_ext)) + { + global.params.mapfile = files.tdata()[i]; + continue; + } + +#if TARGET_WINDOS + if (FileName::equals(ext, "res")) + { + global.params.resfile = files.tdata()[i]; + continue; + } + + if (FileName::equals(ext, "def")) + { + global.params.deffile = files.tdata()[i]; + continue; + } + + if (FileName::equals(ext, "exe")) + { + assert(0); // should have already been handled + } +#endif + + /* Examine extension to see if it is a valid + * D source file extension + */ + if (FileName::equals(ext, global.mars_ext) || + FileName::equals(ext, global.hdr_ext) || + FileName::equals(ext, "dd") || + FileName::equals(ext, "htm") || + FileName::equals(ext, "html") || + FileName::equals(ext, "xhtml")) + { + ext--; // skip onto '.' + assert(*ext == '.'); + name = (char *)mem.malloc((ext - p) + 1); + memcpy(name, p, ext - p); + name[ext - p] = 0; // strip extension + + if (name[0] == 0 || + strcmp(name, "..") == 0 || + strcmp(name, ".") == 0) + { + Linvalid: + error("invalid file name '%s'", files.tdata()[i]); + fatal(); + } + } + else + { error("unrecognized file extension %s\n", ext); + fatal(); + } + } + else + { name = p; + if (!*name) + goto Linvalid; + } + + /* At this point, name is the D source file name stripped of + * its path and extension. + */ + + Identifier *id = Lexer::idPool(name); + m = new Module(files[i], id, global.params.doDocComments, global.params.doHdrGeneration); + modules.push(m); + + if (firstmodule) + { global.params.objfiles->push(m->objfile->name->str); + firstmodule = 0; + } + } + +#if WINDOWS_SEH + __try + { +#endif + // Read files +#define ASYNCREAD 1 +#if ASYNCREAD + // Multi threaded + AsyncRead *aw = AsyncRead::create(modules.dim); + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + aw->addFile(m->srcfile); + } + aw->start(); +#else + // Single threaded + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + m->read(0); + } +#endif + + // Parse files + bool anydocfiles = false; + size_t filecount = modules.dim; + for (size_t filei = 0, modi = 0; filei < filecount; filei++, modi++) + { + m = modules[modi]; + if (global.params.verbose) + printf("parse %s\n", m->toChars()); + if (!Module::rootModule) + Module::rootModule = m; + m->importedFrom = m; + if (!global.params.oneobj || modi == 0 || m->isDocFile) + m->deleteObjFile(); +#if ASYNCREAD + if (aw->read(filei)) + { + error("cannot read file %s", m->srcfile->name->toChars()); + } +#endif + m->parse(); + if (m->isDocFile) + { + anydocfiles = true; + m->gendocfile(); + + // Remove m from list of modules + modules.remove(modi); + modi--; + + // Remove m's object file from list of object files + for (size_t j = 0; j < global.params.objfiles->dim; j++) + { + if (m->objfile->name->str == global.params.objfiles->tdata()[j]) + { + global.params.objfiles->remove(j); + break; + } + } + + if (global.params.objfiles->dim == 0) + global.params.link = 0; + } + } +#if ASYNCREAD + AsyncRead::dispose(aw); +#endif + + if (anydocfiles && modules.dim && + (global.params.oneobj || global.params.objname)) + { + error("conflicting Ddoc and obj generation options"); + fatal(); + } + if (global.errors) + fatal(); + if (global.params.doHdrGeneration) + { + /* Generate 'header' import files. + * Since 'header' import files must be independent of command + * line switches and what else is imported, they are generated + * before any semantic analysis. + */ + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("import %s\n", m->toChars()); + m->genhdrfile(); + } + } + if (global.errors) + fatal(); + + // load all unconditional imports for better symbol resolving + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("importall %s\n", m->toChars()); + m->importAll(0); + } + if (global.errors) + fatal(); + + backend_init(); + + // Do semantic analysis + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("semantic %s\n", m->toChars()); + m->semantic(); + } + if (global.errors) + fatal(); + + Module::dprogress = 1; + Module::runDeferredSemantic(); + + // Do pass 2 semantic analysis + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("semantic2 %s\n", m->toChars()); + m->semantic2(); + } + if (global.errors) + fatal(); + + // Do pass 3 semantic analysis + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("semantic3 %s\n", m->toChars()); + m->semantic3(); + } + if (global.errors) + fatal(); + + if (global.params.moduleDeps != NULL) + { + assert(global.params.moduleDepsFile != NULL); + + File deps(global.params.moduleDepsFile); + OutBuffer* ob = global.params.moduleDeps; + deps.setbuffer((void*)ob->data, ob->offset); + deps.writev(); + } + + + // Scan for functions to inline + if (global.params.useInline) + { + /* The problem with useArrayBounds and useAssert is that the + * module being linked to may not have generated them, so if + * we inline functions from those modules, the symbols for them will + * not be found at link time. + */ + if (!global.params.useArrayBounds && !global.params.useAssert) + { + // Do pass 3 semantic analysis on all imported modules, + // since otherwise functions in them cannot be inlined + for (size_t i = 0; i < Module::amodules.dim; i++) + { + m = Module::amodules[i]; + if (global.params.verbose) + printf("semantic3 %s\n", m->toChars()); + m->semantic3(); + } + if (global.errors) + fatal(); + } + + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("inline scan %s\n", m->toChars()); + m->inlineScan(); + } + } + + // Do not attempt to generate output files if errors or warnings occurred + if (global.errors || global.warnings) + fatal(); + + printCtfePerformanceStats(); + + Library *library = NULL; + if (global.params.lib) + { + library = new Library(); + library->setFilename(global.params.objdir, global.params.libname); + + // Add input object and input library files to output library + for (size_t i = 0; i < libmodules.dim; i++) + { + char *p = libmodules[i]; + library->addObject(p, NULL, 0); + } + } + + // Generate output files + + if (global.params.doXGeneration) + json_generate(&modules); + + if (global.params.oneobj) + { + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("code %s\n", m->toChars()); + if (i == 0) + obj_start(m->srcfile->toChars()); + m->genobjfile(0); + if (!global.errors && global.params.doDocComments) + m->gendocfile(); + } + if (!global.errors && modules.dim) + { + obj_end(library, modules.tdata()[0]->objfile); + } + } + else + { + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("code %s\n", m->toChars()); + if (global.params.obj) + { obj_start(m->srcfile->toChars()); + m->genobjfile(global.params.multiobj); + obj_end(library, m->objfile); + obj_write_deferred(library); + } + if (global.errors) + { + if (!global.params.lib) + m->deleteObjFile(); + } + else + { + if (global.params.doDocComments) + m->gendocfile(); + } + } + } + + if (global.params.lib && !global.errors) + library->write(); + +#if WINDOWS_SEH + } + __except (__ehfilter(GetExceptionInformation())) + { + printf("Stack overflow\n"); + fatal(); + } +#endif + backend_term(); + if (global.errors) + fatal(); + + if (!global.params.objfiles->dim) + { + if (global.params.link) + error("no object files to link"); + } + else + { + if (global.params.link) + status = runLINK(); + + if (global.params.run) + { + if (!status) + { + status = runProgram(); + + /* Delete .obj files and .exe file + */ + for (size_t i = 0; i < modules.dim; i++) + { + Module *m = modules[i]; + m->deleteObjFile(); + if (global.params.oneobj) + break; + } + deleteExeFile(); + } + } + } + + return status; +} + + + +/*********************************** + * Parse and append contents of environment variable envvar + * to argc and argv[]. + * The string is separated into arguments, processing \ and ". + */ + +void getenv_setargv(const char *envvar, int *pargc, char** *pargv) +{ + char *p; + + int instring; + int slash; + char c; + + char *env = getenv(envvar); + if (!env) + return; + + env = mem.strdup(env); // create our own writable copy + + int argc = *pargc; + Strings *argv = new Strings(); + argv->setDim(argc); + + for (size_t i = 0; i < argc; i++) + argv->tdata()[i] = (*pargv)[i]; + + size_t j = 1; // leave argv[0] alone + while (1) + { + int wildcard = 1; // do wildcard expansion + switch (*env) + { + case ' ': + case '\t': + env++; + break; + + case 0: + goto Ldone; + + case '"': + wildcard = 0; + default: + argv->push(env); // append + //argv->insert(j, env); // insert at position j + j++; + argc++; + p = env; + slash = 0; + instring = 0; + c = 0; + + while (1) + { + c = *env++; + switch (c) + { + case '"': + p -= (slash >> 1); + if (slash & 1) + { p--; + goto Laddc; + } + instring ^= 1; + slash = 0; + continue; + + case ' ': + case '\t': + if (instring) + goto Laddc; + *p = 0; + //if (wildcard) + //wildcardexpand(); // not implemented + break; + + case '\\': + slash++; + *p++ = c; + continue; + + case 0: + *p = 0; + //if (wildcard) + //wildcardexpand(); // not implemented + goto Ldone; + + default: + Laddc: + slash = 0; + *p++ = c; + continue; + } + break; + } + } + } + +Ldone: + *pargc = argc; + *pargv = argv->tdata(); +} + +#if WINDOWS_SEH + +long __cdecl __ehfilter(LPEXCEPTION_POINTERS ep) +{ + //printf("%x\n", ep->ExceptionRecord->ExceptionCode); + if (ep->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW) + { +#ifndef DEBUG + return EXCEPTION_EXECUTE_HANDLER; +#endif + } + return EXCEPTION_CONTINUE_SEARCH; +} + +#endif diff --git a/mars.h b/mars.h new file mode 100644 index 00000000..24cd00b2 --- /dev/null +++ b/mars.h @@ -0,0 +1,441 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_MARS_H +#define DMD_MARS_H + +#ifdef __DMC__ +#pragma once +#endif + +/* +It is very important to use version control macros correctly - the +idea is that host and target are independent. If these are done +correctly, cross compilers can be built. +The host compiler and host operating system are also different, +and are predefined by the host compiler. The ones used in +dmd are: + +Macros defined by the compiler, not the code: + + Compiler: + __DMC__ Digital Mars compiler + _MSC_VER Microsoft compiler + __GNUC__ Gnu compiler + __clang__ Clang compiler + + Host operating system: + _WIN32 Microsoft NT, Windows 95, Windows 98, Win32s, + Windows 2000, Win XP, Vista + _WIN64 Windows for AMD64 + linux Linux + __APPLE__ Mac OSX + __FreeBSD__ FreeBSD + __OpenBSD__ OpenBSD + __sun&&__SVR4 Solaris, OpenSolaris (yes, both macros are necessary) + +For the target systems, there are the target operating system and +the target object file format: + + Target operating system: + TARGET_WINDOS Covers 32 bit windows and 64 bit windows + TARGET_LINUX Covers 32 and 64 bit linux + TARGET_OSX Covers 32 and 64 bit Mac OSX + TARGET_FREEBSD Covers 32 and 64 bit FreeBSD + TARGET_OPENBSD Covers 32 and 64 bit OpenBSD + TARGET_SOLARIS Covers 32 and 64 bit Solaris + TARGET_NET Covers .Net + + It is expected that the compiler for each platform will be able + to generate 32 and 64 bit code from the same compiler binary. + + Target object module format: + OMFOBJ Intel Object Module Format, used on Windows + ELFOBJ Elf Object Module Format, used on linux, FreeBSD, OpenBSD and Solaris + MACHOBJ Mach-O Object Module Format, used on Mac OSX + + There are currently no macros for byte endianness order. + */ + + +#include +#include +#include + +#ifdef __DMC__ +#ifdef DEBUG +#undef assert +#define assert(e) (static_cast((e) || (printf("assert %s(%d) %s\n", __FILE__, __LINE__, #e), halt()))) +#endif +#endif + +#ifdef DEBUG +#define UNITTEST 1 +#endif +void unittests(); + +#ifdef IN_GCC +/* Changes for the GDC compiler by David Friedman */ +#endif + +#define DMDV1 0 +#define DMDV2 1 // Version 2.0 features +#define BREAKABI 1 // 0 if not ready to break the ABI just yet +#define STRUCTTHISREF DMDV2 // if 'this' for struct is a reference, not a pointer +#define SNAN_DEFAULT_INIT DMDV2 // if floats are default initialized to signalling NaN +#define SARRAYVALUE DMDV2 // static arrays are value types +#define MODULEINFO_IS_STRUCT DMDV2 // if ModuleInfo is a struct rather than a class + +// Set if C++ mangling is done by the front end +#define CPP_MANGLE (DMDV2 && (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS)) + +/* Other targets are TARGET_LINUX, TARGET_OSX, TARGET_FREEBSD, TARGET_OPENBSD and + * TARGET_SOLARIS, which are + * set on the command line via the compiler makefile. + */ + +#if _WIN32 +#ifndef TARGET_WINDOS +#define TARGET_WINDOS 1 // Windows dmd generates Windows targets +#endif +#ifndef OMFOBJ +#define OMFOBJ TARGET_WINDOS +#endif +#endif + +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +#ifndef ELFOBJ +#define ELFOBJ 1 +#endif +#endif + +#if TARGET_OSX +#ifndef MACHOBJ +#define MACHOBJ 1 +#endif +#endif + + +struct OutBuffer; + +// Can't include arraytypes.h here, need to declare these directly. +template struct ArrayBase; +typedef ArrayBase Identifiers; +typedef ArrayBase Strings; + +// Put command line switches in here +struct Param +{ + char obj; // write object file + char link; // perform link + char dll; // generate shared dynamic library + char lib; // write library file instead of object file(s) + char multiobj; // break one object file into multiple ones + char oneobj; // write one object file instead of multiple ones + char trace; // insert profiling hooks + char quiet; // suppress non-error messages + char verbose; // verbose compile + char vtls; // identify thread local variables + char symdebug; // insert debug symbolic information + char alwaysframe; // always emit standard stack frame + char optimize; // run optimizer + char map; // generate linker .map file + char cpu; // target CPU + char is64bit; // generate 64 bit code + char isLinux; // generate code for linux + char isOSX; // generate code for Mac OSX + char isWindows; // generate code for Windows + char isFreeBSD; // generate code for FreeBSD + char isOPenBSD; // generate code for OpenBSD + char isSolaris; // generate code for Solaris + char scheduler; // which scheduler to use + char useDeprecated; // allow use of deprecated features + char useAssert; // generate runtime code for assert()'s + char useInvariants; // generate class invariant checks + char useIn; // generate precondition checks + char useOut; // generate postcondition checks + char useArrayBounds; // 0: no array bounds checks + // 1: array bounds checks for safe functions only + // 2: array bounds checks for all functions + char noboundscheck; // no array bounds checking at all + char useSwitchError; // check for switches without a default + char useUnitTests; // generate unittest code + char useInline; // inline expand functions + char release; // build release version + char preservePaths; // !=0 means don't strip path from source file + char warnings; // 0: enable warnings + // 1: warnings as errors + // 2: informational warnings (no errors) + char pic; // generate position-independent-code for shared libs + char cov; // generate code coverage data + char nofloat; // code should not pull in floating point support + char Dversion; // D version number + char ignoreUnsupportedPragmas; // rather than error on them + char enforcePropertySyntax; + + char *argv0; // program name + Strings *imppath; // array of char*'s of where to look for import modules + Strings *fileImppath; // array of char*'s of where to look for file import modules + char *objdir; // .obj/.lib file output directory + char *objname; // .obj file output name + char *libname; // .lib file output name + + char doDocComments; // process embedded documentation comments + char *docdir; // write documentation file to docdir directory + char *docname; // write documentation file to docname + Strings *ddocfiles; // macro include files for Ddoc + + char doHdrGeneration; // process embedded documentation comments + char *hdrdir; // write 'header' file to docdir directory + char *hdrname; // write 'header' file to docname + + char doXGeneration; // write JSON file + char *xfilename; // write JSON file to xfilename + + unsigned debuglevel; // debug level + Strings *debugids; // debug identifiers + + unsigned versionlevel; // version level + Strings *versionids; // version identifiers + + bool dump_source; + + const char *defaultlibname; // default library for non-debug builds + const char *debuglibname; // default library for debug builds + + char *moduleDepsFile; // filename for deps output + OutBuffer *moduleDeps; // contents to be written to deps file + + // Hidden debug switches + char debuga; + char debugb; + char debugc; + char debugf; + char debugr; + char debugw; + char debugx; + char debugy; + + char run; // run resulting executable + size_t runargs_length; + char** runargs; // arguments for executable + + // Linker stuff + Strings *objfiles; + Strings *linkswitches; + Strings *libfiles; + char *deffile; + char *resfile; + char *exefile; + char *mapfile; +}; + +struct Global +{ + const char *mars_ext; + const char *sym_ext; + const char *obj_ext; + const char *lib_ext; + const char *dll_ext; + const char *doc_ext; // for Ddoc generated files + const char *ddoc_ext; // for Ddoc macro include files + const char *hdr_ext; // for D 'header' import files + const char *json_ext; // for JSON files + const char *map_ext; // for .map files + const char *copyright; + const char *written; + Strings *path; // Array of char*'s which form the import lookup path + Strings *filePath; // Array of char*'s which form the file import lookup path + int structalign; + const char *version; + + Param params; + unsigned errors; // number of errors reported so far + unsigned warnings; // number of warnings reported so far + unsigned gag; // !=0 means gag reporting of errors & warnings + unsigned gaggedErrors; // number of errors reported while gagged + + // Start gagging. Return the current number of gagged errors + unsigned startGagging(); + + /* End gagging, restoring the old gagged state. + * Return true if errors occured while gagged. + */ + bool endGagging(unsigned oldGagged); + + Global(); +}; + +extern Global global; + +/* Set if Windows Structured Exception Handling C extensions are supported. + * Apparently, VC has dropped support for these? + */ +#define WINDOWS_SEH (_WIN32 && __DMC__) + + +#ifdef __DMC__ + typedef _Complex long double complex_t; +#else + #ifndef IN_GCC + #include "complex_t.h" + #endif + #ifdef __APPLE__ + //#include "complex.h"//This causes problems with include the c++ and not the C "complex.h" + #endif +#endif + +// Be careful not to care about sign when using dinteger_t +//typedef uint64_t integer_t; +typedef uint64_t dinteger_t; // use this instead of integer_t to + // avoid conflicts with system #include's + +// Signed and unsigned variants +typedef int64_t sinteger_t; +typedef uint64_t uinteger_t; + +typedef int8_t d_int8; +typedef uint8_t d_uns8; +typedef int16_t d_int16; +typedef uint16_t d_uns16; +typedef int32_t d_int32; +typedef uint32_t d_uns32; +typedef int64_t d_int64; +typedef uint64_t d_uns64; + +typedef float d_float32; +typedef double d_float64; +typedef long double d_float80; + +typedef d_uns8 d_char; +typedef d_uns16 d_wchar; +typedef d_uns32 d_dchar; + +#ifdef IN_GCC +#include "d-gcc-real.h" +#else +typedef long double real_t; +#endif + +// Modify OutBuffer::writewchar to write the correct size of wchar +#if _WIN32 +#define writewchar writeword +#else +// This needs a configuration test... +#define writewchar write4 +#endif + +#ifdef IN_GCC +#include "d-gcc-complex_t.h" +#endif + +struct Module; + +//typedef unsigned Loc; // file location +struct Loc +{ + const char *filename; + unsigned linnum; + + Loc() + { + linnum = 0; + filename = NULL; + } + + Loc(int x) + { + linnum = x; + filename = NULL; + } + + Loc(Module *mod, unsigned linnum); + + char *toChars(); + bool equals(const Loc& loc); +}; + +#ifndef GCC_SAFE_DMD +#define TRUE 1 +#define FALSE 0 +#endif + +#define INTERFACE_OFFSET 0 // if 1, put classinfo as first entry + // in interface vtbl[]'s +#define INTERFACE_VIRTUAL 0 // 1 means if an interface appears + // in the inheritance graph multiple + // times, only one is used + +enum LINK +{ + LINKdefault, + LINKd, + LINKc, + LINKcpp, + LINKwindows, + LINKpascal, +}; + +enum DYNCAST +{ + DYNCAST_OBJECT, + DYNCAST_EXPRESSION, + DYNCAST_DSYMBOL, + DYNCAST_TYPE, + DYNCAST_IDENTIFIER, + DYNCAST_TUPLE, +}; + +enum MATCH +{ + MATCHnomatch, // no match + MATCHconvert, // match with conversions +#if DMDV2 + MATCHconst, // match with conversion to const +#endif + MATCHexact // exact match +}; + +typedef uint64_t StorageClass; + + +void warning(Loc loc, const char *format, ...); +void error(Loc loc, const char *format, ...); +void errorSupplemental(Loc loc, const char *format, ...); +void verror(Loc loc, const char *format, va_list); +void vwarning(Loc loc, const char *format, va_list); +void verrorSupplemental(Loc loc, const char *format, va_list); +void fatal(); +void err_nomem(); +int runLINK(); +void deleteExeFile(); +int runProgram(); +const char *inifile(const char *argv0, const char *inifile); +void halt(); +void util_progress(); + +/*** Where to send error messages ***/ +#if IN_GCC +#define stdmsg stderr +#else +#define stdmsg stderr +#endif + +struct Dsymbol; +struct Library; +struct File; +void obj_start(char *srcfile); +void obj_end(Library *library, File *objfile); +void obj_append(Dsymbol *s); +void obj_write_deferred(Library *library); + +const char *importHint(const char *s); + +#endif /* DMD_MARS_H */ diff --git a/module.c b/module.c new file mode 100644 index 00000000..716f36d8 --- /dev/null +++ b/module.c @@ -0,0 +1,1190 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include + +#if (defined (__SVR4) && defined (__sun)) +#include +#endif + +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#endif + +#if IN_GCC +#include "gdc_alloca.h" +#endif + +#include "rmem.h" + +#include "mars.h" +#include "module.h" +#include "parse.h" +#include "scope.h" +#include "identifier.h" +#include "id.h" +#include "import.h" +#include "dsymbol.h" +#include "hdrgen.h" +#include "lexer.h" + +#define MARS 1 +#include "html.h" + +#ifdef IN_GCC +#include "d-dmd-gcc.h" +#endif + +ClassDeclaration *Module::moduleinfo; + +Module *Module::rootModule; +DsymbolTable *Module::modules; +Modules Module::amodules; + +Dsymbols Module::deferred; // deferred Dsymbol's needing semantic() run on them +unsigned Module::dprogress; + +void Module::init() +{ + modules = new DsymbolTable(); +} + +Module::Module(char *filename, Identifier *ident, int doDocComment, int doHdrGen) + : Package(ident) +{ + FileName *srcfilename; + FileName *objfilename; + FileName *symfilename; + +// printf("Module::Module(filename = '%s', ident = '%s')\n", filename, ident->toChars()); + this->arg = filename; + md = NULL; + errors = 0; + numlines = 0; + members = NULL; + isHtml = 0; + isDocFile = 0; + needmoduleinfo = 0; +#ifdef IN_GCC + strictlyneedmoduleinfo = 0; +#endif + selfimports = 0; + insearch = 0; + searchCacheIdent = NULL; + searchCacheSymbol = NULL; + searchCacheFlags = 0; + semanticstarted = 0; + semanticRun = 0; + decldefs = NULL; + vmoduleinfo = NULL; + massert = NULL; + munittest = NULL; + marray = NULL; + sictor = NULL; + sctor = NULL; + sdtor = NULL; + ssharedctor = NULL; + sshareddtor = NULL; + stest = NULL; + sfilename = NULL; + root = 0; + importedFrom = NULL; + srcfile = NULL; + docfile = NULL; + + debuglevel = 0; + debugids = NULL; + debugidsNot = NULL; + versionlevel = 0; + versionids = NULL; + versionidsNot = NULL; + + macrotable = NULL; + escapetable = NULL; + safe = FALSE; + doppelganger = 0; + cov = NULL; + covb = NULL; + + nameoffset = 0; + namelen = 0; + + srcfilename = FileName::defaultExt(filename, global.mars_ext); + if (!srcfilename->equalsExt(global.mars_ext) && + !srcfilename->equalsExt(global.hdr_ext) && + !srcfilename->equalsExt("dd")) + { + if (srcfilename->equalsExt("html") || + srcfilename->equalsExt("htm") || + srcfilename->equalsExt("xhtml")) + { if (!global.params.useDeprecated) + error("html source files is deprecated %s", srcfilename->toChars()); + isHtml = 1; + } + else + { error("source file name '%s' must have .%s extension", srcfilename->toChars(), global.mars_ext); + fatal(); + } + } + + char *argobj; + if (global.params.objname) + argobj = global.params.objname; +#if 0 + else if (global.params.preservePaths) + argobj = filename; + else + argobj = FileName::name(filename); + if (!FileName::absolute(argobj)) + { + argobj = FileName::combine(global.params.objdir, argobj); + } +#else // Bugzilla 3547 + else + { + if (global.params.preservePaths) + argobj = filename; + else + argobj = FileName::name(filename); + if (!FileName::absolute(argobj)) + { + argobj = FileName::combine(global.params.objdir, argobj); + } + } +#endif + + if (global.params.objname) + objfilename = new FileName(argobj, 0); + else + objfilename = FileName::forceExt(argobj, global.obj_ext); + + symfilename = FileName::forceExt(filename, global.sym_ext); + + srcfile = new File(srcfilename); + + if (doDocComment) + { + setDocfile(); + } + + if (doHdrGen) + { + setHdrfile(); + } + + objfile = new File(objfilename); + symfile = new File(symfilename); +} + +void Module::setDocfile() +{ + FileName *docfilename; + char *argdoc; + + if (global.params.docname) + argdoc = global.params.docname; + else if (global.params.preservePaths) + argdoc = (char *)arg; + else + argdoc = FileName::name((char *)arg); + if (!FileName::absolute(argdoc)) + { //FileName::ensurePathExists(global.params.docdir); + argdoc = FileName::combine(global.params.docdir, argdoc); + } + if (global.params.docname) + docfilename = new FileName(argdoc, 0); + else + docfilename = FileName::forceExt(argdoc, global.doc_ext); + + if (docfilename->equals(srcfile->name)) + { error("Source file and documentation file have same name '%s'", srcfile->name->str); + fatal(); + } + + docfile = new File(docfilename); +} + +void Module::setHdrfile() +{ + FileName *hdrfilename; + char *arghdr; + + if (global.params.hdrname) + arghdr = global.params.hdrname; + else if (global.params.preservePaths) + arghdr = (char *)arg; + else + arghdr = FileName::name((char *)arg); + if (!FileName::absolute(arghdr)) + { //FileName::ensurePathExists(global.params.hdrdir); + arghdr = FileName::combine(global.params.hdrdir, arghdr); + } + if (global.params.hdrname) + hdrfilename = new FileName(arghdr, 0); + else + hdrfilename = FileName::forceExt(arghdr, global.hdr_ext); + + if (hdrfilename->equals(srcfile->name)) + { error("Source file and 'header' file have same name '%s'", srcfile->name->str); + fatal(); + } + + hdrfile = new File(hdrfilename); +} + +void Module::deleteObjFile() +{ + if (global.params.obj) + objfile->remove(); + if (docfile) + docfile->remove(); +} + +Module::~Module() +{ +} + +const char *Module::kind() +{ + return "module"; +} + +Module *Module::load(Loc loc, Identifiers *packages, Identifier *ident) +{ Module *m; + char *filename; + + //printf("Module::load(ident = '%s')\n", ident->toChars()); + + // Build module filename by turning: + // foo.bar.baz + // into: + // foo\bar\baz + filename = ident->toChars(); + if (packages && packages->dim) + { + OutBuffer buf; + + for (size_t i = 0; i < packages->dim; i++) + { Identifier *pid = packages->tdata()[i]; + + buf.writestring(pid->toChars()); +#if _WIN32 + buf.writeByte('\\'); +#else + buf.writeByte('/'); +#endif + } + buf.writestring(filename); + buf.writeByte(0); + filename = (char *)buf.extractData(); + } + + m = new Module(filename, ident, 0, 0); + m->loc = loc; + + /* Search along global.path for .di file, then .d file. + */ + char *result = NULL; + FileName *fdi = FileName::forceExt(filename, global.hdr_ext); + FileName *fd = FileName::forceExt(filename, global.mars_ext); + char *sdi = fdi->toChars(); + char *sd = fd->toChars(); + + if (FileName::exists(sdi)) + result = sdi; + else if (FileName::exists(sd)) + result = sd; + else if (FileName::absolute(filename)) + ; + else if (!global.path) + ; + else + { + for (size_t i = 0; i < global.path->dim; i++) + { + char *p = (*global.path)[i]; + char *n = FileName::combine(p, sdi); + if (FileName::exists(n)) + { result = n; + break; + } + mem.free(n); + n = FileName::combine(p, sd); + if (FileName::exists(n)) + { result = n; + break; + } + mem.free(n); + } + } + if (result) + m->srcfile = new File(result); + + if (global.params.verbose) + { + printf("import "); + if (packages) + { + for (size_t i = 0; i < packages->dim; i++) + { Identifier *pid = packages->tdata()[i]; + printf("%s.", pid->toChars()); + } + } + printf("%s\t(%s)\n", ident->toChars(), m->srcfile->toChars()); + } + + m->read(loc); + m->parse(); + +#ifdef IN_GCC + d_gcc_magic_module(m); +#endif + + return m; +} + +void Module::read(Loc loc) +{ + //printf("Module::read('%s') file '%s'\n", toChars(), srcfile->toChars()); + if (srcfile->read()) + { error(loc, "is in file '%s' which cannot be read", srcfile->toChars()); + if (!global.gag) + { /* Print path + */ + if (global.path) + { + for (size_t i = 0; i < global.path->dim; i++) + { + char *p = global.path->tdata()[i]; + fprintf(stdmsg, "import path[%zd] = %s\n", i, p); + } + } + else + fprintf(stdmsg, "Specify path to file '%s' with -I switch\n", srcfile->toChars()); + } + fatal(); + } +} + +inline unsigned readwordLE(unsigned short *p) +{ +#if LITTLE_ENDIAN + return *p; +#else + return (((unsigned char *)p)[1] << 8) | ((unsigned char *)p)[0]; +#endif +} + +inline unsigned readwordBE(unsigned short *p) +{ + return (((unsigned char *)p)[0] << 8) | ((unsigned char *)p)[1]; +} + +inline unsigned readlongLE(unsigned *p) +{ +#if LITTLE_ENDIAN + return *p; +#else + return ((unsigned char *)p)[0] | + (((unsigned char *)p)[1] << 8) | + (((unsigned char *)p)[2] << 16) | + (((unsigned char *)p)[3] << 24); +#endif +} + +inline unsigned readlongBE(unsigned *p) +{ + return ((unsigned char *)p)[3] | + (((unsigned char *)p)[2] << 8) | + (((unsigned char *)p)[1] << 16) | + (((unsigned char *)p)[0] << 24); +} + +#if IN_GCC +void Module::parse(bool dump_source) +#else +void Module::parse() +#endif +{ char *srcname; + unsigned char *buf; + unsigned buflen; + unsigned le; + unsigned bom; + + //printf("Module::parse()\n"); + + srcname = srcfile->name->toChars(); + //printf("Module::parse(srcname = '%s')\n", srcname); + + buf = srcfile->buffer; + buflen = srcfile->len; + + if (buflen >= 2) + { + /* Convert all non-UTF-8 formats to UTF-8. + * BOM : http://www.unicode.org/faq/utf_bom.html + * 00 00 FE FF UTF-32BE, big-endian + * FF FE 00 00 UTF-32LE, little-endian + * FE FF UTF-16BE, big-endian + * FF FE UTF-16LE, little-endian + * EF BB BF UTF-8 + */ + + bom = 1; // assume there's a BOM + if (buf[0] == 0xFF && buf[1] == 0xFE) + { + if (buflen >= 4 && buf[2] == 0 && buf[3] == 0) + { // UTF-32LE + le = 1; + + Lutf32: + OutBuffer dbuf; + unsigned *pu = (unsigned *)(buf); + unsigned *pumax = &pu[buflen / 4]; + + if (buflen & 3) + { error("odd length of UTF-32 char source %u", buflen); + fatal(); + } + + dbuf.reserve(buflen / 4); + for (pu += bom; pu < pumax; pu++) + { unsigned u; + + u = le ? readlongLE(pu) : readlongBE(pu); + if (u & ~0x7F) + { + if (u > 0x10FFFF) + { error("UTF-32 value %08x greater than 0x10FFFF", u); + fatal(); + } + dbuf.writeUTF8(u); + } + else + dbuf.writeByte(u); + } + dbuf.writeByte(0); // add 0 as sentinel for scanner + buflen = dbuf.offset - 1; // don't include sentinel in count + buf = (unsigned char *) dbuf.extractData(); + } + else + { // UTF-16LE (X86) + // Convert it to UTF-8 + le = 1; + + Lutf16: + OutBuffer dbuf; + unsigned short *pu = (unsigned short *)(buf); + unsigned short *pumax = &pu[buflen / 2]; + + if (buflen & 1) + { error("odd length of UTF-16 char source %u", buflen); + fatal(); + } + + dbuf.reserve(buflen / 2); + for (pu += bom; pu < pumax; pu++) + { unsigned u; + + u = le ? readwordLE(pu) : readwordBE(pu); + if (u & ~0x7F) + { if (u >= 0xD800 && u <= 0xDBFF) + { unsigned u2; + + if (++pu > pumax) + { error("surrogate UTF-16 high value %04x at EOF", u); + fatal(); + } + u2 = le ? readwordLE(pu) : readwordBE(pu); + if (u2 < 0xDC00 || u2 > 0xDFFF) + { error("surrogate UTF-16 low value %04x out of range", u2); + fatal(); + } + u = (u - 0xD7C0) << 10; + u |= (u2 - 0xDC00); + } + else if (u >= 0xDC00 && u <= 0xDFFF) + { error("unpaired surrogate UTF-16 value %04x", u); + fatal(); + } + else if (u == 0xFFFE || u == 0xFFFF) + { error("illegal UTF-16 value %04x", u); + fatal(); + } + dbuf.writeUTF8(u); + } + else + dbuf.writeByte(u); + } + dbuf.writeByte(0); // add 0 as sentinel for scanner + buflen = dbuf.offset - 1; // don't include sentinel in count + buf = (unsigned char *) dbuf.extractData(); + } + } + else if (buf[0] == 0xFE && buf[1] == 0xFF) + { // UTF-16BE + le = 0; + goto Lutf16; + } + else if (buflen >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0xFE && buf[3] == 0xFF) + { // UTF-32BE + le = 0; + goto Lutf32; + } + else if (buflen >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) + { // UTF-8 + + buf += 3; + buflen -= 3; + } + else + { + /* There is no BOM. Make use of Arcane Jill's insight that + * the first char of D source must be ASCII to + * figure out the encoding. + */ + + bom = 0; + if (buflen >= 4) + { if (buf[1] == 0 && buf[2] == 0 && buf[3] == 0) + { // UTF-32LE + le = 1; + goto Lutf32; + } + else if (buf[0] == 0 && buf[1] == 0 && buf[2] == 0) + { // UTF-32BE + le = 0; + goto Lutf32; + } + } + if (buflen >= 2) + { + if (buf[1] == 0) + { // UTF-16LE + le = 1; + goto Lutf16; + } + else if (buf[0] == 0) + { // UTF-16BE + le = 0; + goto Lutf16; + } + } + + // It's UTF-8 + if (buf[0] >= 0x80) + { error("source file must start with BOM or ASCII character, not \\x%02X", buf[0]); + fatal(); + } + } + } + +#ifdef IN_GCC + // dump utf-8 encoded source + if (dump_source) + { // %% srcname could contain a path ... + d_gcc_dump_source(srcname, "utf-8", buf, buflen); + } +#endif + + /* If it starts with the string "Ddoc", then it's a documentation + * source file. + */ + if (buflen >= 4 && memcmp(buf, "Ddoc", 4) == 0) + { + comment = buf + 4; + isDocFile = 1; + if (!docfile) + setDocfile(); + return; + } + if (isHtml) + { + OutBuffer *dbuf = new OutBuffer(); + Html h(srcname, buf, buflen); + h.extractCode(dbuf); + buf = dbuf->data; + buflen = dbuf->offset; +#ifdef IN_GCC + // dump extracted source + if (dump_source) + d_gcc_dump_source(srcname, "d.utf-8", buf, buflen); +#endif + } + Parser p(this, buf, buflen, docfile != NULL); + p.nextToken(); + members = p.parseModule(); + + ::free(srcfile->buffer); + srcfile->buffer = NULL; + srcfile->len = 0; + + md = p.md; + numlines = p.loc.linnum; + + DsymbolTable *dst; + + if (md) + { this->ident = md->id; + this->safe = md->safe; + dst = Package::resolve(md->packages, &this->parent, NULL); + } + else + { + dst = modules; + + /* Check to see if module name is a valid identifier + */ + if (!Lexer::isValidIdentifier(this->ident->toChars())) + error("has non-identifier characters in filename, use module declaration instead"); + } + + // Update global list of modules + if (!dst->insert(this)) + { + Dsymbol *prev = dst->lookup(ident); + assert(prev); + Module *mprev = prev->isModule(); + if (mprev) + error(loc, "from file %s conflicts with another module %s from file %s", + srcname, mprev->toChars(), mprev->srcfile->toChars()); + else + { + Package *pkg = prev->isPackage(); + assert(pkg); + error(loc, "from file %s conflicts with package name %s", + srcname, pkg->toChars()); + } + } + else + { + amodules.push(this); + } +} + +void Module::importAll(Scope *prevsc) +{ + //printf("+Module::importAll(this = %p, '%s'): parent = %p\n", this, toChars(), parent); + + if (scope) + return; // already done + + if (isDocFile) + { + error("is a Ddoc file, cannot import it"); + return; + } + + /* Note that modules get their own scope, from scratch. + * This is so regardless of where in the syntax a module + * gets imported, it is unaffected by context. + * Ignore prevsc. + */ + Scope *sc = Scope::createGlobal(this); // create root scope + + // Add import of "object", even for the "object" module. + // If it isn't there, some compiler rewrites, like + // classinst == classinst -> .object.opEquals(classinst, classinst) + // would fail inside object.d. + if (members->dim == 0 || ((*members)[0])->ident != Id::object) + { + Import *im = new Import(0, NULL, Id::object, NULL, 0); + members->shift(im); + } + + if (!symtab) + { + // Add all symbols into module's symbol table + symtab = new DsymbolTable(); + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->addMember(NULL, sc->scopesym, 1); + } + } + // anything else should be run after addMember, so version/debug symbols are defined + + /* Set scope for the symbols so that if we forward reference + * a symbol, it can possibly be resolved on the spot. + * If this works out well, it can be extended to all modules + * before any semantic() on any of them. + */ + setScope(sc); // remember module scope for semantic + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + s->setScope(sc); + } + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->importAll(sc); + } + + sc = sc->pop(); + sc->pop(); // 2 pops because Scope::createGlobal() created 2 +} + +void Module::semantic() +{ + if (semanticstarted) + return; + + //printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent); + semanticstarted = 1; + + // Note that modules get their own scope, from scratch. + // This is so regardless of where in the syntax a module + // gets imported, it is unaffected by context. + Scope *sc = scope; // see if already got one from importAll() + if (!sc) + { printf("test2\n"); + Scope::createGlobal(this); // create root scope + } + + //printf("Module = %p, linkage = %d\n", sc->scopesym, sc->linkage); + +#if 0 + // Add import of "object" if this module isn't "object" + if (ident != Id::object) + { + Import *im = new Import(0, NULL, Id::object, NULL, 0); + members->shift(im); + } + + // Add all symbols into module's symbol table + symtab = new DsymbolTable(); + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = (Dsymbol *)members->data[i]; + s->addMember(NULL, sc->scopesym, 1); + } + + /* Set scope for the symbols so that if we forward reference + * a symbol, it can possibly be resolved on the spot. + * If this works out well, it can be extended to all modules + * before any semantic() on any of them. + */ + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = (Dsymbol *)members->data[i]; + s->setScope(sc); + } +#endif + + // Do semantic() on members that don't depend on others + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + + //printf("\tModule('%s'): '%s'.semantic0()\n", toChars(), s->toChars()); + s->semantic0(sc); + } + + // Pass 1 semantic routines: do public side of the definition + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + + //printf("\tModule('%s'): '%s'.semantic()\n", toChars(), s->toChars()); + s->semantic(sc); + runDeferredSemantic(); + } + + if (!scope) + { sc = sc->pop(); + sc->pop(); // 2 pops because Scope::createGlobal() created 2 + } + semanticRun = semanticstarted; + //printf("-Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent); +} + +void Module::semantic2() +{ + if (deferred.dim) + { + for (size_t i = 0; i < deferred.dim; i++) + { + Dsymbol *sd = deferred.tdata()[i]; + + sd->error("unable to resolve forward reference in definition"); + } + return; + } + //printf("Module::semantic2('%s'): parent = %p\n", toChars(), parent); + if (semanticstarted >= 2) + return; + assert(semanticstarted == 1); + semanticstarted = 2; + + // Note that modules get their own scope, from scratch. + // This is so regardless of where in the syntax a module + // gets imported, it is unaffected by context. + Scope *sc = Scope::createGlobal(this); // create root scope + //printf("Module = %p\n", sc.scopesym); + + // Pass 2 semantic routines: do initializers and function bodies + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s; + + s = members->tdata()[i]; + s->semantic2(sc); + } + + sc = sc->pop(); + sc->pop(); + semanticRun = semanticstarted; + //printf("-Module::semantic2('%s'): parent = %p\n", toChars(), parent); +} + +void Module::semantic3() +{ + //printf("Module::semantic3('%s'): parent = %p\n", toChars(), parent); + if (semanticstarted >= 3) + return; + assert(semanticstarted == 2); + semanticstarted = 3; + + // Note that modules get their own scope, from scratch. + // This is so regardless of where in the syntax a module + // gets imported, it is unaffected by context. + Scope *sc = Scope::createGlobal(this); // create root scope + //printf("Module = %p\n", sc.scopesym); + + // Pass 3 semantic routines: do initializers and function bodies + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s; + + s = members->tdata()[i]; + //printf("Module %s: %s.semantic3()\n", toChars(), s->toChars()); + s->semantic3(sc); + } + + sc = sc->pop(); + sc->pop(); + semanticRun = semanticstarted; +} + +void Module::inlineScan() +{ + if (semanticstarted >= 4) + return; + assert(semanticstarted == 3); + semanticstarted = 4; + + // Note that modules get their own scope, from scratch. + // This is so regardless of where in the syntax a module + // gets imported, it is unaffected by context. + //printf("Module = %p\n", sc.scopesym); + + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + //if (global.params.verbose) + //printf("inline scan symbol %s\n", s->toChars()); + + s->inlineScan(); + } + semanticRun = semanticstarted; +} + +/**************************************************** + */ + +void Module::gensymfile() +{ + OutBuffer buf; + HdrGenState hgs; + + //printf("Module::gensymfile()\n"); + + buf.printf("// Sym file generated from '%s'", srcfile->toChars()); + buf.writenl(); + + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + + s->toCBuffer(&buf, &hgs); + } + + // Transfer image to file + symfile->setbuffer(buf.data, buf.offset); + buf.data = NULL; + + symfile->writev(); +} + +/********************************** + * Determine if we need to generate an instance of ModuleInfo + * for this Module. + */ + +int Module::needModuleInfo() +{ + //printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov); + return needmoduleinfo || global.params.cov; +} + +Dsymbol *Module::search(Loc loc, Identifier *ident, int flags) +{ + /* Since modules can be circularly referenced, + * need to stop infinite recursive searches. + * This is done with the cache. + */ + + //printf("%s Module::search('%s', flags = %d) insearch = %d\n", toChars(), ident->toChars(), flags, insearch); + Dsymbol *s; + if (insearch) + s = NULL; + else if (searchCacheIdent == ident && searchCacheFlags == flags) + { + s = searchCacheSymbol; + //printf("%s Module::search('%s', flags = %d) insearch = %d searchCacheSymbol = %s\n", toChars(), ident->toChars(), flags, insearch, searchCacheSymbol ? searchCacheSymbol->toChars() : "null"); + } + else + { + insearch = 1; + s = ScopeDsymbol::search(loc, ident, flags); + insearch = 0; + + searchCacheIdent = ident; + searchCacheSymbol = s; + searchCacheFlags = flags; + } + return s; +} + +Dsymbol *Module::symtabInsert(Dsymbol *s) +{ + searchCacheIdent = 0; // symbol is inserted, so invalidate cache + return Package::symtabInsert(s); +} + +void Module::clearCache() +{ + for (size_t i = 0; i < amodules.dim; i++) + { Module *m = amodules.tdata()[i]; + m->searchCacheIdent = NULL; + } +} + +/******************************************* + * Can't run semantic on s now, try again later. + */ + +void Module::addDeferredSemantic(Dsymbol *s) +{ + // Don't add it if it is already there + for (size_t i = 0; i < deferred.dim; i++) + { + Dsymbol *sd = deferred.tdata()[i]; + + if (sd == s) + return; + } + + //printf("Module::addDeferredSemantic('%s')\n", s->toChars()); + deferred.push(s); +} + + +/****************************************** + * Run semantic() on deferred symbols. + */ + +void Module::runDeferredSemantic() +{ + if (dprogress == 0) + return; + + static int nested; + if (nested) + return; + //if (deferred.dim) printf("+Module::runDeferredSemantic('%s'), len = %d\n", toChars(), deferred.dim); + nested++; + + size_t len; + do + { + dprogress = 0; + len = deferred.dim; + if (!len) + break; + + Dsymbol **todo; + Dsymbol *tmp; + if (len == 1) + { + todo = &tmp; + } + else + { + todo = (Dsymbol **)alloca(len * sizeof(Dsymbol *)); + assert(todo); + } + memcpy(todo, deferred.tdata(), len * sizeof(Dsymbol *)); + deferred.setDim(0); + + for (size_t i = 0; i < len; i++) + { + Dsymbol *s = todo[i]; + + s->semantic(NULL); + //printf("deferred: %s, parent = %s\n", s->toChars(), s->parent->toChars()); + } + //printf("\tdeferred.dim = %d, len = %d, dprogress = %d\n", deferred.dim, len, dprogress); + } while (deferred.dim < len || dprogress); // while making progress + nested--; + //printf("-Module::runDeferredSemantic('%s'), len = %d\n", toChars(), deferred.dim); +} + +/************************************ + * Recursively look at every module this module imports, + * return TRUE if it imports m. + * Can be used to detect circular imports. + */ + +int Module::imports(Module *m) +{ + //printf("%s Module::imports(%s)\n", toChars(), m->toChars()); + int aimports_dim = aimports.dim; +#if 0 + for (size_t i = 0; i < aimports.dim; i++) + { Module *mi = (Module *)aimports.data[i]; + printf("\t[%d] %s\n", i, mi->toChars()); + } +#endif + for (size_t i = 0; i < aimports.dim; i++) + { Module *mi = aimports.tdata()[i]; + if (mi == m) + return TRUE; + if (!mi->insearch) + { + mi->insearch = 1; + int r = mi->imports(m); + if (r) + return r; + } + } + return FALSE; +} + +/************************************* + * Return !=0 if module imports itself. + */ + +int Module::selfImports() +{ + //printf("Module::selfImports() %s\n", toChars()); + if (!selfimports) + { + for (size_t i = 0; i < amodules.dim; i++) + { Module *mi = amodules.tdata()[i]; + //printf("\t[%d] %s\n", i, mi->toChars()); + mi->insearch = 0; + } + + selfimports = imports(this) + 1; + + for (size_t i = 0; i < amodules.dim; i++) + { Module *mi = amodules.tdata()[i]; + //printf("\t[%d] %s\n", i, mi->toChars()); + mi->insearch = 0; + } + } + return selfimports - 1; +} + + +/* =========================== ModuleDeclaration ===================== */ + +ModuleDeclaration::ModuleDeclaration(Identifiers *packages, Identifier *id, bool safe) +{ + this->packages = packages; + this->id = id; + this->safe = safe; +} + +char *ModuleDeclaration::toChars() +{ + OutBuffer buf; + + if (packages && packages->dim) + { + for (size_t i = 0; i < packages->dim; i++) + { Identifier *pid = packages->tdata()[i]; + + buf.writestring(pid->toChars()); + buf.writeByte('.'); + } + } + buf.writestring(id->toChars()); + buf.writeByte(0); + return (char *)buf.extractData(); +} + +/* =========================== Package ===================== */ + +Package::Package(Identifier *ident) + : ScopeDsymbol(ident) +{ +} + + +const char *Package::kind() +{ + return "package"; +} + + +DsymbolTable *Package::resolve(Identifiers *packages, Dsymbol **pparent, Package **ppkg) +{ + DsymbolTable *dst = Module::modules; + Dsymbol *parent = NULL; + + //printf("Package::resolve()\n"); + if (ppkg) + *ppkg = NULL; + + if (packages) + { + for (size_t i = 0; i < packages->dim; i++) + { Identifier *pid = packages->tdata()[i]; + Dsymbol *p; + + p = dst->lookup(pid); + if (!p) + { + p = new Package(pid); + dst->insert(p); + p->parent = parent; + ((ScopeDsymbol *)p)->symtab = new DsymbolTable(); + } + else + { + assert(p->isPackage()); +#if TARGET_NET //dot net needs modules and packages with same name +#else + if (p->isModule()) + { p->error("module and package have the same name"); + fatal(); + break; + } +#endif + } + parent = p; + dst = ((Package *)p)->symtab; + if (ppkg && !*ppkg) + *ppkg = (Package *)p; + } + if (pparent) + { + *pparent = parent; + } + } + return dst; +} diff --git a/module.h b/module.h new file mode 100644 index 00000000..6b045985 --- /dev/null +++ b/module.h @@ -0,0 +1,194 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2008 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_MODULE_H +#define DMD_MODULE_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "root.h" +#include "dsymbol.h" + +struct ModuleInfoDeclaration; +struct ClassDeclaration; +struct ModuleDeclaration; +struct Macro; +struct Escape; +struct VarDeclaration; +struct Library; + +// Back end +#if IN_GCC +union tree_node; typedef union tree_node elem; +#else +struct elem; +#endif + +struct Package : ScopeDsymbol +{ + Package(Identifier *ident); + const char *kind(); + + static DsymbolTable *resolve(Identifiers *packages, Dsymbol **pparent, Package **ppkg); + + Package *isPackage() { return this; } + + virtual void semantic(Scope *sc) { } +}; + +struct Module : Package +{ + static Module *rootModule; + static DsymbolTable *modules; // symbol table of all modules + static Modules amodules; // array of all modules + static Dsymbols deferred; // deferred Dsymbol's needing semantic() run on them + static unsigned dprogress; // progress resolving the deferred list + static void init(); + + static ClassDeclaration *moduleinfo; + + + const char *arg; // original argument name + ModuleDeclaration *md; // if !NULL, the contents of the ModuleDeclaration declaration + File *srcfile; // input source file + File *objfile; // output .obj file + File *hdrfile; // 'header' file + File *symfile; // output symbol file + File *docfile; // output documentation file + unsigned errors; // if any errors in file + unsigned numlines; // number of lines in source file + int isHtml; // if it is an HTML file + int isDocFile; // if it is a documentation input file, not D source + int needmoduleinfo; +#ifdef IN_GCC + int strictlyneedmoduleinfo; +#endif + + int selfimports; // 0: don't know, 1: does not, 2: does + int selfImports(); // returns !=0 if module imports itself + + int insearch; + Identifier *searchCacheIdent; + Dsymbol *searchCacheSymbol; // cached value of search + int searchCacheFlags; // cached flags + + int semanticstarted; // has semantic() been started? + int semanticRun; // has semantic() been done? + int root; // != 0 if this is a 'root' module, + // i.e. a module that will be taken all the + // way to an object file + Module *importedFrom; // module from command line we're imported from, + // i.e. a module that will be taken all the + // way to an object file + + Dsymbols *decldefs; // top level declarations for this Module + + Modules aimports; // all imported modules + + ModuleInfoDeclaration *vmoduleinfo; + + unsigned debuglevel; // debug level + Strings *debugids; // debug identifiers + Strings *debugidsNot; // forward referenced debug identifiers + + unsigned versionlevel; // version level + Strings *versionids; // version identifiers + Strings *versionidsNot; // forward referenced version identifiers + + Macro *macrotable; // document comment macros + Escape *escapetable; // document comment escapes + bool safe; // TRUE if module is marked as 'safe' + + size_t nameoffset; // offset of module name from start of ModuleInfo + size_t namelen; // length of module name in characters + + Module(char *arg, Identifier *ident, int doDocComment, int doHdrGen); + ~Module(); + + static Module *load(Loc loc, Identifiers *packages, Identifier *ident); + + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toJsonBuffer(OutBuffer *buf); + const char *kind(); + void setDocfile(); // set docfile member + void read(Loc loc); // read file +#if IN_GCC + void parse(bool dump_source = false); // syntactic parse +#else + void parse(); // syntactic parse +#endif + void importAll(Scope *sc); + void semantic(); // semantic analysis + void semantic2(); // pass 2 semantic analysis + void semantic3(); // pass 3 semantic analysis + void inlineScan(); // scan for functions to inline + void setHdrfile(); // set hdrfile member + void genhdrfile(); // generate D import file + void genobjfile(int multiobj); + void gensymfile(); + void gendocfile(); + int needModuleInfo(); + Dsymbol *search(Loc loc, Identifier *ident, int flags); + Dsymbol *symtabInsert(Dsymbol *s); + void deleteObjFile(); + void addDeferredSemantic(Dsymbol *s); + static void runDeferredSemantic(); + static void clearCache(); + int imports(Module *m); + + // Back end + + int doppelganger; // sub-module + Symbol *cov; // private uint[] __coverage; + unsigned *covb; // bit array of valid code line numbers + + Symbol *sictor; // module order independent constructor + Symbol *sctor; // module constructor + Symbol *sdtor; // module destructor + Symbol *ssharedctor; // module shared constructor + Symbol *sshareddtor; // module shared destructor + Symbol *stest; // module unit test + + Symbol *sfilename; // symbol for filename + + Symbol *massert; // module assert function + Symbol *toModuleAssert(); // get module assert function + + Symbol *munittest; // module unittest failure function + Symbol *toModuleUnittest(); // get module unittest failure function + + Symbol *marray; // module array bounds function + Symbol *toModuleArray(); // get module array bounds function + + + static Symbol *gencritsec(); + elem *toEfilename(); + + Symbol *toSymbol(); + void genmoduleinfo(); + + Module *isModule() { return this; } +}; + + +struct ModuleDeclaration +{ + Identifier *id; + Identifiers *packages; // array of Identifier's representing packages + bool safe; + + ModuleDeclaration(Identifiers *packages, Identifier *id, bool safe); + + char *toChars(); +}; + +#endif /* DMD_MODULE_H */ diff --git a/msc.c b/msc.c new file mode 100644 index 00000000..c154cbd2 --- /dev/null +++ b/msc.c @@ -0,0 +1,408 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2012 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include + +#include "mars.h" + +#include "cc.h" +#include "global.h" +#include "oper.h" +#include "code.h" +#include "type.h" +#include "dt.h" +#include "cgcv.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +extern void ph_init(); + +extern Global global; +extern int REALSIZE; + +Config config; +Configv configv; + +struct Environment; + +/************************************** + * Initialize config variables. + */ + +void out_config_init() +{ + //printf("out_config_init()\n"); + Param *params = &global.params; + + if (!config.target_cpu) + { config.target_cpu = TARGET_PentiumPro; + config.target_scheduler = config.target_cpu; + } + config.fulltypes = CVNONE; + config.fpxmmregs = FALSE; + config.inline8087 = 1; + config.memmodel = 0; + config.flags |= CFGuchar; // make sure TYchar is unsigned + tytab[TYchar] |= TYFLuns; +#if TARGET_WINDOS + if (params->is64bit) + config.exe = EX_WIN64; + else + config.exe = EX_NT; + + // Win32 eh + config.flags2 |= CFG2seh; + + if (params->run) + config.wflags |= WFexe; // EXE file only optimizations + else if (params->link && !global.params.deffile) + config.wflags |= WFexe; // EXE file only optimizations + else if (params->exefile) // if writing out EXE file + { size_t len = strlen(params->exefile); + if (len >= 4 && stricmp(params->exefile + len - 3, "exe") == 0) + config.wflags |= WFexe; + } + config.flags4 |= CFG4underscore; +#endif +#if TARGET_LINUX + if (params->is64bit) + { config.exe = EX_LINUX64; + config.fpxmmregs = TRUE; + } + else + config.exe = EX_LINUX; + config.flags |= CFGnoebp; + config.flags |= CFGalwaysframe; + if (params->pic) + config.flags3 |= CFG3pic; +#endif +#if TARGET_OSX + config.fpxmmregs = TRUE; + if (params->is64bit) + { config.exe = EX_OSX64; + config.fpxmmregs = TRUE; + } + else + config.exe = EX_OSX; + config.flags |= CFGnoebp; + config.flags |= CFGalwaysframe; + if (params->pic) + config.flags3 |= CFG3pic; +#endif +#if TARGET_FREEBSD + if (params->is64bit) + { config.exe = EX_FREEBSD64; + config.fpxmmregs = TRUE; + } + else + config.exe = EX_FREEBSD; + config.flags |= CFGnoebp; + config.flags |= CFGalwaysframe; + if (params->pic) + config.flags3 |= CFG3pic; +#endif +#if TARGET_OPENBSD + if (params->is64bit) + { config.exe = EX_OPENBSD64; + config.fpxmmregs = TRUE; + } + else + config.exe = EX_OPENBSD; + config.flags |= CFGnoebp; + config.flags |= CFGalwaysframe; + if (params->pic) + config.flags3 |= CFG3pic; +#endif +#if TARGET_SOLARIS + if (params->is64bit) + { config.exe = EX_SOLARIS64; + config.fpxmmregs = TRUE; + } + else + config.exe = EX_SOLARIS; + config.flags |= CFGnoebp; + config.flags |= CFGalwaysframe; + if (params->pic) + config.flags3 |= CFG3pic; +#endif + config.flags2 |= CFG2nodeflib; // no default library + config.flags3 |= CFG3eseqds; +#if 0 + if (env->getEEcontext()->EEcompile != 2) + config.flags4 |= CFG4allcomdat; + if (env->nochecks()) + config.flags4 |= CFG4nochecks; // no runtime checking +#elif TARGET_OSX +#else + config.flags4 |= CFG4allcomdat; +#endif + if (params->trace) + config.flags |= CFGtrace; // turn on profiler + if (params->nofloat) + config.flags3 |= CFG3wkfloat; + + configv.verbose = params->verbose; + + if (params->optimize) + go_flag((char *)"-o"); + + if (params->symdebug) + { +#if ELFOBJ || MACHOBJ + configv.addlinenumbers = 1; + config.fulltypes = (params->symdebug == 1) ? CVDWARF_D : CVDWARF_C; +#endif +#if OMFOBJ + configv.addlinenumbers = 1; + config.fulltypes = CV4; +#endif + if (!params->optimize) + config.flags |= CFGalwaysframe; + } + else + { + configv.addlinenumbers = 0; + config.fulltypes = CVNONE; + //config.flags &= ~CFGalwaysframe; + } + + if (params->alwaysframe) + config.flags &= ~CFGalwaysframe; + +#ifdef DEBUG + debugb = params->debugb; + debugc = params->debugc; + debugf = params->debugf; + debugr = params->debugr; + debugw = params->debugw; + debugx = params->debugx; + debugy = params->debugy; +#endif +} + +/******************************* + * Redo tables from 8086/286 to ILP32 + */ + +void util_set32() +{ + _tyrelax[TYenum] = TYlong; + _tyrelax[TYint] = TYlong; + _tyrelax[TYuint] = TYlong; + + tyequiv[TYint] = TYlong; + tyequiv[TYuint] = TYulong; + + for (int i = 0; i < 1; ++i) + { tysize[TYenum + i] = LONGSIZE; + tysize[TYint + i] = LONGSIZE; + tysize[TYuint + i] = LONGSIZE; + tysize[TYjhandle + i] = LONGSIZE; + tysize[TYnptr + i] = LONGSIZE; + tysize[TYnref + i] = LONGSIZE; + } + + for (int i = 0; i < 1; ++i) + { tyalignsize[TYenum + i] = LONGSIZE; + tyalignsize[TYint + i] = LONGSIZE; + tyalignsize[TYuint + i] = LONGSIZE; + tyalignsize[TYnullptr + i] = LONGSIZE; + tyalignsize[TYnptr + i] = LONGSIZE; + tyalignsize[TYnref + i] = LONGSIZE; + } +} + +/******************************* + * Redo tables from 8086/286 to LP64. + */ + +void util_set64() +{ + _tyrelax[TYenum] = TYlong; + _tyrelax[TYint] = TYlong; + _tyrelax[TYuint] = TYlong; + + tyequiv[TYint] = TYlong; + tyequiv[TYuint] = TYulong; + + for (int i = 0; i < 1; ++i) + { tysize[TYenum + i] = LONGSIZE; + tysize[TYint + i] = LONGSIZE; + tysize[TYuint + i] = LONGSIZE; + tysize[TYnptr + i] = 8; + tysize[TYnref + i] = 8; + tysize[TYldouble + i] = REALSIZE; + tysize[TYildouble + i] = REALSIZE; + tysize[TYcldouble + i] = 2 * REALSIZE; + + tyalignsize[TYenum + i] = LONGSIZE; + tyalignsize[TYint + i] = LONGSIZE; + tyalignsize[TYuint + i] = LONGSIZE; + tyalignsize[TYnullptr + i] = 8; + tyalignsize[TYnptr + i] = 8; + tyalignsize[TYnref + i] = 8; +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS || TARGET_OSX + tyalignsize[TYldouble + i] = 16; + tyalignsize[TYildouble + i] = 16; + tyalignsize[TYcldouble + i] = 16; +#else + assert(0); +#endif + tytab[TYjfunc + i] &= ~TYFLpascal; // set so caller cleans the stack (as in C) + } + + TYptrdiff = TYllong; + TYsize = TYullong; + TYsize_t = TYullong; +} + +/*********************************** + * Return aligned 'offset' if it is of size 'size'. + */ + +targ_size_t align(targ_size_t size, targ_size_t offset) +{ + switch (size) + { + case 1: + break; + case 2: + case 4: + case 8: + offset = (offset + size - 1) & ~(size - 1); + break; + default: + if (size >= 16) + offset = (offset + 15) & ~15; + else + offset = (offset + REGSIZE - 1) & ~(REGSIZE - 1); + break; + } + return offset; +} + +/******************************* + * Get size of ty + */ + +targ_size_t size(tym_t ty) +{ + int sz = (tybasic(ty) == TYvoid) ? 1 : tysize(ty); +#ifdef DEBUG + if (sz == -1) + WRTYxx(ty); +#endif + assert(sz!= -1); + return sz; +} + +/******************************* + * Replace (e) with ((stmp = e),stmp) + */ + +elem *exp2_copytotemp(elem *e) +{ + //printf("exp2_copytotemp()\n"); + elem_debug(e); + Symbol *stmp = symbol_genauto(e); + elem *eeq = el_bin(OPeq,e->Ety,el_var(stmp),e); + elem *er = el_bin(OPcomma,e->Ety,eeq,el_var(stmp)); + if (tybasic(e->Ety) == TYstruct || tybasic(e->Ety) == TYarray) + { + eeq->Eoper = OPstreq; + eeq->ET = e->ET; + eeq->E1->ET = e->ET; + er->ET = e->ET; + er->E2->ET = e->ET; + } + return er; +} + +/**************************** + * Generate symbol of type ty at DATA:offset + */ + +symbol *symboldata(targ_size_t offset,tym_t ty) +{ + symbol *s = symbol_generate(SClocstat, type_fake(ty)); + s->Sfl = FLdata; + s->Soffset = offset; + s->Sseg = DATA; + symbol_keep(s); // keep around + return s; +} + +/************************************ + * Add symbol to slist. + */ + +static list_t slist; + +void slist_add(Symbol *s) +{ + list_prepend(&slist,s); +} + +/************************************* + */ + +void slist_reset() +{ + //printf("slist_reset()\n"); + for (list_t sl = slist; sl; sl = list_next(sl)) + { Symbol *s = list_symbol(sl); + +#if MACHOBJ + s->Soffset = 0; +#endif + s->Sxtrnnum = 0; + s->Stypidx = 0; + s->Sflags &= ~(STRoutdef | SFLweak); + if (s->Sclass == SCglobal || s->Sclass == SCcomdat || + s->Sfl == FLudata || s->Sclass == SCstatic) + { s->Sclass = SCextern; + s->Sfl = FLextern; + } + } +} + + +/************************************** + */ + +void backend_init() +{ + ph_init(); + block_init(); + + if (global.params.is64bit) + { + util_set64(); + type_init(); + cod3_set64(); + } + else + { + util_set32(); + type_init(); + cod3_set32(); + } + + rtlsym_init(); // uses fregsaved, so must be after it's set inside cod3_set* + + out_config_init(); +} + +void backend_term() +{ +} diff --git a/mtype.c b/mtype.c new file mode 100644 index 00000000..c7f22412 --- /dev/null +++ b/mtype.c @@ -0,0 +1,9040 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2012 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// http://www.dsource.org/projects/dmd/browser/trunk/src/mtype.c +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#define __C99FEATURES__ 1 // Needed on Solaris for NaN and more +#define __USE_ISOC99 1 // so signbit() gets defined + +#if (defined (__SVR4) && defined (__sun)) +#include +#endif + +#include + +#include +#include +#include + +#if _MSC_VER +#include +#include +#include +#elif __DMC__ +#include +#elif __MINGW32__ +#include +#endif + +#include "rmem.h" +#include "port.h" + +#include "dsymbol.h" +#include "mtype.h" +#include "scope.h" +#include "init.h" +#include "expression.h" +#include "attrib.h" +#include "declaration.h" +#include "template.h" +#include "id.h" +#include "enum.h" +#include "import.h" +#include "aggregate.h" +#include "hdrgen.h" + +FuncDeclaration *hasThis(Scope *sc); +void ObjectNotFound(Identifier *id); + + +#define LOGDOTEXP 0 // log ::dotExp() +#define LOGDEFAULTINIT 0 // log ::defaultInit() + +// Allow implicit conversion of T[] to T* +#define IMPLICIT_ARRAY_TO_PTR global.params.useDeprecated + +/* These have default values for 32 bit code, they get + * adjusted for 64 bit code. + */ + +int PTRSIZE = 4; + +/* REALSIZE = size a real consumes in memory + * REALPAD = 'padding' added to the CPU real size to bring it up to REALSIZE + * REALALIGNSIZE = alignment for reals + */ +#if TARGET_OSX +int REALSIZE = 16; +int REALPAD = 6; +int REALALIGNSIZE = 16; +#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS +int REALSIZE = 12; +int REALPAD = 2; +int REALALIGNSIZE = 4; +#elif TARGET_WINDOS +int REALSIZE = 10; +int REALPAD = 0; +int REALALIGNSIZE = 2; +#else +#error "fix this" +#endif + +int Tsize_t = Tuns32; +int Tptrdiff_t = Tint32; + +/***************************** Type *****************************/ + +ClassDeclaration *Type::typeinfo; +ClassDeclaration *Type::typeinfoclass; +ClassDeclaration *Type::typeinfointerface; +ClassDeclaration *Type::typeinfostruct; +ClassDeclaration *Type::typeinfotypedef; +ClassDeclaration *Type::typeinfopointer; +ClassDeclaration *Type::typeinfoarray; +ClassDeclaration *Type::typeinfostaticarray; +ClassDeclaration *Type::typeinfoassociativearray; +ClassDeclaration *Type::typeinfovector; +ClassDeclaration *Type::typeinfoenum; +ClassDeclaration *Type::typeinfofunction; +ClassDeclaration *Type::typeinfodelegate; +ClassDeclaration *Type::typeinfotypelist; +ClassDeclaration *Type::typeinfoconst; +ClassDeclaration *Type::typeinfoinvariant; +ClassDeclaration *Type::typeinfoshared; +ClassDeclaration *Type::typeinfowild; + +TemplateDeclaration *Type::associativearray; + +Type *Type::tvoidptr; +Type *Type::tstring; +Type *Type::basic[TMAX]; +unsigned char Type::mangleChar[TMAX]; +unsigned char Type::sizeTy[TMAX]; +StringTable Type::stringtable; + + +Type::Type(TY ty) +{ + this->ty = ty; + this->mod = 0; + this->deco = NULL; +#if DMDV2 + this->cto = NULL; + this->ito = NULL; + this->sto = NULL; + this->scto = NULL; + this->wto = NULL; + this->swto = NULL; +#endif + this->pto = NULL; + this->rto = NULL; + this->arrayof = NULL; + this->vtinfo = NULL; + this->ctype = NULL; +} + +Type *Type::syntaxCopy() +{ + print(); + fprintf(stdmsg, "ty = %d\n", ty); + assert(0); + return this; +} + +int Type::equals(Object *o) +{ Type *t; + + t = (Type *)o; + //printf("Type::equals(%s, %s)\n", toChars(), t->toChars()); + if (this == o || + (t && deco == t->deco) && // deco strings are unique + deco != NULL) // and semantic() has been run + { + //printf("deco = '%s', t->deco = '%s'\n", deco, t->deco); + return 1; + } + //if (deco && t && t->deco) printf("deco = '%s', t->deco = '%s'\n", deco, t->deco); + return 0; +} + +char Type::needThisPrefix() +{ + return 'M'; // name mangling prefix for functions needing 'this' +} + +void Type::init() +{ + stringtable.init(1543); + Lexer::initKeywords(); + + for (size_t i = 0; i < TMAX; i++) + sizeTy[i] = sizeof(TypeBasic); + sizeTy[Tsarray] = sizeof(TypeSArray); + sizeTy[Tarray] = sizeof(TypeDArray); + sizeTy[Taarray] = sizeof(TypeAArray); + sizeTy[Tpointer] = sizeof(TypePointer); + sizeTy[Treference] = sizeof(TypeReference); + sizeTy[Tfunction] = sizeof(TypeFunction); + sizeTy[Tdelegate] = sizeof(TypeDelegate); + sizeTy[Tident] = sizeof(TypeIdentifier); + sizeTy[Tinstance] = sizeof(TypeInstance); + sizeTy[Ttypeof] = sizeof(TypeTypeof); + sizeTy[Tenum] = sizeof(TypeEnum); + sizeTy[Ttypedef] = sizeof(TypeTypedef); + sizeTy[Tstruct] = sizeof(TypeStruct); + sizeTy[Tclass] = sizeof(TypeClass); + sizeTy[Ttuple] = sizeof(TypeTuple); + sizeTy[Tslice] = sizeof(TypeSlice); + sizeTy[Treturn] = sizeof(TypeReturn); + sizeTy[Terror] = sizeof(TypeError); + sizeTy[Tnull] = sizeof(TypeNull); + + mangleChar[Tarray] = 'A'; + mangleChar[Tsarray] = 'G'; + mangleChar[Taarray] = 'H'; + mangleChar[Tpointer] = 'P'; + mangleChar[Treference] = 'R'; + mangleChar[Tfunction] = 'F'; + mangleChar[Tident] = 'I'; + mangleChar[Tclass] = 'C'; + mangleChar[Tstruct] = 'S'; + mangleChar[Tenum] = 'E'; + mangleChar[Ttypedef] = 'T'; + mangleChar[Tdelegate] = 'D'; + + mangleChar[Tnone] = 'n'; + mangleChar[Tvoid] = 'v'; + mangleChar[Tint8] = 'g'; + mangleChar[Tuns8] = 'h'; + mangleChar[Tint16] = 's'; + mangleChar[Tuns16] = 't'; + mangleChar[Tint32] = 'i'; + mangleChar[Tuns32] = 'k'; + mangleChar[Tint64] = 'l'; + mangleChar[Tuns64] = 'm'; + mangleChar[Tfloat32] = 'f'; + mangleChar[Tfloat64] = 'd'; + mangleChar[Tfloat80] = 'e'; + + mangleChar[Timaginary32] = 'o'; + mangleChar[Timaginary64] = 'p'; + mangleChar[Timaginary80] = 'j'; + mangleChar[Tcomplex32] = 'q'; + mangleChar[Tcomplex64] = 'r'; + mangleChar[Tcomplex80] = 'c'; + + mangleChar[Tbool] = 'b'; + mangleChar[Tascii] = 'a'; + mangleChar[Twchar] = 'u'; + mangleChar[Tdchar] = 'w'; + + // '@' shouldn't appear anywhere in the deco'd names + mangleChar[Tinstance] = '@'; + mangleChar[Terror] = '@'; + mangleChar[Ttypeof] = '@'; + mangleChar[Ttuple] = 'B'; + mangleChar[Tslice] = '@'; + mangleChar[Treturn] = '@'; + mangleChar[Tvector] = '@'; + + mangleChar[Tnull] = 'n'; // same as TypeNone + + for (size_t i = 0; i < TMAX; i++) + { if (!mangleChar[i]) + fprintf(stdmsg, "ty = %zd\n", i); + assert(mangleChar[i]); + } + + // Set basic types + static TY basetab[] = + { Tvoid, Tint8, Tuns8, Tint16, Tuns16, Tint32, Tuns32, Tint64, Tuns64, + Tfloat32, Tfloat64, Tfloat80, + Timaginary32, Timaginary64, Timaginary80, + Tcomplex32, Tcomplex64, Tcomplex80, + Tbool, + Tascii, Twchar, Tdchar }; + + for (size_t i = 0; i < sizeof(basetab) / sizeof(basetab[0]); i++) + { Type *t = new TypeBasic(basetab[i]); + t = t->merge(); + basic[basetab[i]] = t; + } + basic[Terror] = new TypeError(); + + tnull = new TypeNull(); + tnull->deco = tnull->merge()->deco; + + tvoidptr = tvoid->pointerTo(); + tstring = tchar->invariantOf()->arrayOf(); + + if (global.params.is64bit) + { + PTRSIZE = 8; + if (global.params.isLinux || global.params.isFreeBSD || global.params.isSolaris) + { + REALSIZE = 16; + REALPAD = 6; + REALALIGNSIZE = 16; + } + Tsize_t = Tuns64; + Tptrdiff_t = Tint64; + } + else + { + PTRSIZE = 4; +#if TARGET_OSX + REALSIZE = 16; + REALPAD = 6; +#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + REALSIZE = 12; + REALPAD = 2; +#else + REALSIZE = 10; + REALPAD = 0; +#endif + Tsize_t = Tuns32; + Tptrdiff_t = Tint32; + } +} + +d_uns64 Type::size() +{ + return size(0); +} + +d_uns64 Type::size(Loc loc) +{ + error(loc, "no size for type %s", toChars()); + return 1; +} + +unsigned Type::alignsize() +{ + return size(0); +} + +Type *Type::semantic(Loc loc, Scope *sc) +{ + return merge(); +} + +Type *Type::trySemantic(Loc loc, Scope *sc) +{ + //printf("+trySemantic(%s) %d\n", toChars(), global.errors); + unsigned errors = global.startGagging(); + Type *t = semantic(loc, sc); + if (global.endGagging(errors)) // if any errors happened + { + t = NULL; + } + //printf("-trySemantic(%s) %d\n", toChars(), global.errors); + return t; +} + +/******************************** + * Convert to 'const'. + */ + +Type *Type::constOf() +{ + //printf("Type::constOf() %p %s\n", this, toChars()); + if (mod == MODconst) + return this; + if (cto) + { assert(cto->mod == MODconst); + return cto; + } + Type *t = makeConst(); + t = t->merge(); + t->fixTo(this); + //printf("-Type::constOf() %p %s\n", t, t->toChars()); + return t; +} + +/******************************** + * Convert to 'immutable'. + */ + +Type *Type::invariantOf() +{ + //printf("Type::invariantOf() %p %s\n", this, toChars()); + if (isImmutable()) + { + return this; + } + if (ito) + { + assert(ito->isImmutable()); + return ito; + } + Type *t = makeInvariant(); + t = t->merge(); + t->fixTo(this); + //printf("\t%p\n", t); + return t; +} + +/******************************** + * Make type mutable. + */ + +Type *Type::mutableOf() +{ + //printf("Type::mutableOf() %p, %s\n", this, toChars()); + Type *t = this; + if (isConst()) + { if (isShared()) + t = sto; // shared const => shared + else + t = cto; // const => naked + assert(!t || t->isMutable()); + } + else if (isImmutable()) + { t = ito; // immutable => naked + assert(!t || (t->isMutable() && !t->isShared())); + } + else if (isWild()) + { + if (isShared()) + t = sto; // shared wild => shared + else + t = wto; // wild => naked + assert(!t || t->isMutable()); + } + if (!t) + { + t = makeMutable(); + t = t->merge(); + t->fixTo(this); + } + assert(t->isMutable()); + return t; +} + +Type *Type::sharedOf() +{ + //printf("Type::sharedOf() %p, %s\n", this, toChars()); + if (mod == MODshared) + { + return this; + } + if (sto) + { + assert(sto->isShared()); + return sto; + } + Type *t = makeShared(); + t = t->merge(); + t->fixTo(this); + //printf("\t%p\n", t); + return t; +} + +Type *Type::sharedConstOf() +{ + //printf("Type::sharedConstOf() %p, %s\n", this, toChars()); + if (mod == (MODshared | MODconst)) + { + return this; + } + if (scto) + { + assert(scto->mod == (MODshared | MODconst)); + return scto; + } + Type *t = makeSharedConst(); + t = t->merge(); + t->fixTo(this); + //printf("\t%p\n", t); + return t; +} + + +/******************************** + * Make type unshared. + * 0 => 0 + * const => const + * immutable => immutable + * shared => 0 + * shared const => const + * wild => wild + * shared wild => wild + */ + +Type *Type::unSharedOf() +{ + //printf("Type::unSharedOf() %p, %s\n", this, toChars()); + Type *t = this; + + if (isShared()) + { + if (isConst()) + t = cto; // shared const => const + else if (isWild()) + t = wto; // shared wild => wild + else + t = sto; + assert(!t || !t->isShared()); + } + + if (!t) + { + unsigned sz = sizeTy[ty]; + t = (Type *)mem.malloc(sz); + memcpy(t, this, sz); + t->mod = mod & ~MODshared; + t->deco = NULL; + t->arrayof = NULL; + t->pto = NULL; + t->rto = NULL; + t->cto = NULL; + t->ito = NULL; + t->sto = NULL; + t->scto = NULL; + t->wto = NULL; + t->swto = NULL; + t->vtinfo = NULL; + t = t->merge(); + + t->fixTo(this); + } + assert(!t->isShared()); + return t; +} + +/******************************** + * Convert to 'wild'. + */ + +Type *Type::wildOf() +{ + //printf("Type::wildOf() %p %s\n", this, toChars()); + if (mod == MODwild) + { + return this; + } + if (wto) + { + assert(wto->isWild()); + return wto; + } + Type *t = makeWild(); + t = t->merge(); + t->fixTo(this); + //printf("\t%p %s\n", t, t->toChars()); + return t; +} + +Type *Type::sharedWildOf() +{ + //printf("Type::sharedWildOf() %p, %s\n", this, toChars()); + if (mod == (MODshared | MODwild)) + { + return this; + } + if (swto) + { + assert(swto->mod == (MODshared | MODwild)); + return swto; + } + Type *t = makeSharedWild(); + t = t->merge(); + t->fixTo(this); + //printf("\t%p\n", t); + return t; +} + +/********************************** + * For our new type 'this', which is type-constructed from t, + * fill in the cto, ito, sto, scto, wto shortcuts. + */ + +void Type::fixTo(Type *t) +{ + ito = t->ito; +#if 0 + /* Cannot do these because these are not fully transitive: + * there can be a shared ptr to immutable, for example. + * Immutable subtypes are always immutable, though. + */ + cto = t->cto; + sto = t->sto; + scto = t->scto; +#endif + + assert(mod != t->mod); +#define X(m, n) (((m) << 4) | (n)) + switch (X(mod, t->mod)) + { + case X(0, MODconst): + cto = t; + break; + + case X(0, MODimmutable): + ito = t; + break; + + case X(0, MODshared): + sto = t; + break; + + case X(0, MODshared | MODconst): + scto = t; + break; + + case X(0, MODwild): + wto = t; + break; + + case X(0, MODshared | MODwild): + swto = t; + break; + + + case X(MODconst, 0): + cto = NULL; + goto L2; + + case X(MODconst, MODimmutable): + ito = t; + goto L2; + + case X(MODconst, MODshared): + sto = t; + goto L2; + + case X(MODconst, MODshared | MODconst): + scto = t; + goto L2; + + case X(MODconst, MODwild): + wto = t; + goto L2; + + case X(MODconst, MODshared | MODwild): + swto = t; + L2: + t->cto = this; + break; + + + case X(MODimmutable, 0): + ito = NULL; + goto L3; + + case X(MODimmutable, MODconst): + cto = t; + goto L3; + + case X(MODimmutable, MODshared): + sto = t; + goto L3; + + case X(MODimmutable, MODshared | MODconst): + scto = t; + goto L3; + + case X(MODimmutable, MODwild): + wto = t; + goto L3; + + case X(MODimmutable, MODshared | MODwild): + swto = t; + L3: + t->ito = this; + if (t->cto) t->cto->ito = this; + if (t->sto) t->sto->ito = this; + if (t->scto) t->scto->ito = this; + if (t->wto) t->wto->ito = this; + if (t->swto) t->swto->ito = this; + break; + + + case X(MODshared, 0): + sto = NULL; + goto L4; + + case X(MODshared, MODconst): + cto = t; + goto L4; + + case X(MODshared, MODimmutable): + ito = t; + goto L4; + + case X(MODshared, MODshared | MODconst): + scto = t; + goto L4; + + case X(MODshared, MODwild): + wto = t; + goto L4; + + case X(MODshared, MODshared | MODwild): + swto = t; + L4: + t->sto = this; + break; + + + case X(MODshared | MODconst, 0): + scto = NULL; + goto L5; + + case X(MODshared | MODconst, MODconst): + cto = t; + goto L5; + + case X(MODshared | MODconst, MODimmutable): + ito = t; + goto L5; + + case X(MODshared | MODconst, MODwild): + wto = t; + goto L5; + + case X(MODshared | MODconst, MODshared): + sto = t; + goto L5; + + case X(MODshared | MODconst, MODshared | MODwild): + swto = t; + L5: + t->scto = this; + break; + + + case X(MODwild, 0): + wto = NULL; + goto L6; + + case X(MODwild, MODconst): + cto = t; + goto L6; + + case X(MODwild, MODimmutable): + ito = t; + goto L6; + + case X(MODwild, MODshared): + sto = t; + goto L6; + + case X(MODwild, MODshared | MODconst): + scto = t; + goto L6; + + case X(MODwild, MODshared | MODwild): + swto = t; + L6: + t->wto = this; + break; + + + case X(MODshared | MODwild, 0): + swto = NULL; + goto L7; + + case X(MODshared | MODwild, MODconst): + cto = t; + goto L7; + + case X(MODshared | MODwild, MODimmutable): + ito = t; + goto L7; + + case X(MODshared | MODwild, MODshared): + sto = t; + goto L7; + + case X(MODshared | MODwild, MODshared | MODconst): + scto = t; + goto L7; + + case X(MODshared | MODwild, MODwild): + wto = t; + L7: + t->swto = this; + break; + + + default: + assert(0); + } +#undef X + + check(); + t->check(); + //printf("fixTo: %s, %s\n", toChars(), t->toChars()); +} + +/*************************** + * Look for bugs in constructing types. + */ + +void Type::check() +{ + switch (mod) + { + case 0: + if (cto) assert(cto->mod == MODconst); + if (ito) assert(ito->mod == MODimmutable); + if (sto) assert(sto->mod == MODshared); + if (scto) assert(scto->mod == (MODshared | MODconst)); + if (wto) assert(wto->mod == MODwild); + if (swto) assert(swto->mod == (MODshared | MODwild)); + break; + + case MODconst: + if (cto) assert(cto->mod == 0); + if (ito) assert(ito->mod == MODimmutable); + if (sto) assert(sto->mod == MODshared); + if (scto) assert(scto->mod == (MODshared | MODconst)); + if (wto) assert(wto->mod == MODwild); + if (swto) assert(swto->mod == (MODshared | MODwild)); + break; + + case MODimmutable: + if (cto) assert(cto->mod == MODconst); + if (ito) assert(ito->mod == 0); + if (sto) assert(sto->mod == MODshared); + if (scto) assert(scto->mod == (MODshared | MODconst)); + if (wto) assert(wto->mod == MODwild); + if (swto) assert(swto->mod == (MODshared | MODwild)); + break; + + case MODshared: + if (cto) assert(cto->mod == MODconst); + if (ito) assert(ito->mod == MODimmutable); + if (sto) assert(sto->mod == 0); + if (scto) assert(scto->mod == (MODshared | MODconst)); + if (wto) assert(wto->mod == MODwild); + if (swto) assert(swto->mod == (MODshared | MODwild)); + break; + + case MODshared | MODconst: + if (cto) assert(cto->mod == MODconst); + if (ito) assert(ito->mod == MODimmutable); + if (sto) assert(sto->mod == MODshared); + if (scto) assert(scto->mod == 0); + if (wto) assert(wto->mod == MODwild); + if (swto) assert(swto->mod == (MODshared | MODwild)); + break; + + case MODwild: + if (cto) assert(cto->mod == MODconst); + if (ito) assert(ito->mod == MODimmutable); + if (sto) assert(sto->mod == MODshared); + if (scto) assert(scto->mod == (MODshared | MODconst)); + if (wto) assert(wto->mod == 0); + if (swto) assert(swto->mod == (MODshared | MODwild)); + break; + + case MODshared | MODwild: + if (cto) assert(cto->mod == MODconst); + if (ito) assert(ito->mod == MODimmutable); + if (sto) assert(sto->mod == MODshared); + if (scto) assert(scto->mod == (MODshared | MODconst)); + if (wto) assert(wto->mod == MODwild); + if (swto) assert(swto->mod == 0); + break; + + default: + assert(0); + } + + Type *tn = nextOf(); + if (tn && ty != Tfunction && tn->ty != Tfunction) + { // Verify transitivity + switch (mod) + { + case 0: + break; + + case MODconst: + assert(tn->mod & MODimmutable || tn->mod & MODconst); + break; + + case MODimmutable: + assert(tn->mod == MODimmutable); + break; + + case MODshared: + assert(tn->mod & MODimmutable || tn->mod & MODshared); + break; + + case MODshared | MODconst: + assert(tn->mod & MODimmutable || tn->mod & (MODshared | MODconst)); + break; + + case MODwild: + assert(tn->mod); + break; + + case MODshared | MODwild: + assert(tn->mod == MODimmutable || tn->mod == (MODshared | MODconst) || tn->mod == (MODshared | MODwild)); + break; + + default: + assert(0); + } + tn->check(); + } +} + +Type *Type::makeConst() +{ + //printf("Type::makeConst() %p, %s\n", this, toChars()); + if (cto) + return cto; + unsigned sz = sizeTy[ty]; + Type *t = (Type *)mem.malloc(sz); + memcpy(t, this, sz); + t->mod = MODconst; + t->deco = NULL; + t->arrayof = NULL; + t->pto = NULL; + t->rto = NULL; + t->cto = NULL; + t->ito = NULL; + t->sto = NULL; + t->scto = NULL; + t->wto = NULL; + t->swto = NULL; + t->vtinfo = NULL; + t->ctype = NULL; + //printf("-Type::makeConst() %p, %s\n", t, toChars()); + return t; +} + +Type *Type::makeInvariant() +{ + if (ito) + return ito; + unsigned sz = sizeTy[ty]; + Type *t = (Type *)mem.malloc(sz); + memcpy(t, this, sz); + t->mod = MODimmutable; + t->deco = NULL; + t->arrayof = NULL; + t->pto = NULL; + t->rto = NULL; + t->cto = NULL; + t->ito = NULL; + t->sto = NULL; + t->scto = NULL; + t->wto = NULL; + t->swto = NULL; + t->vtinfo = NULL; + t->ctype = NULL; + return t; +} + +Type *Type::makeShared() +{ + if (sto) + return sto; + unsigned sz = sizeTy[ty]; + Type *t = (Type *)mem.malloc(sz); + memcpy(t, this, sz); + t->mod = MODshared; + t->deco = NULL; + t->arrayof = NULL; + t->pto = NULL; + t->rto = NULL; + t->cto = NULL; + t->ito = NULL; + t->sto = NULL; + t->scto = NULL; + t->wto = NULL; + t->swto = NULL; + t->vtinfo = NULL; + t->ctype = NULL; + return t; +} + +Type *Type::makeSharedConst() +{ + if (scto) + return scto; + unsigned sz = sizeTy[ty]; + Type *t = (Type *)mem.malloc(sz); + memcpy(t, this, sz); + t->mod = MODshared | MODconst; + t->deco = NULL; + t->arrayof = NULL; + t->pto = NULL; + t->rto = NULL; + t->cto = NULL; + t->ito = NULL; + t->sto = NULL; + t->scto = NULL; + t->wto = NULL; + t->swto = NULL; + t->vtinfo = NULL; + t->ctype = NULL; + return t; +} + +Type *Type::makeWild() +{ + if (wto) + return wto; + unsigned sz = sizeTy[ty]; + Type *t = (Type *)mem.malloc(sz); + memcpy(t, this, sz); + t->mod = MODwild; + t->deco = NULL; + t->arrayof = NULL; + t->pto = NULL; + t->rto = NULL; + t->cto = NULL; + t->ito = NULL; + t->sto = NULL; + t->scto = NULL; + t->wto = NULL; + t->swto = NULL; + t->vtinfo = NULL; + t->ctype = NULL; + return t; +} + +Type *Type::makeSharedWild() +{ + if (swto) + return swto; + unsigned sz = sizeTy[ty]; + Type *t = (Type *)mem.malloc(sz); + memcpy(t, this, sz); + t->mod = MODshared | MODwild; + t->deco = NULL; + t->arrayof = NULL; + t->pto = NULL; + t->rto = NULL; + t->cto = NULL; + t->ito = NULL; + t->sto = NULL; + t->scto = NULL; + t->wto = NULL; + t->swto = NULL; + t->vtinfo = NULL; + t->ctype = NULL; + return t; +} + +Type *Type::makeMutable() +{ + unsigned sz = sizeTy[ty]; + Type *t = (Type *)mem.malloc(sz); + memcpy(t, this, sz); + t->mod = mod & MODshared; + t->deco = NULL; + t->arrayof = NULL; + t->pto = NULL; + t->rto = NULL; + t->cto = NULL; + t->ito = NULL; + t->sto = NULL; + t->scto = NULL; + t->wto = NULL; + t->swto = NULL; + t->vtinfo = NULL; + t->ctype = NULL; + return t; +} + +/************************************* + * Apply STCxxxx bits to existing type. + * Use *before* semantic analysis is run. + */ + +Type *Type::addSTC(StorageClass stc) +{ Type *t = this; + + if (stc & STCconst) + { if (t->isShared()) + t = t->makeSharedConst(); + else + t = t->makeConst(); + } + if (stc & STCimmutable) + t = t->makeInvariant(); + if (stc & STCshared) + { if (t->isConst()) + t = t->makeSharedConst(); + else + t = t->makeShared(); + } + if (stc & STCwild) + { if (t->isShared()) + t = t->makeSharedWild(); + else + t = t->makeWild(); + } + return t; +} + +/************************************ + * Apply MODxxxx bits to existing type. + */ + +Type *Type::castMod(unsigned mod) +{ Type *t; + + switch (mod) + { + case 0: + t = unSharedOf()->mutableOf(); + break; + + case MODconst: + t = unSharedOf()->constOf(); + break; + + case MODimmutable: + t = invariantOf(); + break; + + case MODshared: + t = mutableOf()->sharedOf(); + break; + + case MODshared | MODconst: + t = sharedConstOf(); + break; + + case MODwild: + t = unSharedOf()->wildOf(); + break; + + case MODshared | MODwild: + t = sharedWildOf(); + break; + + default: + assert(0); + } + return t; +} + +/************************************ + * Add MODxxxx bits to existing type. + * We're adding, not replacing, so adding const to + * a shared type => "shared const" + */ + +Type *Type::addMod(unsigned mod) +{ Type *t = this; + + /* Add anything to immutable, and it remains immutable + */ + if (!t->isImmutable()) + { + //printf("addMod(%x) %s\n", mod, toChars()); + switch (mod) + { + case 0: + break; + + case MODconst: + if (isShared()) + t = sharedConstOf(); + else + t = constOf(); + break; + + case MODimmutable: + t = invariantOf(); + break; + + case MODshared: + if (isConst()) + t = sharedConstOf(); + else if (isWild()) + t = sharedWildOf(); + else + t = sharedOf(); + break; + + case MODshared | MODconst: + t = sharedConstOf(); + break; + + case MODwild: + if (isConst()) + ; + else if (isShared()) + t = sharedWildOf(); + else + t = wildOf(); + break; + + case MODshared | MODwild: + if (isConst()) + t = sharedConstOf(); + else + t = sharedWildOf(); + break; + + default: + assert(0); + } + } + return t; +} + +/************************************ + * Add storage class modifiers to type. + */ + +Type *Type::addStorageClass(StorageClass stc) +{ + /* Just translate to MOD bits and let addMod() do the work + */ + unsigned mod = 0; + + if (stc & STCimmutable) + mod = MODimmutable; + else + { if (stc & (STCconst | STCin)) + mod = MODconst; + else if (stc & STCwild) // const takes precedence over wild + mod |= MODwild; + if (stc & STCshared) + mod |= MODshared; + } + return addMod(mod); +} + +Type *Type::pointerTo() +{ + if (ty == Terror) + return this; + if (!pto) + { Type *t; + + t = new TypePointer(this); + pto = t->merge(); + } + return pto; +} + +Type *Type::referenceTo() +{ + if (ty == Terror) + return this; + if (!rto) + { Type *t; + + t = new TypeReference(this); + rto = t->merge(); + } + return rto; +} + +Type *Type::arrayOf() +{ + if (ty == Terror) + return this; + if (!arrayof) + { Type *t; + + t = new TypeDArray(this); + arrayof = t->merge(); + } + return arrayof; +} + +Type *Type::aliasthisOf() +{ + AggregateDeclaration *ad = NULL; + if (ty == Tclass) + { + ad = ((TypeClass *)this)->sym; + goto L1; + } + else if (ty == Tstruct) + { + ad = ((TypeStruct *)this)->sym; + L1: + if (!ad->aliasthis) + return NULL; + + Declaration *d = ad->aliasthis->isDeclaration(); + if (d) + { assert(d->type); + Type *t = d->type; + if (d->isVarDeclaration() && d->needThis()) + { + t = t->addMod(this->mod); + } + else if (d->isFuncDeclaration()) + { + FuncDeclaration *fd = (FuncDeclaration *)d; + Expression *ethis = this->defaultInit(0); + fd = fd->overloadResolve(0, ethis, NULL); + if (fd) + t = ((TypeFunction *)fd->type)->next; + } + return t; + } + EnumDeclaration *ed = ad->aliasthis->isEnumDeclaration(); + if (ed) + { + Type *t = ed->type; + return t; + } + //printf("%s\n", ad->aliasthis->kind()); + } + return NULL; +} + +Dsymbol *Type::toDsymbol(Scope *sc) +{ + return NULL; +} + +/******************************* + * If this is a shell around another type, + * get that other type. + */ + +Type *Type::toBasetype() +{ + return this; +} + +/*************************** + * Return !=0 if modfrom can be implicitly converted to modto + */ +int MODimplicitConv(unsigned char modfrom, unsigned char modto) +{ + if (modfrom == modto) + return 1; + + //printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto); + #define X(m, n) (((m) << 4) | (n)) + switch (X(modfrom, modto)) + { + case X(0, MODconst): + case X(MODimmutable, MODconst): + case X(MODwild, MODconst): + case X(MODimmutable, MODconst | MODshared): + case X(MODshared, MODconst | MODshared): + case X(MODwild | MODshared, MODconst | MODshared): + return 1; + + default: + return 0; + } + #undef X +} + +/*************************** + * Return !=0 if a method of type '() modfrom' can call a method of type '() modto'. + */ +int MODmethodConv(unsigned char modfrom, unsigned char modto) +{ + if (MODimplicitConv(modfrom, modto)) + return 1; + + #define X(m, n) (((m) << 4) | (n)) + switch (X(modfrom, modto)) + { + case X(0, MODwild): + case X(MODimmutable, MODwild): + case X(MODconst, MODwild): + case X(MODshared, MODshared|MODwild): + case X(MODshared|MODimmutable, MODshared|MODwild): + case X(MODshared|MODconst, MODshared|MODwild): + return 1; + + default: + return 0; + } + #undef X +} + +/*************************** + * Merge mod bits to form common mod. + */ +int MODmerge(unsigned char mod1, unsigned char mod2) +{ + if (mod1 == mod2) + return mod1; + + //printf("MODmerge(1 = %x, 2 = %x)\n", modfrom, modto); + #define X(m, n) (((m) << 4) | (n)) + // cases are commutative + #define Y(m, n) X(m, n): case X(n, m) + switch (X(mod1, mod2)) + { +#if 0 + case X(0, 0): + case X(MODconst, MODconst): + case X(MODimmutable, MODimmutable): + case X(MODshared, MODshared): + case X(MODshared | MODconst, MODshared | MODconst): + case X(MODwild, MODwild): + case X(MODshared | MODwild, MODshared | MODwild): +#endif + + case Y(0, MODconst): + case Y(0, MODimmutable): + case Y(MODconst, MODimmutable): + case Y(MODconst, MODwild): + case Y(0, MODwild): + case Y(MODimmutable, MODwild): + return MODconst; + + case Y(0, MODshared): + return MODshared; + + case Y(0, MODshared | MODconst): + case Y(MODconst, MODshared): + case Y(MODconst, MODshared | MODconst): + case Y(MODimmutable, MODshared): + case Y(MODimmutable, MODshared | MODconst): + case Y(MODshared, MODshared | MODconst): + case Y(0, MODshared | MODwild): + case Y(MODconst, MODshared | MODwild): + case Y(MODimmutable, MODshared | MODwild): + case Y(MODshared, MODwild): + case Y(MODshared, MODshared | MODwild): + case Y(MODshared | MODconst, MODwild): + case Y(MODshared | MODconst, MODshared | MODwild): + return MODshared | MODconst; + + case Y(MODwild, MODshared | MODwild): + return MODshared | MODwild; + + default: + assert(0); + } + #undef Y + #undef X + assert(0); + return 0; +} + +/********************************* + * Mangling for mod. + */ +void MODtoDecoBuffer(OutBuffer *buf, unsigned char mod) +{ + switch (mod) + { case 0: + break; + case MODconst: + buf->writeByte('x'); + break; + case MODimmutable: + buf->writeByte('y'); + break; + case MODshared: + buf->writeByte('O'); + break; + case MODshared | MODconst: + buf->writestring("Ox"); + break; + case MODwild: + buf->writestring("Ng"); + break; + case MODshared | MODwild: + buf->writestring("ONg"); + break; + default: + assert(0); + } +} + +/********************************* + * Name for mod. + */ +void MODtoBuffer(OutBuffer *buf, unsigned char mod) +{ + switch (mod) + { case 0: + break; + + case MODimmutable: + buf->writestring(Token::tochars[TOKimmutable]); + break; + + case MODshared: + buf->writestring(Token::tochars[TOKshared]); + break; + + case MODshared | MODconst: + buf->writestring(Token::tochars[TOKshared]); + buf->writeByte(' '); + case MODconst: + buf->writestring(Token::tochars[TOKconst]); + break; + + case MODshared | MODwild: + buf->writestring(Token::tochars[TOKshared]); + buf->writeByte(' '); + case MODwild: + buf->writestring(Token::tochars[TOKwild]); + break; + default: + assert(0); + } +} + +/******************************** + * Name mangling. + * Input: + * flag 0x100 do not do const/invariant + */ + +void Type::toDecoBuffer(OutBuffer *buf, int flag) +{ + if (flag != mod && flag != 0x100) + { + MODtoDecoBuffer(buf, mod); + } + buf->writeByte(mangleChar[ty]); +} + +/******************************** + * For pretty-printing a type. + */ + +char *Type::toChars() +{ OutBuffer *buf; + HdrGenState hgs; + + buf = new OutBuffer(); + toCBuffer(buf, NULL, &hgs); + return buf->toChars(); +} + +void Type::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs) +{ + toCBuffer2(buf, hgs, 0); + if (ident) + { buf->writeByte(' '); + buf->writestring(ident->toChars()); + } +} + +void Type::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + buf->writestring(toChars()); +} + +void Type::toCBuffer3(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { + if (!(mod & MODshared) && (this->mod & MODshared)) + { + MODtoBuffer(buf, this->mod & MODshared); + buf->writeByte('('); + } + int m1 = this->mod & ~MODshared; + int m2 = (mod ^ m1) & m1; + if (m2) + { + MODtoBuffer(buf, m2); + buf->writeByte('('); + toCBuffer2(buf, hgs, this->mod); + buf->writeByte(')'); + } + else + toCBuffer2(buf, hgs, this->mod); + if (!(mod & MODshared) && (this->mod & MODshared)) + { + buf->writeByte(')'); + } + } +} + +void Type::modToBuffer(OutBuffer *buf) +{ + if (mod) + { + buf->writeByte(' '); + MODtoBuffer(buf, mod); + } +} + +/************************************ + */ + +Type *Type::merge() +{ + if (ty == Terror) return this; + if (ty == Ttypeof) return this; + if (ty == Tident) return this; + if (ty == Tinstance) return this; + if (nextOf() && !nextOf()->merge()->deco) + return this; + + //printf("merge(%s)\n", toChars()); + Type *t = this; + assert(t); + if (!deco) + { + OutBuffer buf; + StringValue *sv; + + //if (next) + //next = next->merge(); + toDecoBuffer(&buf); + sv = stringtable.update((char *)buf.data, buf.offset); + if (sv->ptrvalue) + { t = (Type *) sv->ptrvalue; +#ifdef DEBUG + if (!t->deco) + printf("t = %s\n", t->toChars()); +#endif + assert(t->deco); + //printf("old value, deco = '%s' %p\n", t->deco, t->deco); + } + else + { + sv->ptrvalue = this; + deco = sv->lstring.string; + //printf("new value, deco = '%s' %p\n", t->deco, t->deco); + } + } + return t; +} + +/************************************* + * This version does a merge even if the deco is already computed. + * Necessary for types that have a deco, but are not merged. + */ +Type *Type::merge2() +{ + //printf("merge2(%s)\n", toChars()); + Type *t = this; + assert(t); + if (!t->deco) + return t->merge(); + + StringValue *sv = stringtable.lookup((char *)t->deco, strlen(t->deco)); + if (sv && sv->ptrvalue) + { t = (Type *) sv->ptrvalue; + assert(t->deco); + } + else + assert(0); + return t; +} + +int Type::isintegral() +{ + return FALSE; +} + +int Type::isfloating() +{ + return FALSE; +} + +int Type::isreal() +{ + return FALSE; +} + +int Type::isimaginary() +{ + return FALSE; +} + +int Type::iscomplex() +{ + return FALSE; +} + +int Type::isscalar() +{ + return FALSE; +} + +int Type::isunsigned() +{ + return FALSE; +} + +ClassDeclaration *Type::isClassHandle() +{ + return NULL; +} + +int Type::isscope() +{ + return FALSE; +} + +int Type::isString() +{ + return FALSE; +} + +/************************** + * Given: + * T a, b; + * Can we assign: + * a = b; + * ? + */ +int Type::isAssignable() +{ + return TRUE; +} + +int Type::checkBoolean() +{ + return isscalar(); +} + +/******************************** + * TRUE if when type goes out of scope, it needs a destructor applied. + * Only applies to value types, not ref types. + */ +int Type::needsDestruction() +{ + return FALSE; +} + +/********************************* + * Check type to see if it is based on a deprecated symbol. + */ + +void Type::checkDeprecated(Loc loc, Scope *sc) +{ + Dsymbol *s = toDsymbol(sc); + + if (s) + s->checkDeprecated(loc, sc); +} + + +Expression *Type::defaultInit(Loc loc) +{ +#if LOGDEFAULTINIT + printf("Type::defaultInit() '%s'\n", toChars()); +#endif + return NULL; +} + +/*************************************** + * Use when we prefer the default initializer to be a literal, + * rather than a global immutable variable. + */ +Expression *Type::defaultInitLiteral(Loc loc) +{ +#if LOGDEFAULTINIT + printf("Type::defaultInitLiteral() '%s'\n", toChars()); +#endif + return defaultInit(loc); +} + +int Type::isZeroInit(Loc loc) +{ + return 0; // assume not +} + +int Type::isBaseOf(Type *t, int *poffset) +{ + return 0; // assume not +} + +/******************************** + * Determine if 'this' can be implicitly converted + * to type 'to'. + * Returns: + * MATCHnomatch, MATCHconvert, MATCHconst, MATCHexact + */ + +MATCH Type::implicitConvTo(Type *to) +{ + //printf("Type::implicitConvTo(this=%p, to=%p)\n", this, to); + //printf("from: %s\n", toChars()); + //printf("to : %s\n", to->toChars()); + if (this == to) + return MATCHexact; + return MATCHnomatch; +} + +/******************************* + * Determine if converting 'this' to 'to' is an identity operation, + * a conversion to const operation, or the types aren't the same. + * Returns: + * MATCHexact 'this' == 'to' + * MATCHconst 'to' is const + * MATCHnomatch conversion to mutable or invariant + */ + +MATCH Type::constConv(Type *to) +{ + //printf("Type::constConv(this = %s, to = %s)\n", toChars(), to->toChars()); + if (equals(to)) + return MATCHexact; + if (ty == to->ty && MODimplicitConv(mod, to->mod)) + return MATCHconst; + return MATCHnomatch; +} + +/*************************************** + * Return MOD bits matching this type to wild parameter type (tprm). + */ + +unsigned Type::wildConvTo(Type *tprm) +{ + //printf("Type::wildConvTo this = '%s', tprm = '%s'\n", toChars(), tprm->toChars()); + + if (tprm->isWild() && implicitConvTo(tprm->substWildTo(MODconst))) + { + if (isWild()) + return MODwild; + else if (isConst()) + return MODconst; + else if (isImmutable()) + return MODimmutable; + else if (isMutable()) + return MODmutable; + else + assert(0); + } + return 0; +} + +Type *Type::substWildTo(unsigned mod) +{ + //printf("+Type::substWildTo this = %s, mod = x%x\n", toChars(), mod); + Type *t; + + if (nextOf()) + { + t = nextOf()->substWildTo(mod); + if (t == nextOf()) + t = this; + else + { + if (ty == Tpointer) + t = t->pointerTo(); + else if (ty == Tarray) + t = t->arrayOf(); + else if (ty == Tsarray) + t = new TypeSArray(t, ((TypeSArray *)this)->dim->syntaxCopy()); + else if (ty == Taarray) + { + t = new TypeAArray(t, ((TypeAArray *)this)->index->syntaxCopy()); + t = t->merge(); + } + else + assert(0); + + t = t->addMod(this->mod); + } + } + else + t = this; + + if (isWild()) + { + if (mod & MODconst) + t = isShared() ? t->sharedConstOf() : t->constOf(); + else if (mod & MODimmutable) + t = t->invariantOf(); + else if (mod & MODwild) + t = isShared() ? t->sharedWildOf() : t->wildOf(); + else + t = isShared() ? t->sharedOf() : t->mutableOf(); + } + + //printf("-Type::substWildTo t = %s\n", t->toChars()); + return t; +} + +/************************** + * Return type with the top level of it being mutable. + */ +Type *Type::toHeadMutable() +{ + if (!mod) + return this; + return mutableOf(); +} + +Expression *Type::getProperty(Loc loc, Identifier *ident) +{ Expression *e; + +#if LOGDOTEXP + printf("Type::getProperty(type = '%s', ident = '%s')\n", toChars(), ident->toChars()); +#endif + if (ident == Id::__sizeof) + { + e = new IntegerExp(loc, size(loc), Type::tsize_t); + } + else if (ident == Id::size) + { + error(loc, ".size property should be replaced with .sizeof"); + e = new ErrorExp(); + } + else if (ident == Id::__xalignof) + { + e = new IntegerExp(loc, alignsize(), Type::tsize_t); + } + else if (ident == Id::typeinfo) + { + if (!global.params.useDeprecated) + error(loc, ".typeinfo deprecated, use typeid(type)"); + e = getTypeInfo(NULL); + } + else if (ident == Id::init) + { + if (ty == Tvoid) + error(loc, "void does not have an initializer"); + if (ty == Tfunction) + error(loc, "function does not have an initializer"); + e = defaultInitLiteral(loc); + } + else if (ident == Id::mangleof) + { const char *s; + if (!deco) + { s = toChars(); + error(loc, "forward reference of type %s.mangleof", s); + } + else + s = deco; + e = new StringExp(loc, (char *)s, strlen(s), 'c'); + Scope sc; + e = e->semantic(&sc); + } + else if (ident == Id::stringof) + { char *s = toChars(); + e = new StringExp(loc, s, strlen(s), 'c'); + Scope sc; + e = e->semantic(&sc); + } + else + { + Dsymbol *s = NULL; + if (ty == Tstruct || ty == Tclass || ty == Tenum || ty == Ttypedef) + s = toDsymbol(NULL); + if (s) + s = s->search_correct(ident); + if (this != Type::terror) + { + if (s) + error(loc, "no property '%s' for type '%s', did you mean '%s'?", ident->toChars(), toChars(), s->toChars()); + else + error(loc, "no property '%s' for type '%s'", ident->toChars(), toChars()); + } + e = new ErrorExp(); + } + return e; +} + +Expression *Type::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ VarDeclaration *v = NULL; + +#if LOGDOTEXP + printf("Type::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); +#endif + if (e->op == TOKdotvar) + { + DotVarExp *dv = (DotVarExp *)e; + v = dv->var->isVarDeclaration(); + } + else if (e->op == TOKvar) + { + VarExp *ve = (VarExp *)e; + v = ve->var->isVarDeclaration(); + } + if (v) + { + if (ident == Id::offset) + { + if (!global.params.useDeprecated) + error(e->loc, ".offset deprecated, use .offsetof"); + goto Loffset; + } + else if (ident == Id::offsetof) + { + Loffset: + if (v->storage_class & STCfield) + { + e = new IntegerExp(e->loc, v->offset, Type::tsize_t); + return e; + } + } + else if (ident == Id::init) + { +#if 0 + if (v->init) + { + if (v->init->isVoidInitializer()) + error(e->loc, "%s.init is void", v->toChars()); + else + { Loc loc = e->loc; + e = v->init->toExpression(); + if (e->op == TOKassign || e->op == TOKconstruct || e->op == TOKblit) + { + e = ((AssignExp *)e)->e2; + + /* Take care of case where we used a 0 + * to initialize the struct. + */ + if (e->type == Type::tint32 && + e->isBool(0) && + v->type->toBasetype()->ty == Tstruct) + { + e = v->type->defaultInit(e->loc); + } + } + e = e->optimize(WANTvalue | WANTinterpret); +// if (!e->isConst()) +// error(loc, ".init cannot be evaluated at compile time"); + } + goto Lreturn; + } +#endif + e = defaultInitLiteral(e->loc); + goto Lreturn; + } + } + if (ident == Id::typeinfo) + { + if (!global.params.useDeprecated) + error(e->loc, ".typeinfo deprecated, use typeid(type)"); + e = getTypeInfo(sc); + } + else if (ident == Id::stringof) + { /* Bugzilla 3796: this should demangle e->type->deco rather than + * pretty-printing the type. + */ + char *s = e->toChars(); + e = new StringExp(e->loc, s, strlen(s), 'c'); + } + else + e = getProperty(e->loc, ident); + +Lreturn: + e = e->semantic(sc); + return e; +} + +/*************************************** + * Figures out what to do with an undefined member reference + * for classes and structs. + */ +Expression *Type::noMember(Scope *sc, Expression *e, Identifier *ident) +{ + assert(ty == Tstruct || ty == Tclass); + AggregateDeclaration *sym = toDsymbol(sc)->isAggregateDeclaration(); + assert(sym); + + if (ident != Id::__sizeof && + ident != Id::__xalignof && + ident != Id::init && + ident != Id::mangleof && + ident != Id::stringof && + ident != Id::offsetof) + { + /* Look for overloaded opDot() to see if we should forward request + * to it. + */ + Dsymbol *fd = search_function(sym, Id::opDot); + if (fd) + { /* Rewrite e.ident as: + * e.opDot().ident + */ + e = build_overload(e->loc, sc, e, NULL, fd); + e = new DotIdExp(e->loc, e, ident); + return e->semantic(sc); + } + + /* Look for overloaded opDispatch to see if we should forward request + * to it. + */ + fd = search_function(sym, Id::opDispatch); + if (fd) + { + /* Rewrite e.ident as: + * e.opDispatch!("ident") + */ + TemplateDeclaration *td = fd->isTemplateDeclaration(); + if (!td) + { + fd->error("must be a template opDispatch(string s), not a %s", fd->kind()); + return new ErrorExp(); + } + StringExp *se = new StringExp(e->loc, ident->toChars()); + Objects *tiargs = new Objects(); + tiargs->push(se); + e = new DotTemplateInstanceExp(e->loc, e, Id::opDispatch, tiargs); + ((DotTemplateInstanceExp *)e)->ti->tempdecl = td; + //return e; + e = e->semantic(sc); + return e; + } + + /* See if we should forward to the alias this. + */ + if (sym->aliasthis) + { /* Rewrite e.ident as: + * e.aliasthis.ident + */ + e = new DotIdExp(e->loc, e, sym->aliasthis->ident); + e = new DotIdExp(e->loc, e, ident); + return e->semantic(sc); + } + } + + return Type::dotExp(sc, e, ident); +} + +unsigned Type::memalign(unsigned salign) +{ + return salign; +} + +void Type::error(Loc loc, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + ::verror(loc, format, ap); + va_end( ap ); +} + +void Type::warning(Loc loc, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + ::vwarning(loc, format, ap); + va_end( ap ); +} + +Identifier *Type::getTypeInfoIdent(int internal) +{ + // _init_10TypeInfo_%s + OutBuffer buf; + Identifier *id; + char *name; + int len; + + if (internal) + { buf.writeByte(mangleChar[ty]); + if (ty == Tarray) + buf.writeByte(mangleChar[((TypeArray *)this)->next->ty]); + } + else + toDecoBuffer(&buf); + len = buf.offset; + name = (char *)alloca(19 + sizeof(len) * 3 + len + 1); + buf.writeByte(0); +#if TARGET_OSX + // The LINKc will prepend the _ + sprintf(name, "D%dTypeInfo_%s6__initZ", 9 + len, buf.data); +#else + sprintf(name, "_D%dTypeInfo_%s6__initZ", 9 + len, buf.data); +#endif + if (global.params.isWindows) + name++; // C mangling will add it back in + //printf("name = %s\n", name); + id = Lexer::idPool(name); + return id; +} + +TypeBasic *Type::isTypeBasic() +{ + return NULL; +} + + +void Type::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps) +{ + //printf("Type::resolve() %s, %d\n", toChars(), ty); + Type *t = semantic(loc, sc); + *pt = t; + *pe = NULL; + *ps = NULL; +} + +/******************************* + * If one of the subtypes of this type is a TypeIdentifier, + * i.e. it's an unresolved type, return that type. + */ + +Type *Type::reliesOnTident() +{ + return NULL; +} + +/*************************************** + * Return !=0 if the type or any of its subtypes is wild. + */ + +int Type::hasWild() +{ + return mod & MODwild; +} + +/******************************** + * We've mistakenly parsed this as a type. + * Redo it as an Expression. + * NULL if cannot. + */ + +Expression *Type::toExpression() +{ + return NULL; +} + +/*************************************** + * Return !=0 if type has pointers that need to + * be scanned by the GC during a collection cycle. + */ + +int Type::hasPointers() +{ + //printf("Type::hasPointers() %s, %d\n", toChars(), ty); + return FALSE; +} + +/************************************* + * If this is a type of something, return that something. + */ + +Type *Type::nextOf() +{ + return NULL; +} + +/**************************************** + * Return the mask that an integral type will + * fit into. + */ +uinteger_t Type::sizemask() +{ uinteger_t m; + + switch (toBasetype()->ty) + { + case Tbool: m = 1; break; + case Tchar: + case Tint8: + case Tuns8: m = 0xFF; break; + case Twchar: + case Tint16: + case Tuns16: m = 0xFFFFUL; break; + case Tdchar: + case Tint32: + case Tuns32: m = 0xFFFFFFFFUL; break; + case Tint64: + case Tuns64: m = 0xFFFFFFFFFFFFFFFFULL; break; + default: + assert(0); + } + return m; +} + +/* ============================= TypeError =========================== */ + +TypeError::TypeError() + : Type(Terror) +{ +} + +Type *TypeError::syntaxCopy() +{ + // No semantic analysis done, no need to copy + return this; +} + +void TypeError::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs) +{ + buf->writestring("_error_"); +} + +d_uns64 TypeError::size(Loc loc) { return 1; } +Expression *TypeError::getProperty(Loc loc, Identifier *ident) { return new ErrorExp(); } +Expression *TypeError::dotExp(Scope *sc, Expression *e, Identifier *ident) { return new ErrorExp(); } +Expression *TypeError::defaultInit(Loc loc) { return new ErrorExp(); } +Expression *TypeError::defaultInitLiteral(Loc loc) { return new ErrorExp(); } + +/* ============================= TypeNext =========================== */ + +TypeNext::TypeNext(TY ty, Type *next) + : Type(ty) +{ + this->next = next; +} + +void TypeNext::toDecoBuffer(OutBuffer *buf, int flag) +{ + Type::toDecoBuffer(buf, flag); + assert(next != this); + //printf("this = %p, ty = %d, next = %p, ty = %d\n", this, this->ty, next, next->ty); + next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod); +} + +void TypeNext::checkDeprecated(Loc loc, Scope *sc) +{ + Type::checkDeprecated(loc, sc); + if (next) // next can be NULL if TypeFunction and auto return type + next->checkDeprecated(loc, sc); +} + + +Type *TypeNext::reliesOnTident() +{ + return next->reliesOnTident(); +} + +int TypeNext::hasWild() +{ + return mod & MODwild || (next && next->hasWild()); +} + + +/******************************* + * For TypeFunction, nextOf() can return NULL if the function return + * type is meant to be inferred, and semantic() hasn't yet ben run + * on the function. After semantic(), it must no longer be NULL. + */ + +Type *TypeNext::nextOf() +{ + return next; +} + +Type *TypeNext::makeConst() +{ + //printf("TypeNext::makeConst() %p, %s\n", this, toChars()); + if (cto) + { assert(cto->mod == MODconst); + return cto; + } + TypeNext *t = (TypeNext *)Type::makeConst(); + if (ty != Tfunction && next->ty != Tfunction && + //(next->deco || next->ty == Tfunction) && + !next->isImmutable() && !next->isConst()) + { if (next->isShared()) + t->next = next->sharedConstOf(); + else + t->next = next->constOf(); + } + if (ty == Taarray) + { + ((TypeAArray *)t)->impl = NULL; // lazily recompute it + } + //printf("TypeNext::makeConst() returns %p, %s\n", t, t->toChars()); + return t; +} + +Type *TypeNext::makeInvariant() +{ + //printf("TypeNext::makeInvariant() %s\n", toChars()); + if (ito) + { assert(ito->isImmutable()); + return ito; + } + TypeNext *t = (TypeNext *)Type::makeInvariant(); + if (ty != Tfunction && next->ty != Tfunction && + //(next->deco || next->ty == Tfunction) && + !next->isImmutable()) + { t->next = next->invariantOf(); + } + if (ty == Taarray) + { + ((TypeAArray *)t)->impl = NULL; // lazily recompute it + } + return t; +} + +Type *TypeNext::makeShared() +{ + //printf("TypeNext::makeShared() %s\n", toChars()); + if (sto) + { assert(sto->mod == MODshared); + return sto; + } + TypeNext *t = (TypeNext *)Type::makeShared(); + if (ty != Tfunction && next->ty != Tfunction && + //(next->deco || next->ty == Tfunction) && + !next->isImmutable() && !next->isShared()) + { + if (next->isConst()) + t->next = next->sharedConstOf(); + else if (next->isWild()) + t->next = next->sharedWildOf(); + else + t->next = next->sharedOf(); + } + if (ty == Taarray) + { + ((TypeAArray *)t)->impl = NULL; // lazily recompute it + } + //printf("TypeNext::makeShared() returns %p, %s\n", t, t->toChars()); + return t; +} + +Type *TypeNext::makeSharedConst() +{ + //printf("TypeNext::makeSharedConst() %s\n", toChars()); + if (scto) + { assert(scto->mod == (MODshared | MODconst)); + return scto; + } + TypeNext *t = (TypeNext *)Type::makeSharedConst(); + if (ty != Tfunction && next->ty != Tfunction && + //(next->deco || next->ty == Tfunction) && + !next->isImmutable() && !next->isSharedConst()) + { + t->next = next->sharedConstOf(); + } + if (ty == Taarray) + { + ((TypeAArray *)t)->impl = NULL; // lazily recompute it + } + //printf("TypeNext::makeSharedConst() returns %p, %s\n", t, t->toChars()); + return t; +} + +Type *TypeNext::makeWild() +{ + //printf("TypeNext::makeWild() %s\n", toChars()); + if (wto) + { assert(wto->mod == MODwild); + return wto; + } + TypeNext *t = (TypeNext *)Type::makeWild(); + if (ty != Tfunction && next->ty != Tfunction && + //(next->deco || next->ty == Tfunction) && + !next->isImmutable() && !next->isConst() && !next->isWild()) + { + if (next->isShared()) + t->next = next->sharedWildOf(); + else + t->next = next->wildOf(); + } + if (ty == Taarray) + { + ((TypeAArray *)t)->impl = NULL; // lazily recompute it + } + //printf("TypeNext::makeWild() returns %p, %s\n", t, t->toChars()); + return t; +} + +Type *TypeNext::makeSharedWild() +{ + //printf("TypeNext::makeSharedWild() %s\n", toChars()); + if (swto) + { assert(swto->isSharedWild()); + return swto; + } + TypeNext *t = (TypeNext *)Type::makeSharedWild(); + if (ty != Tfunction && next->ty != Tfunction && + //(next->deco || next->ty == Tfunction) && + !next->isImmutable() && !next->isSharedConst()) + { + if (next->isConst()) + t->next = next->sharedConstOf(); + else + t->next = next->sharedWildOf(); + } + if (ty == Taarray) + { + ((TypeAArray *)t)->impl = NULL; // lazily recompute it + } + //printf("TypeNext::makeSharedWild() returns %p, %s\n", t, t->toChars()); + return t; +} + +Type *TypeNext::makeMutable() +{ + //printf("TypeNext::makeMutable() %p, %s\n", this, toChars()); + TypeNext *t = (TypeNext *)Type::makeMutable(); + if ((ty != Tfunction && next->ty != Tfunction && + //(next->deco || next->ty == Tfunction) && + next->isWild()) || ty == Tsarray) + { + t->next = next->mutableOf(); + } + if (ty == Taarray) + { + ((TypeAArray *)t)->impl = NULL; // lazily recompute it + } + //printf("TypeNext::makeMutable() returns %p, %s\n", t, t->toChars()); + return t; +} + +MATCH TypeNext::constConv(Type *to) +{ + //printf("TypeNext::constConv from = %s, to = %s\n", toChars(), to->toChars()); + if (equals(to)) + return MATCHexact; + + if (!(ty == to->ty && MODimplicitConv(mod, to->mod))) + return MATCHnomatch; + + Type *tn = to->nextOf(); + if (!(tn && next->ty == tn->ty)) + return MATCHnomatch; + + MATCH m; + if (to->isConst()) // whole tail const conversion + { // Recursive shared level check + m = next->constConv(tn); + if (m == MATCHexact) + m = MATCHconst; + } + else + { //printf("\tnext => %s, to->next => %s\n", next->toChars(), tn->toChars()); + m = next->equals(tn) ? MATCHconst : MATCHnomatch; + } + return m; +} + +unsigned TypeNext::wildConvTo(Type *tprm) +{ + if (ty == Tfunction) + return 0; + + unsigned mod = 0; + Type *tn = tprm->nextOf(); + if (!tn) + return 0; + mod = next->wildConvTo(tn); + if (!mod) + mod = Type::wildConvTo(tprm); + + return mod; +} + + +void TypeNext::transitive() +{ + /* Invoke transitivity of type attributes + */ + next = next->addMod(mod); +} + +/* ============================= TypeBasic =========================== */ + +TypeBasic::TypeBasic(TY ty) + : Type(ty) +{ const char *d; + unsigned flags; + +#define TFLAGSintegral 1 +#define TFLAGSfloating 2 +#define TFLAGSunsigned 4 +#define TFLAGSreal 8 +#define TFLAGSimaginary 0x10 +#define TFLAGScomplex 0x20 +#define TFLAGSvector 0x40 // valid for a SIMD vector type + + flags = 0; + switch (ty) + { + case Tvoid: d = Token::toChars(TOKvoid); + flags |= TFLAGSvector; + break; + + case Tint8: d = Token::toChars(TOKint8); + flags |= TFLAGSintegral | TFLAGSvector; + break; + + case Tuns8: d = Token::toChars(TOKuns8); + flags |= TFLAGSintegral | TFLAGSunsigned | TFLAGSvector; + break; + + case Tint16: d = Token::toChars(TOKint16); + flags |= TFLAGSintegral | TFLAGSvector; + break; + + case Tuns16: d = Token::toChars(TOKuns16); + flags |= TFLAGSintegral | TFLAGSunsigned | TFLAGSvector; + break; + + case Tint32: d = Token::toChars(TOKint32); + flags |= TFLAGSintegral | TFLAGSvector; + break; + + case Tuns32: d = Token::toChars(TOKuns32); + flags |= TFLAGSintegral | TFLAGSunsigned | TFLAGSvector; + break; + + case Tfloat32: d = Token::toChars(TOKfloat32); + flags |= TFLAGSfloating | TFLAGSreal | TFLAGSvector; + break; + + case Tint64: d = Token::toChars(TOKint64); + flags |= TFLAGSintegral | TFLAGSvector; + break; + + case Tuns64: d = Token::toChars(TOKuns64); + flags |= TFLAGSintegral | TFLAGSunsigned | TFLAGSvector; + break; + + case Tfloat64: d = Token::toChars(TOKfloat64); + flags |= TFLAGSfloating | TFLAGSreal | TFLAGSvector; + break; + + case Tfloat80: d = Token::toChars(TOKfloat80); + flags |= TFLAGSfloating | TFLAGSreal; + break; + + case Timaginary32: d = Token::toChars(TOKimaginary32); + flags |= TFLAGSfloating | TFLAGSimaginary; + break; + + case Timaginary64: d = Token::toChars(TOKimaginary64); + flags |= TFLAGSfloating | TFLAGSimaginary; + break; + + case Timaginary80: d = Token::toChars(TOKimaginary80); + flags |= TFLAGSfloating | TFLAGSimaginary; + break; + + case Tcomplex32: d = Token::toChars(TOKcomplex32); + flags |= TFLAGSfloating | TFLAGScomplex; + break; + + case Tcomplex64: d = Token::toChars(TOKcomplex64); + flags |= TFLAGSfloating | TFLAGScomplex; + break; + + case Tcomplex80: d = Token::toChars(TOKcomplex80); + flags |= TFLAGSfloating | TFLAGScomplex; + break; + + case Tbool: d = "bool"; + flags |= TFLAGSintegral | TFLAGSunsigned; + break; + + case Tascii: d = Token::toChars(TOKchar); + flags |= TFLAGSintegral | TFLAGSunsigned; + break; + + case Twchar: d = Token::toChars(TOKwchar); + flags |= TFLAGSintegral | TFLAGSunsigned; + break; + + case Tdchar: d = Token::toChars(TOKdchar); + flags |= TFLAGSintegral | TFLAGSunsigned; + break; + + default: assert(0); + } + this->dstring = d; + this->flags = flags; + merge(); +} + +Type *TypeBasic::syntaxCopy() +{ + // No semantic analysis done on basic types, no need to copy + return this; +} + + +char *TypeBasic::toChars() +{ + return Type::toChars(); +} + +void TypeBasic::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + //printf("TypeBasic::toCBuffer2(mod = %d, this->mod = %d)\n", mod, this->mod); + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + buf->writestring(dstring); +} + +d_uns64 TypeBasic::size(Loc loc) +{ unsigned size; + + //printf("TypeBasic::size()\n"); + switch (ty) + { + case Tint8: + case Tuns8: size = 1; break; + case Tint16: + case Tuns16: size = 2; break; + case Tint32: + case Tuns32: + case Tfloat32: + case Timaginary32: + size = 4; break; + case Tint64: + case Tuns64: + case Tfloat64: + case Timaginary64: + size = 8; break; + case Tfloat80: + case Timaginary80: + size = REALSIZE; break; + case Tcomplex32: + size = 8; break; + case Tcomplex64: + size = 16; break; + case Tcomplex80: + size = REALSIZE * 2; break; + + case Tvoid: + //size = Type::size(); // error message + size = 1; + break; + + case Tbool: size = 1; break; + case Tascii: size = 1; break; + case Twchar: size = 2; break; + case Tdchar: size = 4; break; + + default: + assert(0); + break; + } + //printf("TypeBasic::size() = %d\n", size); + return size; +} + +unsigned TypeBasic::alignsize() +{ unsigned sz; + + switch (ty) + { + case Tfloat80: + case Timaginary80: + case Tcomplex80: + sz = REALALIGNSIZE; + break; + +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + case Tint64: + case Tuns64: + sz = global.params.is64bit ? 8 : 4; + break; + + case Tfloat64: + case Timaginary64: + sz = global.params.is64bit ? 8 : 4; + break; + + case Tcomplex32: + sz = 4; + break; + + case Tcomplex64: + sz = global.params.is64bit ? 8 : 4; + break; +#endif + + default: + sz = size(0); + break; + } + return sz; +} + + +Expression *TypeBasic::getProperty(Loc loc, Identifier *ident) +{ + Expression *e; + d_int64 ivalue; +#ifdef IN_GCC + real_t fvalue; +#else + d_float80 fvalue; +#endif + + //printf("TypeBasic::getProperty('%s')\n", ident->toChars()); + if (ident == Id::max) + { + switch (ty) + { + case Tint8: ivalue = 0x7F; goto Livalue; + case Tuns8: ivalue = 0xFF; goto Livalue; + case Tint16: ivalue = 0x7FFFUL; goto Livalue; + case Tuns16: ivalue = 0xFFFFUL; goto Livalue; + case Tint32: ivalue = 0x7FFFFFFFUL; goto Livalue; + case Tuns32: ivalue = 0xFFFFFFFFUL; goto Livalue; + case Tint64: ivalue = 0x7FFFFFFFFFFFFFFFLL; goto Livalue; + case Tuns64: ivalue = 0xFFFFFFFFFFFFFFFFULL; goto Livalue; + case Tbool: ivalue = 1; goto Livalue; + case Tchar: ivalue = 0xFF; goto Livalue; + case Twchar: ivalue = 0xFFFFUL; goto Livalue; + case Tdchar: ivalue = 0x10FFFFUL; goto Livalue; + + case Tcomplex32: + case Timaginary32: + case Tfloat32: fvalue = FLT_MAX; goto Lfvalue; + case Tcomplex64: + case Timaginary64: + case Tfloat64: fvalue = DBL_MAX; goto Lfvalue; + case Tcomplex80: + case Timaginary80: + case Tfloat80: fvalue = Port::ldbl_max; goto Lfvalue; + } + } + else if (ident == Id::min) + { + switch (ty) + { + case Tint8: ivalue = -128; goto Livalue; + case Tuns8: ivalue = 0; goto Livalue; + case Tint16: ivalue = -32768; goto Livalue; + case Tuns16: ivalue = 0; goto Livalue; + case Tint32: ivalue = -2147483647L - 1; goto Livalue; + case Tuns32: ivalue = 0; goto Livalue; + case Tint64: ivalue = (-9223372036854775807LL-1LL); goto Livalue; + case Tuns64: ivalue = 0; goto Livalue; + case Tbool: ivalue = 0; goto Livalue; + case Tchar: ivalue = 0; goto Livalue; + case Twchar: ivalue = 0; goto Livalue; + case Tdchar: ivalue = 0; goto Livalue; + + case Tcomplex32: + case Timaginary32: + case Tfloat32: + case Tcomplex64: + case Timaginary64: + case Tfloat64: + case Tcomplex80: + case Timaginary80: + case Tfloat80: + // For backwards compatibility - eventually, deprecate + goto Lmin_normal; + } + } + else if (ident == Id::min_normal) + { + Lmin_normal: + switch (ty) + { + case Tcomplex32: + case Timaginary32: + case Tfloat32: fvalue = FLT_MIN; goto Lfvalue; + case Tcomplex64: + case Timaginary64: + case Tfloat64: fvalue = DBL_MIN; goto Lfvalue; + case Tcomplex80: + case Timaginary80: + case Tfloat80: fvalue = LDBL_MIN; goto Lfvalue; + } + } + else if (ident == Id::nan) + { + switch (ty) + { + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: + case Timaginary32: + case Timaginary64: + case Timaginary80: + case Tfloat32: + case Tfloat64: + case Tfloat80: + { + fvalue = Port::nan; + goto Lfvalue; + } + } + } + else if (ident == Id::infinity) + { + switch (ty) + { + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: + case Timaginary32: + case Timaginary64: + case Timaginary80: + case Tfloat32: + case Tfloat64: + case Tfloat80: + fvalue = Port::infinity; + goto Lfvalue; + } + } + else if (ident == Id::dig) + { + switch (ty) + { + case Tcomplex32: + case Timaginary32: + case Tfloat32: ivalue = FLT_DIG; goto Lint; + case Tcomplex64: + case Timaginary64: + case Tfloat64: ivalue = DBL_DIG; goto Lint; + case Tcomplex80: + case Timaginary80: + case Tfloat80: ivalue = LDBL_DIG; goto Lint; + } + } + else if (ident == Id::epsilon) + { + switch (ty) + { + case Tcomplex32: + case Timaginary32: + case Tfloat32: fvalue = FLT_EPSILON; goto Lfvalue; + case Tcomplex64: + case Timaginary64: + case Tfloat64: fvalue = DBL_EPSILON; goto Lfvalue; + case Tcomplex80: + case Timaginary80: + case Tfloat80: fvalue = LDBL_EPSILON; goto Lfvalue; + } + } + else if (ident == Id::mant_dig) + { + switch (ty) + { + case Tcomplex32: + case Timaginary32: + case Tfloat32: ivalue = FLT_MANT_DIG; goto Lint; + case Tcomplex64: + case Timaginary64: + case Tfloat64: ivalue = DBL_MANT_DIG; goto Lint; + case Tcomplex80: + case Timaginary80: + case Tfloat80: ivalue = LDBL_MANT_DIG; goto Lint; + } + } + else if (ident == Id::max_10_exp) + { + switch (ty) + { + case Tcomplex32: + case Timaginary32: + case Tfloat32: ivalue = FLT_MAX_10_EXP; goto Lint; + case Tcomplex64: + case Timaginary64: + case Tfloat64: ivalue = DBL_MAX_10_EXP; goto Lint; + case Tcomplex80: + case Timaginary80: + case Tfloat80: ivalue = LDBL_MAX_10_EXP; goto Lint; + } + } + else if (ident == Id::max_exp) + { + switch (ty) + { + case Tcomplex32: + case Timaginary32: + case Tfloat32: ivalue = FLT_MAX_EXP; goto Lint; + case Tcomplex64: + case Timaginary64: + case Tfloat64: ivalue = DBL_MAX_EXP; goto Lint; + case Tcomplex80: + case Timaginary80: + case Tfloat80: ivalue = LDBL_MAX_EXP; goto Lint; + } + } + else if (ident == Id::min_10_exp) + { + switch (ty) + { + case Tcomplex32: + case Timaginary32: + case Tfloat32: ivalue = FLT_MIN_10_EXP; goto Lint; + case Tcomplex64: + case Timaginary64: + case Tfloat64: ivalue = DBL_MIN_10_EXP; goto Lint; + case Tcomplex80: + case Timaginary80: + case Tfloat80: ivalue = LDBL_MIN_10_EXP; goto Lint; + } + } + else if (ident == Id::min_exp) + { + switch (ty) + { + case Tcomplex32: + case Timaginary32: + case Tfloat32: ivalue = FLT_MIN_EXP; goto Lint; + case Tcomplex64: + case Timaginary64: + case Tfloat64: ivalue = DBL_MIN_EXP; goto Lint; + case Tcomplex80: + case Timaginary80: + case Tfloat80: ivalue = LDBL_MIN_EXP; goto Lint; + } + } + + return Type::getProperty(loc, ident); + +Livalue: + e = new IntegerExp(loc, ivalue, this); + return e; + +Lfvalue: + if (isreal() || isimaginary()) + e = new RealExp(loc, fvalue, this); + else + { + complex_t cvalue; + +#if __DMC__ + //((real_t *)&cvalue)[0] = fvalue; + //((real_t *)&cvalue)[1] = fvalue; + cvalue = fvalue + fvalue * I; +#else + cvalue.re = fvalue; + cvalue.im = fvalue; +#endif + //for (int i = 0; i < 20; i++) + // printf("%02x ", ((unsigned char *)&cvalue)[i]); + //printf("\n"); + e = new ComplexExp(loc, cvalue, this); + } + return e; + +Lint: + e = new IntegerExp(loc, ivalue, Type::tint32); + return e; +} + +Expression *TypeBasic::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ +#if LOGDOTEXP + printf("TypeBasic::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); +#endif + Type *t; + + if (ident == Id::re) + { + switch (ty) + { + case Tcomplex32: t = tfloat32; goto L1; + case Tcomplex64: t = tfloat64; goto L1; + case Tcomplex80: t = tfloat80; goto L1; + L1: + e = e->castTo(sc, t); + break; + + case Tfloat32: + case Tfloat64: + case Tfloat80: + break; + + case Timaginary32: t = tfloat32; goto L2; + case Timaginary64: t = tfloat64; goto L2; + case Timaginary80: t = tfloat80; goto L2; + L2: + e = new RealExp(0, 0.0, t); + break; + + default: + e = Type::getProperty(e->loc, ident); + break; + } + } + else if (ident == Id::im) + { Type *t2; + + switch (ty) + { + case Tcomplex32: t = timaginary32; t2 = tfloat32; goto L3; + case Tcomplex64: t = timaginary64; t2 = tfloat64; goto L3; + case Tcomplex80: t = timaginary80; t2 = tfloat80; goto L3; + L3: + e = e->castTo(sc, t); + e->type = t2; + break; + + case Timaginary32: t = tfloat32; goto L4; + case Timaginary64: t = tfloat64; goto L4; + case Timaginary80: t = tfloat80; goto L4; + L4: + e = e->copy(); + e->type = t; + break; + + case Tfloat32: + case Tfloat64: + case Tfloat80: + e = new RealExp(0, 0.0, this); + break; + + default: + e = Type::getProperty(e->loc, ident); + break; + } + } + else + { + return Type::dotExp(sc, e, ident); + } + e = e->semantic(sc); + return e; +} + +Expression *TypeBasic::defaultInit(Loc loc) +{ dinteger_t value = 0; + +#if SNAN_DEFAULT_INIT + /* + * Use a payload which is different from the machine NaN, + * so that uninitialised variables can be + * detected even if exceptions are disabled. + */ + union + { unsigned short us[8]; + long double ld; + } snan = {{ 0, 0, 0, 0xA000, 0x7FFF }}; + /* + * Although long doubles are 10 bytes long, some + * C ABIs pad them out to 12 or even 16 bytes, so + * leave enough space in the snan array. + */ + assert(REALSIZE <= sizeof(snan)); + d_float80 fvalue = snan.ld; +#endif + +#if LOGDEFAULTINIT + printf("TypeBasic::defaultInit() '%s'\n", toChars()); +#endif + switch (ty) + { + case Tchar: + value = 0xFF; + break; + + case Twchar: + case Tdchar: + value = 0xFFFF; + break; + + case Timaginary32: + case Timaginary64: + case Timaginary80: + case Tfloat32: + case Tfloat64: + case Tfloat80: +#if SNAN_DEFAULT_INIT + return new RealExp(loc, fvalue, this); +#else + return getProperty(loc, Id::nan); +#endif + + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: +#if SNAN_DEFAULT_INIT + { // Can't use fvalue + I*fvalue (the im part becomes a quiet NaN). + complex_t cvalue; + ((real_t *)&cvalue)[0] = fvalue; + ((real_t *)&cvalue)[1] = fvalue; + return new ComplexExp(loc, cvalue, this); + } +#else + return getProperty(loc, Id::nan); +#endif + + case Tvoid: + error(loc, "void does not have a default initializer"); + return new ErrorExp(); + } + return new IntegerExp(loc, value, this); +} + +int TypeBasic::isZeroInit(Loc loc) +{ + switch (ty) + { + case Tchar: + case Twchar: + case Tdchar: + case Timaginary32: + case Timaginary64: + case Timaginary80: + case Tfloat32: + case Tfloat64: + case Tfloat80: + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: + return 0; // no + } + return 1; // yes +} + +int TypeBasic::isintegral() +{ + //printf("TypeBasic::isintegral('%s') x%x\n", toChars(), flags); + return flags & TFLAGSintegral; +} + +int TypeBasic::isfloating() +{ + return flags & TFLAGSfloating; +} + +int TypeBasic::isreal() +{ + return flags & TFLAGSreal; +} + +int TypeBasic::isimaginary() +{ + return flags & TFLAGSimaginary; +} + +int TypeBasic::iscomplex() +{ + return flags & TFLAGScomplex; +} + +int TypeBasic::isunsigned() +{ + return flags & TFLAGSunsigned; +} + +int TypeBasic::isscalar() +{ + return flags & (TFLAGSintegral | TFLAGSfloating); +} + +MATCH TypeBasic::implicitConvTo(Type *to) +{ + //printf("TypeBasic::implicitConvTo(%s) from %s\n", to->toChars(), toChars()); + if (this == to) + return MATCHexact; + +#if DMDV2 + if (ty == to->ty) + { + if (mod == to->mod) + return MATCHexact; + else if (MODimplicitConv(mod, to->mod)) + return MATCHconst; + else if (!((mod ^ to->mod) & MODshared)) // for wild matching + return MATCHconst; + else + return MATCHconvert; + } +#endif + + if (ty == Tvoid || to->ty == Tvoid) + return MATCHnomatch; + if (to->ty == Tbool) + return MATCHnomatch; + + TypeBasic *tob; + if (to->ty == Tvector) + { + TypeVector *tv = (TypeVector *)to; + tob = tv->elementType(); + } + else + tob = to->isTypeBasic(); + if (!tob) + return MATCHnomatch; + + if (flags & TFLAGSintegral) + { + // Disallow implicit conversion of integers to imaginary or complex + if (tob->flags & (TFLAGSimaginary | TFLAGScomplex)) + return MATCHnomatch; + +#if DMDV2 + // If converting from integral to integral + if (tob->flags & TFLAGSintegral) + { d_uns64 sz = size(0); + d_uns64 tosz = tob->size(0); + + /* Can't convert to smaller size + */ + if (sz > tosz) + return MATCHnomatch; + + /* Can't change sign if same size + */ + /*if (sz == tosz && (flags ^ tob->flags) & TFLAGSunsigned) + return MATCHnomatch;*/ + } +#endif + } + else if (flags & TFLAGSfloating) + { + // Disallow implicit conversion of floating point to integer + if (tob->flags & TFLAGSintegral) + return MATCHnomatch; + + assert(tob->flags & TFLAGSfloating); + + // Disallow implicit conversion from complex to non-complex + if (flags & TFLAGScomplex && !(tob->flags & TFLAGScomplex)) + return MATCHnomatch; + + // Disallow implicit conversion of real or imaginary to complex + if (flags & (TFLAGSreal | TFLAGSimaginary) && + tob->flags & TFLAGScomplex) + return MATCHnomatch; + + // Disallow implicit conversion to-from real and imaginary + if ((flags & (TFLAGSreal | TFLAGSimaginary)) != + (tob->flags & (TFLAGSreal | TFLAGSimaginary))) + return MATCHnomatch; + } + return MATCHconvert; +} + +TypeBasic *TypeBasic::isTypeBasic() +{ + return (TypeBasic *)this; +} + +/* ============================= TypeVector =========================== */ + +/* The basetype must be one of: + * byte[16],ubyte[16],short[8],ushort[8],int[4],uint[4],long[2],ulong[2],float[4],double[2] + */ +TypeVector::TypeVector(Loc loc, Type *basetype) + : Type(Tvector) +{ + this->basetype = basetype; +} + +Type *TypeVector::syntaxCopy() +{ + return new TypeVector(0, basetype->syntaxCopy()); +} + +Type *TypeVector::semantic(Loc loc, Scope *sc) +{ + int errors = global.errors; + basetype = basetype->semantic(loc, sc); + if (errors != global.errors) + return terror; + basetype = basetype->toBasetype()->mutableOf(); + if (basetype->ty != Tsarray || basetype->size() != 16) + { error(loc, "base type of __vector must be a 16 byte static array, not %s", basetype->toChars()); + return terror; + } + TypeSArray *t = (TypeSArray *)basetype; + TypeBasic *tb = t->nextOf()->isTypeBasic(); + if (!tb || !(tb->flags & TFLAGSvector)) + { error(loc, "base type of __vector must be a static array of an arithmetic type, not %s", t->toChars()); + return terror; + } + return merge(); +} + +TypeBasic *TypeVector::elementType() +{ + assert(basetype->ty == Tsarray); + TypeSArray *t = (TypeSArray *)basetype; + TypeBasic *tb = t->nextOf()->isTypeBasic(); + assert(tb); + return tb; +} + +int TypeVector::checkBoolean() +{ + return FALSE; +} + +char *TypeVector::toChars() +{ + return Type::toChars(); +} + +void TypeVector::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + //printf("TypeVector::toCBuffer2(mod = %d, this->mod = %d)\n", mod, this->mod); + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + buf->writestring("__vector("); + basetype->toCBuffer2(buf, hgs, this->mod); + buf->writestring(")"); +} + +void TypeVector::toDecoBuffer(OutBuffer *buf, int flag) +{ + if (flag != mod && flag != 0x100) + { + MODtoDecoBuffer(buf, mod); + } + buf->writestring("Nh"); + basetype->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod); +} + +d_uns64 TypeVector::size(Loc loc) +{ + return 16; +} + +unsigned TypeVector::alignsize() +{ + return 16; +} + +Expression *TypeVector::getProperty(Loc loc, Identifier *ident) +{ + return basetype->getProperty(loc, ident); +} + +Expression *TypeVector::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ +#if LOGDOTEXP + printf("TypeVector::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); +#endif + if (ident == Id::array) + { + e = e->castTo(sc, basetype); + return e; + } + return basetype->dotExp(sc, e->castTo(sc, basetype), ident); +} + +Expression *TypeVector::defaultInit(Loc loc) +{ + return basetype->defaultInit(loc); +} + +int TypeVector::isZeroInit(Loc loc) +{ + return basetype->isZeroInit(loc); +} + +int TypeVector::isintegral() +{ + //printf("TypeVector::isintegral('%s') x%x\n", toChars(), flags); + return basetype->nextOf()->isintegral(); +} + +int TypeVector::isfloating() +{ + return basetype->nextOf()->isfloating(); +} + +int TypeVector::isunsigned() +{ + return basetype->nextOf()->isunsigned(); +} + +int TypeVector::isscalar() +{ + return basetype->nextOf()->isscalar(); +} + +MATCH TypeVector::implicitConvTo(Type *to) +{ + //printf("TypeVector::implicitConvTo(%s) from %s\n", to->toChars(), toChars()); + if (this == to) + return MATCHexact; + if (ty == to->ty) + return MATCHconvert; + return MATCHnomatch; +} + +/***************************** TypeArray *****************************/ + +TypeArray::TypeArray(TY ty, Type *next) + : TypeNext(ty, next) +{ +} + +Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ + Type *n = this->next->toBasetype(); // uncover any typedef's + +#if LOGDOTEXP + printf("TypeArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); +#endif + + if (!n->isMutable()) + if (ident == Id::sort || ident == Id::reverse) + error(e->loc, "can only %s a mutable array\n", ident->toChars()); + + if (ident == Id::reverse && (n->ty == Tchar || n->ty == Twchar)) + { + Expression *ec; + FuncDeclaration *fd; + Expressions *arguments; + const char *nm; + static const char *name[2] = { "_adReverseChar", "_adReverseWchar" }; + + nm = name[n->ty == Twchar]; + fd = FuncDeclaration::genCfunc(Type::tindex, nm); + ec = new VarExp(0, fd); + e = e->castTo(sc, n->arrayOf()); // convert to dynamic array + arguments = new Expressions(); + arguments->push(e); + e = new CallExp(e->loc, ec, arguments); + e->type = next->arrayOf(); + } + else if (ident == Id::sort && (n->ty == Tchar || n->ty == Twchar)) + { + Expression *ec; + FuncDeclaration *fd; + Expressions *arguments; + const char *nm; + static const char *name[2] = { "_adSortChar", "_adSortWchar" }; + + nm = name[n->ty == Twchar]; + fd = FuncDeclaration::genCfunc(Type::tindex, nm); + ec = new VarExp(0, fd); + e = e->castTo(sc, n->arrayOf()); // convert to dynamic array + arguments = new Expressions(); + arguments->push(e); + e = new CallExp(e->loc, ec, arguments); + e->type = next->arrayOf(); + } + else if (ident == Id::reverse || ident == Id::dup || ident == Id::idup) + { + Expression *ec; + FuncDeclaration *fd; + Expressions *arguments; + int size = next->size(e->loc); + int dup; + + Expression *olde = e; + assert(size); + dup = (ident == Id::dup || ident == Id::idup); + fd = FuncDeclaration::genCfunc(Type::tindex, dup ? Id::adDup : Id::adReverse); + ec = new VarExp(0, fd); + e = e->castTo(sc, n->arrayOf()); // convert to dynamic array + arguments = new Expressions(); + if (dup) + arguments->push(getTypeInfo(sc)); + arguments->push(e); + if (!dup) + arguments->push(new IntegerExp(0, size, Type::tsize_t)); + e = new CallExp(e->loc, ec, arguments); + if (ident == Id::idup) + { Type *einv = next->invariantOf(); + if (next->implicitConvTo(einv) < MATCHconst) + error(e->loc, "cannot implicitly convert element type %s to immutable in %s.idup", + next->toChars(), olde->toChars()); + e->type = einv->arrayOf(); + } + else if (ident == Id::dup) + { + Type *emut = next->mutableOf(); + if (next->implicitConvTo(emut) < MATCHconst) + error(e->loc, "cannot implicitly convert element type %s to mutable in %s.dup", + next->toChars(), olde->toChars()); + e->type = emut->arrayOf(); + } + else + e->type = next->mutableOf()->arrayOf(); + } + else if (ident == Id::sort) + { + Expression *ec; + FuncDeclaration *fd; + Expressions *arguments; + + fd = FuncDeclaration::genCfunc(tint32->arrayOf(), "_adSort"); + ec = new VarExp(0, fd); + e = e->castTo(sc, n->arrayOf()); // convert to dynamic array + arguments = new Expressions(); + arguments->push(e); + arguments->push(n->ty == Tsarray + ? n->getTypeInfo(sc) // don't convert to dynamic array + : n->getInternalTypeInfo(sc)); + e = new CallExp(e->loc, ec, arguments); + e->type = next->arrayOf(); + } + else + { + e = Type::dotExp(sc, e, ident); + } + e = e->semantic(sc); + return e; +} + + +/***************************** TypeSArray *****************************/ + +TypeSArray::TypeSArray(Type *t, Expression *dim) + : TypeArray(Tsarray, t) +{ + //printf("TypeSArray(%s)\n", dim->toChars()); + this->dim = dim; +} + +Type *TypeSArray::syntaxCopy() +{ + Type *t = next->syntaxCopy(); + Expression *e = dim->syntaxCopy(); + t = new TypeSArray(t, e); + t->mod = mod; + return t; +} + +d_uns64 TypeSArray::size(Loc loc) +{ dinteger_t sz; + + if (!dim) + return Type::size(loc); + sz = dim->toInteger(); + + { dinteger_t n, n2; + + n = next->size(); + n2 = n * sz; + if (n && (n2 / n) != sz) + goto Loverflow; + sz = n2; + } + return sz; + +Loverflow: + error(loc, "index %jd overflow for static array", sz); + return 1; +} + +unsigned TypeSArray::alignsize() +{ + return next->alignsize(); +} + +/************************** + * This evaluates exp while setting length to be the number + * of elements in the tuple t. + */ +Expression *semanticLength(Scope *sc, Type *t, Expression *exp) +{ + if (t->ty == Ttuple) + { ScopeDsymbol *sym = new ArrayScopeSymbol(sc, (TypeTuple *)t); + sym->parent = sc->scopesym; + sc = sc->push(sym); + + exp = exp->semantic(sc); + + sc->pop(); + } + else + exp = exp->semantic(sc); + return exp; +} + +Expression *semanticLength(Scope *sc, TupleDeclaration *s, Expression *exp) +{ + ScopeDsymbol *sym = new ArrayScopeSymbol(sc, s); + sym->parent = sc->scopesym; + sc = sc->push(sym); + + exp = exp->semantic(sc); + + sc->pop(); + return exp; +} + +void TypeSArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps) +{ + //printf("TypeSArray::resolve() %s\n", toChars()); + next->resolve(loc, sc, pe, pt, ps); + //printf("s = %p, e = %p, t = %p\n", *ps, *pe, *pt); + if (*pe) + { // It's really an index expression + Expression *e = new IndexExp(loc, *pe, dim); + *pe = e; + } + else if (*ps) + { Dsymbol *s = *ps; + TupleDeclaration *td = s->isTupleDeclaration(); + if (td) + { + ScopeDsymbol *sym = new ArrayScopeSymbol(sc, td); + sym->parent = sc->scopesym; + sc = sc->push(sym); + + dim = dim->semantic(sc); + dim = dim->optimize(WANTvalue | WANTinterpret); + uinteger_t d = dim->toUInteger(); + + sc = sc->pop(); + + if (d >= td->objects->dim) + { error(loc, "tuple index %ju exceeds length %u", d, td->objects->dim); + goto Ldefault; + } + Object *o = td->objects->tdata()[(size_t)d]; + if (o->dyncast() == DYNCAST_DSYMBOL) + { + *ps = (Dsymbol *)o; + return; + } + if (o->dyncast() == DYNCAST_EXPRESSION) + { + *ps = NULL; + *pe = (Expression *)o; + return; + } + if (o->dyncast() == DYNCAST_TYPE) + { + *ps = NULL; + *pt = (Type *)o; + return; + } + + /* Create a new TupleDeclaration which + * is a slice [d..d+1] out of the old one. + * Do it this way because TemplateInstance::semanticTiargs() + * can handle unresolved Objects this way. + */ + Objects *objects = new Objects; + objects->setDim(1); + objects->tdata()[0] = o; + + TupleDeclaration *tds = new TupleDeclaration(loc, td->ident, objects); + *ps = tds; + } + else + goto Ldefault; + } + else + { + Ldefault: + Type::resolve(loc, sc, pe, pt, ps); + } +} + +Type *TypeSArray::semantic(Loc loc, Scope *sc) +{ + //printf("TypeSArray::semantic() %s\n", toChars()); + + Type *t; + Expression *e; + Dsymbol *s; + next->resolve(loc, sc, &e, &t, &s); + if (dim && s && s->isTupleDeclaration()) + { TupleDeclaration *sd = s->isTupleDeclaration(); + + dim = semanticLength(sc, sd, dim); + dim = dim->optimize(WANTvalue | WANTinterpret); + uinteger_t d = dim->toUInteger(); + + if (d >= sd->objects->dim) + { error(loc, "tuple index %ju exceeds %u", d, sd->objects->dim); + return Type::terror; + } + Object *o = sd->objects->tdata()[(size_t)d]; + if (o->dyncast() != DYNCAST_TYPE) + { error(loc, "%s is not a type", toChars()); + return Type::terror; + } + t = (Type *)o; + return t; + } + + Type *tn = next->semantic(loc,sc); + if (tn->ty == Terror) + return terror; + + Type *tbn = tn->toBasetype(); + + if (dim) + { dinteger_t n, n2; + + int errors = global.errors; + dim = semanticLength(sc, tbn, dim); + if (errors != global.errors) + goto Lerror; + + dim = dim->optimize(WANTvalue); + if (sc && sc->parameterSpecialization && dim->op == TOKvar && + ((VarExp *)dim)->var->storage_class & STCtemplateparameter) + { + /* It could be a template parameter N which has no value yet: + * template Foo(T : T[N], size_t N); + */ + return this; + } + dim = dim->optimize(WANTvalue | WANTinterpret); + dinteger_t d1 = dim->toInteger(); + dim = dim->implicitCastTo(sc, tsize_t); + dim = dim->optimize(WANTvalue); + dinteger_t d2 = dim->toInteger(); + + if (dim->op == TOKerror) + goto Lerror; + + if (d1 != d2) + goto Loverflow; + + if (tbn->isintegral() || + tbn->isfloating() || + tbn->ty == Tpointer || + tbn->ty == Tarray || + tbn->ty == Tsarray || + tbn->ty == Taarray || + tbn->ty == Tclass) + { + /* Only do this for types that don't need to have semantic() + * run on them for the size, since they may be forward referenced. + */ + n = tbn->size(loc); + n2 = n * d2; + if ((int)n2 < 0) + goto Loverflow; + if (n2 >= 0x1000000) // put a 'reasonable' limit on it + goto Loverflow; + if (n && n2 / n != d2) + { + Loverflow: + error(loc, "index %jd overflow for static array", d1); + goto Lerror; + } + } + } + switch (tbn->ty) + { + case Ttuple: + { // Index the tuple to get the type + assert(dim); + TypeTuple *tt = (TypeTuple *)tbn; + uinteger_t d = dim->toUInteger(); + + if (d >= tt->arguments->dim) + { error(loc, "tuple index %ju exceeds %u", d, tt->arguments->dim); + goto Lerror; + } + Parameter *arg = tt->arguments->tdata()[(size_t)d]; + return arg->type; + } + case Tstruct: + { TypeStruct *ts = (TypeStruct *)tbn; + if (0 && ts->sym->isnested) + { error(loc, "cannot have static array of inner struct %s", ts->toChars()); + goto Lerror; + } + break; + } + case Tfunction: + case Tnone: + error(loc, "can't have array of %s", tbn->toChars()); + goto Lerror; + } + if (tbn->isscope()) + { error(loc, "cannot have array of scope %s", tbn->toChars()); + goto Lerror; + } + + /* Ensure things like const(immutable(T)[3]) become immutable(T[3]) + * and const(T)[3] become const(T[3]) + */ + next = tn; + transitive(); + t = addMod(tn->mod); + + return t->merge(); + +Lerror: + return Type::terror; +} + +void TypeSArray::toDecoBuffer(OutBuffer *buf, int flag) +{ + Type::toDecoBuffer(buf, flag); + if (dim) + buf->printf("%ju", dim->toInteger()); + if (next) + /* Note that static arrays are value types, so + * for a parameter, propagate the 0x100 to the next + * level, since for T[4][3], any const should apply to the T, + * not the [4]. + */ + next->toDecoBuffer(buf, (flag & 0x100) ? flag : mod); +} + +void TypeSArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + next->toCBuffer2(buf, hgs, this->mod); + buf->printf("[%s]", dim->toChars()); +} + +Expression *TypeSArray::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ +#if LOGDOTEXP + printf("TypeSArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); +#endif + if (ident == Id::length) + { + e = dim; + } + else if (ident == Id::ptr) + { + e = e->castTo(sc, next->pointerTo()); + } + else + { + e = TypeArray::dotExp(sc, e, ident); + } + e = e->semantic(sc); + return e; +} + +int TypeSArray::isString() +{ + TY nty = next->toBasetype()->ty; + return nty == Tchar || nty == Twchar || nty == Tdchar; +} + +unsigned TypeSArray::memalign(unsigned salign) +{ + return next->memalign(salign); +} + +MATCH TypeSArray::constConv(Type *to) +{ + if (to->ty == Tsarray) + { + TypeSArray *tsa = (TypeSArray *)to; + if (!dim->equals(tsa->dim)) + return MATCHnomatch; + } + return TypeNext::constConv(to); +} + +MATCH TypeSArray::implicitConvTo(Type *to) +{ + //printf("TypeSArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars()); + + // Allow implicit conversion of static array to pointer or dynamic array + if (IMPLICIT_ARRAY_TO_PTR && to->ty == Tpointer) + { + TypePointer *tp = (TypePointer *)to; + + if (!MODimplicitConv(next->mod, tp->next->mod)) + return MATCHnomatch; + + if (tp->next->ty == Tvoid || next->constConv(tp->next) != MATCHnomatch) + { + return MATCHconvert; + } + return MATCHnomatch; + } + if (to->ty == Tarray) + { + TypeDArray *ta = (TypeDArray *)to; + + if (!MODimplicitConv(next->mod, ta->next->mod)) + return MATCHnomatch; + + /* Allow conversion to void[] + */ + if (ta->next->ty == Tvoid) + { + return MATCHconvert; + } + + MATCH m = next->constConv(ta->next); + if (m != MATCHnomatch) + { + return MATCHconvert; + } + return MATCHnomatch; + } + + if (to->ty == Tsarray) + { + if (this == to) + return MATCHexact; + + TypeSArray *tsa = (TypeSArray *)to; + + if (dim->equals(tsa->dim)) + { + /* Since static arrays are value types, allow + * conversions from const elements to non-const + * ones, just like we allow conversion from const int + * to int. + */ + MATCH m = next->implicitConvTo(tsa->next); + if (m >= MATCHconst) + { + if (mod != to->mod) + m = MATCHconst; + return m; + } + } + } + return MATCHnomatch; +} + +Expression *TypeSArray::defaultInit(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeSArray::defaultInit() '%s'\n", toChars()); +#endif + return next->defaultInit(loc); +} + +int TypeSArray::isZeroInit(Loc loc) +{ + return next->isZeroInit(loc); +} + +int TypeSArray::needsDestruction() +{ + return next->needsDestruction(); +} + +Expression *TypeSArray::defaultInitLiteral(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeSArray::defaultInitLiteral() '%s'\n", toChars()); +#endif + size_t d = dim->toInteger(); + Expression *elementinit = next->defaultInitLiteral(loc); + Expressions *elements = new Expressions(); + elements->setDim(d); + for (size_t i = 0; i < d; i++) + elements->tdata()[i] = elementinit; + ArrayLiteralExp *ae = new ArrayLiteralExp(0, elements); + ae->type = this; + return ae; +} + +Expression *TypeSArray::toExpression() +{ + Expression *e = next->toExpression(); + if (e) + { Expressions *arguments = new Expressions(); + arguments->push(dim); + e = new ArrayExp(dim->loc, e, arguments); + } + return e; +} + +int TypeSArray::hasPointers() +{ + /* Don't want to do this, because: + * struct S { T* array[0]; } + * may be a variable length struct. + */ + //if (dim->toInteger() == 0) + //return FALSE; + + if (next->ty == Tvoid) + // Arrays of void contain arbitrary data, which may include pointers + return TRUE; + else + return next->hasPointers(); +} + +/***************************** TypeDArray *****************************/ + +TypeDArray::TypeDArray(Type *t) + : TypeArray(Tarray, t) +{ + //printf("TypeDArray(t = %p)\n", t); +} + +Type *TypeDArray::syntaxCopy() +{ + Type *t = next->syntaxCopy(); + if (t == next) + t = this; + else + { t = new TypeDArray(t); + t->mod = mod; + } + return t; +} + +d_uns64 TypeDArray::size(Loc loc) +{ + //printf("TypeDArray::size()\n"); + return PTRSIZE * 2; +} + +unsigned TypeDArray::alignsize() +{ + // A DArray consists of two ptr-sized values, so align it on pointer size + // boundary + return PTRSIZE; +} + +Type *TypeDArray::semantic(Loc loc, Scope *sc) +{ Type *tn = next; + + tn = next->semantic(loc,sc); + Type *tbn = tn->toBasetype(); + switch (tbn->ty) + { + case Tfunction: + case Tnone: + case Ttuple: + error(loc, "can't have array of %s", tbn->toChars()); + case Terror: + return Type::terror; + + case Tstruct: + { TypeStruct *ts = (TypeStruct *)tbn; + if (0 && ts->sym->isnested) + error(loc, "cannot have dynamic array of inner struct %s", ts->toChars()); + break; + } + } + if (tn->isscope()) + error(loc, "cannot have array of scope %s", tn->toChars()); + + next = tn; + transitive(); + return merge(); +} + +void TypeDArray::toDecoBuffer(OutBuffer *buf, int flag) +{ + Type::toDecoBuffer(buf, flag); + if (next) + next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod); +} + +void TypeDArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + if (equals(tstring)) + buf->writestring("string"); + else + { next->toCBuffer2(buf, hgs, this->mod); + buf->writestring("[]"); + } +} + +Expression *TypeDArray::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ +#if LOGDOTEXP + printf("TypeDArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); +#endif + if (ident == Id::length) + { + if (e->op == TOKstring) + { StringExp *se = (StringExp *)e; + + return new IntegerExp(se->loc, se->len, Type::tindex); + } + if (e->op == TOKnull) + return new IntegerExp(e->loc, 0, Type::tindex); + e = new ArrayLengthExp(e->loc, e); + e->type = Type::tsize_t; + return e; + } + else if (ident == Id::ptr) + { + e = e->castTo(sc, next->pointerTo()); + return e; + } + else + { + e = TypeArray::dotExp(sc, e, ident); + } + return e; +} + +int TypeDArray::isString() +{ + TY nty = next->toBasetype()->ty; + return nty == Tchar || nty == Twchar || nty == Tdchar; +} + +MATCH TypeDArray::implicitConvTo(Type *to) +{ + //printf("TypeDArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars()); + if (equals(to)) + return MATCHexact; + + // Allow implicit conversion of array to pointer + if (IMPLICIT_ARRAY_TO_PTR && to->ty == Tpointer) + { + TypePointer *tp = (TypePointer *)to; + + /* Allow conversion to void* + */ + if (tp->next->ty == Tvoid && + MODimplicitConv(next->mod, tp->next->mod)) + { + return MATCHconvert; + } + + return next->constConv(to); + } + + if (to->ty == Tarray) + { + TypeDArray *ta = (TypeDArray *)to; + + if (!MODimplicitConv(next->mod, ta->next->mod)) + return MATCHnomatch; // not const-compatible + + // Check head inout conversion: + // T [] -> inout(const(T)[]) + // const(T)[] -> inout(const(T)[]) + if (isMutable() && ta->isWild()) + if ((next->isMutable() || next->isConst()) && ta->next->isConst()) + return MATCHnomatch; + + /* Allow conversion to void[] + */ + if (next->ty != Tvoid && ta->next->ty == Tvoid) + { + return MATCHconvert; + } + + MATCH m = next->constConv(ta->next); + if (m != MATCHnomatch) + { + if (m == MATCHexact && mod != to->mod) + m = MATCHconst; + return m; + } + } + return Type::implicitConvTo(to); +} + +Expression *TypeDArray::defaultInit(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeDArray::defaultInit() '%s'\n", toChars()); +#endif + return new NullExp(loc, this); +} + +int TypeDArray::isZeroInit(Loc loc) +{ + return 1; +} + +int TypeDArray::checkBoolean() +{ + return TRUE; +} + +int TypeDArray::hasPointers() +{ + return TRUE; +} + + +/***************************** TypeAArray *****************************/ + +TypeAArray::TypeAArray(Type *t, Type *index) + : TypeArray(Taarray, t) +{ + this->index = index; + this->impl = NULL; + this->loc = 0; + this->sc = NULL; +} + +Type *TypeAArray::syntaxCopy() +{ + Type *t = next->syntaxCopy(); + Type *ti = index->syntaxCopy(); + if (t == next && ti == index) + t = this; + else + { t = new TypeAArray(t, ti); + t->mod = mod; + } + return t; +} + +d_uns64 TypeAArray::size(Loc loc) +{ + return PTRSIZE /* * 2*/; +} + + +Type *TypeAArray::semantic(Loc loc, Scope *sc) +{ + //printf("TypeAArray::semantic() %s index->ty = %d\n", toChars(), index->ty); + if (deco) + return this; + + this->loc = loc; + this->sc = sc; + if (sc) + sc->setNoFree(); + + // Deal with the case where we thought the index was a type, but + // in reality it was an expression. + if (index->ty == Tident || index->ty == Tinstance || index->ty == Tsarray) + { + Expression *e; + Type *t; + Dsymbol *s; + + index->resolve(loc, sc, &e, &t, &s); + if (e) + { // It was an expression - + // Rewrite as a static array + TypeSArray *tsa; + + tsa = new TypeSArray(next, e); + return tsa->semantic(loc,sc); + } + else if (t) + index = t; + else + { index->error(loc, "index is not a type or an expression"); + return Type::terror; + } + } + else + index = index->semantic(loc,sc); + + if (index->nextOf() && !index->nextOf()->isImmutable()) + { + index = index->constOf()->mutableOf(); +#if 0 +printf("index is %p %s\n", index, index->toChars()); +index->check(); +printf("index->mod = x%x\n", index->mod); +printf("index->ito = x%x\n", index->ito); +if (index->ito) { +printf("index->ito->mod = x%x\n", index->ito->mod); +printf("index->ito->ito = x%x\n", index->ito->ito); +} +#endif + } + + switch (index->toBasetype()->ty) + { + case Tfunction: + case Tvoid: + case Tnone: + case Ttuple: + error(loc, "can't have associative array key of %s", index->toBasetype()->toChars()); + case Terror: + return Type::terror; + } + next = next->semantic(loc,sc); + transitive(); + + switch (next->toBasetype()->ty) + { + case Tfunction: + case Tvoid: + case Tnone: + error(loc, "can't have associative array of %s", next->toChars()); + case Terror: + return Type::terror; + } + if (next->isscope()) + { error(loc, "cannot have array of scope %s", next->toChars()); + return Type::terror; + } + return merge(); +} + +StructDeclaration *TypeAArray::getImpl() +{ + // Do it lazily + if (!impl) + { + Type *index = this->index; + Type *next = this->next; + if (index->reliesOnTident() || next->reliesOnTident()) + { + error(loc, "cannot create associative array %s", toChars()); + index = terror; + next = terror; + + // Head off future failures + StructDeclaration *s = new StructDeclaration(0, NULL); + s->type = terror; + impl = s; + return impl; + } + /* This is really a proxy for the template instance AssocArray!(index, next) + * But the instantiation can fail if it is a template specialization field + * which has Tident's instead of real types. + */ + Objects *tiargs = new Objects(); + tiargs->push(index); + tiargs->push(next); + + // Create AssociativeArray!(index, next) +#if 1 + if (! Type::associativearray) + { + ObjectNotFound(Id::AssociativeArray); + } + TemplateInstance *ti = new TemplateInstance(loc, Type::associativearray, tiargs); +#else + //Expression *e = new IdentifierExp(loc, Id::object); + Expression *e = new IdentifierExp(loc, Id::empty); + //e = new DotIdExp(loc, e, Id::object); + DotTemplateInstanceExp *dti = new DotTemplateInstanceExp(loc, + e, + Id::AssociativeArray, + tiargs); + dti->semantic(sc); + TemplateInstance *ti = dti->ti; +#endif + ti->semantic(sc); + ti->semantic2(sc); + ti->semantic3(sc); + impl = ti->toAlias()->isStructDeclaration(); +#ifdef DEBUG + if (!impl) + { Dsymbol *s = ti->toAlias(); + printf("%s %s\n", s->kind(), s->toChars()); + } +#endif + assert(impl); + } + return impl; +} + +void TypeAArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps) +{ + //printf("TypeAArray::resolve() %s\n", toChars()); + + // Deal with the case where we thought the index was a type, but + // in reality it was an expression. + if (index->ty == Tident || index->ty == Tinstance || index->ty == Tsarray) + { + Expression *e; + Type *t; + Dsymbol *s; + + index->resolve(loc, sc, &e, &t, &s); + if (e) + { // It was an expression - + // Rewrite as a static array + + TypeSArray *tsa = new TypeSArray(next, e); + return tsa->resolve(loc, sc, pe, pt, ps); + } + else if (t) + index = t; + else + index->error(loc, "index is not a type or an expression"); + } + Type::resolve(loc, sc, pe, pt, ps); +} + + +Expression *TypeAArray::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ +#if LOGDOTEXP + printf("TypeAArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); +#endif +#if 0 + if (ident == Id::length) + { + Expression *ec; + FuncDeclaration *fd; + Expressions *arguments; + + fd = FuncDeclaration::genCfunc(Type::tsize_t, Id::aaLen); + ec = new VarExp(0, fd); + arguments = new Expressions(); + arguments->push(e); + e = new CallExp(e->loc, ec, arguments); + e->type = ((TypeFunction *)fd->type)->next; + } + else + if (ident == Id::keys) + { + Expression *ec; + FuncDeclaration *fd; + Expressions *arguments; + int size = index->size(e->loc); + + assert(size); + fd = FuncDeclaration::genCfunc(Type::tindex, Id::aaKeys); + ec = new VarExp(0, fd); + arguments = new Expressions(); + arguments->push(e); + arguments->push(new IntegerExp(0, size, Type::tsize_t)); + e = new CallExp(e->loc, ec, arguments); + e->type = index->arrayOf(); + } + else if (ident == Id::values) + { + Expression *ec; + FuncDeclaration *fd; + Expressions *arguments; + + fd = FuncDeclaration::genCfunc(Type::tindex, Id::aaValues); + ec = new VarExp(0, fd); + arguments = new Expressions(); + arguments->push(e); + size_t keysize = index->size(e->loc); + keysize = (keysize + PTRSIZE - 1) & ~(PTRSIZE - 1); + arguments->push(new IntegerExp(0, keysize, Type::tsize_t)); + arguments->push(new IntegerExp(0, next->size(e->loc), Type::tsize_t)); + e = new CallExp(e->loc, ec, arguments); + e->type = next->arrayOf(); + } + else if (ident == Id::rehash) + { + Expression *ec; + FuncDeclaration *fd; + Expressions *arguments; + + fd = FuncDeclaration::genCfunc(Type::tint64, Id::aaRehash); + ec = new VarExp(0, fd); + arguments = new Expressions(); + arguments->push(e->addressOf(sc)); + arguments->push(index->getInternalTypeInfo(sc)); + e = new CallExp(e->loc, ec, arguments); + e->type = this; + } + else +#endif + if (ident != Id::__sizeof && + ident != Id::__xalignof && + ident != Id::init && + ident != Id::mangleof && + ident != Id::stringof && + ident != Id::offsetof) + { + e->type = getImpl()->type; + e = e->type->dotExp(sc, e, ident); + } + else + e = Type::dotExp(sc, e, ident); + return e; +} + +void TypeAArray::toDecoBuffer(OutBuffer *buf, int flag) +{ + Type::toDecoBuffer(buf, flag); + index->toDecoBuffer(buf); + next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod); +} + +void TypeAArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + next->toCBuffer2(buf, hgs, this->mod); + buf->writeByte('['); + index->toCBuffer2(buf, hgs, 0); + buf->writeByte(']'); +} + +Expression *TypeAArray::defaultInit(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeAArray::defaultInit() '%s'\n", toChars()); +#endif + return new NullExp(loc, this); +} + +int TypeAArray::isZeroInit(Loc loc) +{ + return TRUE; +} + +int TypeAArray::checkBoolean() +{ + return TRUE; +} + +int TypeAArray::hasPointers() +{ + return TRUE; +} + +MATCH TypeAArray::implicitConvTo(Type *to) +{ + //printf("TypeAArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars()); + if (equals(to)) + return MATCHexact; + + if (to->ty == Taarray) + { TypeAArray *ta = (TypeAArray *)to; + + if (!MODimplicitConv(next->mod, ta->next->mod)) + return MATCHnomatch; // not const-compatible + + if (!MODimplicitConv(index->mod, ta->index->mod)) + return MATCHnomatch; // not const-compatible + + // Check head inout conversion: + // V [K] -> inout(const(V)[K]) + // const(V)[K] -> inout(const(V)[K]) + if (isMutable() && ta->isWild()) + if ((next->isMutable() || next->isConst()) && ta->next->isConst()) + return MATCHnomatch; + + MATCH m = next->constConv(ta->next); + MATCH mi = index->constConv(ta->index); + if (m != MATCHnomatch && mi != MATCHnomatch) + { + if (m == MATCHexact && mod != to->mod) + m = MATCHconst; + if (mi < m) + m = mi; + return m; + } + } + else if (to->ty == Tstruct && ((TypeStruct *)to)->sym->ident == Id::AssociativeArray) + { + int errs = global.startGagging(); + Type *from = getImpl()->type; + if (global.endGagging(errs)) + { + return MATCHnomatch; + } + return from->implicitConvTo(to); + } + return Type::implicitConvTo(to); +} + +MATCH TypeAArray::constConv(Type *to) +{ + if (to->ty == Taarray) + { + TypeAArray *taa = (TypeAArray *)to; + MATCH mindex = index->constConv(taa->index); + MATCH mkey = next->constConv(taa->next); + // Pick the worst match + return mkey < mindex ? mkey : mindex; + } + return Type::constConv(to); +} + +/***************************** TypePointer *****************************/ + +TypePointer::TypePointer(Type *t) + : TypeNext(Tpointer, t) +{ +} + +Type *TypePointer::syntaxCopy() +{ + Type *t = next->syntaxCopy(); + if (t == next) + t = this; + else + { t = new TypePointer(t); + t->mod = mod; + } + return t; +} + +Type *TypePointer::semantic(Loc loc, Scope *sc) +{ + //printf("TypePointer::semantic()\n"); + if (deco) + return this; + Type *n = next->semantic(loc, sc); + switch (n->toBasetype()->ty) + { + case Ttuple: + error(loc, "can't have pointer to %s", n->toChars()); + case Terror: + return Type::terror; + } + if (n != next) + { + deco = NULL; + } + next = n; + transitive(); + return merge(); +} + + +d_uns64 TypePointer::size(Loc loc) +{ + return PTRSIZE; +} + +void TypePointer::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + //printf("TypePointer::toCBuffer2() next = %d\n", next->ty); + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + next->toCBuffer2(buf, hgs, this->mod); + if (next->ty != Tfunction) + buf->writeByte('*'); +} + +MATCH TypePointer::implicitConvTo(Type *to) +{ + //printf("TypePointer::implicitConvTo(to = %s) %s\n", to->toChars(), toChars()); + + if (equals(to)) + return MATCHexact; + if (next->ty == Tfunction) + { + if (to->ty == Tpointer) + { + TypePointer *tp = (TypePointer*)to; + if (tp->next->ty == Tfunction) + { + if (next->equals(tp->next)) + return MATCHconst; + + if (next->covariant(tp->next) == 1) + return MATCHconvert; + } + else if (tp->next->ty == Tvoid) + { + // Allow conversions to void* + return MATCHconvert; + } + } + return MATCHnomatch; + } + else if (to->ty == Tpointer) + { + TypePointer *tp = (TypePointer *)to; + assert(tp->next); + + if (!MODimplicitConv(next->mod, tp->next->mod)) + return MATCHnomatch; // not const-compatible + + // Check head inout conversion: + // T * -> inout(const(T)*) + // const(T)* -> inout(const(T)*) + if (isMutable() && tp->isWild()) + if ((next->isMutable() || next->isConst()) && tp->next->isConst()) + return MATCHnomatch; + + /* Alloc conversion to void* + */ + if (next->ty != Tvoid && tp->next->ty == Tvoid) + { + return MATCHconvert; + } + + MATCH m = next->constConv(tp->next); + if (m != MATCHnomatch) + { + if (m == MATCHexact && mod != to->mod) + m = MATCHconst; + return m; + } + } + return MATCHnomatch; +} + +MATCH TypePointer::constConv(Type *to) +{ + if (next->ty == Tfunction) + { + if (to->nextOf() && next->equals(((TypeNext*)to)->next)) + return Type::constConv(to); + else + return MATCHnomatch; + } + return TypeNext::constConv(to); +} + +int TypePointer::isscalar() +{ + return TRUE; +} + +Expression *TypePointer::defaultInit(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypePointer::defaultInit() '%s'\n", toChars()); +#endif + return new NullExp(loc, this); +} + +int TypePointer::isZeroInit(Loc loc) +{ + return 1; +} + +int TypePointer::hasPointers() +{ + return TRUE; +} + + +/***************************** TypeReference *****************************/ + +TypeReference::TypeReference(Type *t) + : TypeNext(Treference, t) +{ + // BUG: what about references to static arrays? +} + +Type *TypeReference::syntaxCopy() +{ + Type *t = next->syntaxCopy(); + if (t == next) + t = this; + else + { t = new TypeReference(t); + t->mod = mod; + } + return t; +} + +Type *TypeReference::semantic(Loc loc, Scope *sc) +{ + //printf("TypeReference::semantic()\n"); + Type *n = next->semantic(loc, sc); + if (n != next) + deco = NULL; + next = n; + transitive(); + return merge(); +} + + +d_uns64 TypeReference::size(Loc loc) +{ + return PTRSIZE; +} + +void TypeReference::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + next->toCBuffer2(buf, hgs, this->mod); + buf->writeByte('&'); +} + +Expression *TypeReference::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ +#if LOGDOTEXP + printf("TypeReference::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); +#endif + + // References just forward things along + return next->dotExp(sc, e, ident); +} + +Expression *TypeReference::defaultInit(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeReference::defaultInit() '%s'\n", toChars()); +#endif + return new NullExp(loc, this); +} + +int TypeReference::isZeroInit(Loc loc) +{ + return 1; +} + + +/***************************** TypeFunction *****************************/ + +TypeFunction::TypeFunction(Parameters *parameters, Type *treturn, int varargs, enum LINK linkage, StorageClass stc) + : TypeNext(Tfunction, treturn) +{ +//if (!treturn) *(char*)0=0; +// assert(treturn); + assert(0 <= varargs && varargs <= 2); + this->parameters = parameters; + this->varargs = varargs; + this->linkage = linkage; + this->inuse = 0; + this->isnothrow = false; + this->purity = PUREimpure; + this->isproperty = false; + this->isref = false; + this->fargs = NULL; + + if (stc & STCpure) + this->purity = PUREfwdref; + if (stc & STCnothrow) + this->isnothrow = true; + if (stc & STCproperty) + this->isproperty = true; + + if (stc & STCref) + this->isref = true; + + this->trust = TRUSTdefault; + if (stc & STCsafe) + this->trust = TRUSTsafe; + if (stc & STCsystem) + this->trust = TRUSTsystem; + if (stc & STCtrusted) + this->trust = TRUSTtrusted; +} + +Type *TypeFunction::syntaxCopy() +{ + Type *treturn = next ? next->syntaxCopy() : NULL; + Parameters *params = Parameter::arraySyntaxCopy(parameters); + TypeFunction *t = new TypeFunction(params, treturn, varargs, linkage); + t->mod = mod; + t->isnothrow = isnothrow; + t->purity = purity; + t->isproperty = isproperty; + t->isref = isref; + t->trust = trust; + t->fargs = fargs; + return t; +} + +/******************************* + * Covariant means that 'this' can substitute for 't', + * i.e. a pure function is a match for an impure type. + * Returns: + * 0 types are distinct + * 1 this is covariant with t + * 2 arguments match as far as overloading goes, + * but types are not covariant + * 3 cannot determine covariance because of forward references + */ + +int Type::covariant(Type *t) +{ +#if 0 + printf("Type::covariant(t = %s) %s\n", t->toChars(), toChars()); + printf("deco = %p, %p\n", deco, t->deco); +// printf("ty = %d\n", next->ty); + printf("mod = %x, %x\n", mod, t->mod); +#endif + + int inoutmismatch = 0; + + TypeFunction *t1; + TypeFunction *t2; + + if (equals(t)) + return 1; // covariant + + if (ty != Tfunction || t->ty != Tfunction) + goto Ldistinct; + + t1 = (TypeFunction *)this; + t2 = (TypeFunction *)t; + + if (t1->varargs != t2->varargs) + goto Ldistinct; + + if (t1->parameters && t2->parameters) + { + size_t dim = Parameter::dim(t1->parameters); + if (dim != Parameter::dim(t2->parameters)) + goto Ldistinct; + + for (size_t i = 0; i < dim; i++) + { Parameter *arg1 = Parameter::getNth(t1->parameters, i); + Parameter *arg2 = Parameter::getNth(t2->parameters, i); + + if (!arg1->type->equals(arg2->type)) + { +#if 0 // turn on this for contravariant argument types, see bugzilla 3075 + // BUG: cannot convert ref to const to ref to immutable + // We can add const, but not subtract it + if (arg2->type->implicitConvTo(arg1->type) < MATCHconst) +#endif + goto Ldistinct; + } + const StorageClass sc = STCref | STCin | STCout | STClazy; + if ((arg1->storageClass & sc) != (arg2->storageClass & sc)) + inoutmismatch = 1; + // We can add scope, but not subtract it + if (!(arg1->storageClass & STCscope) && (arg2->storageClass & STCscope)) + inoutmismatch = 1; + } + } + else if (t1->parameters != t2->parameters) + { + size_t dim1 = !t1->parameters ? 0 : t1->parameters->dim; + size_t dim2 = !t2->parameters ? 0 : t2->parameters->dim; + if (dim1 || dim2) + goto Ldistinct; + } + + // The argument lists match + if (inoutmismatch) + goto Lnotcovariant; + if (t1->linkage != t2->linkage) + goto Lnotcovariant; + + { + // Return types + Type *t1n = t1->next; + Type *t2n = t2->next; + + if (!t1n || !t2n) // happens with return type inference + goto Lnotcovariant; + + if (t1n->equals(t2n)) + goto Lcovariant; + if (t1n->ty == Tclass && t2n->ty == Tclass) + { + /* If same class type, but t2n is const, then it's + * covariant. Do this test first because it can work on + * forward references. + */ + if (((TypeClass *)t1n)->sym == ((TypeClass *)t2n)->sym && + MODimplicitConv(t1n->mod, t2n->mod)) + goto Lcovariant; + + // If t1n is forward referenced: + ClassDeclaration *cd = ((TypeClass *)t1n)->sym; +// if (cd->scope) +// cd->semantic(NULL); +#if 0 + if (!cd->baseClass && cd->baseclasses->dim && !cd->isInterfaceDeclaration()) +#else + if (!cd->isBaseInfoComplete()) +#endif + { + return 3; // forward references + } + } + if (t1n->ty == Tstruct && t2n->ty == Tstruct) + { + if (((TypeStruct *)t1n)->sym == ((TypeStruct *)t2n)->sym && + MODimplicitConv(t1n->mod, t2n->mod)) + goto Lcovariant; + } + else if (t1n->ty == t2n->ty && t1n->implicitConvTo(t2n)) + goto Lcovariant; + } + goto Lnotcovariant; + +Lcovariant: + /* Can convert mutable to const + */ + if (!MODimplicitConv(t2->mod, t1->mod)) + goto Lnotcovariant; +#if 0 + if (t1->mod != t2->mod) + { + if (!(t1->mod & MODconst) && (t2->mod & MODconst)) + goto Lnotcovariant; + if (!(t1->mod & MODshared) && (t2->mod & MODshared)) + goto Lnotcovariant; + } +#endif + + /* Can convert pure to impure, and nothrow to throw + */ + if (!t1->purity && t2->purity) + goto Lnotcovariant; + + if (!t1->isnothrow && t2->isnothrow) + goto Lnotcovariant; + + if (t1->isref != t2->isref) + goto Lnotcovariant; + + /* Can convert safe/trusted to system + */ + if (t1->trust <= TRUSTsystem && t2->trust >= TRUSTtrusted) + goto Lnotcovariant; + + //printf("\tcovaraint: 1\n"); + return 1; + +Ldistinct: + //printf("\tcovaraint: 0\n"); + return 0; + +Lnotcovariant: + //printf("\tcovaraint: 2\n"); + return 2; +} + +void TypeFunction::toDecoBuffer(OutBuffer *buf, int flag) +{ unsigned char mc; + + //printf("TypeFunction::toDecoBuffer() this = %p %s\n", this, toChars()); + //static int nest; if (++nest == 50) *(char*)0=0; + if (inuse) + { inuse = 2; // flag error to caller + return; + } + inuse++; + MODtoDecoBuffer(buf, mod); + switch (linkage) + { + case LINKd: mc = 'F'; break; + case LINKc: mc = 'U'; break; + case LINKwindows: mc = 'W'; break; + case LINKpascal: mc = 'V'; break; + case LINKcpp: mc = 'R'; break; + default: + assert(0); + } + buf->writeByte(mc); + if (purity || isnothrow || isproperty || isref || trust) + { + if (purity) + buf->writestring("Na"); + if (isnothrow) + buf->writestring("Nb"); + if (isref) + buf->writestring("Nc"); + if (isproperty) + buf->writestring("Nd"); + switch (trust) + { + case TRUSTtrusted: + buf->writestring("Ne"); + break; + case TRUSTsafe: + buf->writestring("Nf"); + break; + } + } + // Write argument types + Parameter::argsToDecoBuffer(buf, parameters); + //if (buf->data[buf->offset - 1] == '@') halt(); + buf->writeByte('Z' - varargs); // mark end of arg list + assert(next); + next->toDecoBuffer(buf); + inuse--; +} + +void TypeFunction::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs) +{ + toCBufferWithAttributes(buf, ident, hgs, this, NULL); +} + +void TypeFunction::toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TypeFunction *attrs, TemplateDeclaration *td) +{ + //printf("TypeFunction::toCBuffer() this = %p\n", this); + if (inuse) + { inuse = 2; // flag error to caller + return; + } + inuse++; + + /* Use 'storage class' style for attributes + */ + if (attrs->mod) + { + MODtoBuffer(buf, attrs->mod); + buf->writeByte(' '); + } + + if (attrs->purity) + buf->writestring("pure "); + if (attrs->isnothrow) + buf->writestring("nothrow "); + if (attrs->isproperty) + buf->writestring("@property "); + if (attrs->isref) + buf->writestring("ref "); + + switch (attrs->trust) + { + case TRUSTsystem: + buf->writestring("@system "); + break; + + case TRUSTtrusted: + buf->writestring("@trusted "); + break; + + case TRUSTsafe: + buf->writestring("@safe "); + break; + } + + if (hgs->ddoc != 1) + { + const char *p = NULL; + switch (attrs->linkage) + { + case LINKd: p = NULL; break; + case LINKc: p = "C"; break; + case LINKwindows: p = "Windows"; break; + case LINKpascal: p = "Pascal"; break; + case LINKcpp: p = "C++"; break; + default: + assert(0); + } + if (!hgs->hdrgen && p) + { + buf->writestring("extern ("); + buf->writestring(p); + buf->writestring(") "); + } + } + + if (!ident || ident->toHChars2() == ident->toChars()) + { if (next) + next->toCBuffer2(buf, hgs, 0); + else if (hgs->ddoc) + buf->writestring("auto"); + } + + if (ident) + { + if (next || hgs->ddoc) + buf->writeByte(' '); + buf->writestring(ident->toHChars2()); + } + + if (td) + { buf->writeByte('('); + for (size_t i = 0; i < td->origParameters->dim; i++) + { + TemplateParameter *tp = td->origParameters->tdata()[i]; + if (i) + buf->writestring(", "); + tp->toCBuffer(buf, hgs); + } + buf->writeByte(')'); + } + Parameter::argsToCBuffer(buf, hgs, parameters, varargs); + inuse--; +} + +void TypeFunction::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + //printf("TypeFunction::toCBuffer2() this = %p, ref = %d\n", this, isref); + if (inuse) + { inuse = 2; // flag error to caller + return; + } + inuse++; + if (hgs->ddoc != 1) + { + const char *p = NULL; + switch (linkage) + { + case LINKd: p = NULL; break; + case LINKc: p = "C"; break; + case LINKwindows: p = "Windows"; break; + case LINKpascal: p = "Pascal"; break; + case LINKcpp: p = "C++"; break; + default: + assert(0); + } + if (!hgs->hdrgen && p) + { + buf->writestring("extern ("); + buf->writestring(p); + buf->writestring(") "); + } + } + if (next) + { + next->toCBuffer2(buf, hgs, 0); + buf->writeByte(' '); + } + buf->writestring("function"); + Parameter::argsToCBuffer(buf, hgs, parameters, varargs); + attributesToCBuffer(buf, mod); + inuse--; +} + +void TypeFunction::attributesToCBuffer(OutBuffer *buf, int mod) +{ + /* Use postfix style for attributes + */ + if (mod != this->mod) + { + modToBuffer(buf); + } + if (purity) + buf->writestring(" pure"); + if (isnothrow) + buf->writestring(" nothrow"); + if (isproperty) + buf->writestring(" @property"); + if (isref) + buf->writestring(" ref"); + + switch (trust) + { + case TRUSTsystem: + buf->writestring(" @system"); + break; + + case TRUSTtrusted: + buf->writestring(" @trusted"); + break; + + case TRUSTsafe: + buf->writestring(" @safe"); + break; + } +} + +Type *TypeFunction::semantic(Loc loc, Scope *sc) +{ + if (deco) // if semantic() already run + { + //printf("already done\n"); + return this; + } + //printf("TypeFunction::semantic() this = %p\n", this); + //printf("TypeFunction::semantic() %s, sc->stc = %llx, fargs = %p\n", toChars(), sc->stc, fargs); + + /* Copy in order to not mess up original. + * This can produce redundant copies if inferring return type, + * as semantic() will get called again on this. + */ + TypeFunction *tf = (TypeFunction *)mem.malloc(sizeof(TypeFunction)); + memcpy(tf, this, sizeof(TypeFunction)); + if (parameters) + { tf->parameters = (Parameters *)parameters->copy(); + for (size_t i = 0; i < parameters->dim; i++) + { Parameter *arg = parameters->tdata()[i]; + Parameter *cpy = (Parameter *)mem.malloc(sizeof(Parameter)); + memcpy(cpy, arg, sizeof(Parameter)); + tf->parameters->tdata()[i] = cpy; + } + } + + if (sc->stc & STCpure) + tf->purity = PUREfwdref; + if (sc->stc & STCnothrow) + tf->isnothrow = TRUE; + if (sc->stc & STCref) + tf->isref = TRUE; + if (sc->stc & STCsafe) + tf->trust = TRUSTsafe; + if (sc->stc & STCtrusted) + tf->trust = TRUSTtrusted; + if (sc->stc & STCproperty) + tf->isproperty = TRUE; + + tf->linkage = sc->linkage; + + /* If the parent is @safe, then this function defaults to safe + * too. + */ + if (tf->trust == TRUSTdefault) + for (Dsymbol *p = sc->func; p; p = p->toParent2()) + { FuncDeclaration *fd = p->isFuncDeclaration(); + if (fd) + { + if (fd->isSafe()) + tf->trust = TRUSTsafe; // default to @safe + break; + } + } + + bool wildreturn = FALSE; + if (tf->next) + { + sc = sc->push(); + sc->stc &= ~(STC_TYPECTOR | STC_FUNCATTR); + tf->next = tf->next->semantic(loc,sc); + sc = sc->pop(); +#if !SARRAYVALUE + if (tf->next->toBasetype()->ty == Tsarray) + { error(loc, "functions cannot return static array %s", tf->next->toChars()); + tf->next = Type::terror; + } +#endif + if (tf->next->toBasetype()->ty == Tfunction) + { error(loc, "functions cannot return a function"); + tf->next = Type::terror; + } + if (tf->next->toBasetype()->ty == Ttuple) + { error(loc, "functions cannot return a tuple"); + tf->next = Type::terror; + } + if (tf->next->isscope() && !(sc->flags & SCOPEctor)) + error(loc, "functions cannot return scope %s", tf->next->toChars()); + if (tf->next->toBasetype()->ty == Tvoid) + tf->isref = FALSE; // rewrite "ref void" as just "void" + if (tf->next->hasWild() && + !(tf->next->ty == Tpointer && tf->next->nextOf()->ty == Tfunction || tf->next->ty == Tdelegate)) + wildreturn = TRUE; + } + + bool wildparams = FALSE; + bool wildsubparams = FALSE; + if (tf->parameters) + { + /* Create a scope for evaluating the default arguments for the parameters + */ + Scope *argsc = sc->push(); + argsc->stc = 0; // don't inherit storage class + argsc->protection = PROTpublic; + argsc->func = NULL; + + size_t dim = Parameter::dim(tf->parameters); + for (size_t i = 0; i < dim; i++) + { Parameter *fparam = Parameter::getNth(tf->parameters, i); + + tf->inuse++; + fparam->type = fparam->type->semantic(loc, argsc); + if (tf->inuse == 1) tf->inuse--; + + fparam->type = fparam->type->addStorageClass(fparam->storageClass); + + if (fparam->storageClass & (STCauto | STCalias | STCstatic)) + { + if (!fparam->type) + continue; + } + + Type *t = fparam->type->toBasetype(); + + if (fparam->storageClass & (STCout | STCref | STClazy)) + { + //if (t->ty == Tsarray) + //error(loc, "cannot have out or ref parameter of type %s", t->toChars()); + if (fparam->storageClass & STCout && fparam->type->mod & (STCconst | STCimmutable)) + error(loc, "cannot have const or immutable out parameter of type %s", t->toChars()); + } + if (!(fparam->storageClass & STClazy) && t->ty == Tvoid) + error(loc, "cannot have parameter of type %s", fparam->type->toChars()); + + if (t->hasWild() && + !(t->ty == Tpointer && t->nextOf()->ty == Tfunction || t->ty == Tdelegate)) + { + wildparams = TRUE; + if (tf->next && !wildreturn) + error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with 'ref')"); + } + else if (!wildsubparams && t->hasWild()) + wildsubparams = TRUE; + + if (fparam->defaultArg) + { + fparam->defaultArg = fparam->defaultArg->semantic(argsc); + fparam->defaultArg = resolveProperties(argsc, fparam->defaultArg); + fparam->defaultArg = fparam->defaultArg->implicitCastTo(argsc, fparam->type); + } + + /* If fparam after semantic() turns out to be a tuple, the number of parameters may + * change. + */ + if (t->ty == Ttuple) + { + TypeTuple *tt = (TypeTuple *)t; + if (fparam->storageClass && tt->arguments && tt->arguments->dim) + { + /* Propagate additional storage class from tuple parameters to their + * element-parameters. + * Make a copy, as original may be referenced elsewhere. + */ + size_t tdim = tt->arguments->dim; + Parameters *newparams = new Parameters(); + newparams->setDim(tdim); + for (size_t j = 0; j < tdim; j++) + { Parameter *narg = (*tt->arguments)[j]; + newparams->tdata()[j] = new Parameter(narg->storageClass | fparam->storageClass, + narg->type, narg->ident, narg->defaultArg); + } + fparam->type = new TypeTuple(newparams); + } + fparam->storageClass = 0; + + /* Reset number of parameters, and back up one to do this fparam again, + * now that it is a tuple + */ + dim = Parameter::dim(tf->parameters); + i--; + continue; + } + + /* Resolve "auto ref" storage class to be either ref or value, + * based on the argument matching the parameter + */ + if (fparam->storageClass & STCauto) + { + if (fargs && i < fargs->dim) + { Expression *farg = fargs->tdata()[i]; + if (farg->isLvalue()) + ; // ref parameter + else + fparam->storageClass &= ~STCref; // value parameter + } + else + error(loc, "auto can only be used for template function parameters"); + } + + // Remove redundant storage classes for type, they are already applied + fparam->storageClass &= ~(STC_TYPECTOR | STCin); + } + argsc->pop(); + } + if (tf->isWild()) + wildparams = TRUE; + + if (wildreturn && !wildparams) + error(loc, "inout on return means inout must be on a parameter as well for %s", toChars()); + if (wildsubparams && wildparams) + error(loc, "inout must be all or none on top level for %s", toChars()); + + if (tf->next) + tf->deco = tf->merge()->deco; + + if (tf->inuse) + { error(loc, "recursive type"); + tf->inuse = 0; + return terror; + } + + if (tf->isproperty && (tf->varargs || Parameter::dim(tf->parameters) > 1)) + error(loc, "properties can only have zero or one parameter"); + + if (tf->varargs == 1 && tf->linkage != LINKd && Parameter::dim(tf->parameters) == 0) + error(loc, "variadic functions with non-D linkage must have at least one parameter"); + + /* Don't return merge(), because arg identifiers and default args + * can be different + * even though the types match + */ + return tf; +} + + +/******************************************** + * Do this lazily, as the parameter types might be forward referenced. + */ +void TypeFunction::purityLevel() +{ + TypeFunction *tf = this; + if (tf->purity == PUREfwdref) + { /* Evaluate what kind of purity based on the modifiers for the parameters + */ + tf->purity = PUREstrong; // assume strong until something weakens it + if (tf->parameters) + { + size_t dim = Parameter::dim(tf->parameters); + for (size_t i = 0; i < dim; i++) + { Parameter *fparam = Parameter::getNth(tf->parameters, i); + if (fparam->storageClass & STClazy) + { + tf->purity = PUREweak; + break; + } + if (fparam->storageClass & STCout) + { + tf->purity = PUREweak; + break; + } + if (!fparam->type) + continue; + if (fparam->storageClass & STCref) + { + if (!(fparam->type->mod & (MODconst | MODimmutable | MODwild))) + { tf->purity = PUREweak; + break; + } + if (fparam->type->mod & MODconst) + { tf->purity = PUREconst; + continue; + } + } + Type *t = fparam->type->toBasetype(); + if (!t->hasPointers()) + continue; + if (t->mod & (MODimmutable | MODwild)) + continue; + /* The rest of this is too strict; fix later. + * For example, the only pointer members of a struct may be immutable, + * which would maintain strong purity. + */ + if (t->mod & MODconst) + { tf->purity = PUREconst; + continue; + } + Type *tn = t->nextOf(); + if (tn) + { tn = tn->toBasetype(); + if (tn->ty == Tpointer || tn->ty == Tarray) + { /* Accept immutable(T)* and immutable(T)[] as being strongly pure + */ + if (tn->mod & (MODimmutable | MODwild)) + continue; + if (tn->mod & MODconst) + { tf->purity = PUREconst; + continue; + } + } + } + /* Should catch delegates and function pointers, and fold in their purity + */ + tf->purity = PUREweak; // err on the side of too strict + break; + } + } + } +} + + +/******************************** + * 'args' are being matched to function 'this' + * Determine match level. + * Input: + * flag 1 performing a partial ordering match + * Returns: + * MATCHxxxx + */ + +int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag) +{ + //printf("TypeFunction::callMatch() %s\n", toChars()); + MATCH match = MATCHexact; // assume exact match + unsigned wildmatch = 0; + + if (ethis) + { Type *t = ethis->type; + if (t->toBasetype()->ty == Tpointer) + t = t->toBasetype()->nextOf(); // change struct* to struct + if (t->mod != mod) + { + if (MODimplicitConv(t->mod, mod)) + match = MATCHconst; + else if ((mod & MODwild) + && MODimplicitConv(t->mod, (mod & ~MODwild) | MODconst)) + { + match = MATCHconst; + } + else + return MATCHnomatch; + } + if (isWild()) + { + if (t->isWild()) + wildmatch |= MODwild; + else if (t->isConst()) + wildmatch |= MODconst; + else if (t->isImmutable()) + wildmatch |= MODimmutable; + else + wildmatch |= MODmutable; + } + } + + size_t nparams = Parameter::dim(parameters); + size_t nargs = args ? args->dim : 0; + if (nparams == nargs) + ; + else if (nargs > nparams) + { + if (varargs == 0) + goto Nomatch; // too many args; no match + match = MATCHconvert; // match ... with a "conversion" match level + } + + for (size_t u = 0; u < nargs; u++) + { + if (u >= nparams) + break; + Parameter *p = Parameter::getNth(parameters, u); + Expression *arg = args->tdata()[u]; + assert(arg); + + if (!(p->storageClass & STClazy && p->type->ty == Tvoid && arg->type->ty != Tvoid)) + { + unsigned mod = arg->type->wildConvTo(p->type); + if (mod) + { + wildmatch |= mod; + } + } + } + if (wildmatch) + { /* Calculate wild matching modifier + */ + if (wildmatch & MODconst || wildmatch & (wildmatch - 1)) + wildmatch = MODconst; + else if (wildmatch & MODimmutable) + wildmatch = MODimmutable; + else if (wildmatch & MODwild) + wildmatch = MODwild; + else + { assert(wildmatch & MODmutable); + wildmatch = MODmutable; + } + } + + for (size_t u = 0; u < nparams; u++) + { MATCH m; + + // BUG: what about out and ref? + + Parameter *p = Parameter::getNth(parameters, u); + assert(p); + if (u >= nargs) + { + if (p->defaultArg) + continue; + goto L1; // try typesafe variadics + } + { + Expression *arg = args->tdata()[u]; + assert(arg); + + if (arg->op == TOKfunction) + { FuncExp *fe = (FuncExp *)arg; + Type *pt = p->type; + arg = ((FuncExp *)arg)->inferType(NULL, pt); + if (!arg) + goto L1; // try typesafe variadics + } + + //printf("arg: %s, type: %s\n", arg->toChars(), arg->type->toChars()); + + Type *targ = arg->type; + Type *tprm = wildmatch ? p->type->substWildTo(wildmatch) : p->type; + + // Non-lvalues do not match ref or out parameters + if (p->storageClass & STCref) + { if (!arg->isLvalue()) + { if (arg->op == TOKstring && tprm->ty == Tsarray) + { if (targ->ty != Tsarray) + targ = new TypeSArray(targ->nextOf(), + new IntegerExp(0, ((StringExp *)arg)->len, + Type::tindex)); + } + else + goto Nomatch; + } + + /* Don't allow static arrays to be passed to mutable references + * to static arrays if the argument cannot be modified. + */ + Type *targb = targ->toBasetype(); + Type *tprmb = tprm->toBasetype(); + //printf("%s\n", targb->toChars()); + //printf("%s\n", tprmb->toChars()); + if (targb->nextOf() && tprmb->ty == Tsarray && + !MODimplicitConv(targb->nextOf()->mod, tprmb->nextOf()->mod)) + goto Nomatch; + + // ref variable behaves like head-const reference + if (!targb->constConv(tprmb)) + goto Nomatch; + } + else if (p->storageClass & STCout) + { if (!arg->isLvalue()) + goto Nomatch; + } + + if (p->storageClass & STClazy && tprm->ty == Tvoid && targ->ty != Tvoid) + m = MATCHconvert; + else + { + //printf("%s of type %s implicitConvTo %s\n", arg->toChars(), targ->toChars(), tprm->toChars()); + if (flag) + // for partial ordering, value is an irrelevant mockup, just look at the type + m = targ->implicitConvTo(tprm); + else + m = arg->implicitConvTo(tprm); + //printf("match %d\n", m); + } + } + + /* prefer matching the element type rather than the array + * type when more arguments are present with T[]... + */ + if (varargs == 2 && u + 1 == nparams && nargs > nparams) + goto L1; + + //printf("\tm = %d\n", m); + if (m == MATCHnomatch) // if no match + { + L1: + if (varargs == 2 && u + 1 == nparams) // if last varargs param + { Type *tb = p->type->toBasetype(); + TypeSArray *tsa; + dinteger_t sz; + + switch (tb->ty) + { + case Tsarray: + tsa = (TypeSArray *)tb; + sz = tsa->dim->toInteger(); + if (sz != nargs - u) + goto Nomatch; + case Tarray: + { TypeArray *ta = (TypeArray *)tb; + for (; u < nargs; u++) + { + Expression *arg = args->tdata()[u]; + assert(arg); +#if 1 + if (arg->op == TOKfunction) + { FuncExp *fe = (FuncExp *)arg; + Type *pt = tb->nextOf(); + arg = ((FuncExp *)arg)->inferType(NULL, pt); + if (!arg) + goto Nomatch; + } + + /* If lazy array of delegates, + * convert arg(s) to delegate(s) + */ + Type *tret = p->isLazyArray(); + if (tret) + { + if (ta->next->equals(arg->type)) + { m = MATCHexact; + } + else + { + m = arg->implicitConvTo(tret); + if (m == MATCHnomatch) + { + if (tret->toBasetype()->ty == Tvoid) + m = MATCHconvert; + } + } + } + else + m = arg->implicitConvTo(ta->next); +#else + m = arg->implicitConvTo(ta->next); +#endif + if (m == MATCHnomatch) + goto Nomatch; + if (m < match) + match = m; + } + goto Ldone; + } + case Tclass: + // Should see if there's a constructor match? + // Or just leave it ambiguous? + goto Ldone; + + default: + goto Nomatch; + } + } + goto Nomatch; + } + if (m < match) + match = m; // pick worst match + } + +Ldone: + //printf("match = %d\n", match); + return match; + +Nomatch: + //printf("no match\n"); + return MATCHnomatch; +} + +Type *TypeFunction::reliesOnTident() +{ + size_t dim = Parameter::dim(parameters); + for (size_t i = 0; i < dim; i++) + { Parameter *fparam = Parameter::getNth(parameters, i); + Type *t = fparam->type->reliesOnTident(); + if (t) + return t; + } + return next ? next->reliesOnTident() : NULL; +} + +/******************************************** + * Return TRUE if there are lazy parameters. + */ +bool TypeFunction::hasLazyParameters() +{ + size_t dim = Parameter::dim(parameters); + for (size_t i = 0; i < dim; i++) + { Parameter *fparam = Parameter::getNth(parameters, i); + if (fparam->storageClass & STClazy) + return TRUE; + } + return FALSE; +} + +/*************************** + * Examine function signature for parameter p and see if + * p can 'escape' the scope of the function. + */ + +bool TypeFunction::parameterEscapes(Parameter *p) +{ + + /* Scope parameters do not escape. + * Allow 'lazy' to imply 'scope' - + * lazy parameters can be passed along + * as lazy parameters to the next function, but that isn't + * escaping. + */ + if (p->storageClass & (STCscope | STClazy)) + return FALSE; + + if (purity) + { /* With pure functions, we need only be concerned if p escapes + * via any return statement. + */ + Type* tret = nextOf()->toBasetype(); + if (!isref && !tret->hasPointers()) + { /* The result has no references, so p could not be escaping + * that way. + */ + return FALSE; + } + } + + /* Assume it escapes in the absence of better information. + */ + return TRUE; +} + +Expression *TypeFunction::defaultInit(Loc loc) +{ + error(loc, "function does not have a default initializer"); + return new ErrorExp(); +} + +/***************************** TypeDelegate *****************************/ + +TypeDelegate::TypeDelegate(Type *t) + : TypeNext(Tfunction, t) +{ + ty = Tdelegate; +} + +Type *TypeDelegate::syntaxCopy() +{ + Type *t = next->syntaxCopy(); + if (t == next) + t = this; + else + { t = new TypeDelegate(t); + t->mod = mod; + } + return t; +} + +Type *TypeDelegate::semantic(Loc loc, Scope *sc) +{ + if (deco) // if semantic() already run + { + //printf("already done\n"); + return this; + } + next = next->semantic(loc,sc); + /* In order to deal with Bugzilla 4028, perhaps default arguments should + * be removed from next before the merge. + */ + + /* Don't return merge(), because arg identifiers and default args + * can be different + * even though the types match + */ + //deco = merge()->deco; + //return this; + return merge(); +} + +d_uns64 TypeDelegate::size(Loc loc) +{ + return PTRSIZE * 2; +} + +unsigned TypeDelegate::alignsize() +{ +#if DMDV1 + // See Bugzilla 942 for discussion + if (!global.params.is64bit) + return PTRSIZE * 2; +#endif + return PTRSIZE; +} + +MATCH TypeDelegate::implicitConvTo(Type *to) +{ + //printf("TypeDelegate::implicitConvTo(this=%p, to=%p)\n", this, to); + //printf("from: %s\n", toChars()); + //printf("to : %s\n", to->toChars()); + if (this == to) + return MATCHexact; +#if 1 // not allowing covariant conversions because it interferes with overriding + if (to->ty == Tdelegate && this->nextOf()->covariant(to->nextOf()) == 1) + return MATCHconvert; +#endif + return MATCHnomatch; +} + +void TypeDelegate::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + TypeFunction *tf = (TypeFunction *)next; + + tf->next->toCBuffer2(buf, hgs, 0); + buf->writestring(" delegate"); + Parameter::argsToCBuffer(buf, hgs, tf->parameters, tf->varargs); + tf->attributesToCBuffer(buf, mod); +} + +Expression *TypeDelegate::defaultInit(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeDelegate::defaultInit() '%s'\n", toChars()); +#endif + return new NullExp(loc, this); +} + +int TypeDelegate::isZeroInit(Loc loc) +{ + return 1; +} + +int TypeDelegate::checkBoolean() +{ + return TRUE; +} + +Expression *TypeDelegate::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ +#if LOGDOTEXP + printf("TypeDelegate::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); +#endif + if (ident == Id::ptr) + { + e->type = tvoidptr; + return e; + } + else if (ident == Id::funcptr) + { + e = e->addressOf(sc); + e->type = tvoidptr; + e = new AddExp(e->loc, e, new IntegerExp(PTRSIZE)); + e->type = tvoidptr; + e = new PtrExp(e->loc, e); + e->type = next->pointerTo(); + return e; + } + else + { + e = Type::dotExp(sc, e, ident); + } + return e; +} + +int TypeDelegate::hasPointers() +{ + return TRUE; +} + + + +/***************************** TypeQualified *****************************/ + +TypeQualified::TypeQualified(TY ty, Loc loc) + : Type(ty) +{ + this->loc = loc; +} + +void TypeQualified::syntaxCopyHelper(TypeQualified *t) +{ + //printf("TypeQualified::syntaxCopyHelper(%s) %s\n", t->toChars(), toChars()); + idents.setDim(t->idents.dim); + for (size_t i = 0; i < idents.dim; i++) + { + Identifier *id = t->idents.tdata()[i]; + if (id->dyncast() == DYNCAST_DSYMBOL) + { + TemplateInstance *ti = (TemplateInstance *)id; + + ti = (TemplateInstance *)ti->syntaxCopy(NULL); + id = (Identifier *)ti; + } + idents.tdata()[i] = id; + } +} + + +void TypeQualified::addIdent(Identifier *ident) +{ + idents.push(ident); +} + +void TypeQualified::toCBuffer2Helper(OutBuffer *buf, HdrGenState *hgs) +{ + for (size_t i = 0; i < idents.dim; i++) + { Identifier *id = idents.tdata()[i]; + + buf->writeByte('.'); + + if (id->dyncast() == DYNCAST_DSYMBOL) + { + TemplateInstance *ti = (TemplateInstance *)id; + ti->toCBuffer(buf, hgs); + } + else + buf->writestring(id->toChars()); + } +} + +d_uns64 TypeQualified::size(Loc loc) +{ + error(this->loc, "size of type %s is not known", toChars()); + return 1; +} + +/************************************* + * Takes an array of Identifiers and figures out if + * it represents a Type or an Expression. + * Output: + * if expression, *pe is set + * if type, *pt is set + */ + +void TypeQualified::resolveHelper(Loc loc, Scope *sc, + Dsymbol *s, Dsymbol *scopesym, + Expression **pe, Type **pt, Dsymbol **ps) +{ + VarDeclaration *v; + EnumMember *em; + Expression *e; + +#if 0 + printf("TypeQualified::resolveHelper(sc = %p, idents = '%s')\n", sc, toChars()); + if (scopesym) + printf("\tscopesym = '%s'\n", scopesym->toChars()); +#endif + *pe = NULL; + *pt = NULL; + *ps = NULL; + if (s) + { + //printf("\t1: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind()); + s->checkDeprecated(loc, sc); // check for deprecated aliases + s = s->toAlias(); + //printf("\t2: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind()); + for (size_t i = 0; i < idents.dim; i++) + { + Identifier *id = idents.tdata()[i]; + Dsymbol *sm = s->searchX(loc, sc, id); + //printf("\t3: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind()); + //printf("\tgetType = '%s'\n", s->getType()->toChars()); + if (!sm) + { Type *t; + + v = s->isVarDeclaration(); + if (v && id == Id::length) + { + e = v->getConstInitializer(); + if (!e) + e = new VarExp(loc, v); + t = e->type; + if (!t) + goto Lerror; + goto L3; + } + else if (v && (id == Id::stringof || id == Id::offsetof)) + { + e = new DsymbolExp(loc, s, 0); + do + { + id = idents.tdata()[i]; + e = new DotIdExp(loc, e, id); + } while (++i < idents.dim); + e = e->semantic(sc); + *pe = e; + return; + } + + t = s->getType(); + if (!t && s->isDeclaration()) + { t = s->isDeclaration()->type; + if (!t && s->isTupleDeclaration()) + { + e = new TupleExp(loc, s->isTupleDeclaration()); + e = e->semantic(sc); + t = e->type; + } + } + if (t) + { + sm = t->toDsymbol(sc); + if (sm) + { sm = sm->search(loc, id, 0); + if (sm) + goto L2; + } + //e = t->getProperty(loc, id); + e = new TypeExp(loc, t); + e = t->dotExp(sc, e, id); + i++; + L3: + for (; i < idents.dim; i++) + { + id = idents.tdata()[i]; + //printf("e: '%s', id: '%s', type = %s\n", e->toChars(), id->toChars(), e->type->toChars()); + e = new DotIdExp(e->loc, e, id); + e = e->semantic(sc); + } + if (e->op == TOKtype) + *pt = e->type; + else + *pe = e; + } + else + { + Lerror: + error(loc, "identifier '%s' of '%s' is not defined", id->toChars(), toChars()); + *pe = new ErrorExp(); + } + return; + } + L2: + s = sm->toAlias(); + } + + v = s->isVarDeclaration(); + if (v) + { + *pe = new VarExp(loc, v); + return; + } +#if 0 + fd = s->isFuncDeclaration(); + if (fd) + { + *pe = new DsymbolExp(loc, fd, 1); + return; + } +#endif + em = s->isEnumMember(); + if (em) + { + // It's not a type, it's an expression + *pe = em->value->copy(); + return; + } + +L1: + Type *t = s->getType(); + if (!t) + { + // If the symbol is an import, try looking inside the import + Import *si; + + si = s->isImport(); + if (si) + { + s = si->search(loc, s->ident, 0); + if (s && s != si) + goto L1; + s = si; + } + *ps = s; + return; + } + if (t->ty == Tinstance && t != this && !t->deco) + { error(loc, "forward reference to '%s'", t->toChars()); + return; + } + + if (t != this) + { + if (t->reliesOnTident()) + { + if (s->scope) + t = t->semantic(loc, s->scope); + else + { + /* Attempt to find correct scope in which to evaluate t. + * Not sure if this is right or not, or if we should just + * give forward reference error if s->scope is not set. + */ + for (Scope *scx = sc; 1; scx = scx->enclosing) + { + if (!scx) + { error(loc, "forward reference to '%s'", t->toChars()); + return; + } + if (scx->scopesym == scopesym) + { + t = t->semantic(loc, scx); + break; + } + } + } + } + } + if (t->ty == Ttuple) + *pt = t; + else + *pt = t->merge(); + } + if (!s) + { + const char *p = toChars(); + const char *n = importHint(p); + if (n) + error(loc, "'%s' is not defined, perhaps you need to import %s; ?", p, n); + else + { + Identifier *id = new Identifier(p, TOKidentifier); + s = sc->search_correct(id); + if (s) + error(loc, "undefined identifier %s, did you mean %s %s?", p, s->kind(), s->toChars()); + else + error(loc, "undefined identifier %s", p); + } + *pt = Type::terror; + } +} + +/***************************** TypeIdentifier *****************************/ + +TypeIdentifier::TypeIdentifier(Loc loc, Identifier *ident) + : TypeQualified(Tident, loc) +{ + this->ident = ident; +} + + +Type *TypeIdentifier::syntaxCopy() +{ + TypeIdentifier *t; + + t = new TypeIdentifier(loc, ident); + t->syntaxCopyHelper(this); + t->mod = mod; + return t; +} + +void TypeIdentifier::toDecoBuffer(OutBuffer *buf, int flag) +{ unsigned len; + char *name; + + Type::toDecoBuffer(buf, flag); + name = ident->toChars(); + len = strlen(name); + buf->printf("%d%s", len, name); +} + +void TypeIdentifier::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + buf->writestring(this->ident->toChars()); + toCBuffer2Helper(buf, hgs); +} + +/************************************* + * Takes an array of Identifiers and figures out if + * it represents a Type or an Expression. + * Output: + * if expression, *pe is set + * if type, *pt is set + */ + +void TypeIdentifier::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps) +{ + Dsymbol *scopesym; + + //printf("TypeIdentifier::resolve(sc = %p, idents = '%s')\n", sc, toChars()); + + if ((ident->equals(Id::super) || ident->equals(Id::This)) && !hasThis(sc)) + { + AggregateDeclaration *ad = sc->getStructClassScope(); + if (ad) + { + ClassDeclaration *cd = ad->isClassDeclaration(); + if (cd) + { + if (ident->equals(Id::This)) + ident = cd->ident; + else if (cd->baseClass && ident->equals(Id::super)) + ident = cd->baseClass->ident; + } + else + { + StructDeclaration *sd = ad->isStructDeclaration(); + if (sd && ident->equals(Id::This)) + ident = sd->ident; + } + } + } + + Dsymbol *s = sc->search(loc, ident, &scopesym); + resolveHelper(loc, sc, s, scopesym, pe, pt, ps); + if (*pt) + (*pt) = (*pt)->addMod(mod); +} + +/***************************************** + * See if type resolves to a symbol, if so, + * return that symbol. + */ + +Dsymbol *TypeIdentifier::toDsymbol(Scope *sc) +{ + //printf("TypeIdentifier::toDsymbol('%s')\n", toChars()); + if (!sc) + return NULL; + //printf("ident = '%s'\n", ident->toChars()); + + Dsymbol *scopesym; + Dsymbol *s = sc->search(loc, ident, &scopesym); + if (s) + { + for (size_t i = 0; i < idents.dim; i++) + { + Identifier *id = idents.tdata()[i]; + s = s->searchX(loc, sc, id); + if (!s) // failed to find a symbol + { //printf("\tdidn't find a symbol\n"); + break; + } + } + } + return s; +} + +Type *TypeIdentifier::semantic(Loc loc, Scope *sc) +{ + Type *t; + Expression *e; + Dsymbol *s; + + //printf("TypeIdentifier::semantic(%s)\n", toChars()); + resolve(loc, sc, &e, &t, &s); + if (t) + { + //printf("\tit's a type %d, %s, %s\n", t->ty, t->toChars(), t->deco); + + if (t->ty == Ttypedef) + { TypeTypedef *tt = (TypeTypedef *)t; + + if (tt->sym->sem == 1) + error(loc, "circular reference of typedef %s", tt->toChars()); + } + t = t->addMod(mod); + } + else + { +#ifdef DEBUG + if (!global.gag) + printf("1: "); +#endif + if (s) + { + s->error(loc, "is used as a type"); + //halt(); + } + else + error(loc, "%s is used as a type", toChars()); + t = terror; + } + //t->print(); + return t; +} + +Type *TypeIdentifier::reliesOnTident() +{ + return this; +} + +Expression *TypeIdentifier::toExpression() +{ + Expression *e = new IdentifierExp(loc, ident); + for (size_t i = 0; i < idents.dim; i++) + { + Identifier *id = idents.tdata()[i]; + e = new DotIdExp(loc, e, id); + } + + return e; +} + +/***************************** TypeInstance *****************************/ + +TypeInstance::TypeInstance(Loc loc, TemplateInstance *tempinst) + : TypeQualified(Tinstance, loc) +{ + this->tempinst = tempinst; +} + +Type *TypeInstance::syntaxCopy() +{ + //printf("TypeInstance::syntaxCopy() %s, %d\n", toChars(), idents.dim); + TypeInstance *t; + + t = new TypeInstance(loc, (TemplateInstance *)tempinst->syntaxCopy(NULL)); + t->syntaxCopyHelper(this); + t->mod = mod; + return t; +} + + +void TypeInstance::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + tempinst->toCBuffer(buf, hgs); + toCBuffer2Helper(buf, hgs); +} + +void TypeInstance::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps) +{ + // Note close similarity to TypeIdentifier::resolve() + + Dsymbol *s; + + *pe = NULL; + *pt = NULL; + *ps = NULL; + +#if 0 + if (!idents.dim) + { + error(loc, "template instance '%s' has no identifier", toChars()); + return; + } +#endif + //id = (Identifier *)idents.data[0]; + //printf("TypeInstance::resolve(sc = %p, idents = '%s')\n", sc, id->toChars()); + s = tempinst; + if (s) + { //printf("s = %s\n", s->toChars()); + s->semantic(sc); + } + resolveHelper(loc, sc, s, NULL, pe, pt, ps); + if (*pt) + *pt = (*pt)->addMod(mod); + //printf("pt = '%s'\n", (*pt)->toChars()); +} + +Type *TypeInstance::semantic(Loc loc, Scope *sc) +{ + Type *t; + Expression *e; + Dsymbol *s; + + //printf("TypeInstance::semantic(%s)\n", toChars()); + + if (sc->parameterSpecialization) + { + unsigned errors = global.startGagging(); + + resolve(loc, sc, &e, &t, &s); + + if (global.endGagging(errors)) + { + return this; + } + } + else + resolve(loc, sc, &e, &t, &s); + + if (!t) + { + error(loc, "%s is used as a type", toChars()); + t = terror; + } + return t; +} + +Dsymbol *TypeInstance::toDsymbol(Scope *sc) +{ + Type *t; + Expression *e; + Dsymbol *s; + + //printf("TypeInstance::semantic(%s)\n", toChars()); + + if (sc->parameterSpecialization) + { + unsigned errors = global.startGagging(); + + resolve(loc, sc, &e, &t, &s); + + if (global.endGagging(errors)) + return NULL; + } + else + resolve(loc, sc, &e, &t, &s); + + return s; +} + + +/***************************** TypeTypeof *****************************/ + +TypeTypeof::TypeTypeof(Loc loc, Expression *exp) + : TypeQualified(Ttypeof, loc) +{ + this->exp = exp; + inuse = 0; +} + +Type *TypeTypeof::syntaxCopy() +{ + //printf("TypeTypeof::syntaxCopy() %s\n", toChars()); + TypeTypeof *t; + + t = new TypeTypeof(loc, exp->syntaxCopy()); + t->syntaxCopyHelper(this); + t->mod = mod; + return t; +} + +Dsymbol *TypeTypeof::toDsymbol(Scope *sc) +{ + Type *t; + + t = semantic(loc, sc); + if (t == this) + return NULL; + return t->toDsymbol(sc); +} + +void TypeTypeof::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + buf->writestring("typeof("); + exp->toCBuffer(buf, hgs); + buf->writeByte(')'); + toCBuffer2Helper(buf, hgs); +} + +Type *TypeTypeof::semantic(Loc loc, Scope *sc) +{ + Type *t; + + //printf("TypeTypeof::semantic() %s\n", toChars()); + + //static int nest; if (++nest == 50) *(char*)0=0; + if (inuse) + { + inuse = 2; + error(loc, "circular typeof definition"); + return Type::terror; + } + inuse++; + +#if 0 + /* Special case for typeof(this) and typeof(super) since both + * should work even if they are not inside a non-static member function + */ + if (exp->op == TOKthis || exp->op == TOKsuper) + { + // Find enclosing struct or class + for (Dsymbol *s = sc->parent; 1; s = s->parent) + { + ClassDeclaration *cd; + StructDeclaration *sd; + + if (!s) + { + error(loc, "%s is not in a struct or class scope", exp->toChars()); + goto Lerr; + } + cd = s->isClassDeclaration(); + if (cd) + { + if (exp->op == TOKsuper) + { + cd = cd->baseClass; + if (!cd) + { error(loc, "class %s has no 'super'", s->toChars()); + goto Lerr; + } + } + t = cd->type; + break; + } + sd = s->isStructDeclaration(); + if (sd) + { + if (exp->op == TOKsuper) + { + error(loc, "struct %s has no 'super'", sd->toChars()); + goto Lerr; + } + t = sd->type->pointerTo(); + break; + } + } + } + else +#endif + { + Scope *sc2 = sc->push(); + sc2->intypeof++; + sc2->flags |= sc->flags & SCOPEstaticif; + exp = exp->semantic(sc2); +#if DMDV2 + if (exp->type && exp->type->ty == Tfunction && + ((TypeFunction *)exp->type)->isproperty) + exp = resolveProperties(sc2, exp); +#endif + sc2->pop(); + if (exp->op == TOKtype) + { + error(loc, "argument %s to typeof is not an expression", exp->toChars()); + goto Lerr; + } + t = exp->type; + if (!t) + { + error(loc, "expression (%s) has no type", exp->toChars()); + goto Lerr; + } + if (t->ty == Ttypeof) + { error(loc, "forward reference to %s", toChars()); + goto Lerr; + } + + t = t->addMod(mod); + + /* typeof should reflect the true type, + * not what 'auto' would have gotten us. + */ + //t = t->toHeadMutable(); + } + if (idents.dim) + { + Dsymbol *s = t->toDsymbol(sc); + for (size_t i = 0; i < idents.dim; i++) + { + if (!s) + break; + Identifier *id = idents.tdata()[i]; + s = s->searchX(loc, sc, id); + } + + if (s) + { + t = s->getType(); + if (!t) + { error(loc, "%s is not a type", s->toChars()); + goto Lerr; + } + } + else + { error(loc, "cannot resolve .property for %s", toChars()); + goto Lerr; + } + } + inuse--; + return t; + +Lerr: + inuse--; + return terror; +} + +d_uns64 TypeTypeof::size(Loc loc) +{ + if (exp->type) + return exp->type->size(loc); + else + return TypeQualified::size(loc); +} + + + +/***************************** TypeReturn *****************************/ + +TypeReturn::TypeReturn(Loc loc) + : TypeQualified(Treturn, loc) +{ +} + +Type *TypeReturn::syntaxCopy() +{ + TypeReturn *t = new TypeReturn(loc); + t->syntaxCopyHelper(this); + t->mod = mod; + return t; +} + +Dsymbol *TypeReturn::toDsymbol(Scope *sc) +{ + Type *t = semantic(0, sc); + if (t == this) + return NULL; + return t->toDsymbol(sc); +} + +Type *TypeReturn::semantic(Loc loc, Scope *sc) +{ + Type *t; + if (!sc->func) + { error(loc, "typeof(return) must be inside function"); + goto Lerr; + } + t = sc->func->type->nextOf(); + if (!t) + { + error(loc, "cannot use typeof(return) inside function %s with inferred return type", sc->func->toChars()); + goto Lerr; + } + t = t->addMod(mod); + + if (idents.dim) + { + Dsymbol *s = t->toDsymbol(sc); + for (size_t i = 0; i < idents.dim; i++) + { + if (!s) + break; + Identifier *id = idents.tdata()[i]; + s = s->searchX(loc, sc, id); + } + if (s) + { + t = s->getType(); + if (!t) + { error(loc, "%s is not a type", s->toChars()); + goto Lerr; + } + } + else + { error(loc, "cannot resolve .property for %s", toChars()); + goto Lerr; + } + } + return t; + +Lerr: + return terror; +} + +void TypeReturn::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + buf->writestring("typeof(return)"); + toCBuffer2Helper(buf, hgs); +} + + +/***************************** TypeEnum *****************************/ + +TypeEnum::TypeEnum(EnumDeclaration *sym) + : Type(Tenum) +{ + this->sym = sym; +} + +char *TypeEnum::toChars() +{ + if (mod) + return Type::toChars(); + return sym->toChars(); +} + +Type *TypeEnum::syntaxCopy() +{ + return this; +} + +Type *TypeEnum::semantic(Loc loc, Scope *sc) +{ + //printf("TypeEnum::semantic() %s\n", toChars()); + //sym->semantic(sc); + return merge(); +} + +d_uns64 TypeEnum::size(Loc loc) +{ + if (!sym->memtype) + { + error(loc, "enum %s is forward referenced", sym->toChars()); + return 4; + } + return sym->memtype->size(loc); +} + +unsigned TypeEnum::alignsize() +{ + if (!sym->memtype) + { +#ifdef DEBUG + printf("1: "); +#endif + error(0, "enum %s is forward referenced", sym->toChars()); + return 4; + } + return sym->memtype->alignsize(); +} + +Dsymbol *TypeEnum::toDsymbol(Scope *sc) +{ + return sym; +} + +Type *TypeEnum::toBasetype() +{ + if (sym->scope) + { // Enum is forward referenced. We don't need to resolve the whole thing, + // just the base type + if (sym->memtype) + { sym->memtype = sym->memtype->semantic(sym->loc, sym->scope); + } + else + { if (!sym->isAnonymous()) + sym->memtype = Type::tint32; + } + } + if (!sym->memtype) + { +#ifdef DEBUG + printf("2: "); +#endif + error(sym->loc, "enum %s is forward referenced", sym->toChars()); + return tint32; + } + return sym->memtype->toBasetype(); +} + +void TypeEnum::toDecoBuffer(OutBuffer *buf, int flag) +{ + const char *name = sym->mangle(); + Type::toDecoBuffer(buf, flag); + buf->printf("%s", name); +} + +void TypeEnum::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + buf->writestring(sym->toChars()); +} + +Expression *TypeEnum::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ +#if LOGDOTEXP + printf("TypeEnum::dotExp(e = '%s', ident = '%s') '%s'\n", e->toChars(), ident->toChars(), toChars()); +#endif + Dsymbol *s = sym->search(e->loc, ident, 0); + if (!s) + { + if (ident == Id::max || + ident == Id::min || + ident == Id::init || + ident == Id::mangleof || + !sym->memtype + ) + { + return getProperty(e->loc, ident); + } + return sym->memtype->dotExp(sc, e, ident); + } + EnumMember *m = s->isEnumMember(); + Expression *em = m->value->copy(); + em->loc = e->loc; + return em; +} + +Expression *TypeEnum::getProperty(Loc loc, Identifier *ident) +{ Expression *e; + + if (ident == Id::max) + { + if (!sym->maxval) + goto Lfwd; + e = sym->maxval; + } + else if (ident == Id::min) + { + if (!sym->minval) + goto Lfwd; + e = sym->minval; + } + else if (ident == Id::init) + { + e = defaultInitLiteral(loc); + } + else if (ident == Id::stringof) + { char *s = toChars(); + e = new StringExp(loc, s, strlen(s), 'c'); + Scope sc; + e = e->semantic(&sc); + } + else if (ident == Id::mangleof) + { + e = Type::getProperty(loc, ident); + } + else + { + e = toBasetype()->getProperty(loc, ident); + } + return e; + +Lfwd: + error(loc, "forward reference of %s.%s", toChars(), ident->toChars()); + return new ErrorExp(); +} + +int TypeEnum::isintegral() +{ + return sym->memtype->isintegral(); +} + +int TypeEnum::isfloating() +{ + return sym->memtype->isfloating(); +} + +int TypeEnum::isreal() +{ + return sym->memtype->isreal(); +} + +int TypeEnum::isimaginary() +{ + return sym->memtype->isimaginary(); +} + +int TypeEnum::iscomplex() +{ + return sym->memtype->iscomplex(); +} + +int TypeEnum::isunsigned() +{ + return sym->memtype->isunsigned(); +} + +int TypeEnum::isscalar() +{ + return sym->memtype->isscalar(); +} + +int TypeEnum::isAssignable() +{ + return sym->memtype->isAssignable(); +} + +int TypeEnum::checkBoolean() +{ + return sym->memtype->checkBoolean(); +} + +int TypeEnum::needsDestruction() +{ + return sym->memtype->needsDestruction(); +} + +MATCH TypeEnum::implicitConvTo(Type *to) +{ MATCH m; + + //printf("TypeEnum::implicitConvTo()\n"); + if (ty == to->ty && sym == ((TypeEnum *)to)->sym) + m = (mod == to->mod) ? MATCHexact : MATCHconst; + else if (sym->memtype->implicitConvTo(to)) + m = MATCHconvert; // match with conversions + else + m = MATCHnomatch; // no match + return m; +} + +MATCH TypeEnum::constConv(Type *to) +{ + if (equals(to)) + return MATCHexact; + if (ty == to->ty && sym == ((TypeEnum *)to)->sym && + MODimplicitConv(mod, to->mod)) + return MATCHconst; + return MATCHnomatch; +} + + +Expression *TypeEnum::defaultInit(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeEnum::defaultInit() '%s'\n", toChars()); +#endif + // Initialize to first member of enum + //printf("%s\n", sym->defaultval->type->toChars()); + if (!sym->defaultval) + { + error(loc, "forward reference of %s.init", toChars()); + return new ErrorExp(); + } + return sym->defaultval; +} + +int TypeEnum::isZeroInit(Loc loc) +{ + if (!sym->defaultval && sym->scope) + { // Enum is forward referenced. We need to resolve the whole thing. + sym->semantic(NULL); + } + if (!sym->defaultval) + { +#ifdef DEBUG + printf("3: "); +#endif + error(loc, "enum %s is forward referenced", sym->toChars()); + return 0; + } + return sym->defaultval->isBool(FALSE); +} + +int TypeEnum::hasPointers() +{ + return toBasetype()->hasPointers(); +} + +/***************************** TypeTypedef *****************************/ + +TypeTypedef::TypeTypedef(TypedefDeclaration *sym) + : Type(Ttypedef) +{ + this->sym = sym; +} + +Type *TypeTypedef::syntaxCopy() +{ + return this; +} + +char *TypeTypedef::toChars() +{ + return Type::toChars(); +} + +Type *TypeTypedef::semantic(Loc loc, Scope *sc) +{ + //printf("TypeTypedef::semantic(%s), sem = %d\n", toChars(), sym->sem); + int errors = global.errors; + sym->semantic(sc); + if (errors != global.errors) + return terror; + return merge(); +} + +d_uns64 TypeTypedef::size(Loc loc) +{ + return sym->basetype->size(loc); +} + +unsigned TypeTypedef::alignsize() +{ + return sym->basetype->alignsize(); +} + +Dsymbol *TypeTypedef::toDsymbol(Scope *sc) +{ + return sym; +} + +void TypeTypedef::toDecoBuffer(OutBuffer *buf, int flag) +{ + Type::toDecoBuffer(buf, flag); + const char *name = sym->mangle(); + buf->printf("%s", name); +} + +void TypeTypedef::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + //printf("TypeTypedef::toCBuffer2() '%s'\n", sym->toChars()); + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + buf->writestring(sym->toChars()); +} + +Expression *TypeTypedef::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ +#if LOGDOTEXP + printf("TypeTypedef::dotExp(e = '%s', ident = '%s') '%s'\n", e->toChars(), ident->toChars(), toChars()); +#endif + if (ident == Id::init) + { + return Type::dotExp(sc, e, ident); + } + return sym->basetype->dotExp(sc, e, ident); +} + +Expression *TypeTypedef::getProperty(Loc loc, Identifier *ident) +{ +#if LOGDOTEXP + printf("TypeTypedef::getProperty(ident = '%s') '%s'\n", ident->toChars(), toChars()); +#endif + if (ident == Id::init) + { + return Type::getProperty(loc, ident); + } + return sym->basetype->getProperty(loc, ident); +} + +int TypeTypedef::isintegral() +{ + //printf("TypeTypedef::isintegral()\n"); + //printf("sym = '%s'\n", sym->toChars()); + //printf("basetype = '%s'\n", sym->basetype->toChars()); + return sym->basetype->isintegral(); +} + +int TypeTypedef::isfloating() +{ + return sym->basetype->isfloating(); +} + +int TypeTypedef::isreal() +{ + return sym->basetype->isreal(); +} + +int TypeTypedef::isimaginary() +{ + return sym->basetype->isimaginary(); +} + +int TypeTypedef::iscomplex() +{ + return sym->basetype->iscomplex(); +} + +int TypeTypedef::isunsigned() +{ + return sym->basetype->isunsigned(); +} + +int TypeTypedef::isscalar() +{ + return sym->basetype->isscalar(); +} + +int TypeTypedef::isAssignable() +{ + return sym->basetype->isAssignable(); +} + +int TypeTypedef::checkBoolean() +{ + return sym->basetype->checkBoolean(); +} + +int TypeTypedef::needsDestruction() +{ + return sym->basetype->needsDestruction(); +} + +Type *TypeTypedef::toBasetype() +{ + if (sym->inuse) + { + sym->error("circular definition"); + sym->basetype = Type::terror; + return Type::terror; + } + sym->inuse = 1; + Type *t = sym->basetype->toBasetype(); + sym->inuse = 0; + t = t->addMod(mod); + return t; +} + +MATCH TypeTypedef::implicitConvTo(Type *to) +{ MATCH m; + + //printf("TypeTypedef::implicitConvTo(to = %s) %s\n", to->toChars(), toChars()); + if (equals(to)) + m = MATCHexact; // exact match + else if (sym->basetype->implicitConvTo(to)) + m = MATCHconvert; // match with conversions + else if (ty == to->ty && sym == ((TypeTypedef *)to)->sym) + { + m = constConv(to); + } + else + m = MATCHnomatch; // no match + return m; +} + +MATCH TypeTypedef::constConv(Type *to) +{ + if (equals(to)) + return MATCHexact; + if (ty == to->ty && sym == ((TypeTypedef *)to)->sym) + return sym->basetype->implicitConvTo(((TypeTypedef *)to)->sym->basetype); + return MATCHnomatch; +} + +Type *TypeTypedef::toHeadMutable() +{ + if (!mod) + return this; + + Type *tb = toBasetype(); + Type *t = tb->toHeadMutable(); + if (t->equals(tb)) + return this; + else + return mutableOf(); +} + +Expression *TypeTypedef::defaultInit(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeTypedef::defaultInit() '%s'\n", toChars()); +#endif + if (sym->init) + { + //sym->init->toExpression()->print(); + return sym->init->toExpression(); + } + Type *bt = sym->basetype; + Expression *e = bt->defaultInit(loc); + e->type = this; + while (bt->ty == Tsarray) + { TypeSArray *tsa = (TypeSArray *)bt; + e->type = tsa->next; + bt = tsa->next->toBasetype(); + } + return e; +} + +Expression *TypeTypedef::defaultInitLiteral(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeTypedef::defaultInitLiteral() '%s'\n", toChars()); +#endif + if (sym->init) + { + //sym->init->toExpression()->print(); + return sym->init->toExpression(); + } + Type *bt = sym->basetype; + Expression *e = bt->defaultInitLiteral(loc); + e->type = this; + return e; +} + +int TypeTypedef::isZeroInit(Loc loc) +{ + if (sym->init) + { + if (sym->init->isVoidInitializer()) + return 1; // initialize voids to 0 + Expression *e = sym->init->toExpression(); + if (e && e->isBool(FALSE)) + return 1; + return 0; // assume not + } + if (sym->inuse) + { + sym->error("circular definition"); + sym->basetype = Type::terror; + } + sym->inuse = 1; + int result = sym->basetype->isZeroInit(loc); + sym->inuse = 0; + return result; +} + +int TypeTypedef::hasPointers() +{ + return toBasetype()->hasPointers(); +} + +int TypeTypedef::hasWild() +{ + assert(toBasetype()); + return mod & MODwild || toBasetype()->hasWild(); +} + +/***************************** TypeStruct *****************************/ + +TypeStruct::TypeStruct(StructDeclaration *sym) + : Type(Tstruct) +{ + this->sym = sym; +} + +char *TypeStruct::toChars() +{ + //printf("sym.parent: %s, deco = %s\n", sym->parent->toChars(), deco); + if (mod) + return Type::toChars(); + TemplateInstance *ti = sym->parent->isTemplateInstance(); + if (ti && ti->toAlias() == sym) + { + return ti->toChars(); + } + return sym->toChars(); +} + +Type *TypeStruct::syntaxCopy() +{ + return this; +} + +Type *TypeStruct::semantic(Loc loc, Scope *sc) +{ + //printf("TypeStruct::semantic('%s')\n", sym->toChars()); + + /* Cannot do semantic for sym because scope chain may not + * be right. + */ + //sym->semantic(sc); + + return merge(); +} + +d_uns64 TypeStruct::size(Loc loc) +{ + return sym->size(loc); +} + +unsigned TypeStruct::alignsize() +{ unsigned sz; + + sym->size(0); // give error for forward references + sz = sym->alignsize; + if (sz > sym->structalign) + sz = sym->structalign; + return sz; +} + +Dsymbol *TypeStruct::toDsymbol(Scope *sc) +{ + return sym; +} + +void TypeStruct::toDecoBuffer(OutBuffer *buf, int flag) +{ + const char *name = sym->mangle(); + //printf("TypeStruct::toDecoBuffer('%s') = '%s'\n", toChars(), name); + Type::toDecoBuffer(buf, flag); + buf->printf("%s", name); +} + +void TypeStruct::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + TemplateInstance *ti = sym->parent->isTemplateInstance(); + if (ti && ti->toAlias() == sym) + buf->writestring(ti->toChars()); + else + buf->writestring(sym->toChars()); +} + +Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ + VarDeclaration *v; + Dsymbol *s; + DotVarExp *de; + Declaration *d; + +#if LOGDOTEXP + printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); +#endif + if (!sym->members) + { + error(e->loc, "struct %s is forward referenced", sym->toChars()); + return new ErrorExp(); + } + + /* If e.tupleof + */ + if (ident == Id::tupleof) + { + /* Create a TupleExp out of the fields of the struct e: + * (e.field0, e.field1, e.field2, ...) + */ + e = e->semantic(sc); // do this before turning on noaccesscheck + e->type->size(); // do semantic of type + Expressions *exps = new Expressions; + exps->reserve(sym->fields.dim); + + Expression *ev = e; + for (size_t i = 0; i < sym->fields.dim; i++) + { VarDeclaration *v = sym->fields.tdata()[i]; + Expression *fe; + if (i == 0 && sc->func && sym->fields.dim > 1 && + e->hasSideEffect()) + { + Identifier *id = Lexer::uniqueId("__tup"); + ExpInitializer *ei = new ExpInitializer(e->loc, e); + VarDeclaration *vd = new VarDeclaration(e->loc, NULL, id, ei); + vd->storage_class |= STCctfe | STCref | STCforeach; + + ev = new VarExp(e->loc, vd); + fe = new CommaExp(e->loc, new DeclarationExp(e->loc, vd), ev); + fe = new DotVarExp(e->loc, fe, v); + } + else + fe = new DotVarExp(ev->loc, ev, v); + exps->push(fe); + } + e = new TupleExp(e->loc, exps); + sc = sc->push(); + sc->noaccesscheck = 1; + e = e->semantic(sc); + sc->pop(); + return e; + } + + if (e->op == TOKdotexp) + { DotExp *de = (DotExp *)e; + + if (de->e1->op == TOKimport) + { + assert(0); // cannot find a case where this happens; leave + // assert in until we do + ScopeExp *se = (ScopeExp *)de->e1; + + s = se->sds->search(e->loc, ident, 0); + e = de->e1; + goto L1; + } + } + + s = sym->search(e->loc, ident, 0); +L1: + if (!s) + { + return noMember(sc, e, ident); + } + if (!s->isFuncDeclaration()) // because of overloading + s->checkDeprecated(e->loc, sc); + s = s->toAlias(); + + v = s->isVarDeclaration(); + if (v && !v->isDataseg()) + { + Expression *ei = v->getConstInitializer(); + if (ei) + { e = ei->copy(); // need to copy it if it's a StringExp + e = e->semantic(sc); + return e; + } + } + + if (s->getType()) + { + //return new DotTypeExp(e->loc, e, s); + return new TypeExp(e->loc, s->getType()); + } + + EnumMember *em = s->isEnumMember(); + if (em) + { + assert(em->value); + return em->value->copy(); + } + + TemplateMixin *tm = s->isTemplateMixin(); + if (tm) + { + Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, tm)); + de->type = e->type; + return de; + } + + TemplateDeclaration *td = s->isTemplateDeclaration(); + if (td) + { + e = new DotTemplateExp(e->loc, e, td); + e = e->semantic(sc); + return e; + } + + TemplateInstance *ti = s->isTemplateInstance(); + if (ti) + { if (!ti->semanticRun) + { + if (global.errors) + return new ErrorExp(); // TemplateInstance::semantic() will fail anyway + ti->semantic(sc); + } + s = ti->inst->toAlias(); + if (!s->isTemplateInstance()) + goto L1; + Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, ti)); + de->type = e->type; + return de; + } + + if (s->isImport() || s->isModule() || s->isPackage()) + { + e = new DsymbolExp(e->loc, s, 0); + e = e->semantic(sc); + return e; + } + + OverloadSet *o = s->isOverloadSet(); + if (o) + { + OverExp *oe = new OverExp(o); + if (e->op == TOKtype) + return oe; + return new DotExp(e->loc, e, oe); + } + + d = s->isDeclaration(); +#ifdef DEBUG + if (!d) + printf("d = %s '%s'\n", s->kind(), s->toChars()); +#endif + assert(d); + + if (e->op == TOKtype) + { FuncDeclaration *fd = sc->func; + + if (d->isTupleDeclaration()) + { + e = new TupleExp(e->loc, d->isTupleDeclaration()); + e = e->semantic(sc); + return e; + } + else if (d->needThis() && fd && fd->vthis) + { + e = new DotVarExp(e->loc, new ThisExp(e->loc), d); + e = e->semantic(sc); + return e; + } + return new VarExp(e->loc, d, 1); + } + + if (d->isDataseg()) + { + // (e, d) + VarExp *ve; + + accessCheck(e->loc, sc, e, d); + ve = new VarExp(e->loc, d); + e = new CommaExp(e->loc, e, ve); + e = e->semantic(sc); + return e; + } + + if (v) + { + if (v->toParent() != sym) + sym->error(e->loc, "'%s' is not a member", v->toChars()); + + // *(&e + offset) + accessCheck(e->loc, sc, e, d); +#if 0 + Expression *b = new AddrExp(e->loc, e); + b->type = e->type->pointerTo(); + b = new AddExp(e->loc, b, new IntegerExp(e->loc, v->offset, Type::tint32)); + b->type = v->type->pointerTo(); + b = new PtrExp(e->loc, b); + b->type = v->type->addMod(e->type->mod); + return b; +#endif + } + + de = new DotVarExp(e->loc, e, d); + return de->semantic(sc); +} + +unsigned TypeStruct::memalign(unsigned salign) +{ + sym->size(0); // give error for forward references + return sym->structalign; +} + +Expression *TypeStruct::defaultInit(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeStruct::defaultInit() '%s'\n", toChars()); +#endif + Symbol *s = sym->toInitializer(); + Declaration *d = new SymbolDeclaration(sym->loc, s, sym); + assert(d); + d->type = this; + return new VarExp(sym->loc, d); +} + +/*************************************** + * Use when we prefer the default initializer to be a literal, + * rather than a global immutable variable. + */ +Expression *TypeStruct::defaultInitLiteral(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars()); +#endif + if (sym->isNested()) + return defaultInit(loc); + Expressions *structelems = new Expressions(); + structelems->setDim(sym->fields.dim); + for (size_t j = 0; j < structelems->dim; j++) + { + VarDeclaration *vd = sym->fields.tdata()[j]; + Expression *e; + if (vd->init) + { if (vd->init->isVoidInitializer()) + e = NULL; + else + e = vd->init->toExpression(); + } + else + e = vd->type->defaultInitLiteral(); + structelems->tdata()[j] = e; + } + StructLiteralExp *structinit = new StructLiteralExp(loc, (StructDeclaration *)sym, structelems); + // Why doesn't the StructLiteralExp constructor do this, when + // sym->type != NULL ? + structinit->type = sym->type; + return structinit; +} + + +int TypeStruct::isZeroInit(Loc loc) +{ + return sym->zeroInit; +} + +int TypeStruct::checkBoolean() +{ + return FALSE; +} + +int TypeStruct::needsDestruction() +{ + return sym->dtor != NULL; +} + +int TypeStruct::isAssignable() +{ + int assignable = TRUE; + unsigned offset; + + /* If any of the fields are const or invariant, + * then one cannot assign this struct. + */ + for (size_t i = 0; i < sym->fields.dim; i++) + { VarDeclaration *v = sym->fields.tdata()[i]; + //printf("%s [%d] v = (%s) %s, v->offset = %d, v->parent = %s", sym->toChars(), i, v->kind(), v->toChars(), v->offset, v->parent->kind()); + if (i == 0) + ; + else if (v->offset == offset) + { + /* If any fields of anonymous union are assignable, + * then regard union as assignable. + * This is to support unsafe things like Rebindable templates. + */ + if (assignable) + continue; + } + else + { + if (!assignable) + return FALSE; + } + assignable = v->type->isMutable() && v->type->isAssignable(); + offset = v->offset; + //printf(" -> assignable = %d\n", assignable); + } + + return assignable; +} + +int TypeStruct::hasPointers() +{ + // Probably should cache this information in sym rather than recompute + StructDeclaration *s = sym; + + sym->size(0); // give error for forward references + for (size_t i = 0; i < s->fields.dim; i++) + { + Dsymbol *sm = s->fields.tdata()[i]; + Declaration *d = sm->isDeclaration(); + if (d->storage_class & STCref || d->hasPointers()) + return TRUE; + } + return FALSE; +} + +MATCH TypeStruct::implicitConvTo(Type *to) +{ MATCH m; + + //printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to->toChars()); + if (to->ty == Taarray) + { + /* If there is an error instantiating AssociativeArray!(), it shouldn't + * be reported -- it just means implicit conversion is impossible. + */ + int errs = global.startGagging(); + to = ((TypeAArray*)to)->getImpl()->type; + if (global.endGagging(errs)) + { + return MATCHnomatch; + } + } + + if (ty == to->ty && sym == ((TypeStruct *)to)->sym) + { m = MATCHexact; // exact match + if (mod != to->mod) + { + if (MODimplicitConv(mod, to->mod)) + m = MATCHconst; + else + { /* Check all the fields. If they can all be converted, + * allow the conversion. + */ + for (size_t i = 0; i < sym->fields.dim; i++) + { Dsymbol *s = sym->fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v && v->storage_class & STCfield); + + // 'from' type + Type *tvf = v->type->addMod(mod); + + // 'to' type + Type *tv = v->type->castMod(to->mod); + + //printf("\t%s => %s, match = %d\n", v->type->toChars(), tv->toChars(), tvf->implicitConvTo(tv)); + if (tvf->implicitConvTo(tv) < MATCHconst) + return MATCHnomatch; + } + m = MATCHconst; + } + } + } + else if (sym->aliasthis) + m = aliasthisOf()->implicitConvTo(to); + else + m = MATCHnomatch; // no match + return m; +} + +MATCH TypeStruct::constConv(Type *to) +{ + if (equals(to)) + return MATCHexact; + if (ty == to->ty && sym == ((TypeStruct *)to)->sym && + MODimplicitConv(mod, to->mod)) + return MATCHconst; + return MATCHnomatch; +} + +unsigned TypeStruct::wildConvTo(Type *tprm) +{ + if (ty == tprm->ty && sym == ((TypeStruct *)tprm)->sym) + return Type::wildConvTo(tprm); + + if (sym->aliasthis) + { Type *t = aliasthisOf(); + assert(t); + return t->wildConvTo(tprm); + } + + return 0; +} + +Type *TypeStruct::toHeadMutable() +{ + return this; +} + + +/***************************** TypeClass *****************************/ + +TypeClass::TypeClass(ClassDeclaration *sym) + : Type(Tclass) +{ + this->sym = sym; +} + +char *TypeClass::toChars() +{ + if (mod) + return Type::toChars(); + return (char *)sym->toPrettyChars(); +} + +Type *TypeClass::syntaxCopy() +{ + return this; +} + +Type *TypeClass::semantic(Loc loc, Scope *sc) +{ + //printf("TypeClass::semantic(%s)\n", sym->toChars()); + if (deco) + return this; + //printf("\t%s\n", merge()->deco); + return merge(); +} + +d_uns64 TypeClass::size(Loc loc) +{ + return PTRSIZE; +} + +Dsymbol *TypeClass::toDsymbol(Scope *sc) +{ + return sym; +} + +void TypeClass::toDecoBuffer(OutBuffer *buf, int flag) +{ + const char *name = sym->mangle(); + //printf("TypeClass::toDecoBuffer('%s' flag=%d mod=%x) = '%s'\n", toChars(), flag, mod, name); + Type::toDecoBuffer(buf, flag); + buf->printf("%s", name); +} + +void TypeClass::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + buf->writestring(sym->toChars()); +} + +Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ + VarDeclaration *v; + Dsymbol *s; + +#if LOGDOTEXP + printf("TypeClass::dotExp(e='%s', ident='%s')\n", e->toChars(), ident->toChars()); +#endif + + if (e->op == TOKdotexp) + { DotExp *de = (DotExp *)e; + + if (de->e1->op == TOKimport) + { + ScopeExp *se = (ScopeExp *)de->e1; + + s = se->sds->search(e->loc, ident, 0); + e = de->e1; + goto L1; + } + } + + if (ident == Id::tupleof) + { + /* Create a TupleExp + */ + e = e->semantic(sc); // do this before turning on noaccesscheck + + /* If this is called in the middle of a class declaration, + * class Inner { + * int x; + * alias typeof(Inner.tupleof) T; + * int y; + * } + * then Inner.y will be omitted from the tuple. + */ + // Detect that error, and at least try to run semantic() on it if we can + sym->size(e->loc); + + Expressions *exps = new Expressions; + exps->reserve(sym->fields.dim); + + Expression *ev = e; + for (size_t i = 0; i < sym->fields.dim; i++) + { VarDeclaration *v = sym->fields.tdata()[i]; + // Don't include hidden 'this' pointer + if (v->isThisDeclaration()) + continue; + Expression *fe; + if (i == 0 && sc->func && sym->fields.dim > 1 && + e->hasSideEffect()) + { + Identifier *id = Lexer::uniqueId("__tup"); + ExpInitializer *ei = new ExpInitializer(e->loc, e); + VarDeclaration *vd = new VarDeclaration(e->loc, NULL, id, ei); + vd->storage_class |= STCctfe | STCref | STCforeach; + + ev = new VarExp(e->loc, vd); + fe = new CommaExp(e->loc, new DeclarationExp(e->loc, vd), ev); + fe = new DotVarExp(e->loc, fe, v); + } + else + fe = new DotVarExp(e->loc, ev, v); + exps->push(fe); + } + e = new TupleExp(e->loc, exps); + sc = sc->push(); + sc->noaccesscheck = 1; + e = e->semantic(sc); + sc->pop(); + return e; + } + + s = sym->search(e->loc, ident, 0); +L1: + if (!s) + { + // See if it's a base class + if (Dsymbol *cbase = sym->searchBase(e->loc, ident)) + { + e = new DotTypeExp(0, e, cbase); + return e; + } + + if (ident == Id::classinfo) + { + assert(ClassDeclaration::classinfo); + Type *t = ClassDeclaration::classinfo->type; + if (e->op == TOKtype || e->op == TOKdottype) + { + /* For type.classinfo, we know the classinfo + * at compile time. + */ + if (!sym->vclassinfo) + sym->vclassinfo = new TypeInfoClassDeclaration(sym->type); + e = new VarExp(e->loc, sym->vclassinfo); + e = e->addressOf(sc); + e->type = t; // do this so we don't get redundant dereference + } + else + { /* For class objects, the classinfo reference is the first + * entry in the vtbl[] + */ + e = new PtrExp(e->loc, e); + e->type = t->pointerTo(); + if (sym->isInterfaceDeclaration()) + { + if (sym->isCPPinterface()) + { /* C++ interface vtbl[]s are different in that the + * first entry is always pointer to the first virtual + * function, not classinfo. + * We can't get a .classinfo for it. + */ + error(e->loc, "no .classinfo for C++ interface objects"); + } + /* For an interface, the first entry in the vtbl[] + * is actually a pointer to an instance of struct Interface. + * The first member of Interface is the .classinfo, + * so add an extra pointer indirection. + */ + e->type = e->type->pointerTo(); + e = new PtrExp(e->loc, e); + e->type = t->pointerTo(); + } + e = new PtrExp(e->loc, e, t); + } + return e; + } + + if (ident == Id::__vptr) + { /* The pointer to the vtbl[] + * *cast(invariant(void*)**)e + */ + e = e->castTo(sc, tvoidptr->invariantOf()->pointerTo()->pointerTo()); + e = new PtrExp(e->loc, e); + e = e->semantic(sc); + return e; + } + + if (ident == Id::__monitor) + { /* The handle to the monitor (call it a void*) + * *(cast(void**)e + 1) + */ + e = e->castTo(sc, tvoidptr->pointerTo()); + e = new AddExp(e->loc, e, new IntegerExp(1)); + e = new PtrExp(e->loc, e); + e = e->semantic(sc); + return e; + } + + if (ident == Id::typeinfo) + { + if (!global.params.useDeprecated) + error(e->loc, ".typeinfo deprecated, use typeid(type)"); + return getTypeInfo(sc); + } + if (ident == Id::outer && sym->vthis) + { + s = sym->vthis; + } + else + { + return noMember(sc, e, ident); + } + } + if (!s->isFuncDeclaration()) // because of overloading + s->checkDeprecated(e->loc, sc); + s = s->toAlias(); + v = s->isVarDeclaration(); + if (v && !v->isDataseg()) + { Expression *ei = v->getConstInitializer(); + + if (ei) + { e = ei->copy(); // need to copy it if it's a StringExp + e = e->semantic(sc); + return e; + } + } + + if (s->getType()) + { +// if (e->op == TOKtype) + return new TypeExp(e->loc, s->getType()); +// return new DotTypeExp(e->loc, e, s); + } + + EnumMember *em = s->isEnumMember(); + if (em) + { + assert(em->value); + return em->value->copy(); + } + + TemplateMixin *tm = s->isTemplateMixin(); + if (tm) + { + Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, tm)); + de->type = e->type; + return de; + } + + TemplateDeclaration *td = s->isTemplateDeclaration(); + if (td) + { + e = new DotTemplateExp(e->loc, e, td); + e = e->semantic(sc); + return e; + } + + TemplateInstance *ti = s->isTemplateInstance(); + if (ti) + { if (!ti->semanticRun) + { + if (global.errors) + return new ErrorExp(); // TemplateInstance::semantic() will fail anyway + ti->semantic(sc); + } + s = ti->inst->toAlias(); + if (!s->isTemplateInstance()) + goto L1; + Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, ti)); + de->type = e->type; + return de; + } + +#if 0 // shouldn't this be here? + if (s->isImport() || s->isModule() || s->isPackage()) + { + e = new DsymbolExp(e->loc, s, 0); + e = e->semantic(sc); + return e; + } +#endif + + OverloadSet *o = s->isOverloadSet(); + if (o) + { + OverExp *oe = new OverExp(o); + if (e->op == TOKtype) + return oe; + return new DotExp(e->loc, e, oe); + } + + Declaration *d = s->isDeclaration(); + if (!d) + { + e->error("%s.%s is not a declaration", e->toChars(), ident->toChars()); + return new ErrorExp(); + } + + if (e->op == TOKtype) + { + /* It's: + * Class.d + */ + if (d->isTupleDeclaration()) + { + e = new TupleExp(e->loc, d->isTupleDeclaration()); + e = e->semantic(sc); + return e; + } + else if (d->needThis() && (hasThis(sc) || !(sc->intypeof || d->isFuncDeclaration()))) + { + if (sc->func) + { + ClassDeclaration *thiscd; + thiscd = sc->func->toParent()->isClassDeclaration(); + + if (thiscd) + { + ClassDeclaration *cd = e->type->isClassHandle(); + + if (cd == thiscd) + { + e = new ThisExp(e->loc); + e = new DotTypeExp(e->loc, e, cd); + DotVarExp *de = new DotVarExp(e->loc, e, d); + e = de->semantic(sc); + return e; + } + else if ((!cd || !cd->isBaseOf(thiscd, NULL)) && + !d->isFuncDeclaration()) + e->error("'this' is required, but %s is not a base class of %s", e->type->toChars(), thiscd->toChars()); + } + } + + /* Rewrite as: + * this.d + */ + DotVarExp *de = new DotVarExp(e->loc, new ThisExp(e->loc), d); + e = de->semantic(sc); + return e; + } + else + { + VarExp *ve = new VarExp(e->loc, d, 1); + return ve; + } + } + + if (d->isDataseg()) + { + // (e, d) + VarExp *ve; + + accessCheck(e->loc, sc, e, d); + ve = new VarExp(e->loc, d); + e = new CommaExp(e->loc, e, ve); + e = e->semantic(sc); + return e; + } + + if (d->parent && d->toParent()->isModule()) + { + // (e, d) + VarExp *ve = new VarExp(e->loc, d, 1); + e = new CommaExp(e->loc, e, ve); + e->type = d->type; + return e; + } + + DotVarExp *de = new DotVarExp(e->loc, e, d); + return de->semantic(sc); +} + +ClassDeclaration *TypeClass::isClassHandle() +{ + return sym; +} + +int TypeClass::isscope() +{ + return sym->isscope; +} + +int TypeClass::isBaseOf(Type *t, int *poffset) +{ + if (t->ty == Tclass) + { ClassDeclaration *cd; + + cd = ((TypeClass *)t)->sym; + if (sym->isBaseOf(cd, poffset)) + return 1; + } + return 0; +} + +MATCH TypeClass::implicitConvTo(Type *to) +{ + //printf("TypeClass::implicitConvTo(to = '%s') %s\n", to->toChars(), toChars()); + MATCH m = constConv(to); + if (m != MATCHnomatch) + return m; + + ClassDeclaration *cdto = to->isClassHandle(); + if (cdto) + { + if (cdto->scope) + cdto->semantic(NULL); + if (cdto->isBaseOf(sym, NULL)) + { //printf("'to' is base\n"); + return MATCHconvert; + } + } + + if (global.params.Dversion == 1) + { + // Allow conversion to (void *) + if (to->ty == Tpointer && ((TypePointer *)to)->next->ty == Tvoid) + return MATCHconvert; + } + + m = MATCHnomatch; + if (sym->aliasthis) + m = aliasthisOf()->implicitConvTo(to); + + return m; +} + +MATCH TypeClass::constConv(Type *to) +{ + if (equals(to)) + return MATCHexact; + if (ty == to->ty && sym == ((TypeClass *)to)->sym && + MODimplicitConv(mod, to->mod)) + return MATCHconst; + + /* Conversion derived to const(base) + */ + int offset = 0; + if (to->isBaseOf(this, &offset) && offset == 0 && !to->isMutable()) + return MATCHconvert; + + return MATCHnomatch; +} + +unsigned TypeClass::wildConvTo(Type *tprm) +{ + Type *tcprm = tprm->substWildTo(MODconst); + + if (constConv(tcprm)) + return Type::wildConvTo(tprm); + + ClassDeclaration *cdprm = tcprm->isClassHandle(); + if (cdprm && cdprm->isBaseOf(sym, NULL)) + return Type::wildConvTo(tprm); + + if (sym->aliasthis) + return aliasthisOf()->wildConvTo(tprm); + + return 0; +} + +Type *TypeClass::toHeadMutable() +{ + return this; +} + +Expression *TypeClass::defaultInit(Loc loc) +{ +#if LOGDEFAULTINIT + printf("TypeClass::defaultInit() '%s'\n", toChars()); +#endif + return new NullExp(loc, this); +} + +int TypeClass::isZeroInit(Loc loc) +{ + return 1; +} + +int TypeClass::checkBoolean() +{ + return TRUE; +} + +int TypeClass::hasPointers() +{ + return TRUE; +} + +/***************************** TypeTuple *****************************/ + +TypeTuple::TypeTuple(Parameters *arguments) + : Type(Ttuple) +{ + //printf("TypeTuple(this = %p)\n", this); + this->arguments = arguments; + //printf("TypeTuple() %p, %s\n", this, toChars()); +#ifdef DEBUG + if (arguments) + { + for (size_t i = 0; i < arguments->dim; i++) + { + Parameter *arg = arguments->tdata()[i]; + assert(arg && arg->type); + } + } +#endif +} + +/**************** + * Form TypeTuple from the types of the expressions. + * Assume exps[] is already tuple expanded. + */ + +TypeTuple::TypeTuple(Expressions *exps) + : Type(Ttuple) +{ + Parameters *arguments = new Parameters; + if (exps) + { + arguments->setDim(exps->dim); + for (size_t i = 0; i < exps->dim; i++) + { Expression *e = exps->tdata()[i]; + if (e->type->ty == Ttuple) + e->error("cannot form tuple of tuples"); + Parameter *arg = new Parameter(STCundefined, e->type, NULL, NULL); + arguments->tdata()[i] = arg; + } + } + this->arguments = arguments; + //printf("TypeTuple() %p, %s\n", this, toChars()); +} + +/******************************************* + * Type tuple with 0, 1 or 2 types in it. + */ +TypeTuple::TypeTuple() + : Type(Ttuple) +{ + arguments = new Parameters(); +} + +TypeTuple::TypeTuple(Type *t1) + : Type(Ttuple) +{ + arguments = new Parameters(); + arguments->push(new Parameter(0, t1, NULL, NULL)); +} + +TypeTuple::TypeTuple(Type *t1, Type *t2) + : Type(Ttuple) +{ + arguments = new Parameters(); + arguments->push(new Parameter(0, t1, NULL, NULL)); + arguments->push(new Parameter(0, t2, NULL, NULL)); +} + +Type *TypeTuple::syntaxCopy() +{ + Parameters *args = Parameter::arraySyntaxCopy(arguments); + Type *t = new TypeTuple(args); + t->mod = mod; + return t; +} + +Type *TypeTuple::semantic(Loc loc, Scope *sc) +{ + //printf("TypeTuple::semantic(this = %p)\n", this); + //printf("TypeTuple::semantic() %p, %s\n", this, toChars()); + if (!deco) + deco = merge()->deco; + + /* Don't return merge(), because a tuple with one type has the + * same deco as that type. + */ + return this; +} + +int TypeTuple::equals(Object *o) +{ Type *t; + + t = (Type *)o; + //printf("TypeTuple::equals(%s, %s)\n", toChars(), t->toChars()); + if (this == t) + { + return 1; + } + if (t->ty == Ttuple) + { TypeTuple *tt = (TypeTuple *)t; + + if (arguments->dim == tt->arguments->dim) + { + for (size_t i = 0; i < tt->arguments->dim; i++) + { Parameter *arg1 = arguments->tdata()[i]; + Parameter *arg2 = tt->arguments->tdata()[i]; + + if (!arg1->type->equals(arg2->type)) + return 0; + } + return 1; + } + } + return 0; +} + +Type *TypeTuple::reliesOnTident() +{ + if (arguments) + { + for (size_t i = 0; i < arguments->dim; i++) + { + Parameter *arg = arguments->tdata()[i]; + Type *t = arg->type->reliesOnTident(); + if (t) + return t; + } + } + return NULL; +} + +#if 0 +Type *TypeTuple::makeConst() +{ + //printf("TypeTuple::makeConst() %s\n", toChars()); + if (cto) + return cto; + TypeTuple *t = (TypeTuple *)Type::makeConst(); + t->arguments = new Parameters(); + t->arguments->setDim(arguments->dim); + for (size_t i = 0; i < arguments->dim; i++) + { Parameter *arg = arguments->tdata()[i]; + Parameter *narg = new Parameter(arg->storageClass, arg->type->constOf(), arg->ident, arg->defaultArg); + t->arguments->tdata()[i] = (Parameter *)narg; + } + return t; +} +#endif + +void TypeTuple::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + Parameter::argsToCBuffer(buf, hgs, arguments, 0); +} + +void TypeTuple::toDecoBuffer(OutBuffer *buf, int flag) +{ + //printf("TypeTuple::toDecoBuffer() this = %p, %s\n", this, toChars()); + Type::toDecoBuffer(buf, flag); + OutBuffer buf2; + Parameter::argsToDecoBuffer(&buf2, arguments); + unsigned len = buf2.offset; + buf->printf("%d%.*s", len, len, (char *)buf2.extractData()); +} + +Expression *TypeTuple::getProperty(Loc loc, Identifier *ident) +{ Expression *e; + +#if LOGDOTEXP + printf("TypeTuple::getProperty(type = '%s', ident = '%s')\n", toChars(), ident->toChars()); +#endif + if (ident == Id::length) + { + e = new IntegerExp(loc, arguments->dim, Type::tsize_t); + } + else + { + error(loc, "no property '%s' for tuple '%s'", ident->toChars(), toChars()); + e = new ErrorExp(); + } + return e; +} + +/***************************** TypeSlice *****************************/ + +/* This is so we can slice a TypeTuple */ + +TypeSlice::TypeSlice(Type *next, Expression *lwr, Expression *upr) + : TypeNext(Tslice, next) +{ + //printf("TypeSlice[%s .. %s]\n", lwr->toChars(), upr->toChars()); + this->lwr = lwr; + this->upr = upr; +} + +Type *TypeSlice::syntaxCopy() +{ + Type *t = new TypeSlice(next->syntaxCopy(), lwr->syntaxCopy(), upr->syntaxCopy()); + t->mod = mod; + return t; +} + +Type *TypeSlice::semantic(Loc loc, Scope *sc) +{ + //printf("TypeSlice::semantic() %s\n", toChars()); + next = next->semantic(loc, sc); + transitive(); + //printf("next: %s\n", next->toChars()); + + Type *tbn = next->toBasetype(); + if (tbn->ty != Ttuple) + { error(loc, "can only slice tuple types, not %s", tbn->toChars()); + return Type::terror; + } + TypeTuple *tt = (TypeTuple *)tbn; + + lwr = semanticLength(sc, tbn, lwr); + lwr = lwr->optimize(WANTvalue | WANTinterpret); + uinteger_t i1 = lwr->toUInteger(); + + upr = semanticLength(sc, tbn, upr); + upr = upr->optimize(WANTvalue | WANTinterpret); + uinteger_t i2 = upr->toUInteger(); + + if (!(i1 <= i2 && i2 <= tt->arguments->dim)) + { error(loc, "slice [%ju..%ju] is out of range of [0..%u]", i1, i2, tt->arguments->dim); + return Type::terror; + } + + Parameters *args = new Parameters; + args->reserve(i2 - i1); + for (size_t i = i1; i < i2; i++) + { Parameter *arg = tt->arguments->tdata()[i]; + args->push(arg); + } + + Type *t = (new TypeTuple(args))->semantic(loc, sc); + return t; +} + +void TypeSlice::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps) +{ + next->resolve(loc, sc, pe, pt, ps); + if (*pe) + { // It's really a slice expression + Expression *e; + e = new SliceExp(loc, *pe, lwr, upr); + *pe = e; + } + else if (*ps) + { Dsymbol *s = *ps; + TupleDeclaration *td = s->isTupleDeclaration(); + if (td) + { + /* It's a slice of a TupleDeclaration + */ + ScopeDsymbol *sym = new ArrayScopeSymbol(sc, td); + sym->parent = sc->scopesym; + sc = sc->push(sym); + + lwr = lwr->semantic(sc); + lwr = lwr->optimize(WANTvalue | WANTinterpret); + uinteger_t i1 = lwr->toUInteger(); + + upr = upr->semantic(sc); + upr = upr->optimize(WANTvalue | WANTinterpret); + uinteger_t i2 = upr->toUInteger(); + + sc = sc->pop(); + + if (!(i1 <= i2 && i2 <= td->objects->dim)) + { error(loc, "slice [%ju..%ju] is out of range of [0..%u]", i1, i2, td->objects->dim); + goto Ldefault; + } + + if (i1 == 0 && i2 == td->objects->dim) + { + *ps = td; + return; + } + + /* Create a new TupleDeclaration which + * is a slice [i1..i2] out of the old one. + */ + Objects *objects = new Objects; + objects->setDim(i2 - i1); + for (size_t i = 0; i < objects->dim; i++) + { + objects->tdata()[i] = td->objects->tdata()[(size_t)i1 + i]; + } + + TupleDeclaration *tds = new TupleDeclaration(loc, td->ident, objects); + *ps = tds; + } + else + goto Ldefault; + } + else + { + Ldefault: + Type::resolve(loc, sc, pe, pt, ps); + } +} + +void TypeSlice::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + next->toCBuffer2(buf, hgs, this->mod); + + buf->printf("[%s .. ", lwr->toChars()); + buf->printf("%s]", upr->toChars()); +} + +/***************************** TypeNull *****************************/ + +TypeNull::TypeNull() + : Type(Tnull) +{ +} + +Type *TypeNull::syntaxCopy() +{ + // No semantic analysis done, no need to copy + return this; +} + +MATCH TypeNull::implicitConvTo(Type *to) +{ + //printf("TypeNull::implicitConvTo(this=%p, to=%p)\n", this, to); + //printf("from: %s\n", toChars()); + //printf("to : %s\n", to->toChars()); + MATCH m = Type::implicitConvTo(to); + if (m) + return m; + + // NULL implicitly converts to any pointer type or dynamic array + //if (type->ty == Tpointer && type->nextOf()->ty == Tvoid) + { + Type *tb= to->toBasetype(); + if (tb->ty == Tpointer || tb->ty == Tarray || + tb->ty == Taarray || tb->ty == Tclass || + tb->ty == Tdelegate) + return MATCHconst; + } + + return MATCHnomatch; +} + +void TypeNull::toDecoBuffer(OutBuffer *buf, int flag) +{ + //tvoidptr->toDecoBuffer(buf, flag); + Type::toDecoBuffer(buf, flag); +} + +void TypeNull::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs) +{ + buf->writestring("typeof(null)"); +} + +d_uns64 TypeNull::size(Loc loc) { return tvoidptr->size(loc); } +//Expression *TypeNull::getProperty(Loc loc, Identifier *ident) { return new ErrorExp(); } +//Expression *TypeNull::dotExp(Scope *sc, Expression *e, Identifier *ident) { return new ErrorExp(); } +Expression *TypeNull::defaultInit(Loc loc) { return new NullExp(0, Type::tnull); } +//Expression *TypeNull::defaultInitLiteral(Loc loc) { return new ErrorExp(); } + +/***************************** Parameter *****************************/ + +Parameter::Parameter(StorageClass storageClass, Type *type, Identifier *ident, Expression *defaultArg) +{ + this->type = type; + this->ident = ident; + this->storageClass = storageClass; + this->defaultArg = defaultArg; +} + +Parameter *Parameter::syntaxCopy() +{ + Parameter *a = new Parameter(storageClass, + type ? type->syntaxCopy() : NULL, + ident, + defaultArg ? defaultArg->syntaxCopy() : NULL); + return a; +} + +Parameters *Parameter::arraySyntaxCopy(Parameters *args) +{ Parameters *a = NULL; + + if (args) + { + a = new Parameters(); + a->setDim(args->dim); + for (size_t i = 0; i < a->dim; i++) + { Parameter *arg = args->tdata()[i]; + + arg = arg->syntaxCopy(); + a->tdata()[i] = arg; + } + } + return a; +} + +char *Parameter::argsTypesToChars(Parameters *args, int varargs) +{ + OutBuffer *buf = new OutBuffer(); + +#if 1 + HdrGenState hgs; + argsToCBuffer(buf, &hgs, args, varargs); +#else + buf->writeByte('('); + if (args) + { OutBuffer argbuf; + HdrGenState hgs; + + for (size_t i = 0; i < args->dim; i++) + { if (i) + buf->writeByte(','); + Parameter *arg = args->tdata()[i]; + argbuf.reset(); + arg->type->toCBuffer2(&argbuf, &hgs, 0); + buf->write(&argbuf); + } + if (varargs) + { + if (i && varargs == 1) + buf->writeByte(','); + buf->writestring("..."); + } + } + buf->writeByte(')'); +#endif + return buf->toChars(); +} + +void Parameter::argsToCBuffer(OutBuffer *buf, HdrGenState *hgs, Parameters *arguments, int varargs) +{ + buf->writeByte('('); + if (arguments) + { + OutBuffer argbuf; + + size_t dim = Parameter::dim(arguments); + for (size_t i = 0; i < dim; i++) + { + if (i) + buf->writestring(", "); + Parameter *arg = Parameter::getNth(arguments, i); + + if (arg->storageClass & STCauto) + buf->writestring("auto "); + + if (arg->storageClass & STCout) + buf->writestring("out "); + else if (arg->storageClass & STCref) + buf->writestring((global.params.Dversion == 1) + ? "inout " : "ref "); + else if (arg->storageClass & STCin) + buf->writestring("in "); + else if (arg->storageClass & STClazy) + buf->writestring("lazy "); + else if (arg->storageClass & STCalias) + buf->writestring("alias "); + + StorageClass stc = arg->storageClass; + if (arg->type && arg->type->mod & MODshared) + stc &= ~STCshared; + + StorageClassDeclaration::stcToCBuffer(buf, + stc & (STCconst | STCimmutable | STCshared | STCscope)); + + argbuf.reset(); + if (arg->storageClass & STCalias) + { if (arg->ident) + argbuf.writestring(arg->ident->toChars()); + } + else + arg->type->toCBuffer(&argbuf, arg->ident, hgs); + if (arg->defaultArg) + { + argbuf.writestring(" = "); + arg->defaultArg->toCBuffer(&argbuf, hgs); + } + buf->write(&argbuf); + } + if (varargs) + { + if (arguments->dim && varargs == 1) + buf->writeByte(','); + buf->writestring("..."); + } + } + buf->writeByte(')'); +} + +static int argsToDecoBufferDg(void *ctx, size_t n, Parameter *arg) +{ + arg->toDecoBuffer((OutBuffer *)ctx); + return 0; +} + +void Parameter::argsToDecoBuffer(OutBuffer *buf, Parameters *arguments) +{ + //printf("Parameter::argsToDecoBuffer()\n"); + // Write argument types + foreach(arguments, &argsToDecoBufferDg, buf); +} + +/**************************************** + * Determine if parameter list is really a template parameter list + * (i.e. it has auto or alias parameters) + */ + +static int isTPLDg(void *ctx, size_t n, Parameter *arg) +{ + if (arg->storageClass & (STCalias | STCauto | STCstatic)) + return 1; + return 0; +} + +int Parameter::isTPL(Parameters *arguments) +{ + //printf("Parameter::isTPL()\n"); + return foreach(arguments, &isTPLDg, NULL); +} + +/**************************************************** + * Determine if parameter is a lazy array of delegates. + * If so, return the return type of those delegates. + * If not, return NULL. + */ + +Type *Parameter::isLazyArray() +{ +// if (inout == Lazy) + { + Type *tb = type->toBasetype(); + if (tb->ty == Tsarray || tb->ty == Tarray) + { + Type *tel = ((TypeArray *)tb)->next->toBasetype(); + if (tel->ty == Tdelegate) + { + TypeDelegate *td = (TypeDelegate *)tel; + TypeFunction *tf = (TypeFunction *)td->next; + + if (!tf->varargs && Parameter::dim(tf->parameters) == 0) + { + return tf->next; // return type of delegate + } + } + } + } + return NULL; +} + +void Parameter::toDecoBuffer(OutBuffer *buf) +{ + if (storageClass & STCscope) + buf->writeByte('M'); + switch (storageClass & (STCin | STCout | STCref | STClazy)) + { case 0: + case STCin: + break; + case STCout: + buf->writeByte('J'); + break; + case STCref: + buf->writeByte('K'); + break; + case STClazy: + buf->writeByte('L'); + break; + default: +#ifdef DEBUG + printf("storageClass = x%llx\n", storageClass & (STCin | STCout | STCref | STClazy)); + halt(); +#endif + assert(0); + } +#if 0 + int mod = 0x100; + if (type->toBasetype()->ty == Tclass) + mod = 0; + type->toDecoBuffer(buf, mod); +#else + //type->toHeadMutable()->toDecoBuffer(buf, 0); + type->toDecoBuffer(buf, 0); +#endif +} + +/*************************************** + * Determine number of arguments, folding in tuples. + */ + +static int dimDg(void *ctx, size_t n, Parameter *) +{ + ++*(size_t *)ctx; + return 0; +} + +size_t Parameter::dim(Parameters *args) +{ + size_t n = 0; + foreach(args, &dimDg, &n); + return n; +} + +/*************************************** + * Get nth Parameter, folding in tuples. + * Returns: + * Parameter* nth Parameter + * NULL not found, *pn gets incremented by the number + * of Parameters + */ + +struct GetNthParamCtx +{ + size_t nth; + Parameter *arg; +}; + +static int getNthParamDg(void *ctx, size_t n, Parameter *arg) +{ + GetNthParamCtx *p = (GetNthParamCtx *)ctx; + if (n == p->nth) + { p->arg = arg; + return 1; + } + return 0; +} + +Parameter *Parameter::getNth(Parameters *args, size_t nth, size_t *pn) +{ + GetNthParamCtx ctx = { nth, NULL }; + int res = foreach(args, &getNthParamDg, &ctx); + return res ? ctx.arg : NULL; +} + +/*************************************** + * Expands tuples in args in depth first order. Calls + * dg(void *ctx, size_t argidx, Parameter *arg) for each Parameter. + * If dg returns !=0, stops and returns that value else returns 0. + * Use this function to avoid the O(N + N^2/2) complexity of + * calculating dim and calling N times getNth. + */ + +int Parameter::foreach(Parameters *args, Parameter::ForeachDg dg, void *ctx, size_t *pn) +{ + assert(dg); + if (!args) + return 0; + + size_t n = pn ? *pn : 0; // take over index + int result = 0; + for (size_t i = 0; i < args->dim; i++) + { Parameter *arg = args->tdata()[i]; + Type *t = arg->type->toBasetype(); + + if (t->ty == Ttuple) + { TypeTuple *tu = (TypeTuple *)t; + result = foreach(tu->arguments, dg, ctx, &n); + } + else + result = dg(ctx, n++, arg); + + if (result) + break; + } + + if (pn) + *pn = n; // update index + return result; +} diff --git a/mtype.h b/mtype.h new file mode 100644 index 00000000..b5bedf9e --- /dev/null +++ b/mtype.h @@ -0,0 +1,992 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2010 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_MTYPE_H +#define DMD_MTYPE_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "root.h" +#include "stringtable.h" + +#include "arraytypes.h" +#include "expression.h" + +struct Scope; +struct Identifier; +struct Expression; +struct StructDeclaration; +struct ClassDeclaration; +struct VarDeclaration; +struct EnumDeclaration; +struct TypedefDeclaration; +struct TypeInfoDeclaration; +struct Dsymbol; +struct TemplateInstance; +struct CppMangleState; +struct TemplateDeclaration; +enum LINK; + +struct TypeBasic; +struct HdrGenState; +struct Parameter; + +// Back end +#if IN_GCC +union tree_node; typedef union tree_node TYPE; +typedef TYPE type; +#else +typedef struct TYPE type; +#endif +struct Symbol; +struct TypeTuple; + +enum ENUMTY +{ + Tarray, // slice array, aka T[] + Tsarray, // static array, aka T[dimension] + Taarray, // associative array, aka T[type] + Tpointer, + Treference, + Tfunction, + Tident, + Tclass, + Tstruct, + Tenum, + + Ttypedef, + Tdelegate, + Tnone, + Tvoid, + Tint8, + Tuns8, + Tint16, + Tuns16, + Tint32, + Tuns32, + + Tint64, + Tuns64, + Tfloat32, + Tfloat64, + Tfloat80, + Timaginary32, + Timaginary64, + Timaginary80, + Tcomplex32, + Tcomplex64, + + Tcomplex80, + Tbool, + Tchar, + Twchar, + Tdchar, + Terror, + Tinstance, + Ttypeof, + Ttuple, + Tslice, + + Treturn, + Tnull, + Tvector, + TMAX +}; +typedef unsigned char TY; // ENUMTY + +#define Tascii Tchar + +extern int Tsize_t; +extern int Tptrdiff_t; + + +struct Type : Object +{ + TY ty; + unsigned char mod; // modifiers MODxxxx + /* pick this order of numbers so switch statements work better + */ + #define MODconst 1 // type is const + #define MODimmutable 4 // type is immutable + #define MODshared 2 // type is shared + #define MODwild 8 // type is wild + #define MODmutable 0x10 // type is mutable (only used in wildcard matching) + char *deco; + + /* These are cached values that are lazily evaluated by constOf(), invariantOf(), etc. + * They should not be referenced by anybody but mtype.c. + * They can be NULL if not lazily evaluated yet. + * Note that there is no "shared immutable", because that is just immutable + * Naked == no MOD bits + */ + + Type *cto; // MODconst ? naked version of this type : const version + Type *ito; // MODimmutable ? naked version of this type : immutable version + Type *sto; // MODshared ? naked version of this type : shared mutable version + Type *scto; // MODshared|MODconst ? naked version of this type : shared const version + Type *wto; // MODwild ? naked version of this type : wild version + Type *swto; // MODshared|MODwild ? naked version of this type : shared wild version + + Type *pto; // merged pointer to this type + Type *rto; // reference to this type + Type *arrayof; // array of this type + TypeInfoDeclaration *vtinfo; // TypeInfo object for this Type + + type *ctype; // for back end + + #define tvoid basic[Tvoid] + #define tint8 basic[Tint8] + #define tuns8 basic[Tuns8] + #define tint16 basic[Tint16] + #define tuns16 basic[Tuns16] + #define tint32 basic[Tint32] + #define tuns32 basic[Tuns32] + #define tint64 basic[Tint64] + #define tuns64 basic[Tuns64] + #define tfloat32 basic[Tfloat32] + #define tfloat64 basic[Tfloat64] + #define tfloat80 basic[Tfloat80] + + #define timaginary32 basic[Timaginary32] + #define timaginary64 basic[Timaginary64] + #define timaginary80 basic[Timaginary80] + + #define tcomplex32 basic[Tcomplex32] + #define tcomplex64 basic[Tcomplex64] + #define tcomplex80 basic[Tcomplex80] + + #define tbool basic[Tbool] + #define tchar basic[Tchar] + #define twchar basic[Twchar] + #define tdchar basic[Tdchar] + + // Some special types + #define tshiftcnt tint32 // right side of shift expression +// #define tboolean tint32 // result of boolean expression + #define tboolean tbool // result of boolean expression + #define tindex tsize_t // array/ptr index + static Type *tvoidptr; // void* + static Type *tstring; // immutable(char)[] + #define terror basic[Terror] // for error recovery + + #define tnull basic[Tnull] // for null type + + #define tsize_t basic[Tsize_t] // matches size_t alias + #define tptrdiff_t basic[Tptrdiff_t] // matches ptrdiff_t alias + #define thash_t tsize_t // matches hash_t alias + + static ClassDeclaration *typeinfo; + static ClassDeclaration *typeinfoclass; + static ClassDeclaration *typeinfointerface; + static ClassDeclaration *typeinfostruct; + static ClassDeclaration *typeinfotypedef; + static ClassDeclaration *typeinfopointer; + static ClassDeclaration *typeinfoarray; + static ClassDeclaration *typeinfostaticarray; + static ClassDeclaration *typeinfoassociativearray; + static ClassDeclaration *typeinfovector; + static ClassDeclaration *typeinfoenum; + static ClassDeclaration *typeinfofunction; + static ClassDeclaration *typeinfodelegate; + static ClassDeclaration *typeinfotypelist; + static ClassDeclaration *typeinfoconst; + static ClassDeclaration *typeinfoinvariant; + static ClassDeclaration *typeinfoshared; + static ClassDeclaration *typeinfowild; + + static TemplateDeclaration *associativearray; + + static Type *basic[TMAX]; + static unsigned char mangleChar[TMAX]; + static unsigned char sizeTy[TMAX]; + static StringTable stringtable; + + // These tables are for implicit conversion of binary ops; + // the indices are the type of operand one, followed by operand two. + static unsigned char impcnvResult[TMAX][TMAX]; + static unsigned char impcnvType1[TMAX][TMAX]; + static unsigned char impcnvType2[TMAX][TMAX]; + + // If !=0, give warning on implicit conversion + static unsigned char impcnvWarn[TMAX][TMAX]; + + Type(TY ty); + virtual Type *syntaxCopy(); + int equals(Object *o); + int dyncast() { return DYNCAST_TYPE; } // kludge for template.isType() + int covariant(Type *t); + char *toChars(); + static char needThisPrefix(); + static void init(); + d_uns64 size(); + virtual d_uns64 size(Loc loc); + virtual unsigned alignsize(); + virtual Type *semantic(Loc loc, Scope *sc); + Type *trySemantic(Loc loc, Scope *sc); + virtual void toDecoBuffer(OutBuffer *buf, int flag = 0); + Type *merge(); + Type *merge2(); + virtual void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); + virtual void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + void toCBuffer3(OutBuffer *buf, HdrGenState *hgs, int mod); + void modToBuffer(OutBuffer *buf); +#if CPP_MANGLE + virtual void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + virtual int isintegral(); + virtual int isfloating(); // real, imaginary, or complex + virtual int isreal(); + virtual int isimaginary(); + virtual int iscomplex(); + virtual int isscalar(); + virtual int isunsigned(); + virtual int isscope(); + virtual int isString(); + virtual int isAssignable(); + virtual int checkBoolean(); // if can be converted to boolean value + virtual void checkDeprecated(Loc loc, Scope *sc); + int isConst() { return mod & MODconst; } + int isImmutable() { return mod & MODimmutable; } + int isMutable() { return !(mod & (MODconst | MODimmutable | MODwild)); } + int isShared() { return mod & MODshared; } + int isSharedConst() { return mod == (MODshared | MODconst); } + int isWild() { return mod & MODwild; } + int isSharedWild() { return mod == (MODshared | MODwild); } + int isNaked() { return mod == 0; } + Type *constOf(); + Type *invariantOf(); + Type *mutableOf(); + Type *sharedOf(); + Type *sharedConstOf(); + Type *unSharedOf(); + Type *wildOf(); + Type *sharedWildOf(); + void fixTo(Type *t); + void check(); + Type *addSTC(StorageClass stc); + Type *castMod(unsigned mod); + Type *addMod(unsigned mod); + Type *addStorageClass(StorageClass stc); + Type *pointerTo(); + Type *referenceTo(); + Type *arrayOf(); + Type *aliasthisOf(); + virtual Type *makeConst(); + virtual Type *makeInvariant(); + virtual Type *makeShared(); + virtual Type *makeSharedConst(); + virtual Type *makeWild(); + virtual Type *makeSharedWild(); + virtual Type *makeMutable(); + virtual Dsymbol *toDsymbol(Scope *sc); + virtual Type *toBasetype(); + virtual int isBaseOf(Type *t, int *poffset); + virtual MATCH implicitConvTo(Type *to); + virtual MATCH constConv(Type *to); + virtual unsigned wildConvTo(Type *tprm); + Type *substWildTo(unsigned mod); + virtual Type *toHeadMutable(); + virtual ClassDeclaration *isClassHandle(); + virtual Expression *getProperty(Loc loc, Identifier *ident); + virtual Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + Expression *noMember(Scope *sc, Expression *e, Identifier *ident); + virtual unsigned memalign(unsigned salign); + virtual Expression *defaultInit(Loc loc = 0); + virtual Expression *defaultInitLiteral(Loc loc = 0); + virtual int isZeroInit(Loc loc = 0); // if initializer is 0 + virtual dt_t **toDt(dt_t **pdt); + Identifier *getTypeInfoIdent(int internal); + virtual MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); + virtual void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps); + Expression *getInternalTypeInfo(Scope *sc); + Expression *getTypeInfo(Scope *sc); + virtual TypeInfoDeclaration *getTypeInfoDeclaration(); + virtual int builtinTypeInfo(); + virtual Type *reliesOnTident(); + virtual int hasWild(); + virtual Expression *toExpression(); + virtual int hasPointers(); + virtual TypeTuple *toArgTypes(); + virtual Type *nextOf(); + uinteger_t sizemask(); + virtual int needsDestruction(); + + static void error(Loc loc, const char *format, ...); + static void warning(Loc loc, const char *format, ...); + + // For backend + virtual unsigned totym(); + virtual type *toCtype(); + virtual type *toCParamtype(); + virtual Symbol *toSymbol(); + + // For eliminating dynamic_cast + virtual TypeBasic *isTypeBasic(); +}; + +struct TypeError : Type +{ + TypeError(); + Type *syntaxCopy(); + + void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); + + d_uns64 size(Loc loc); + Expression *getProperty(Loc loc, Identifier *ident); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + Expression *defaultInit(Loc loc); + Expression *defaultInitLiteral(Loc loc); +}; + +struct TypeNext : Type +{ + Type *next; + + TypeNext(TY ty, Type *next); + void toDecoBuffer(OutBuffer *buf, int flag); + void checkDeprecated(Loc loc, Scope *sc); + Type *reliesOnTident(); + int hasWild(); + Type *nextOf(); + Type *makeConst(); + Type *makeInvariant(); + Type *makeShared(); + Type *makeSharedConst(); + Type *makeWild(); + Type *makeSharedWild(); + Type *makeMutable(); + MATCH constConv(Type *to); + unsigned wildConvTo(Type *tprm); + void transitive(); +}; + +struct TypeBasic : Type +{ + const char *dstring; + unsigned flags; + + TypeBasic(TY ty); + Type *syntaxCopy(); + d_uns64 size(Loc loc); + unsigned alignsize(); + Expression *getProperty(Loc loc, Identifier *ident); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + char *toChars(); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + int isintegral(); + int isfloating(); + int isreal(); + int isimaginary(); + int iscomplex(); + int isscalar(); + int isunsigned(); + MATCH implicitConvTo(Type *to); + Expression *defaultInit(Loc loc); + int isZeroInit(Loc loc); + int builtinTypeInfo(); + TypeTuple *toArgTypes(); + + // For eliminating dynamic_cast + TypeBasic *isTypeBasic(); +}; + +struct TypeVector : Type +{ + Type *basetype; + + TypeVector(Loc loc, Type *basetype); + Type *syntaxCopy(); + Type *semantic(Loc loc, Scope *sc); + d_uns64 size(Loc loc); + unsigned alignsize(); + Expression *getProperty(Loc loc, Identifier *ident); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + char *toChars(); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + void toDecoBuffer(OutBuffer *buf, int flag); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + int isintegral(); + int isfloating(); + int isscalar(); + int isunsigned(); + int checkBoolean(); + MATCH implicitConvTo(Type *to); + Expression *defaultInit(Loc loc); + TypeBasic *elementType(); + int isZeroInit(Loc loc); + TypeInfoDeclaration *getTypeInfoDeclaration(); + TypeTuple *toArgTypes(); +}; + +struct TypeArray : TypeNext +{ + TypeArray(TY ty, Type *next); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); +}; + +// Static array, one with a fixed dimension +struct TypeSArray : TypeArray +{ + Expression *dim; + + TypeSArray(Type *t, Expression *dim); + Type *syntaxCopy(); + d_uns64 size(Loc loc); + unsigned alignsize(); + Type *semantic(Loc loc, Scope *sc); + void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps); + void toDecoBuffer(OutBuffer *buf, int flag); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + int isString(); + int isZeroInit(Loc loc); + unsigned memalign(unsigned salign); + MATCH constConv(Type *to); + MATCH implicitConvTo(Type *to); + Expression *defaultInit(Loc loc); + Expression *defaultInitLiteral(Loc loc); + dt_t **toDt(dt_t **pdt); + dt_t **toDtElem(dt_t **pdt, Expression *e); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); + TypeInfoDeclaration *getTypeInfoDeclaration(); + Expression *toExpression(); + int hasPointers(); + int needsDestruction(); + TypeTuple *toArgTypes(); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + + type *toCtype(); + type *toCParamtype(); +}; + +// Dynamic array, no dimension +struct TypeDArray : TypeArray +{ + TypeDArray(Type *t); + Type *syntaxCopy(); + d_uns64 size(Loc loc); + unsigned alignsize(); + Type *semantic(Loc loc, Scope *sc); + void toDecoBuffer(OutBuffer *buf, int flag); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + int isString(); + int isZeroInit(Loc loc); + int checkBoolean(); + MATCH implicitConvTo(Type *to); + Expression *defaultInit(Loc loc); + int builtinTypeInfo(); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); + TypeInfoDeclaration *getTypeInfoDeclaration(); + int hasPointers(); + TypeTuple *toArgTypes(); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + + type *toCtype(); +}; + +struct TypeAArray : TypeArray +{ + Type *index; // key type + Loc loc; + Scope *sc; + + StructDeclaration *impl; // implementation + + TypeAArray(Type *t, Type *index); + Type *syntaxCopy(); + d_uns64 size(Loc loc); + Type *semantic(Loc loc, Scope *sc); + StructDeclaration *getImpl(); + void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps); + void toDecoBuffer(OutBuffer *buf, int flag); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + Expression *defaultInit(Loc loc); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); + int isZeroInit(Loc loc); + int checkBoolean(); + TypeInfoDeclaration *getTypeInfoDeclaration(); + int hasPointers(); + TypeTuple *toArgTypes(); + MATCH implicitConvTo(Type *to); + MATCH constConv(Type *to); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + + // Back end + Symbol *aaGetSymbol(const char *func, int flags); + + type *toCtype(); +}; + +struct TypePointer : TypeNext +{ + TypePointer(Type *t); + Type *syntaxCopy(); + Type *semantic(Loc loc, Scope *sc); + d_uns64 size(Loc loc); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + MATCH implicitConvTo(Type *to); + MATCH constConv(Type *to); + int isscalar(); + Expression *defaultInit(Loc loc); + int isZeroInit(Loc loc); + TypeInfoDeclaration *getTypeInfoDeclaration(); + int hasPointers(); + TypeTuple *toArgTypes(); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + + type *toCtype(); +}; + +struct TypeReference : TypeNext +{ + TypeReference(Type *t); + Type *syntaxCopy(); + Type *semantic(Loc loc, Scope *sc); + d_uns64 size(Loc loc); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + Expression *defaultInit(Loc loc); + int isZeroInit(Loc loc); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif +}; + +enum RET +{ + RETregs = 1, // returned in registers + RETstack = 2, // returned on stack +}; + +enum TRUST +{ + TRUSTdefault = 0, + TRUSTsystem = 1, // @system (same as TRUSTdefault) + TRUSTtrusted = 2, // @trusted + TRUSTsafe = 3, // @safe +}; + +enum PURE +{ + PUREimpure = 0, // not pure at all + PUREweak = 1, // no mutable globals are read or written + PUREconst = 2, // parameters are values or const + PUREstrong = 3, // parameters are values or immutable + PUREfwdref = 4, // it's pure, but not known which level yet +}; + +struct TypeFunction : TypeNext +{ + // .next is the return type + + Parameters *parameters; // function parameters + int varargs; // 1: T t, ...) style for variable number of arguments + // 2: T t ...) style for variable number of arguments + bool isnothrow; // true: nothrow + bool isproperty; // can be called without parentheses + bool isref; // true: returns a reference + enum LINK linkage; // calling convention + enum TRUST trust; // level of trust + enum PURE purity; // PURExxxx + Expressions *fargs; // function arguments + + int inuse; + + TypeFunction(Parameters *parameters, Type *treturn, int varargs, enum LINK linkage, StorageClass stc = 0); + Type *syntaxCopy(); + Type *semantic(Loc loc, Scope *sc); + void purityLevel(); + void toDecoBuffer(OutBuffer *buf, int flag); + void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); + void toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TypeFunction *attrs, TemplateDeclaration *td); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + void attributesToCBuffer(OutBuffer *buf, int mod); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); + TypeInfoDeclaration *getTypeInfoDeclaration(); + Type *reliesOnTident(); + bool hasLazyParameters(); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + bool parameterEscapes(Parameter *p); + + int callMatch(Expression *ethis, Expressions *toargs, int flag = 0); + type *toCtype(); + enum RET retStyle(); + + unsigned totym(); + + Expression *defaultInit(Loc loc); +}; + +struct TypeDelegate : TypeNext +{ + // .next is a TypeFunction + + TypeDelegate(Type *t); + Type *syntaxCopy(); + Type *semantic(Loc loc, Scope *sc); + d_uns64 size(Loc loc); + unsigned alignsize(); + MATCH implicitConvTo(Type *to); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + Expression *defaultInit(Loc loc); + int isZeroInit(Loc loc); + int checkBoolean(); + TypeInfoDeclaration *getTypeInfoDeclaration(); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + int hasPointers(); + TypeTuple *toArgTypes(); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + + type *toCtype(); +}; + +struct TypeQualified : Type +{ + Loc loc; + Identifiers idents; // array of Identifier's representing ident.ident.ident etc. + + TypeQualified(TY ty, Loc loc); + void syntaxCopyHelper(TypeQualified *t); + void addIdent(Identifier *ident); + void toCBuffer2Helper(OutBuffer *buf, HdrGenState *hgs); + d_uns64 size(Loc loc); + void resolveHelper(Loc loc, Scope *sc, Dsymbol *s, Dsymbol *scopesym, + Expression **pe, Type **pt, Dsymbol **ps); +}; + +struct TypeIdentifier : TypeQualified +{ + Identifier *ident; + + TypeIdentifier(Loc loc, Identifier *ident); + Type *syntaxCopy(); + //char *toChars(); + void toDecoBuffer(OutBuffer *buf, int flag); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps); + Dsymbol *toDsymbol(Scope *sc); + Type *semantic(Loc loc, Scope *sc); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); + Type *reliesOnTident(); + Expression *toExpression(); +}; + +/* Similar to TypeIdentifier, but with a TemplateInstance as the root + */ +struct TypeInstance : TypeQualified +{ + TemplateInstance *tempinst; + + TypeInstance(Loc loc, TemplateInstance *tempinst); + Type *syntaxCopy(); + //char *toChars(); + //void toDecoBuffer(OutBuffer *buf, int flag); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps); + Type *semantic(Loc loc, Scope *sc); + Dsymbol *toDsymbol(Scope *sc); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); +}; + +struct TypeTypeof : TypeQualified +{ + Expression *exp; + int inuse; + + TypeTypeof(Loc loc, Expression *exp); + Type *syntaxCopy(); + Dsymbol *toDsymbol(Scope *sc); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + Type *semantic(Loc loc, Scope *sc); + d_uns64 size(Loc loc); +}; + +struct TypeReturn : TypeQualified +{ + TypeReturn(Loc loc); + Type *syntaxCopy(); + Dsymbol *toDsymbol(Scope *sc); + Type *semantic(Loc loc, Scope *sc); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); +}; + +struct TypeStruct : Type +{ + StructDeclaration *sym; + + TypeStruct(StructDeclaration *sym); + d_uns64 size(Loc loc); + unsigned alignsize(); + char *toChars(); + Type *syntaxCopy(); + Type *semantic(Loc loc, Scope *sc); + Dsymbol *toDsymbol(Scope *sc); + void toDecoBuffer(OutBuffer *buf, int flag); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + unsigned memalign(unsigned salign); + Expression *defaultInit(Loc loc); + Expression *defaultInitLiteral(Loc loc); + int isZeroInit(Loc loc); + int isAssignable(); + int checkBoolean(); + int needsDestruction(); + dt_t **toDt(dt_t **pdt); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); + TypeInfoDeclaration *getTypeInfoDeclaration(); + int hasPointers(); + TypeTuple *toArgTypes(); + MATCH implicitConvTo(Type *to); + MATCH constConv(Type *to); + unsigned wildConvTo(Type *tprm); + Type *toHeadMutable(); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + + type *toCtype(); +}; + +struct TypeEnum : Type +{ + EnumDeclaration *sym; + + TypeEnum(EnumDeclaration *sym); + Type *syntaxCopy(); + d_uns64 size(Loc loc); + unsigned alignsize(); + char *toChars(); + Type *semantic(Loc loc, Scope *sc); + Dsymbol *toDsymbol(Scope *sc); + void toDecoBuffer(OutBuffer *buf, int flag); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + Expression *getProperty(Loc loc, Identifier *ident); + int isintegral(); + int isfloating(); + int isreal(); + int isimaginary(); + int iscomplex(); + int isscalar(); + int isunsigned(); + int checkBoolean(); + int isAssignable(); + int needsDestruction(); + MATCH implicitConvTo(Type *to); + MATCH constConv(Type *to); + Type *toBasetype(); + Expression *defaultInit(Loc loc); + int isZeroInit(Loc loc); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); + TypeInfoDeclaration *getTypeInfoDeclaration(); + int hasPointers(); + TypeTuple *toArgTypes(); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + + type *toCtype(); +}; + +struct TypeTypedef : Type +{ + TypedefDeclaration *sym; + + TypeTypedef(TypedefDeclaration *sym); + Type *syntaxCopy(); + d_uns64 size(Loc loc); + unsigned alignsize(); + char *toChars(); + Type *semantic(Loc loc, Scope *sc); + Dsymbol *toDsymbol(Scope *sc); + void toDecoBuffer(OutBuffer *buf, int flag); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + Expression *getProperty(Loc loc, Identifier *ident); + int isintegral(); + int isfloating(); + int isreal(); + int isimaginary(); + int iscomplex(); + int isscalar(); + int isunsigned(); + int checkBoolean(); + int isAssignable(); + int needsDestruction(); + Type *toBasetype(); + MATCH implicitConvTo(Type *to); + MATCH constConv(Type *to); + Type *toHeadMutable(); + Expression *defaultInit(Loc loc); + Expression *defaultInitLiteral(Loc loc); + int isZeroInit(Loc loc); + dt_t **toDt(dt_t **pdt); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); + TypeInfoDeclaration *getTypeInfoDeclaration(); + int hasPointers(); + TypeTuple *toArgTypes(); + int hasWild(); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + + type *toCtype(); + type *toCParamtype(); +}; + +struct TypeClass : Type +{ + ClassDeclaration *sym; + + TypeClass(ClassDeclaration *sym); + d_uns64 size(Loc loc); + char *toChars(); + Type *syntaxCopy(); + Type *semantic(Loc loc, Scope *sc); + Dsymbol *toDsymbol(Scope *sc); + void toDecoBuffer(OutBuffer *buf, int flag); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + ClassDeclaration *isClassHandle(); + int isBaseOf(Type *t, int *poffset); + MATCH implicitConvTo(Type *to); + MATCH constConv(Type *to); + unsigned wildConvTo(Type *tprm); + Type *toHeadMutable(); + Expression *defaultInit(Loc loc); + int isZeroInit(Loc loc); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); + int isscope(); + int checkBoolean(); + TypeInfoDeclaration *getTypeInfoDeclaration(); + int hasPointers(); + TypeTuple *toArgTypes(); + int builtinTypeInfo(); +#if CPP_MANGLE + void toCppMangle(OutBuffer *buf, CppMangleState *cms); +#endif + + type *toCtype(); + + Symbol *toSymbol(); +}; + +struct TypeTuple : Type +{ + Parameters *arguments; // types making up the tuple + + TypeTuple(Parameters *arguments); + TypeTuple(Expressions *exps); + TypeTuple(); + TypeTuple(Type *t1); + TypeTuple(Type *t1, Type *t2); + Type *syntaxCopy(); + Type *semantic(Loc loc, Scope *sc); + int equals(Object *o); + Type *reliesOnTident(); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + void toDecoBuffer(OutBuffer *buf, int flag); + Expression *getProperty(Loc loc, Identifier *ident); + TypeInfoDeclaration *getTypeInfoDeclaration(); +}; + +struct TypeSlice : TypeNext +{ + Expression *lwr; + Expression *upr; + + TypeSlice(Type *next, Expression *lwr, Expression *upr); + Type *syntaxCopy(); + Type *semantic(Loc loc, Scope *sc); + void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); +}; + +struct TypeNull : Type +{ + TypeNull(); + + Type *syntaxCopy(); + void toDecoBuffer(OutBuffer *buf, int flag); + MATCH implicitConvTo(Type *to); + + void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); + + d_uns64 size(Loc loc); + //Expression *getProperty(Loc loc, Identifier *ident); + //Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + Expression *defaultInit(Loc loc); + //Expression *defaultInitLiteral(Loc loc); +}; + +/**************************************************************/ + +//enum InOut { None, In, Out, InOut, Lazy }; + +struct Parameter : Object +{ + //enum InOut inout; + StorageClass storageClass; + Type *type; + Identifier *ident; + Expression *defaultArg; + + Parameter(StorageClass storageClass, Type *type, Identifier *ident, Expression *defaultArg); + Parameter *syntaxCopy(); + Type *isLazyArray(); + void toDecoBuffer(OutBuffer *buf); + static Parameters *arraySyntaxCopy(Parameters *args); + static char *argsTypesToChars(Parameters *args, int varargs); + static void argsCppMangle(OutBuffer *buf, CppMangleState *cms, Parameters *arguments, int varargs); + static void argsToCBuffer(OutBuffer *buf, HdrGenState *hgs, Parameters *arguments, int varargs); + static void argsToDecoBuffer(OutBuffer *buf, Parameters *arguments); + static int isTPL(Parameters *arguments); + static size_t dim(Parameters *arguments); + static Parameter *getNth(Parameters *arguments, size_t nth, size_t *pn = NULL); + + typedef int (*ForeachDg)(void *ctx, size_t paramidx, Parameter *param); + static int foreach(Parameters *args, ForeachDg dg, void *ctx, size_t *pn=NULL); +}; + +extern int PTRSIZE; +extern int REALSIZE; +extern int REALPAD; +extern int Tsize_t; +extern int Tptrdiff_t; + +int arrayTypeCompatible(Loc loc, Type *t1, Type *t2); +int arrayTypeCompatibleWithoutCasting(Loc loc, Type *t1, Type *t2); +void MODtoBuffer(OutBuffer *buf, unsigned char mod); +int MODimplicitConv(unsigned char modfrom, unsigned char modto); +int MODmethodConv(unsigned char modfrom, unsigned char modto); +int MODmerge(unsigned char mod1, unsigned char mod2); + +#endif /* DMD_MTYPE_H */ diff --git a/objfile.h b/objfile.h new file mode 100644 index 00000000..a872f4d0 --- /dev/null +++ b/objfile.h @@ -0,0 +1,61 @@ + + +#ifndef OBJFILE_H +#define OBJFILE_H + +#include "root.h" + +typedef void *SymHandle; +typedef unsigned SegOffset; + +enum ObjFormat +{ + NTCOFF, + ELF +}; + +struct ObjFile : File +{ + ObjFile(FileName *); + ~ObjFile(); + + ObjFile *init(ObjFormat); + + void comment(const char *); // insert comment into object file + void modulename(const char *); // set module name + void library(const char *); // add default library + void startaddress(SegHandle seg, SegOffset offset); // set start address + + // Segments + enum SegHandle + { code = 1, + data, bss + }; + + SymHandle defineSym(const char *name, SegHandle seg, SegOffset offset); + SymHandle externSym(const char *name); + + SegOffset write(SegHandle seg, const void *data, unsigned nbytes); + SegOffset writestring(SegHandle seg, char *string); + SegOffset write8(SegHandle seg, unsigned b); + SegOffset write16(SegHandle seg, unsigned w); + SegOffset write32(SegHandle seg, unsigned long v); + SegOffset write64(SegHandle seg, unsigned long long v); + SegOffset fill0(SegHandle seg, unsigned nbytes); + SegOffset align(SegHandle seg, unsigned size); + SegOffset writefixup(SegHandle seg, SymHandle sym, unsigned value, int selfrelative); + + // Non-binding hint as to how big seg will grow + void reserve(SegHandle seg, SegOffset size); + + // Set actual size + void setSize(SegHandle seg, SegOffset size); + + // Get/set offset for subsequent writes + void setOffset(SegHandle seg, SegOffset offset); + SegOffset getOffset(SegHandle seg); + + SegHandle createSeg(const char *name); +}; + +#endif diff --git a/opover.c b/opover.c new file mode 100644 index 00000000..786fac06 --- /dev/null +++ b/opover.c @@ -0,0 +1,1611 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include +#if _MSC_VER +#include +#else +#include +#endif + +#ifdef __APPLE__ +#define integer_t dmd_integer_t +#endif + +#include "rmem.h" + +//#include "port.h" +#include "mtype.h" +#include "init.h" +#include "expression.h" +#include "statement.h" +#include "scope.h" +#include "id.h" +#include "declaration.h" +#include "aggregate.h" +#include "template.h" + +static Dsymbol *inferApplyArgTypesX(Expression *ethis, FuncDeclaration *fstart, Parameters *arguments); +static void inferApplyArgTypesZ(TemplateDeclaration *tstart, Parameters *arguments); +static int inferApplyArgTypesY(TypeFunction *tf, Parameters *arguments, int flags = 0); +static void templateResolve(Match *m, TemplateDeclaration *td, Scope *sc, Loc loc, Objects *targsi, Expression *ethis, Expressions *arguments); + +/******************************** Expression **************************/ + + +/*********************************** + * Determine if operands of binary op can be reversed + * to fit operator overload. + */ + +int Expression::isCommutative() +{ + return FALSE; // default is no reverse +} + +/*********************************** + * Get Identifier for operator overload. + */ + +Identifier *Expression::opId() +{ + assert(0); + return NULL; +} + +/*********************************** + * Get Identifier for reverse operator overload, + * NULL if not supported for this operator. + */ + +Identifier *Expression::opId_r() +{ + return NULL; +} + +/************************* Operators *****************************/ + +Identifier *UAddExp::opId() { return Id::uadd; } + +Identifier *NegExp::opId() { return Id::neg; } + +Identifier *ComExp::opId() { return Id::com; } + +Identifier *CastExp::opId() { return Id::cast; } + +Identifier *InExp::opId() { return Id::opIn; } +Identifier *InExp::opId_r() { return Id::opIn_r; } + +Identifier *PostExp::opId() { return (op == TOKplusplus) + ? Id::postinc + : Id::postdec; } + +int AddExp::isCommutative() { return TRUE; } +Identifier *AddExp::opId() { return Id::add; } +Identifier *AddExp::opId_r() { return Id::add_r; } + +Identifier *MinExp::opId() { return Id::sub; } +Identifier *MinExp::opId_r() { return Id::sub_r; } + +int MulExp::isCommutative() { return TRUE; } +Identifier *MulExp::opId() { return Id::mul; } +Identifier *MulExp::opId_r() { return Id::mul_r; } + +Identifier *DivExp::opId() { return Id::div; } +Identifier *DivExp::opId_r() { return Id::div_r; } + +Identifier *ModExp::opId() { return Id::mod; } +Identifier *ModExp::opId_r() { return Id::mod_r; } + +#if DMDV2 +Identifier *PowExp::opId() { return Id::pow; } +Identifier *PowExp::opId_r() { return Id::pow_r; } +#endif + +Identifier *ShlExp::opId() { return Id::shl; } +Identifier *ShlExp::opId_r() { return Id::shl_r; } + +Identifier *ShrExp::opId() { return Id::shr; } +Identifier *ShrExp::opId_r() { return Id::shr_r; } + +Identifier *UshrExp::opId() { return Id::ushr; } +Identifier *UshrExp::opId_r() { return Id::ushr_r; } + +int AndExp::isCommutative() { return TRUE; } +Identifier *AndExp::opId() { return Id::iand; } +Identifier *AndExp::opId_r() { return Id::iand_r; } + +int OrExp::isCommutative() { return TRUE; } +Identifier *OrExp::opId() { return Id::ior; } +Identifier *OrExp::opId_r() { return Id::ior_r; } + +int XorExp::isCommutative() { return TRUE; } +Identifier *XorExp::opId() { return Id::ixor; } +Identifier *XorExp::opId_r() { return Id::ixor_r; } + +Identifier *CatExp::opId() { return Id::cat; } +Identifier *CatExp::opId_r() { return Id::cat_r; } + +Identifier * AssignExp::opId() { return Id::assign; } +Identifier * AddAssignExp::opId() { return Id::addass; } +Identifier * MinAssignExp::opId() { return Id::subass; } +Identifier * MulAssignExp::opId() { return Id::mulass; } +Identifier * DivAssignExp::opId() { return Id::divass; } +Identifier * ModAssignExp::opId() { return Id::modass; } +Identifier * AndAssignExp::opId() { return Id::andass; } +Identifier * OrAssignExp::opId() { return Id::orass; } +Identifier * XorAssignExp::opId() { return Id::xorass; } +Identifier * ShlAssignExp::opId() { return Id::shlass; } +Identifier * ShrAssignExp::opId() { return Id::shrass; } +Identifier *UshrAssignExp::opId() { return Id::ushrass; } +Identifier * CatAssignExp::opId() { return Id::catass; } +Identifier * PowAssignExp::opId() { return Id::powass; } + +int EqualExp::isCommutative() { return TRUE; } +Identifier *EqualExp::opId() { return Id::eq; } + +int CmpExp::isCommutative() { return TRUE; } +Identifier *CmpExp::opId() { return Id::cmp; } + +Identifier *ArrayExp::opId() { return Id::index; } +Identifier *PtrExp::opId() { return Id::opStar; } + +/************************************ + * If type is a class or struct, return the symbol for it, + * else NULL + */ +AggregateDeclaration *isAggregate(Type *t) +{ + t = t->toBasetype(); + if (t->ty == Tclass) + { + return ((TypeClass *)t)->sym; + } + else if (t->ty == Tstruct) + { + return ((TypeStruct *)t)->sym; + } + return NULL; +} + +/******************************************* + * Helper function to turn operator into template argument list + */ +Objects *opToArg(Scope *sc, enum TOK op) +{ + /* Remove the = from op= + */ + switch (op) + { + case TOKaddass: op = TOKadd; break; + case TOKminass: op = TOKmin; break; + case TOKmulass: op = TOKmul; break; + case TOKdivass: op = TOKdiv; break; + case TOKmodass: op = TOKmod; break; + case TOKandass: op = TOKand; break; + case TOKorass: op = TOKor; break; + case TOKxorass: op = TOKxor; break; + case TOKshlass: op = TOKshl; break; + case TOKshrass: op = TOKshr; break; + case TOKushrass: op = TOKushr; break; + case TOKcatass: op = TOKcat; break; + case TOKpowass: op = TOKpow; break; + } + Expression *e = new StringExp(0, (char *)Token::toChars(op)); + e = e->semantic(sc); + Objects *targsi = new Objects(); + targsi->push(e); + return targsi; +} + +/************************************ + * Operator overload. + * Check for operator overload, if so, replace + * with function call. + * Return NULL if not an operator overload. + */ + +Expression *UnaExp::op_overload(Scope *sc) +{ + //printf("UnaExp::op_overload() (%s)\n", toChars()); + +#if DMDV2 + if (e1->op == TOKarray) + { + ArrayExp *ae = (ArrayExp *)e1; + ae->e1 = ae->e1->semantic(sc); + ae->e1 = resolveProperties(sc, ae->e1); + + AggregateDeclaration *ad = isAggregate(ae->e1->type); + if (ad) + { + /* Rewrite as: + * a.opIndexUnary!("+")(args); + */ + Dsymbol *fd = search_function(ad, Id::opIndexUnary); + if (fd) + { + Objects *targsi = opToArg(sc, op); + Expression *e = new DotTemplateInstanceExp(loc, ae->e1, fd->ident, targsi); + e = new CallExp(loc, e, ae->arguments); + e = e->semantic(sc); + return e; + } + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis) + { + /* Rewrite op(a[arguments]) as: + * op(a.aliasthis[arguments]) + */ + Expression *e1 = ae->copy(); + ((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); + Expression *e = copy(); + ((UnaExp *)e)->e1 = e1; + e = e->trySemantic(sc); + return e; + } + } + } + else if (e1->op == TOKslice) + { + SliceExp *se = (SliceExp *)e1; + se->e1 = se->e1->semantic(sc); + se->e1 = resolveProperties(sc, se->e1); + + AggregateDeclaration *ad = isAggregate(se->e1->type); + if (ad) + { + /* Rewrite as: + * a.opSliceUnary!("+")(lwr, upr); + */ + Dsymbol *fd = search_function(ad, Id::opSliceUnary); + if (fd) + { + Expressions *a = new Expressions(); + if (se->lwr) + { a->push(se->lwr); + a->push(se->upr); + } + + Objects *targsi = opToArg(sc, op); + Expression *e = new DotTemplateInstanceExp(loc, se->e1, fd->ident, targsi); + e = new CallExp(loc, e, a); + e = e->semantic(sc); + return e; + } + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis) + { + /* Rewrite op(a[lwr..upr]) as: + * op(a.aliasthis[lwr..upr]) + */ + Expression *e1 = se->copy(); + ((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident); + Expression *e = copy(); + ((UnaExp *)e)->e1 = e1; + e = e->trySemantic(sc); + return e; + } + } + } +#endif + + e1 = e1->semantic(sc); + e1 = resolveProperties(sc, e1); + + AggregateDeclaration *ad = isAggregate(e1->type); + if (ad) + { + Dsymbol *fd = NULL; +#if 1 // Old way, kept for compatibility with D1 + if (op != TOKpreplusplus && op != TOKpreminusminus) + { fd = search_function(ad, opId()); + if (fd) + { + if (op == TOKarray) + { + /* Rewrite op e1[arguments] as: + * e1.fd(arguments) + */ + Expression *e = new DotIdExp(loc, e1, fd->ident); + ArrayExp *ae = (ArrayExp *)this; + e = new CallExp(loc, e, ae->arguments); + e = e->semantic(sc); + return e; + } + else + { + // Rewrite +e1 as e1.add() + return build_overload(loc, sc, e1, NULL, fd); + } + } + } +#endif + +#if DMDV2 + /* Rewrite as: + * e1.opUnary!("+")(); + */ + fd = search_function(ad, Id::opUnary); + if (fd) + { + Objects *targsi = opToArg(sc, op); + Expression *e = new DotTemplateInstanceExp(loc, e1, fd->ident, targsi); + e = new CallExp(loc, e); + e = e->semantic(sc); + return e; + } + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis) + { + /* Rewrite op(e1) as: + * op(e1.aliasthis) + */ + Expression *e1 = new DotIdExp(loc, this->e1, ad->aliasthis->ident); + Expression *e = copy(); + ((UnaExp *)e)->e1 = e1; + e = e->trySemantic(sc); + return e; + } +#endif + } + return NULL; +} + +Expression *ArrayExp::op_overload(Scope *sc) +{ + //printf("ArrayExp::op_overload() (%s)\n", toChars()); + AggregateDeclaration *ad = isAggregate(e1->type); + if (ad) + { + Dsymbol *fd = search_function(ad, opId()); + if (fd) + { + for (size_t i = 0; i < arguments->dim; i++) + { Expression *x = arguments->tdata()[i]; + // Create scope for '$' variable for this dimension + ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, this); + sym->loc = loc; + sym->parent = sc->scopesym; + sc = sc->push(sym); + lengthVar = NULL; // Create it only if required + currentDimension = i; // Dimension for $, if required + + x = x->semantic(sc); + x = resolveProperties(sc, x); + if (!x->type) + error("%s has no value", x->toChars()); + if (lengthVar) + { // If $ was used, declare it now + Expression *av = new DeclarationExp(loc, lengthVar); + x = new CommaExp(0, av, x); + x->semantic(sc); + } + arguments->tdata()[i] = x; + sc = sc->pop(); + } + + /* Rewrite op e1[arguments] as: + * e1.opIndex(arguments) + */ + Expression *e = new DotIdExp(loc, e1, fd->ident); + e = new CallExp(loc, e, arguments); + e = e->semantic(sc); + return e; + } + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis) + { + /* Rewrite op(e1) as: + * op(e1.aliasthis) + */ + Expression *e1 = new DotIdExp(loc, this->e1, ad->aliasthis->ident); + Expression *e = copy(); + ((UnaExp *)e)->e1 = e1; + e = e->trySemantic(sc); + return e; + } + } + return NULL; +} + +/*********************************************** + * This is mostly the same as UnaryExp::op_overload(), but has + * a different rewrite. + */ +Expression *CastExp::op_overload(Scope *sc) +{ + //printf("CastExp::op_overload() (%s)\n", toChars()); + AggregateDeclaration *ad = isAggregate(e1->type); + if (ad) + { + Dsymbol *fd = NULL; + /* Rewrite as: + * e1.opCast!(T)(); + */ + fd = search_function(ad, Id::cast); + if (fd) + { +#if 1 // Backwards compatibility with D1 if opCast is a function, not a template + if (fd->isFuncDeclaration()) + { // Rewrite as: e1.opCast() + return build_overload(loc, sc, e1, NULL, fd); + } +#endif + Objects *targsi = new Objects(); + targsi->push(to); + Expression *e = new DotTemplateInstanceExp(loc, e1, fd->ident, targsi); + e = new CallExp(loc, e); + e = e->semantic(sc); + return e; + } + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis) + { + /* Rewrite op(e1) as: + * op(e1.aliasthis) + */ + Expression *e1 = new DotIdExp(loc, this->e1, ad->aliasthis->ident); + Expression *e = copy(); + ((UnaExp *)e)->e1 = e1; + e = e->trySemantic(sc); + return e; + } + } + return NULL; +} + +Expression *BinExp::op_overload(Scope *sc) +{ + //printf("BinExp::op_overload() (%s)\n", toChars()); + + Identifier *id = opId(); + Identifier *id_r = opId_r(); + + Expressions args1; + Expressions args2; + int argsset = 0; + + AggregateDeclaration *ad1 = isAggregate(e1->type); + AggregateDeclaration *ad2 = isAggregate(e2->type); + + Dsymbol *s = NULL; + Dsymbol *s_r = NULL; + +#if 1 // the old D1 scheme + if (ad1 && id) + { + s = search_function(ad1, id); + } + if (ad2 && id_r) + { + s_r = search_function(ad2, id_r); + } +#endif + + Objects *targsi = NULL; +#if DMDV2 + if (op == TOKplusplus || op == TOKminusminus) + { // Bug4099 fix + if (ad1 && search_function(ad1, Id::opUnary)) + return NULL; + } + if (!s && !s_r && op != TOKequal && op != TOKnotequal && op != TOKassign && + op != TOKplusplus && op != TOKminusminus) + { + /* Try the new D2 scheme, opBinary and opBinaryRight + */ + if (ad1) + s = search_function(ad1, Id::opBinary); + if (ad2) + s_r = search_function(ad2, Id::opBinaryRight); + + // Set targsi, the template argument list, which will be the operator string + if (s || s_r) + { + id = Id::opBinary; + id_r = Id::opBinaryRight; + targsi = opToArg(sc, op); + } + } +#endif + + if (s || s_r) + { + /* Try: + * a.opfunc(b) + * b.opfunc_r(a) + * and see which is better. + */ + + args1.setDim(1); + args1.tdata()[0] = e1; + args2.setDim(1); + args2.tdata()[0] = e2; + argsset = 1; + + Match m; + memset(&m, 0, sizeof(m)); + m.last = MATCHnomatch; + + if (s) + { + FuncDeclaration *fd = s->isFuncDeclaration(); + if (fd) + { + overloadResolveX(&m, fd, NULL, &args2); + } + else + { TemplateDeclaration *td = s->isTemplateDeclaration(); + templateResolve(&m, td, sc, loc, targsi, e1, &args2); + } + } + + FuncDeclaration *lastf = m.lastf; + + if (s_r) + { + FuncDeclaration *fd = s_r->isFuncDeclaration(); + if (fd) + { + overloadResolveX(&m, fd, NULL, &args1); + } + else + { TemplateDeclaration *td = s_r->isTemplateDeclaration(); + templateResolve(&m, td, sc, loc, targsi, e2, &args1); + } + } + + if (m.count > 1) + { + // Error, ambiguous + error("overloads %s and %s both match argument list for %s", + m.lastf->type->toChars(), + m.nextf->type->toChars(), + m.lastf->toChars()); + } + else if (m.last == MATCHnomatch) + { + m.lastf = m.anyf; + if (targsi) + goto L1; + } + + Expression *e; + if (op == TOKplusplus || op == TOKminusminus) + // Kludge because operator overloading regards e++ and e-- + // as unary, but it's implemented as a binary. + // Rewrite (e1 ++ e2) as e1.postinc() + // Rewrite (e1 -- e2) as e1.postdec() + e = build_overload(loc, sc, e1, NULL, m.lastf ? m.lastf : s); + else if (lastf && m.lastf == lastf || !s_r && m.last == MATCHnomatch) + // Rewrite (e1 op e2) as e1.opfunc(e2) + e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); + else + // Rewrite (e1 op e2) as e2.opfunc_r(e1) + e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s_r); + return e; + } + +L1: +#if 1 // Retained for D1 compatibility + if (isCommutative() && !targsi) + { + s = NULL; + s_r = NULL; + if (ad1 && id_r) + { + s_r = search_function(ad1, id_r); + } + if (ad2 && id) + { + s = search_function(ad2, id); + } + + if (s || s_r) + { + /* Try: + * a.opfunc_r(b) + * b.opfunc(a) + * and see which is better. + */ + + if (!argsset) + { args1.setDim(1); + args1.tdata()[0] = e1; + args2.setDim(1); + args2.tdata()[0] = e2; + } + + Match m; + memset(&m, 0, sizeof(m)); + m.last = MATCHnomatch; + + if (s_r) + { + FuncDeclaration *fd = s_r->isFuncDeclaration(); + if (fd) + { + overloadResolveX(&m, fd, NULL, &args2); + } + else + { TemplateDeclaration *td = s_r->isTemplateDeclaration(); + templateResolve(&m, td, sc, loc, targsi, e1, &args2); + } + } + FuncDeclaration *lastf = m.lastf; + + if (s) + { + FuncDeclaration *fd = s->isFuncDeclaration(); + if (fd) + { + overloadResolveX(&m, fd, NULL, &args1); + } + else + { TemplateDeclaration *td = s->isTemplateDeclaration(); + templateResolve(&m, td, sc, loc, targsi, e2, &args1); + } + } + + if (m.count > 1) + { + // Error, ambiguous + error("overloads %s and %s both match argument list for %s", + m.lastf->type->toChars(), + m.nextf->type->toChars(), + m.lastf->toChars()); + } + else if (m.last == MATCHnomatch) + { + m.lastf = m.anyf; + } + + Expression *e; + if (lastf && m.lastf == lastf || !s && m.last == MATCHnomatch) + // Rewrite (e1 op e2) as e1.opfunc_r(e2) + e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s_r); + else + // Rewrite (e1 op e2) as e2.opfunc(e1) + e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s); + + // When reversing operands of comparison operators, + // need to reverse the sense of the op + switch (op) + { + case TOKlt: op = TOKgt; break; + case TOKgt: op = TOKlt; break; + case TOKle: op = TOKge; break; + case TOKge: op = TOKle; break; + + // Floating point compares + case TOKule: op = TOKuge; break; + case TOKul: op = TOKug; break; + case TOKuge: op = TOKule; break; + case TOKug: op = TOKul; break; + + // These are symmetric + case TOKunord: + case TOKlg: + case TOKleg: + case TOKue: + break; + } + + return e; + } + } +#endif + +#if DMDV2 + // Try alias this on first operand + if (ad1 && ad1->aliasthis && + !(op == TOKassign && ad2 && ad1 == ad2)) // See Bugzilla 2943 + { + /* Rewrite (e1 op e2) as: + * (e1.aliasthis op e2) + */ + Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); + Expression *e = copy(); + ((BinExp *)e)->e1 = e1; + e = e->trySemantic(sc); + return e; + } + + // Try alias this on second operand + if (ad2 && ad2->aliasthis && + /* Bugzilla 2943: make sure that when we're copying the struct, we don't + * just copy the alias this member + */ + !(op == TOKassign && ad1 && ad1 == ad2)) + { + /* Rewrite (e1 op e2) as: + * (e1 op e2.aliasthis) + */ + Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); + Expression *e = copy(); + ((BinExp *)e)->e2 = e2; + e = e->trySemantic(sc); + return e; + } +#endif + return NULL; +} + +/****************************************** + * Common code for overloading of EqualExp and CmpExp + */ +Expression *BinExp::compare_overload(Scope *sc, Identifier *id) +{ + //printf("BinExp::compare_overload(id = %s) %s\n", id->toChars(), toChars()); + + AggregateDeclaration *ad1 = isAggregate(e1->type); + AggregateDeclaration *ad2 = isAggregate(e2->type); + + Dsymbol *s = NULL; + Dsymbol *s_r = NULL; + + if (ad1) + { + s = search_function(ad1, id); + } + if (ad2) + { + s_r = search_function(ad2, id); + if (s == s_r) + s_r = NULL; + } + + Objects *targsi = NULL; + + if (s || s_r) + { + /* Try: + * a.opEquals(b) + * b.opEquals(a) + * and see which is better. + */ + + Expressions args1; + Expressions args2; + + args1.setDim(1); + args1.tdata()[0] = e1; + args2.setDim(1); + args2.tdata()[0] = e2; + + Match m; + memset(&m, 0, sizeof(m)); + m.last = MATCHnomatch; + + if (0 && s && s_r) + { + printf("s : %s\n", s->toPrettyChars()); + printf("s_r: %s\n", s_r->toPrettyChars()); + } + + if (s) + { + FuncDeclaration *fd = s->isFuncDeclaration(); + if (fd) + { + overloadResolveX(&m, fd, NULL, &args2); + } + else + { TemplateDeclaration *td = s->isTemplateDeclaration(); + templateResolve(&m, td, sc, loc, targsi, NULL, &args2); + } + } + + FuncDeclaration *lastf = m.lastf; + int count = m.count; + + if (s_r) + { + FuncDeclaration *fd = s_r->isFuncDeclaration(); + if (fd) + { + overloadResolveX(&m, fd, NULL, &args1); + } + else + { TemplateDeclaration *td = s_r->isTemplateDeclaration(); + templateResolve(&m, td, sc, loc, targsi, NULL, &args1); + } + } + + if (m.count > 1) + { + /* The following if says "not ambiguous" if there's one match + * from s and one from s_r, in which case we pick s. + * This doesn't follow the spec, but is a workaround for the case + * where opEquals was generated from templates and we cannot figure + * out if both s and s_r came from the same declaration or not. + * The test case is: + * import std.typecons; + * void main() { + * assert(tuple("has a", 2u) == tuple("has a", 1)); + * } + */ + if (!(m.lastf == lastf && m.count == 2 && count == 1)) + { + // Error, ambiguous + error("overloads %s and %s both match argument list for %s", + m.lastf->type->toChars(), + m.nextf->type->toChars(), + m.lastf->toChars()); + } + } + else if (m.last == MATCHnomatch) + { + m.lastf = m.anyf; + } + + Expression *e; + if (lastf && m.lastf == lastf || !s_r && m.last == MATCHnomatch) + // Rewrite (e1 op e2) as e1.opfunc(e2) + e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); + else + { // Rewrite (e1 op e2) as e2.opfunc_r(e1) + e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s_r); + + // When reversing operands of comparison operators, + // need to reverse the sense of the op + switch (op) + { + case TOKlt: op = TOKgt; break; + case TOKgt: op = TOKlt; break; + case TOKle: op = TOKge; break; + case TOKge: op = TOKle; break; + + // Floating point compares + case TOKule: op = TOKuge; break; + case TOKul: op = TOKug; break; + case TOKuge: op = TOKule; break; + case TOKug: op = TOKul; break; + + // The rest are symmetric + default: + break; + } + } + + return e; + } + + // Try alias this on first operand + if (ad1 && ad1->aliasthis) + { + /* Rewrite (e1 op e2) as: + * (e1.aliasthis op e2) + */ + Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); + Expression *e = copy(); + ((BinExp *)e)->e1 = e1; + e = e->trySemantic(sc); + return e; + } + + // Try alias this on second operand + if (ad2 && ad2->aliasthis) + { + /* Rewrite (e1 op e2) as: + * (e1 op e2.aliasthis) + */ + Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); + Expression *e = copy(); + ((BinExp *)e)->e2 = e2; + e = e->trySemantic(sc); + return e; + } + + return NULL; +} + +Expression *EqualExp::op_overload(Scope *sc) +{ + //printf("EqualExp::op_overload() (%s)\n", toChars()); + + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + if (t1->ty == Tclass && t2->ty == Tclass) + { ClassDeclaration *cd1 = t1->isClassHandle(); + ClassDeclaration *cd2 = t2->isClassHandle(); + + if (!(cd1->isCPPinterface() || cd2->isCPPinterface())) + { + /* Rewrite as: + * .object.opEquals(cast(Object)e1, cast(Object)e2) + * The explicit cast is necessary for interfaces, + * see http://d.puremagic.com/issues/show_bug.cgi?id=4088 + */ + Expression *e1x = new CastExp(loc, e1, ClassDeclaration::object->getType()); + Expression *e2x = new CastExp(loc, e2, ClassDeclaration::object->getType()); + + Expression *e = new IdentifierExp(loc, Id::empty); + e = new DotIdExp(loc, e, Id::object); + e = new DotIdExp(loc, e, Id::eq); + e = new CallExp(loc, e, e1x, e2x); + e = e->semantic(sc); + return e; + } + } + + return compare_overload(sc, Id::eq); +} + +Expression *CmpExp::op_overload(Scope *sc) +{ + //printf("CmpExp::op_overload() (%s)\n", toChars()); + + return compare_overload(sc, Id::cmp); +} + +/********************************* + * Operator overloading for op= + */ +Expression *BinAssignExp::op_overload(Scope *sc) +{ + //printf("BinAssignExp::op_overload() (%s)\n", toChars()); + +#if DMDV2 + if (e1->op == TOKarray) + { + ArrayExp *ae = (ArrayExp *)e1; + ae->e1 = ae->e1->semantic(sc); + ae->e1 = resolveProperties(sc, ae->e1); + + AggregateDeclaration *ad = isAggregate(ae->e1->type); + if (ad) + { + /* Rewrite a[args]+=e2 as: + * a.opIndexOpAssign!("+")(e2, args); + */ + Dsymbol *fd = search_function(ad, Id::opIndexOpAssign); + if (fd) + { + Expressions *a = new Expressions(); + a->push(e2); + for (size_t i = 0; i < ae->arguments->dim; i++) + a->push(ae->arguments->tdata()[i]); + + Objects *targsi = opToArg(sc, op); + Expression *e = new DotTemplateInstanceExp(loc, ae->e1, fd->ident, targsi); + e = new CallExp(loc, e, a); + e = e->semantic(sc); + return e; + } + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis) + { + /* Rewrite a[arguments] op= e2 as: + * a.aliasthis[arguments] op= e2 + */ + Expression *e1 = ae->copy(); + ((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); + Expression *e = copy(); + ((UnaExp *)e)->e1 = e1; + e = e->trySemantic(sc); + return e; + } + } + } + else if (e1->op == TOKslice) + { + SliceExp *se = (SliceExp *)e1; + se->e1 = se->e1->semantic(sc); + se->e1 = resolveProperties(sc, se->e1); + + AggregateDeclaration *ad = isAggregate(se->e1->type); + if (ad) + { + /* Rewrite a[lwr..upr]+=e2 as: + * a.opSliceOpAssign!("+")(e2, lwr, upr); + */ + Dsymbol *fd = search_function(ad, Id::opSliceOpAssign); + if (fd) + { + Expressions *a = new Expressions(); + a->push(e2); + if (se->lwr) + { a->push(se->lwr); + a->push(se->upr); + } + + Objects *targsi = opToArg(sc, op); + Expression *e = new DotTemplateInstanceExp(loc, se->e1, fd->ident, targsi); + e = new CallExp(loc, e, a); + e = e->semantic(sc); + return e; + } + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis) + { + /* Rewrite a[lwr..upr] op= e2 as: + * a.aliasthis[lwr..upr] op= e2 + */ + Expression *e1 = se->copy(); + ((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident); + Expression *e = copy(); + ((UnaExp *)e)->e1 = e1; + e = e->trySemantic(sc); + return e; + } + } + } +#endif + + BinExp::semantic(sc); + e1 = resolveProperties(sc, e1); + e2 = resolveProperties(sc, e2); + + Identifier *id = opId(); + + Expressions args2; + + AggregateDeclaration *ad1 = isAggregate(e1->type); + + Dsymbol *s = NULL; + +#if 1 // the old D1 scheme + if (ad1 && id) + { + s = search_function(ad1, id); + } +#endif + + Objects *targsi = NULL; +#if DMDV2 + if (!s) + { /* Try the new D2 scheme, opOpAssign + */ + if (ad1) + s = search_function(ad1, Id::opOpAssign); + + // Set targsi, the template argument list, which will be the operator string + if (s) + { + id = Id::opOpAssign; + targsi = opToArg(sc, op); + } + } +#endif + + if (s) + { + /* Try: + * a.opOpAssign(b) + */ + + args2.setDim(1); + args2.tdata()[0] = e2; + + Match m; + memset(&m, 0, sizeof(m)); + m.last = MATCHnomatch; + + if (s) + { + FuncDeclaration *fd = s->isFuncDeclaration(); + if (fd) + { + overloadResolveX(&m, fd, NULL, &args2); + } + else + { TemplateDeclaration *td = s->isTemplateDeclaration(); + templateResolve(&m, td, sc, loc, targsi, e1, &args2); + } + } + + if (m.count > 1) + { + // Error, ambiguous + error("overloads %s and %s both match argument list for %s", + m.lastf->type->toChars(), + m.nextf->type->toChars(), + m.lastf->toChars()); + } + else if (m.last == MATCHnomatch) + { + m.lastf = m.anyf; + if (targsi) + goto L1; + } + + // Rewrite (e1 op e2) as e1.opOpAssign(e2) + return build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); + } + +L1: + +#if DMDV2 + // Try alias this on first operand + if (ad1 && ad1->aliasthis) + { + /* Rewrite (e1 op e2) as: + * (e1.aliasthis op e2) + */ + Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); + Expression *e = copy(); + ((BinExp *)e)->e1 = e1; + e = e->trySemantic(sc); + return e; + } + + // Try alias this on second operand + AggregateDeclaration *ad2 = isAggregate(e2->type); + if (ad2 && ad2->aliasthis) + { + /* Rewrite (e1 op e2) as: + * (e1 op e2.aliasthis) + */ + Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); + Expression *e = copy(); + ((BinExp *)e)->e2 = e2; + e = e->trySemantic(sc); + return e; + } +#endif + return NULL; +} + +/*********************************** + * Utility to build a function call out of this reference and argument. + */ + +Expression *build_overload(Loc loc, Scope *sc, Expression *ethis, Expression *earg, + Dsymbol *d) +{ + assert(d); + Expression *e; + + //printf("build_overload(id = '%s')\n", id->toChars()); + //earg->print(); + //earg->type->print(); + Declaration *decl = d->isDeclaration(); + if (decl) + e = new DotVarExp(loc, ethis, decl, 0); + else + e = new DotIdExp(loc, ethis, d->ident); + e = new CallExp(loc, e, earg); + + e = e->semantic(sc); + return e; +} + +/*************************************** + * Search for function funcid in aggregate ad. + */ + +Dsymbol *search_function(ScopeDsymbol *ad, Identifier *funcid) +{ + Dsymbol *s; + FuncDeclaration *fd; + TemplateDeclaration *td; + + s = ad->search(0, funcid, 0); + if (s) + { Dsymbol *s2; + + //printf("search_function: s = '%s'\n", s->kind()); + s2 = s->toAlias(); + //printf("search_function: s2 = '%s'\n", s2->kind()); + fd = s2->isFuncDeclaration(); + if (fd && fd->type->ty == Tfunction) + return fd; + + td = s2->isTemplateDeclaration(); + if (td) + return td; + } + return NULL; +} + + +int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply) +{ + Identifier *idapply = (op == TOKforeach) ? Id::apply : Id::applyReverse; +#if DMDV2 + Identifier *idhead = (op == TOKforeach) ? Id::Ffront : Id::Fback; + int sliced = 0; +#endif + Type *tab; + AggregateDeclaration *ad; + + while (1) + { + aggr = aggr->semantic(sc); + aggr = resolveProperties(sc, aggr); + aggr = aggr->optimize(WANTvalue); + if (!aggr->type) + goto Lerr; + + tab = aggr->type->toBasetype(); + switch (tab->ty) + { + case Tarray: + case Tsarray: + case Ttuple: + case Taarray: + break; + + case Tclass: + ad = ((TypeClass *)tab)->sym; + goto Laggr; + + case Tstruct: + ad = ((TypeStruct *)tab)->sym; + goto Laggr; + + Laggr: +#if DMDV2 + if (!sliced) + { + sapply = search_function(ad, idapply); + if (sapply) + { // opApply aggregate + break; + } + + Dsymbol *s = search_function(ad, Id::slice); + if (s) + { Expression *rinit = new SliceExp(aggr->loc, aggr, NULL, NULL); + rinit = rinit->trySemantic(sc); + if (rinit) // if application of [] succeeded + { aggr = rinit; + sliced = 1; + continue; + } + } + } + + if (Dsymbol *shead = search_function(ad, idhead)) + { // range aggregate + break; + } + + if (ad->aliasthis) + { + aggr = new DotIdExp(aggr->loc, aggr, ad->aliasthis->ident); + continue; + } +#else + sapply = search_function(ad, idapply); + if (sapply) + { // opApply aggregate + break; + } +#endif + goto Lerr; + + case Tdelegate: + if (aggr->op == TOKdelegate) + { DelegateExp *de = (DelegateExp *)aggr; + sapply = de->func->isFuncDeclaration(); + } + break; + + case Terror: + break; + + default: + goto Lerr; + } + break; + } + return 1; + +Lerr: + return 0; +} + +/***************************************** + * Given array of arguments and an aggregate type, + * if any of the argument types are missing, attempt to infer + * them from the aggregate type. + */ + +int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) +{ + if (!arguments || !arguments->dim) + return 0; + + if (sapply) // prefer opApply + { + for (size_t u = 0; u < arguments->dim; u++) + { Parameter *arg = arguments->tdata()[u]; + if (arg->type) + arg->type = arg->type->semantic(loc, sc); + } + + Expression *ethis; + Type *tab = aggr->type->toBasetype(); + if (tab->ty == Tclass || tab->ty == Tstruct) + ethis = aggr; + else + { assert(tab->ty == Tdelegate && aggr->op == TOKdelegate); + ethis = ((DelegateExp *)aggr)->e1; + } + + /* Look for like an + * int opApply(int delegate(ref Type [, ...]) dg); + * overload + */ + FuncDeclaration *fd = sapply->isFuncDeclaration(); + if (fd) + { sapply = inferApplyArgTypesX(ethis, fd, arguments); + } +#if 0 + TemplateDeclaration *td = sapply->isTemplateDeclaration(); + if (td) + { inferApplyArgTypesZ(td, arguments); + } +#endif + return sapply ? 1 : 0; + } + + /* Return if no arguments need types. + */ + for (size_t u = 0; u < arguments->dim; u++) + { Parameter *arg = arguments->tdata()[u]; + if (!arg->type) + break; + } + + AggregateDeclaration *ad; + + Parameter *arg = arguments->tdata()[0]; + Type *taggr = aggr->type; + assert(taggr); + Type *tab = taggr->toBasetype(); + switch (tab->ty) + { + case Tarray: + case Tsarray: + case Ttuple: + if (arguments->dim == 2) + { + if (!arg->type) + arg->type = Type::tsize_t; // key type + arg = arguments->tdata()[1]; + } + if (!arg->type && tab->ty != Ttuple) + arg->type = tab->nextOf(); // value type + break; + + case Taarray: + { TypeAArray *taa = (TypeAArray *)tab; + + if (arguments->dim == 2) + { + if (!arg->type) + arg->type = taa->index; // key type + arg = arguments->tdata()[1]; + } + if (!arg->type) + arg->type = taa->next; // value type + break; + } + + case Tclass: + ad = ((TypeClass *)tab)->sym; + goto Laggr; + + case Tstruct: + ad = ((TypeStruct *)tab)->sym; + goto Laggr; + + Laggr: + if (arguments->dim == 1) + { + if (!arg->type) + { + /* Look for a head() or rear() overload + */ + Identifier *id = (op == TOKforeach) ? Id::Ffront : Id::Fback; + Dsymbol *s = search_function(ad, id); + FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL; + if (!fd) + { if (s && s->isTemplateDeclaration()) + break; + break; + } + // Resolve inout qualifier of front type + arg->type = fd->type->nextOf(); + if (arg->type) + arg->type = arg->type->substWildTo(tab->mod); + } + break; + } + break; + + case Tdelegate: + { + if (!inferApplyArgTypesY((TypeFunction *)tab->nextOf(), arguments)) + return 0; + break; + } + + default: + break; // ignore error, caught later + } + return 1; +} + +static Dsymbol *inferApplyArgTypesX(Expression *ethis, FuncDeclaration *fstart, Parameters *arguments) +{ + struct Param3 + { + Parameters *arguments; + int mod; + MATCH match; + FuncDeclaration *fd_best; + FuncDeclaration *fd_ambig; + + static int fp(void *param, FuncDeclaration *f) + { + Param3 *p = (Param3 *)param; + TypeFunction *tf = (TypeFunction *)f->type; + MATCH m = MATCHexact; + + if (f->isThis()) + { if (!MODimplicitConv(p->mod, tf->mod)) + m = MATCHnomatch; + else if (p->mod != tf->mod) + m = MATCHconst; + } + if (!inferApplyArgTypesY(tf, p->arguments, 1)) + m = MATCHnomatch; + + if (m > p->match) + { p->fd_best = f; + p->fd_ambig = NULL; + p->match = m; + } + else if (m == p->match) + p->fd_ambig = f; + return 0; + } + }; + + Param3 p; + p.arguments = arguments; + p.mod = ethis->type->mod; + p.match = MATCHnomatch; + p.fd_best = NULL; + p.fd_ambig = NULL; + overloadApply(fstart, &Param3::fp, &p); + if (p.fd_best) + { + inferApplyArgTypesY((TypeFunction *)p.fd_best->type, arguments); + if (p.fd_ambig) + { ::error(ethis->loc, "%s.%s matches more than one declaration:\n\t%s(%d):%s\nand:\n\t%s(%d):%s", + ethis->toChars(), fstart->ident->toChars(), + p.fd_best ->loc.filename, p.fd_best ->loc.linnum, p.fd_best ->type->toChars(), + p.fd_ambig->loc.filename, p.fd_ambig->loc.linnum, p.fd_ambig->type->toChars()); + p.fd_best = NULL; + } + } + return p.fd_best; +} + +/****************************** + * Infer arguments from type of function. + * Returns: + * 1 match for this function + * 0 no match for this function + */ + +static int inferApplyArgTypesY(TypeFunction *tf, Parameters *arguments, int flags) +{ size_t nparams; + Parameter *p; + + if (Parameter::dim(tf->parameters) != 1) + goto Lnomatch; + p = Parameter::getNth(tf->parameters, 0); + if (p->type->ty != Tdelegate) + goto Lnomatch; + tf = (TypeFunction *)p->type->nextOf(); + assert(tf->ty == Tfunction); + + /* We now have tf, the type of the delegate. Match it against + * the arguments, filling in missing argument types. + */ + nparams = Parameter::dim(tf->parameters); + if (nparams == 0 || tf->varargs) + goto Lnomatch; // not enough parameters + if (arguments->dim != nparams) + goto Lnomatch; // not enough parameters + + for (size_t u = 0; u < nparams; u++) + { + Parameter *arg = arguments->tdata()[u]; + Parameter *param = Parameter::getNth(tf->parameters, u); + if (arg->type) + { if (!arg->type->equals(param->type)) + goto Lnomatch; + } + else if (!flags) + arg->type = param->type; + } + Lmatch: + return 1; + + Lnomatch: + return 0; +} + +/******************************************* + * Infer foreach arg types from a template function opApply which looks like: + * int opApply(alias int func(ref uint))() { ... } + */ + +#if 0 +void inferApplyArgTypesZ(TemplateDeclaration *tstart, Parameters *arguments) +{ + for (TemplateDeclaration *td = tstart; td; td = td->overnext) + { + if (!td->scope) + { + error("forward reference to template %s", td->toChars()); + return; + } + if (!td->onemember || !td->onemember->toAlias()->isFuncDeclaration()) + { + error("is not a function template"); + return; + } + if (!td->parameters || td->parameters->dim != 1) + continue; + TemplateParameter *tp = td->parameters->tdata()[0]; + TemplateAliasParameter *tap = tp->isTemplateAliasParameter(); + if (!tap || !tap->specType || tap->specType->ty != Tfunction) + continue; + TypeFunction *tf = (TypeFunction *)tap->specType; + if (inferApplyArgTypesY(tf, arguments) == 0) // found it + return; + } +} +#endif + +/************************************** + */ + +static void templateResolve(Match *m, TemplateDeclaration *td, Scope *sc, Loc loc, Objects *targsi, Expression *ethis, Expressions *arguments) +{ + FuncDeclaration *fd; + + assert(td); + fd = td->deduceFunctionTemplate(sc, loc, targsi, ethis, arguments, 1); + if (!fd) + return; + m->anyf = fd; + if (m->last >= MATCHexact) + { + m->nextf = fd; + m->count++; + } + else + { + m->last = MATCHexact; + m->lastf = fd; + m->count = 1; + } +} + diff --git a/optimize.c b/optimize.c new file mode 100644 index 00000000..315c1793 --- /dev/null +++ b/optimize.c @@ -0,0 +1,1182 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2012 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include + +#if __DMC__ +#include +#endif + +#include "lexer.h" +#include "mtype.h" +#include "expression.h" +#include "declaration.h" +#include "aggregate.h" +#include "init.h" + + +#ifdef IN_GCC +#include "d-gcc-real.h" + +/* %% fix? */ +extern "C" bool real_isnan (const real_t *); +#endif + +static real_t zero; // work around DMC bug for now + + +/************************************* + * If variable has a const initializer, + * return that initializer. + */ + +Expression *expandVar(int result, VarDeclaration *v) +{ + //printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v->toChars() : "null"); + + Expression *e = NULL; + if (!v) + return e; + if (!v->originalType && v->scope) // semantic() not yet run + v->semantic (v->scope); + + if (v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) + { + if (!v->type) + { + //error("ICE"); + return e; + } + Type *tb = v->type->toBasetype(); + if (result & WANTinterpret || + v->storage_class & STCmanifest || + v->type->toBasetype()->isscalar() || + ((result & WANTexpand) && (tb->ty != Tsarray && tb->ty != Tstruct)) + ) + { + if (v->init) + { + if (v->inuse) + { if (v->storage_class & STCmanifest) + v->error("recursive initialization of constant"); + goto L1; + } + Expression *ei = v->init->toExpression(); + if (!ei) + { if (v->storage_class & STCmanifest) + v->error("enum cannot be initialized with %s", v->init->toChars()); + goto L1; + } + if (ei->op == TOKconstruct || ei->op == TOKblit) + { AssignExp *ae = (AssignExp *)ei; + ei = ae->e2; + if (result & WANTinterpret) + { + v->inuse++; + ei = ei->optimize(result); + v->inuse--; + } + else if (ei->isConst() != 1 && ei->op != TOKstring) + goto L1; + + if (ei->type == v->type) + { // const variable initialized with const expression + } + else if (ei->implicitConvTo(v->type) >= MATCHconst) + { // const var initialized with non-const expression + ei = ei->implicitCastTo(0, v->type); + ei = ei->semantic(0); + } + else + goto L1; + } + if (v->scope) + { + v->inuse++; + e = ei->syntaxCopy(); + e = e->semantic(v->scope); + e = e->implicitCastTo(v->scope, v->type); + // enabling this line causes test22 in test suite to fail + //ei->type = e->type; + v->scope = NULL; + v->inuse--; + } + else if (!ei->type) + { + goto L1; + } + else + // Should remove the copy() operation by + // making all mods to expressions copy-on-write + e = ei->copy(); + } + else + { +#if 1 + goto L1; +#else + // BUG: what if const is initialized in constructor? + e = v->type->defaultInit(); + e->loc = e1->loc; +#endif + } + if (e->type != v->type) + { + e = e->castTo(NULL, v->type); + } + v->inuse++; + e = e->optimize(result); + v->inuse--; + } + } +L1: + //if (e) printf("\te = %p, %s, e->type = %d, %s\n", e, e->toChars(), e->type->ty, e->type->toChars()); + return e; +} + + +Expression *fromConstInitializer(int result, Expression *e1) +{ + //printf("fromConstInitializer(result = %x, %s)\n", result, e1->toChars()); + //static int xx; if (xx++ == 10) assert(0); + Expression *e = e1; + if (e1->op == TOKvar) + { VarExp *ve = (VarExp *)e1; + VarDeclaration *v = ve->var->isVarDeclaration(); + int fwdref = (v && !v->originalType && v->scope); + e = expandVar(result, v); + if (e) + { + // If it is a comma expression involving a declaration, we mustn't + // perform a copy -- we'd get two declarations of the same variable. + // See bugzilla 4465. + if (e->op == TOKcomma && ((CommaExp *)e)->e1->op == TOKdeclaration) + e = e1; + else + + if (e->type != e1->type && e1->type && e1->type->ty != Tident) + { // Type 'paint' operation + e = e->copy(); + e->type = e1->type; + } + e->loc = e1->loc; + } + else + { + e = e1; + /* If we needed to interpret, generate an error. + * Don't give an error if it's a template parameter + */ + if (v && (result & WANTinterpret) && + !(v->storage_class & STCtemplateparameter)) + { + e1->error("variable %s cannot be read at compile time", v->toChars()); + } + } + } + return e; +} + + +Expression *Expression::optimize(int result) +{ + //printf("Expression::optimize(result = x%x) %s\n", result, toChars()); + return this; +} + +Expression *VarExp::optimize(int result) +{ + return fromConstInitializer(result, this); +} + +Expression *TupleExp::optimize(int result) +{ + for (size_t i = 0; i < exps->dim; i++) + { Expression *e = exps->tdata()[i]; + + e = e->optimize(WANTvalue | (result & WANTinterpret)); + exps->tdata()[i] = e; + } + return this; +} + +Expression *ArrayLiteralExp::optimize(int result) +{ + if (elements) + { + for (size_t i = 0; i < elements->dim; i++) + { Expression *e = elements->tdata()[i]; + + e = e->optimize(WANTvalue | (result & (WANTinterpret | WANTexpand))); + elements->tdata()[i] = e; + } + } + return this; +} + +Expression *AssocArrayLiteralExp::optimize(int result) +{ + assert(keys->dim == values->dim); + for (size_t i = 0; i < keys->dim; i++) + { Expression *e = keys->tdata()[i]; + + e = e->optimize(WANTvalue | (result & (WANTinterpret | WANTexpand))); + keys->tdata()[i] = e; + + e = values->tdata()[i]; + e = e->optimize(WANTvalue | (result & (WANTinterpret | WANTexpand))); + values->tdata()[i] = e; + } + return this; +} + +Expression *StructLiteralExp::optimize(int result) +{ + if (elements) + { + for (size_t i = 0; i < elements->dim; i++) + { Expression *e = elements->tdata()[i]; + if (!e) + continue; + e = e->optimize(WANTvalue | (result & (WANTinterpret | WANTexpand))); + elements->tdata()[i] = e; + } + } + return this; +} + +Expression *TypeExp::optimize(int result) +{ + return this; +} + +Expression *UnaExp::optimize(int result) +{ + //printf("UnaExp::optimize() %s\n", toChars()); + e1 = e1->optimize(result); + return this; +} + +Expression *NegExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(result); + if (e1->isConst() == 1) + { + e = Neg(type, e1); + } + else + e = this; + return e; +} + +Expression *ComExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(result); + if (e1->isConst() == 1) + { + e = Com(type, e1); + } + else + e = this; + return e; +} + +Expression *NotExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(result); + if (e1->isConst() == 1) + { + e = Not(type, e1); + } + else + e = this; + return e; +} + +Expression *BoolExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(result); + if (e1->isConst() == 1) + { + e = Bool(type, e1); + } + else + e = this; + return e; +} + +Expression *AddrExp::optimize(int result) +{ Expression *e; + + //printf("AddrExp::optimize(result = %d) %s\n", result, toChars()); + + /* Rewrite &(a,b) as (a,&b) + */ + if (e1->op == TOKcomma) + { CommaExp *ce = (CommaExp *)e1; + AddrExp *ae = new AddrExp(loc, ce->e2); + ae->type = type; + e = new CommaExp(ce->loc, ce->e1, ae); + e->type = type; + return e->optimize(result); + } + + if (e1->op == TOKvar) + { VarExp *ve = (VarExp *)e1; + if (ve->var->storage_class & STCmanifest) + e1 = e1->optimize(result); + } + else + e1 = e1->optimize(result); + + // Convert &*ex to ex + if (e1->op == TOKstar) + { Expression *ex; + + ex = ((PtrExp *)e1)->e1; + if (type->equals(ex->type)) + e = ex; + else + { + e = ex->copy(); + e->type = type; + } + return e; + } + if (e1->op == TOKvar) + { VarExp *ve = (VarExp *)e1; + if (!ve->var->isOut() && !ve->var->isRef() && + !ve->var->isImportedSymbol()) + { + SymOffExp *se = new SymOffExp(loc, ve->var, 0, ve->hasOverloads); + se->type = type; + return se; + } + } + if (e1->op == TOKindex) + { // Convert &array[n] to &array+n + IndexExp *ae = (IndexExp *)e1; + + if (ae->e2->op == TOKint64 && ae->e1->op == TOKvar) + { + dinteger_t index = ae->e2->toInteger(); + VarExp *ve = (VarExp *)ae->e1; + if (ve->type->ty == Tsarray + && !ve->var->isImportedSymbol()) + { + TypeSArray *ts = (TypeSArray *)ve->type; + dinteger_t dim = ts->dim->toInteger(); + if (index < 0 || index >= dim) + error("array index %jd is out of bounds [0..%jd]", index, dim); + e = new SymOffExp(loc, ve->var, index * ts->nextOf()->size()); + e->type = type; + return e; + } + } + } + return this; +} + +Expression *PtrExp::optimize(int result) +{ + //printf("PtrExp::optimize(result = x%x) %s\n", result, toChars()); + e1 = e1->optimize(result); + // Convert *&ex to ex + if (e1->op == TOKaddress) + { Expression *e; + Expression *ex; + + ex = ((AddrExp *)e1)->e1; + if (type->equals(ex->type)) + e = ex; + else + { + e = ex->copy(); + e->type = type; + } + return e; + } + // Constant fold *(&structliteral + offset) + if (e1->op == TOKadd) + { + Expression *e; + e = Ptr(type, e1); + if (e != EXP_CANT_INTERPRET) + return e; + } + + if (e1->op == TOKsymoff) + { SymOffExp *se = (SymOffExp *)e1; + VarDeclaration *v = se->var->isVarDeclaration(); + Expression *e = expandVar(result, v); + if (e && e->op == TOKstructliteral) + { StructLiteralExp *sle = (StructLiteralExp *)e; + e = sle->getField(type, se->offset); + if (e && e != EXP_CANT_INTERPRET) + return e; + } + } + return this; +} + +Expression *DotVarExp::optimize(int result) +{ + //printf("DotVarExp::optimize(result = x%x) %s\n", result, toChars()); + e1 = e1->optimize(result); + + Expression *e = e1; + + if (e1->op == TOKvar) + { VarExp *ve = (VarExp *)e1; + VarDeclaration *v = ve->var->isVarDeclaration(); + e = expandVar(result, v); + } + + if (e && e->op == TOKstructliteral) + { StructLiteralExp *sle = (StructLiteralExp *)e; + VarDeclaration *vf = var->isVarDeclaration(); + if (vf) + { + Expression *e = sle->getField(type, vf->offset); + if (e && e != EXP_CANT_INTERPRET) + return e; + } + } + + return this; +} + +Expression *NewExp::optimize(int result) +{ + if (thisexp) + thisexp = thisexp->optimize(WANTvalue); + + // Optimize parameters + if (newargs) + { + for (size_t i = 0; i < newargs->dim; i++) + { Expression *e = newargs->tdata()[i]; + + e = e->optimize(WANTvalue); + newargs->tdata()[i] = e; + } + } + + if (arguments) + { + for (size_t i = 0; i < arguments->dim; i++) + { Expression *e = arguments->tdata()[i]; + + e = e->optimize(WANTvalue); + arguments->tdata()[i] = e; + } + } + if (result & WANTinterpret) + { + error("cannot evaluate %s at compile time", toChars()); + } + return this; +} + +Expression *CallExp::optimize(int result) +{ + //printf("CallExp::optimize(result = %d) %s\n", result, toChars()); + Expression *e = this; + + // Optimize parameters + if (arguments) + { + for (size_t i = 0; i < arguments->dim; i++) + { Expression *e = arguments->tdata()[i]; + + e = e->optimize(WANTvalue); + arguments->tdata()[i] = e; + } + } + + e1 = e1->optimize(result); +#if 1 + if (result & WANTinterpret) + { + Expression *eresult = interpret(NULL); + if (eresult == EXP_CANT_INTERPRET) + return e; + if (eresult && eresult != EXP_VOID_INTERPRET) + e = eresult; + else + error("cannot evaluate %s at compile time", toChars()); + } +#else + if (e1->op == TOKvar) + { + FuncDeclaration *fd = ((VarExp *)e1)->var->isFuncDeclaration(); + if (fd) + { + enum BUILTIN b = fd->isBuiltin(); + if (b) + { + e = eval_builtin(b, arguments); + if (!e) // failed + e = this; // evaluate at runtime + } + else if (result & WANTinterpret) + { + Expression *eresult = fd->interpret(NULL, arguments); + if (eresult && eresult != EXP_VOID_INTERPRET) + e = eresult; + else + error("cannot evaluate %s at compile time", toChars()); + } + } + } + else if (e1->op == TOKdotvar && result & WANTinterpret) + { DotVarExp *dve = (DotVarExp *)e1; + FuncDeclaration *fd = dve->var->isFuncDeclaration(); + if (fd) + { + Expression *eresult = fd->interpret(NULL, arguments, dve->e1); + if (eresult && eresult != EXP_VOID_INTERPRET) + e = eresult; + else + error("cannot evaluate %s at compile time", toChars()); + } + } +#endif + return e; +} + + +Expression *CastExp::optimize(int result) +{ + //printf("CastExp::optimize(result = %d) %s\n", result, toChars()); + //printf("from %s to %s\n", type->toChars(), to->toChars()); + //printf("from %s\n", type->toChars()); + //printf("e1->type %s\n", e1->type->toChars()); + //printf("type = %p\n", type); + assert(type); + enum TOK op1 = e1->op; +#define X 0 + + Expression *e1old = e1; + e1 = e1->optimize(result); + e1 = fromConstInitializer(result, e1); + + if (e1 == e1old && + e1->op == TOKarrayliteral && + type->toBasetype()->ty == Tpointer && + e1->type->toBasetype()->ty != Tsarray) + { + // Casting this will result in the same expression, and + // infinite loop because of Expression::implicitCastTo() + return this; // no change + } + + if ((e1->op == TOKstring || e1->op == TOKarrayliteral) && + (type->ty == Tpointer || type->ty == Tarray) && + e1->type->nextOf()->size() == type->nextOf()->size() + ) + { + Expression *e = e1->castTo(NULL, type); + if (X) printf(" returning1 %s\n", e->toChars()); + return e; + } + + if (e1->op == TOKstructliteral && + e1->type->implicitConvTo(type) >= MATCHconst) + { + e1->type = type; + if (X) printf(" returning2 %s\n", e1->toChars()); + return e1; + } + + /* The first test here is to prevent infinite loops + */ + if (op1 != TOKarrayliteral && e1->op == TOKarrayliteral) + return e1->castTo(NULL, to); + if (e1->op == TOKnull && + (type->ty == Tpointer || type->ty == Tclass || type->ty == Tarray)) + { + e1->type = type; + if (X) printf(" returning3 %s\n", e1->toChars()); + return e1; + } + + if (result & WANTflags && type->ty == Tclass && e1->type->ty == Tclass) + { + // See if we can remove an unnecessary cast + ClassDeclaration *cdfrom; + ClassDeclaration *cdto; + int offset; + + cdfrom = e1->type->isClassHandle(); + cdto = type->isClassHandle(); + if (cdto->isBaseOf(cdfrom, &offset) && offset == 0) + { + e1->type = type; + if (X) printf(" returning4 %s\n", e1->toChars()); + return e1; + } + } + + // We can convert 'head const' to mutable + if (to->constOf()->equals(e1->type->constOf())) + { + e1->type = type; + if (X) printf(" returning5 %s\n", e1->toChars()); + return e1; + } + + Expression *e; + + if (e1->isConst()) + { + if (e1->op == TOKsymoff) + { + if (type->size() == e1->type->size() && + type->toBasetype()->ty != Tsarray) + { + e1->type = type; + return e1; + } + return this; + } + if (to->toBasetype()->ty == Tvoid) + e = this; + else + e = Cast(type, to, e1); + } + else + e = this; + if (X) printf(" returning6 %s\n", e->toChars()); + return e; +#undef X +} + +Expression *BinExp::optimize(int result) +{ + //printf("BinExp::optimize(result = %d) %s\n", result, toChars()); + if (op != TOKconstruct && op != TOKblit) // don't replace const variable with its initializer + e1 = e1->optimize(result); + e2 = e2->optimize(result); + if (op == TOKshlass || op == TOKshrass || op == TOKushrass) + { + if (e2->isConst() == 1) + { + dinteger_t i2 = e2->toInteger(); + d_uns64 sz = e1->type->size() * 8; + if (i2 < 0 || i2 >= sz) + { error("shift assign by %jd is outside the range 0..%zu", i2, sz - 1); + e2 = new IntegerExp(0); + } + } + } + return this; +} + +Expression *AddExp::optimize(int result) +{ Expression *e; + + //printf("AddExp::optimize(%s)\n", toChars()); + e1 = e1->optimize(result); + e2 = e2->optimize(result); + if (e1->isConst() && e2->isConst()) + { + if (e1->op == TOKsymoff && e2->op == TOKsymoff) + return this; + e = Add(type, e1, e2); + } + else + e = this; + return e; +} + +Expression *MinExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(result); + e2 = e2->optimize(result); + if (e1->isConst() && e2->isConst()) + { + if (e2->op == TOKsymoff) + return this; + e = Min(type, e1, e2); + } + else + e = this; + return e; +} + +Expression *MulExp::optimize(int result) +{ Expression *e; + + //printf("MulExp::optimize(result = %d) %s\n", result, toChars()); + e1 = e1->optimize(result); + e2 = e2->optimize(result); + if (e1->isConst() == 1 && e2->isConst() == 1) + { + e = Mul(type, e1, e2); + } + else + e = this; + return e; +} + +Expression *DivExp::optimize(int result) +{ Expression *e; + + //printf("DivExp::optimize(%s)\n", toChars()); + e1 = e1->optimize(result); + e2 = e2->optimize(result); + if (e1->isConst() == 1 && e2->isConst() == 1) + { + e = Div(type, e1, e2); + } + else + e = this; + return e; +} + +Expression *ModExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(result); + e2 = e2->optimize(result); + if (e1->isConst() == 1 && e2->isConst() == 1) + { + e = Mod(type, e1, e2); + } + else + e = this; + return e; +} + +Expression *shift_optimize(int result, BinExp *e, Expression *(*shift)(Type *, Expression *, Expression *)) +{ Expression *ex = e; + + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->e2->isConst() == 1) + { + dinteger_t i2 = e->e2->toInteger(); + d_uns64 sz = e->e1->type->size() * 8; + if (i2 < 0 || i2 >= sz) + { e->error("shift by %jd is outside the range 0..%zu", i2, sz - 1); + e->e2 = new IntegerExp(0); + } + if (e->e1->isConst() == 1) + ex = (*shift)(e->type, e->e1, e->e2); + } + return ex; +} + +Expression *ShlExp::optimize(int result) +{ + //printf("ShlExp::optimize(result = %d) %s\n", result, toChars()); + return shift_optimize(result, this, Shl); +} + +Expression *ShrExp::optimize(int result) +{ + //printf("ShrExp::optimize(result = %d) %s\n", result, toChars()); + return shift_optimize(result, this, Shr); +} + +Expression *UshrExp::optimize(int result) +{ + //printf("UshrExp::optimize(result = %d) %s\n", result, toChars()); + return shift_optimize(result, this, Ushr); +} + +Expression *AndExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(result); + e2 = e2->optimize(result); + if (e1->isConst() == 1 && e2->isConst() == 1) + e = And(type, e1, e2); + else + e = this; + return e; +} + +Expression *OrExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(result); + e2 = e2->optimize(result); + if (e1->isConst() == 1 && e2->isConst() == 1) + e = Or(type, e1, e2); + else + e = this; + return e; +} + +Expression *XorExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(result); + e2 = e2->optimize(result); + if (e1->isConst() == 1 && e2->isConst() == 1) + e = Xor(type, e1, e2); + else + e = this; + return e; +} + +Expression *PowExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(result); + e2 = e2->optimize(result); + + // Replace 1 ^^ x or 1.0^^x by (x, 1) + if ((e1->op == TOKint64 && e1->toInteger() == 1) || + (e1->op == TOKfloat64 && e1->toReal() == 1.0)) + { + e = new CommaExp(loc, e2, e1); + } + // Replace -1 ^^ x by (x&1) ? -1 : 1, where x is integral + else if (e2->type->isintegral() && e1->op == TOKint64 && (sinteger_t)e1->toInteger() == -1L) + { + Type* resultType = type; + e = new AndExp(loc, e2, new IntegerExp(loc, 1, e2->type)); + e = new CondExp(loc, e, new IntegerExp(loc, -1L, resultType), new IntegerExp(loc, 1L, resultType)); + } + // Replace x ^^ 0 or x^^0.0 by (x, 1) + else if ((e2->op == TOKint64 && e2->toInteger() == 0) || + (e2->op == TOKfloat64 && e2->toReal() == 0.0)) + { + if (e1->type->isintegral()) + e = new IntegerExp(loc, 1, e1->type); + else + e = new RealExp(loc, 1.0, e1->type); + + e = new CommaExp(loc, e1, e); + } + // Replace x ^^ 1 or x^^1.0 by (x) + else if ((e2->op == TOKint64 && e2->toInteger() == 1) || + (e2->op == TOKfloat64 && e2->toReal() == 1.0)) + { + e = e1; + } + // Replace x ^^ -1.0 by (1.0 / x) + else if ((e2->op == TOKfloat64 && e2->toReal() == -1.0)) + { + e = new DivExp(loc, new RealExp(loc, 1.0, e2->type), e1); + } + // All other negative integral powers are illegal + else if ((e1->type->isintegral()) && (e2->op == TOKint64) && (sinteger_t)e2->toInteger() < 0) + { + error("cannot raise %s to a negative integer power. Did you mean (cast(real)%s)^^%s ?", + e1->type->toBasetype()->toChars(), e1->toChars(), e2->toChars()); + e = new ErrorExp(); + } + else + { + // If e2 *could* have been an integer, make it one. + if (e2->op == TOKfloat64 && (e2->toReal() == (sinteger_t)(e2->toReal()))) + e2 = new IntegerExp(loc, e2->toInteger(), Type::tint64); + + if (e1->isConst() == 1 && e2->isConst() == 1) + { + e = Pow(type, e1, e2); + if (e != EXP_CANT_INTERPRET) + return e; + } + e = this; + } + return e; +} + +Expression *CommaExp::optimize(int result) +{ Expression *e; + + //printf("CommaExp::optimize(result = %d) %s\n", result, toChars()); + // Comma needs special treatment, because it may + // contain compiler-generated declarations. We can interpret them, but + // otherwise we must NOT attempt to constant-fold them. + // In particular, if the comma returns a temporary variable, it needs + // to be an lvalue (this is particularly important for struct constructors) + + if (result & WANTinterpret) + { // Interpreting comma needs special treatment, because it may + // contain compiler-generated declarations. + e = interpret(NULL); + return (e == EXP_CANT_INTERPRET) ? this : e; + } + + e1 = e1->optimize(result & WANTinterpret); + e2 = e2->optimize(result); + if (!e1 || e1->op == TOKint64 || e1->op == TOKfloat64 || !e1->hasSideEffect()) + { + e = e2; + if (e) + e->type = type; + } + else + e = this; + //printf("-CommaExp::optimize(result = %d) %s\n", result, e->toChars()); + return e; +} + +Expression *ArrayLengthExp::optimize(int result) +{ Expression *e; + + //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, toChars()); + e1 = e1->optimize(WANTvalue | WANTexpand | (result & WANTinterpret)); + e = this; + if (e1->op == TOKstring || e1->op == TOKarrayliteral || e1->op == TOKassocarrayliteral) + { + e = ArrayLength(type, e1); + } + return e; +} + +Expression *EqualExp::optimize(int result) +{ Expression *e; + + //printf("EqualExp::optimize(result = %x) %s\n", result, toChars()); + e1 = e1->optimize(WANTvalue | (result & WANTinterpret)); + e2 = e2->optimize(WANTvalue | (result & WANTinterpret)); + e = this; + + Expression *e1 = fromConstInitializer(result, this->e1); + Expression *e2 = fromConstInitializer(result, this->e2); + + e = Equal(op, type, e1, e2); + if (e == EXP_CANT_INTERPRET) + e = this; + return e; +} + +Expression *IdentityExp::optimize(int result) +{ + //printf("IdentityExp::optimize(result = %d) %s\n", result, toChars()); + e1 = e1->optimize(WANTvalue | (result & WANTinterpret)); + e2 = e2->optimize(WANTvalue | (result & WANTinterpret)); + Expression *e = this; + + if ((this->e1->isConst() && this->e2->isConst()) || + (this->e1->op == TOKnull && this->e2->op == TOKnull)) + { + e = Identity(op, type, this->e1, this->e2); + if (e == EXP_CANT_INTERPRET) + e = this; + } + return e; +} + +/* It is possible for constant folding to change an array expression of + * unknown length, into one where the length is known. + * If the expression 'arr' is a literal, set lengthVar to be its length. + */ +void setLengthVarIfKnown(VarDeclaration *lengthVar, Expression *arr) +{ + if (!lengthVar) + return; + if (lengthVar->init && !lengthVar->init->isVoidInitializer()) + return; // we have previously calculated the length + size_t len; + if (arr->op == TOKstring) + len = ((StringExp *)arr)->len; + else if (arr->op == TOKarrayliteral) + len = ((ArrayLiteralExp *)arr)->elements->dim; + else + return; // we don't know the length yet + + Expression *dollar = new IntegerExp(0, len, Type::tsize_t); + lengthVar->init = new ExpInitializer(0, dollar); + lengthVar->storage_class |= STCstatic | STCconst; +} + + +Expression *IndexExp::optimize(int result) +{ Expression *e; + + //printf("IndexExp::optimize(result = %d) %s\n", result, toChars()); + Expression *e1 = this->e1->optimize( + WANTvalue | (result & (WANTinterpret| WANTexpand))); + e1 = fromConstInitializer(result, e1); + if (this->e1->op == TOKvar) + { VarExp *ve = (VarExp *)this->e1; + if (ve->var->storage_class & STCmanifest) + { /* We generally don't want to have more than one copy of an + * array literal, but if it's an enum we have to because the + * enum isn't stored elsewhere. See Bugzilla 2559 + */ + this->e1 = e1; + } + } + // We might know $ now + setLengthVarIfKnown(lengthVar, e1); + e2 = e2->optimize(WANTvalue | (result & WANTinterpret)); + e = Index(type, e1, e2); + if (e == EXP_CANT_INTERPRET) + e = this; + return e; +} + + +Expression *SliceExp::optimize(int result) +{ Expression *e; + + //printf("SliceExp::optimize(result = %d) %s\n", result, toChars()); + e = this; + e1 = e1->optimize(WANTvalue | (result & (WANTinterpret|WANTexpand))); + if (!lwr) + { if (e1->op == TOKstring) + { // Convert slice of string literal into dynamic array + Type *t = e1->type->toBasetype(); + if (t->nextOf()) + e = e1->castTo(NULL, t->nextOf()->arrayOf()); + } + return e; + } + e1 = fromConstInitializer(result, e1); + // We might know $ now + setLengthVarIfKnown(lengthVar, e1); + lwr = lwr->optimize(WANTvalue | (result & WANTinterpret)); + upr = upr->optimize(WANTvalue | (result & WANTinterpret)); + e = Slice(type, e1, lwr, upr); + if (e == EXP_CANT_INTERPRET) + e = this; + //printf("-SliceExp::optimize() %s\n", e->toChars()); + return e; +} + +Expression *AndAndExp::optimize(int result) +{ Expression *e; + + //printf("AndAndExp::optimize(%d) %s\n", result, toChars()); + e1 = e1->optimize(WANTflags | (result & WANTinterpret)); + e = this; + if (e1->isBool(FALSE)) + { + if (type->toBasetype()->ty == Tvoid) + e = e2; + else + { e = new CommaExp(loc, e1, new IntegerExp(loc, 0, type)); + e->type = type; + } + e = e->optimize(result); + } + else + { + e2 = e2->optimize(WANTflags | (result & WANTinterpret)); + if (result && e2->type->toBasetype()->ty == Tvoid && !global.errors) + error("void has no value"); + if (e1->isConst()) + { + if (e2->isConst()) + { int n1 = e1->isBool(1); + int n2 = e2->isBool(1); + + e = new IntegerExp(loc, n1 && n2, type); + } + else if (e1->isBool(TRUE)) + { + if (type->toBasetype()->ty == Tvoid) + e = e2; + else e = new BoolExp(loc, e2, type); + } + } + } + return e; +} + +Expression *OrOrExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(WANTflags | (result & WANTinterpret)); + e = this; + if (e1->isBool(TRUE)) + { // Replace with (e1, 1) + e = new CommaExp(loc, e1, new IntegerExp(loc, 1, type)); + e->type = type; + e = e->optimize(result); + } + else + { + e2 = e2->optimize(WANTflags | (result & WANTinterpret)); + if (result && e2->type->toBasetype()->ty == Tvoid && !global.errors) + error("void has no value"); + if (e1->isConst()) + { + if (e2->isConst()) + { int n1 = e1->isBool(1); + int n2 = e2->isBool(1); + + e = new IntegerExp(loc, n1 || n2, type); + } + else if (e1->isBool(FALSE)) + { + if (type->toBasetype()->ty == Tvoid) + e = e2; + else + e = new BoolExp(loc, e2, type); + } + } + } + return e; +} + +Expression *CmpExp::optimize(int result) +{ Expression *e; + + //printf("CmpExp::optimize() %s\n", toChars()); + e1 = e1->optimize(WANTvalue | (result & WANTinterpret)); + e2 = e2->optimize(WANTvalue | (result & WANTinterpret)); + + Expression *e1 = fromConstInitializer(result, this->e1); + Expression *e2 = fromConstInitializer(result, this->e2); + + e = Cmp(op, type, e1, e2); + if (e == EXP_CANT_INTERPRET) + e = this; + return e; +} + +Expression *CatExp::optimize(int result) +{ Expression *e; + + //printf("CatExp::optimize(%d) %s\n", result, toChars()); + e1 = e1->optimize(result); + e2 = e2->optimize(result); + e = Cat(type, e1, e2); + if (e == EXP_CANT_INTERPRET) + e = this; + return e; +} + + +Expression *CondExp::optimize(int result) +{ Expression *e; + + econd = econd->optimize(WANTflags | (result & WANTinterpret)); + if (econd->isBool(TRUE)) + e = e1->optimize(result); + else if (econd->isBool(FALSE)) + e = e2->optimize(result); + else + { e1 = e1->optimize(result); + e2 = e2->optimize(result); + e = this; + } + return e; +} + + diff --git a/parse.c b/parse.c new file mode 100644 index 00000000..addefc74 --- /dev/null +++ b/parse.c @@ -0,0 +1,6706 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +// This is the D parser + +#include +#include + +#include "rmem.h" +#include "lexer.h" +#include "parse.h" +#include "init.h" +#include "attrib.h" +#include "cond.h" +#include "mtype.h" +#include "template.h" +#include "staticassert.h" +#include "expression.h" +#include "statement.h" +#include "module.h" +#include "dsymbol.h" +#include "import.h" +#include "declaration.h" +#include "aggregate.h" +#include "enum.h" +#include "id.h" +#include "version.h" +#include "aliasthis.h" + +// How multiple declarations are parsed. +// If 1, treat as C. +// If 0, treat: +// int *p, i; +// as: +// int* p; +// int* i; +#define CDECLSYNTAX 0 + +// Support C cast syntax: +// (type)(expression) +#define CCASTSYNTAX 1 + +// Support postfix C array declarations, such as +// int a[3][4]; +#define CARRAYDECL 1 + +// Support D1 inout +#define D1INOUT 0 + +Parser::Parser(Module *module, unsigned char *base, unsigned length, int doDocComment) + : Lexer(module, base, 0, length, doDocComment, 0) +{ + //printf("Parser::Parser()\n"); + md = NULL; + linkage = LINKd; + endloc = 0; + inBrackets = 0; + lookingForElse = 0; + //nextToken(); // start up the scanner +} + +Dsymbols *Parser::parseModule() +{ + Dsymbols *decldefs; + + // ModuleDeclation leads off + if (token.value == TOKmodule) + { + unsigned char *comment = token.blockComment; + bool safe = FALSE; + + nextToken(); +#if 0 && DMDV2 + if (token.value == TOKlparen) + { + nextToken(); + if (token.value != TOKidentifier) + { error("module (system) identifier expected"); + goto Lerr; + } + Identifier *id = token.ident; + + if (id == Id::system) + safe = TRUE; + else + error("(safe) expected, not %s", id->toChars()); + nextToken(); + check(TOKrparen); + } +#endif + + if (token.value != TOKidentifier) + { error("Identifier expected following module"); + goto Lerr; + } + else + { + Identifiers *a = NULL; + Identifier *id; + + id = token.ident; + while (nextToken() == TOKdot) + { + if (!a) + a = new Identifiers(); + a->push(id); + nextToken(); + if (token.value != TOKidentifier) + { error("Identifier expected following package"); + goto Lerr; + } + id = token.ident; + } + + md = new ModuleDeclaration(a, id, safe); + + if (token.value != TOKsemicolon) + error("';' expected following module declaration instead of %s", token.toChars()); + nextToken(); + addComment(mod, comment); + } + } + + decldefs = parseDeclDefs(0); + if (token.value != TOKeof) + { error("unrecognized declaration"); + goto Lerr; + } + return decldefs; + +Lerr: + while (token.value != TOKsemicolon && token.value != TOKeof) + nextToken(); + nextToken(); + return new Dsymbols(); +} + +Dsymbols *Parser::parseDeclDefs(int once) +{ Dsymbol *s; + Dsymbols *decldefs; + Dsymbols *a; + Dsymbols *aelse; + enum PROT prot; + StorageClass stc; + StorageClass storageClass; + Condition *condition; + unsigned char *comment; + + //printf("Parser::parseDeclDefs()\n"); + decldefs = new Dsymbols(); + do + { + comment = token.blockComment; + storageClass = STCundefined; + switch (token.value) + { + case TOKenum: + { /* Determine if this is a manifest constant declaration, + * or a conventional enum. + */ + Token *t = peek(&token); + if (t->value == TOKlcurly || t->value == TOKcolon) + s = parseEnum(); + else if (t->value != TOKidentifier) + goto Ldeclaration; + else + { + t = peek(t); + if (t->value == TOKlcurly || t->value == TOKcolon || + t->value == TOKsemicolon) + s = parseEnum(); + else + goto Ldeclaration; + } + break; + } + + case TOKstruct: + case TOKunion: + case TOKclass: + case TOKinterface: + s = parseAggregate(); + break; + + case TOKimport: + s = parseImport(decldefs, 0); + break; + + case TOKtemplate: + s = (Dsymbol *)parseTemplateDeclaration(0); + break; + + case TOKmixin: + { Loc loc = this->loc; + switch (peekNext()) + { + case TOKlparen: + { // mixin(string) + nextToken(); + check(TOKlparen, "mixin"); + Expression *e = parseAssignExp(); + check(TOKrparen); + check(TOKsemicolon); + s = new CompileDeclaration(loc, e); + break; + } + case TOKtemplate: + // mixin template + nextToken(); + s = (Dsymbol *)parseTemplateDeclaration(1); + break; + + default: + s = parseMixin(); + break; + } + break; + } + + case BASIC_TYPES: + case TOKalias: + case TOKtypedef: + case TOKidentifier: + case TOKsuper: + case TOKtypeof: + case TOKdot: + case TOKvector: + Ldeclaration: + a = parseDeclarations(STCundefined, NULL); + decldefs->append(a); + continue; + + case TOKthis: + if (peekNext() == TOKdot) + goto Ldeclaration; + else + s = parseCtor(); + break; + +#if 0 // dead end, use this(this){} instead + case TOKassign: + s = parsePostBlit(); + break; +#endif + case TOKtilde: + s = parseDtor(); + break; + + case TOKinvariant: + { Token *t; + t = peek(&token); + if (t->value == TOKlparen) + { + if (peek(t)->value == TOKrparen) + // invariant() forms start of class invariant + s = parseInvariant(); + else + // invariant(type) + goto Ldeclaration; + } + else + { + if (!global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); + stc = STCimmutable; + goto Lstc; + } + break; + } + + case TOKunittest: + s = parseUnitTest(); + break; + + case TOKnew: + s = parseNew(); + break; + + case TOKdelete: + s = parseDelete(); + break; + + case TOKeof: + case TOKrcurly: + return decldefs; + + case TOKstatic: + nextToken(); + if (token.value == TOKthis) + s = parseStaticCtor(); + else if (token.value == TOKtilde) + s = parseStaticDtor(); + else if (token.value == TOKassert) + s = parseStaticAssert(); + else if (token.value == TOKif) + { condition = parseStaticIfCondition(); + Loc lookingForElseSave = lookingForElse; + lookingForElse = loc; + a = parseBlock(); + lookingForElse = lookingForElseSave; + aelse = NULL; + if (token.value == TOKelse) + { + Loc elseloc = this->loc; + nextToken(); + aelse = parseBlock(); + checkDanglingElse(elseloc); + } + s = new StaticIfDeclaration(condition, a, aelse); + break; + } + else if (token.value == TOKimport) + { + s = parseImport(decldefs, 1); + } + else + { stc = STCstatic; + goto Lstc2; + } + break; + + case TOKconst: + if (peekNext() == TOKlparen) + goto Ldeclaration; + stc = STCconst; + goto Lstc; + + case TOKimmutable: + if (peekNext() == TOKlparen) + goto Ldeclaration; + stc = STCimmutable; + goto Lstc; + + case TOKshared: + { TOK next = peekNext(); + if (next == TOKlparen) + goto Ldeclaration; + if (next == TOKstatic) + { TOK next2 = peekNext2(); + if (next2 == TOKthis) + { s = parseSharedStaticCtor(); + break; + } + if (next2 == TOKtilde) + { s = parseSharedStaticDtor(); + break; + } + } + stc = STCshared; + goto Lstc; + } + + case TOKwild: + if (peekNext() == TOKlparen) + goto Ldeclaration; + stc = STCwild; + goto Lstc; + + case TOKfinal: stc = STCfinal; goto Lstc; + case TOKauto: stc = STCauto; goto Lstc; + case TOKscope: stc = STCscope; goto Lstc; + case TOKoverride: stc = STCoverride; goto Lstc; + case TOKabstract: stc = STCabstract; goto Lstc; + case TOKsynchronized: stc = STCsynchronized; goto Lstc; + case TOKdeprecated: stc = STCdeprecated; goto Lstc; +#if DMDV2 + case TOKnothrow: stc = STCnothrow; goto Lstc; + case TOKpure: stc = STCpure; goto Lstc; + case TOKref: stc = STCref; goto Lstc; + case TOKtls: stc = STCtls; goto Lstc; + case TOKgshared: stc = STCgshared; goto Lstc; + //case TOKmanifest: stc = STCmanifest; goto Lstc; + case TOKat: stc = parseAttribute(); goto Lstc; +#endif + + Lstc: + if (storageClass & stc) + error("redundant storage class %s", Token::toChars(token.value)); + composeStorageClass(storageClass | stc); + nextToken(); + Lstc2: + storageClass |= stc; + switch (token.value) + { + case TOKshared: + // Look for "shared static this" or "shared static ~this" + if (peekNext() == TOKstatic) + { TOK next2 = peekNext2(); + if (next2 == TOKthis || next2 == TOKtilde) + break; + } + case TOKconst: + case TOKinvariant: + case TOKimmutable: + case TOKwild: + // If followed by a (, it is not a storage class + if (peek(&token)->value == TOKlparen) + break; + if (token.value == TOKconst) + stc = STCconst; + else if (token.value == TOKshared) + stc = STCshared; + else if (token.value == TOKwild) + stc = STCwild; + else + { + if (token.value == TOKinvariant && !global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); + stc = STCimmutable; + } + goto Lstc; + case TOKfinal: stc = STCfinal; goto Lstc; + case TOKauto: stc = STCauto; goto Lstc; + case TOKscope: stc = STCscope; goto Lstc; + case TOKoverride: stc = STCoverride; goto Lstc; + case TOKabstract: stc = STCabstract; goto Lstc; + case TOKsynchronized: stc = STCsynchronized; goto Lstc; + case TOKdeprecated: stc = STCdeprecated; goto Lstc; + case TOKnothrow: stc = STCnothrow; goto Lstc; + case TOKpure: stc = STCpure; goto Lstc; + case TOKref: stc = STCref; goto Lstc; + case TOKtls: stc = STCtls; goto Lstc; + case TOKgshared: stc = STCgshared; goto Lstc; + //case TOKmanifest: stc = STCmanifest; goto Lstc; + case TOKat: stc = parseAttribute(); goto Lstc; + default: + break; + } + + /* Look for auto initializers: + * storage_class identifier = initializer; + */ + if (token.value == TOKidentifier && + peek(&token)->value == TOKassign) + { + a = parseAutoDeclarations(storageClass, comment); + decldefs->append(a); + continue; + } + + /* Look for return type inference for template functions. + */ + Token *tk; + if (token.value == TOKidentifier && + (tk = peek(&token))->value == TOKlparen && + skipParens(tk, &tk) && + ((tk = peek(tk)), 1) && + skipAttributes(tk, &tk) && + (tk->value == TOKlparen || + tk->value == TOKlcurly) + ) + { + a = parseDeclarations(storageClass, comment); + decldefs->append(a); + continue; + } + a = parseBlock(); + s = new StorageClassDeclaration(storageClass, a); + break; + + case TOKextern: + if (peek(&token)->value != TOKlparen) + { stc = STCextern; + goto Lstc; + } + { + enum LINK linksave = linkage; + linkage = parseLinkage(); + a = parseBlock(); + s = new LinkDeclaration(linkage, a); + linkage = linksave; + break; + } + case TOKprivate: prot = PROTprivate; goto Lprot; + case TOKpackage: prot = PROTpackage; goto Lprot; + case TOKprotected: prot = PROTprotected; goto Lprot; + case TOKpublic: prot = PROTpublic; goto Lprot; + case TOKexport: prot = PROTexport; goto Lprot; + + Lprot: + nextToken(); + switch (token.value) + { + case TOKprivate: + case TOKpackage: + case TOKprotected: + case TOKpublic: + case TOKexport: + error("redundant protection attribute"); + break; + } + a = parseBlock(); + s = new ProtDeclaration(prot, a); + break; + + case TOKalign: + { unsigned n; + + s = NULL; + nextToken(); + if (token.value == TOKlparen) + { + nextToken(); + if (token.value == TOKint32v && token.uns64value > 0) + n = (unsigned)token.uns64value; + else + { error("positive integer expected, not %s", token.toChars()); + n = 1; + } + nextToken(); + check(TOKrparen); + } + else + n = global.structalign; // default + + a = parseBlock(); + s = new AlignDeclaration(n, a); + break; + } + + case TOKpragma: + { Identifier *ident; + Expressions *args = NULL; + + nextToken(); + check(TOKlparen); + if (token.value != TOKidentifier) + { error("pragma(identifier expected"); + goto Lerror; + } + ident = token.ident; + nextToken(); + if (token.value == TOKcomma && peekNext() != TOKrparen) + args = parseArguments(); // pragma(identifier, args...) + else + check(TOKrparen); // pragma(identifier) + + if (token.value == TOKsemicolon) + a = NULL; + else + a = parseBlock(); + s = new PragmaDeclaration(loc, ident, args, a); + break; + } + + case TOKdebug: + nextToken(); + if (token.value == TOKassign) + { + nextToken(); + if (token.value == TOKidentifier) + s = new DebugSymbol(loc, token.ident); + else if (token.value == TOKint32v || token.value == TOKint64v) + s = new DebugSymbol(loc, (unsigned)token.uns64value); + else + { error("identifier or integer expected, not %s", token.toChars()); + s = NULL; + } + nextToken(); + if (token.value != TOKsemicolon) + error("semicolon expected"); + nextToken(); + break; + } + + condition = parseDebugCondition(); + goto Lcondition; + + case TOKversion: + nextToken(); + if (token.value == TOKassign) + { + nextToken(); + if (token.value == TOKidentifier) + s = new VersionSymbol(loc, token.ident); + else if (token.value == TOKint32v || token.value == TOKint64v) + s = new VersionSymbol(loc, (unsigned)token.uns64value); + else + { error("identifier or integer expected, not %s", token.toChars()); + s = NULL; + } + nextToken(); + if (token.value != TOKsemicolon) + error("semicolon expected"); + nextToken(); + break; + } + condition = parseVersionCondition(); + goto Lcondition; + + Lcondition: + { + Loc lookingForElseSave = lookingForElse; + lookingForElse = loc; + a = parseBlock(); + lookingForElse = lookingForElseSave; + } + aelse = NULL; + if (token.value == TOKelse) + { + Loc elseloc = this->loc; + nextToken(); + aelse = parseBlock(); + checkDanglingElse(elseloc); + } + s = new ConditionalDeclaration(condition, a, aelse); + break; + + case TOKsemicolon: // empty declaration + //error("empty declaration"); + nextToken(); + continue; + + default: + error("Declaration expected, not '%s'",token.toChars()); + Lerror: + while (token.value != TOKsemicolon && token.value != TOKeof) + nextToken(); + nextToken(); + s = NULL; + continue; + } + if (s) + { decldefs->push(s); + addComment(s, comment); + } + } while (!once); + return decldefs; +} + +/********************************************* + * Give error on conflicting storage classes. + */ + +#if DMDV2 +void Parser::composeStorageClass(StorageClass stc) +{ + StorageClass u = stc; + u &= STCconst | STCimmutable | STCmanifest; + if (u & (u - 1)) + error("conflicting storage class %s", Token::toChars(token.value)); + u = stc; + u &= STCgshared | STCshared | STCtls; + if (u & (u - 1)) + error("conflicting storage class %s", Token::toChars(token.value)); + u = stc; + u &= STCsafe | STCsystem | STCtrusted; + if (u & (u - 1)) + error("conflicting attribute @%s", token.toChars()); +} +#endif + +/*********************************************** + * Parse storage class, lexer is on '@' + */ + +#if DMDV2 +StorageClass Parser::parseAttribute() +{ + nextToken(); + StorageClass stc = 0; + if (token.value != TOKidentifier) + { + error("identifier expected after @, not %s", token.toChars()); + } + else if (token.ident == Id::property) + stc = STCproperty; + else if (token.ident == Id::safe) + stc = STCsafe; + else if (token.ident == Id::trusted) + stc = STCtrusted; + else if (token.ident == Id::system) + stc = STCsystem; + else if (token.ident == Id::disable) + stc = STCdisable; + else + error("valid attribute identifiers are @property, @safe, @trusted, @system, @disable not @%s", token.toChars()); + return stc; +} +#endif + +/*********************************************** + * Parse const/immutable/shared/inout/nothrow/pure postfix + */ + +StorageClass Parser::parsePostfix() +{ + StorageClass stc = 0; + + while (1) + { + switch (token.value) + { + case TOKconst: stc |= STCconst; break; + case TOKinvariant: + if (!global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); + case TOKimmutable: stc |= STCimmutable; break; + case TOKshared: stc |= STCshared; break; + case TOKwild: stc |= STCwild; break; + case TOKnothrow: stc |= STCnothrow; break; + case TOKpure: stc |= STCpure; break; + case TOKat: stc |= parseAttribute(); break; + + default: return stc; + } + composeStorageClass(stc); + nextToken(); + } +} + +/******************************************** + * Parse declarations after an align, protection, or extern decl. + */ + +Dsymbols *Parser::parseBlock() +{ + Dsymbols *a = NULL; + + //printf("parseBlock()\n"); + switch (token.value) + { + case TOKsemicolon: + error("declaration expected following attribute, not ';'"); + nextToken(); + break; + + case TOKeof: + error("declaration expected following attribute, not EOF"); + break; + + case TOKlcurly: + { + Loc lookingForElseSave = lookingForElse; + lookingForElse = 0; + + nextToken(); + a = parseDeclDefs(0); + if (token.value != TOKrcurly) + { /* { */ + error("matching '}' expected, not %s", token.toChars()); + } + else + nextToken(); + lookingForElse = lookingForElseSave; + break; + } + + case TOKcolon: + nextToken(); +#if 0 + a = NULL; +#else + a = parseDeclDefs(0); // grab declarations up to closing curly bracket +#endif + break; + + default: + a = parseDeclDefs(1); + break; + } + return a; +} + +/********************************** + * Parse a static assertion. + */ + +StaticAssert *Parser::parseStaticAssert() +{ + Loc loc = this->loc; + Expression *exp; + Expression *msg = NULL; + + //printf("parseStaticAssert()\n"); + nextToken(); + check(TOKlparen); + exp = parseAssignExp(); + if (token.value == TOKcomma) + { nextToken(); + msg = parseAssignExp(); + } + check(TOKrparen); + check(TOKsemicolon); + return new StaticAssert(loc, exp, msg); +} + +/*********************************** + * Parse typeof(expression). + * Current token is on the 'typeof'. + */ + +#if DMDV2 +TypeQualified *Parser::parseTypeof() +{ TypeQualified *t; + Loc loc = this->loc; + + nextToken(); + check(TOKlparen); + if (token.value == TOKreturn) // typeof(return) + { + nextToken(); + t = new TypeReturn(loc); + } + else + { Expression *exp = parseExpression(); // typeof(expression) + t = new TypeTypeof(loc, exp); + } + check(TOKrparen); + return t; +} +#endif + +/*********************************** + * Parse __vector(type). + * Current token is on the '__vector'. + */ + +#if DMDV2 +Type *Parser::parseVector() +{ + Loc loc = this->loc; + nextToken(); + check(TOKlparen); + Type *tb = parseType(); + check(TOKrparen); + return new TypeVector(loc, tb); +} +#endif + +/*********************************** + * Parse extern (linkage) + * The parser is on the 'extern' token. + */ + +enum LINK Parser::parseLinkage() +{ + enum LINK link = LINKdefault; + nextToken(); + assert(token.value == TOKlparen); + nextToken(); + if (token.value == TOKidentifier) + { Identifier *id = token.ident; + + nextToken(); + if (id == Id::Windows) + link = LINKwindows; + else if (id == Id::Pascal) + link = LINKpascal; + else if (id == Id::D) + link = LINKd; + else if (id == Id::C) + { + link = LINKc; + if (token.value == TOKplusplus) + { link = LINKcpp; + nextToken(); + } + } + else if (id == Id::System) + { +#if _WIN32 + link = LINKwindows; +#else + link = LINKc; +#endif + } + else + { + error("valid linkage identifiers are D, C, C++, Pascal, Windows, System"); + link = LINKd; + } + } + else + { + link = LINKd; // default + } + check(TOKrparen); + return link; +} + +/************************************** + * Parse a debug conditional + */ + +Condition *Parser::parseDebugCondition() +{ + Condition *c; + + if (token.value == TOKlparen) + { + nextToken(); + unsigned level = 1; + Identifier *id = NULL; + + if (token.value == TOKidentifier) + id = token.ident; + else if (token.value == TOKint32v || token.value == TOKint64v) + level = (unsigned)token.uns64value; + else + error("identifier or integer expected, not %s", token.toChars()); + nextToken(); + check(TOKrparen); + c = new DebugCondition(mod, level, id); + } + else + c = new DebugCondition(mod, 1, NULL); + return c; + +} + +/************************************** + * Parse a version conditional + */ + +Condition *Parser::parseVersionCondition() +{ + Condition *c; + unsigned level = 1; + Identifier *id = NULL; + + if (token.value == TOKlparen) + { + nextToken(); + if (token.value == TOKidentifier) + id = token.ident; + else if (token.value == TOKint32v || token.value == TOKint64v) + level = (unsigned)token.uns64value; +#if DMDV2 + /* Allow: + * version (unittest) + * even though unittest is a keyword + */ + else if (token.value == TOKunittest) + id = Lexer::idPool(Token::toChars(TOKunittest)); +#endif + else + error("identifier or integer expected, not %s", token.toChars()); + nextToken(); + check(TOKrparen); + + } + else + error("(condition) expected following version"); + c = new VersionCondition(mod, level, id); + return c; + +} + +/*********************************************** + * static if (expression) + * body + * else + * body + */ + +Condition *Parser::parseStaticIfCondition() +{ Expression *exp; + Condition *condition; + Loc loc = this->loc; + + nextToken(); + if (token.value == TOKlparen) + { + nextToken(); + exp = parseAssignExp(); + check(TOKrparen); + } + else + { error("(expression) expected following static if"); + exp = NULL; + } + condition = new StaticIfCondition(loc, exp); + return condition; +} + + +/***************************************** + * Parse a constructor definition: + * this(parameters) { body } + * or postblit: + * this(this) { body } + * or constructor template: + * this(templateparameters)(parameters) { body } + * Current token is 'this'. + */ + +Dsymbol *Parser::parseCtor() +{ + Loc loc = this->loc; + + nextToken(); + if (token.value == TOKlparen && peek(&token)->value == TOKthis) + { // this(this) { ... } + nextToken(); + nextToken(); + check(TOKrparen); + StorageClass stc = parsePostfix(); + PostBlitDeclaration *f = new PostBlitDeclaration(loc, 0, stc); + parseContracts(f); + return f; + } + + /* Look ahead to see if: + * this(...)(...) + * which is a constructor template + */ + TemplateParameters *tpl = NULL; + if (token.value == TOKlparen && peekPastParen(&token)->value == TOKlparen) + { tpl = parseTemplateParameterList(); + + int varargs; + Parameters *parameters = parseParameters(&varargs); + StorageClass stc = parsePostfix(); + + Expression *constraint = tpl ? parseConstraint() : NULL; + + Type *tf = new TypeFunction(parameters, NULL, varargs, linkage, stc); // RetrunType -> auto + tf = tf->addSTC(stc); + + CtorDeclaration *f = new CtorDeclaration(loc, 0, stc, tf); + parseContracts(f); + + // Wrap a template around it + Dsymbols *decldefs = new Dsymbols(); + decldefs->push(f); + TemplateDeclaration *tempdecl = + new TemplateDeclaration(loc, f->ident, tpl, constraint, decldefs, 0); + return tempdecl; + } + + /* Just a regular constructor + */ + int varargs; + Parameters *parameters = parseParameters(&varargs); + StorageClass stc = parsePostfix(); + Type *tf = new TypeFunction(parameters, NULL, varargs, linkage, stc); // RetrunType -> auto + tf = tf->addSTC(stc); + + CtorDeclaration *f = new CtorDeclaration(loc, 0, stc, tf); + parseContracts(f); + return f; +} + +/***************************************** + * Parse a postblit definition: + * =this() { body } + * Current token is '='. + */ + +PostBlitDeclaration *Parser::parsePostBlit() +{ + Loc loc = this->loc; + + nextToken(); + check(TOKthis); + check(TOKlparen); + check(TOKrparen); + + PostBlitDeclaration *f = new PostBlitDeclaration(loc, 0); + parseContracts(f); + return f; +} + +/***************************************** + * Parse a destructor definition: + * ~this() { body } + * Current token is '~'. + */ + +DtorDeclaration *Parser::parseDtor() +{ + DtorDeclaration *f; + Loc loc = this->loc; + + nextToken(); + check(TOKthis); + check(TOKlparen); + check(TOKrparen); + + f = new DtorDeclaration(loc, 0); + parseContracts(f); + return f; +} + +/***************************************** + * Parse a static constructor definition: + * static this() { body } + * Current token is 'this'. + */ + +StaticCtorDeclaration *Parser::parseStaticCtor() +{ + Loc loc = this->loc; + + nextToken(); + check(TOKlparen); + check(TOKrparen); + + StaticCtorDeclaration *f = new StaticCtorDeclaration(loc, 0); + parseContracts(f); + return f; +} + +/***************************************** + * Parse a shared static constructor definition: + * shared static this() { body } + * Current token is 'shared'. + */ + +SharedStaticCtorDeclaration *Parser::parseSharedStaticCtor() +{ + Loc loc = this->loc; + + nextToken(); + nextToken(); + nextToken(); + check(TOKlparen); + check(TOKrparen); + + SharedStaticCtorDeclaration *f = new SharedStaticCtorDeclaration(loc, 0); + parseContracts(f); + return f; +} + +/***************************************** + * Parse a static destructor definition: + * static ~this() { body } + * Current token is '~'. + */ + +StaticDtorDeclaration *Parser::parseStaticDtor() +{ + Loc loc = this->loc; + + nextToken(); + check(TOKthis); + check(TOKlparen); + check(TOKrparen); + + StaticDtorDeclaration *f = new StaticDtorDeclaration(loc, 0); + parseContracts(f); + return f; +} + +/***************************************** + * Parse a shared static destructor definition: + * shared static ~this() { body } + * Current token is 'shared'. + */ + +SharedStaticDtorDeclaration *Parser::parseSharedStaticDtor() +{ + Loc loc = this->loc; + + nextToken(); + nextToken(); + nextToken(); + check(TOKthis); + check(TOKlparen); + check(TOKrparen); + + SharedStaticDtorDeclaration *f = new SharedStaticDtorDeclaration(loc, 0); + parseContracts(f); + return f; +} + +/***************************************** + * Parse an invariant definition: + * invariant() { body } + * Current token is 'invariant'. + */ + +InvariantDeclaration *Parser::parseInvariant() +{ + InvariantDeclaration *f; + Loc loc = this->loc; + + nextToken(); + if (token.value == TOKlparen) // optional () + { + nextToken(); + check(TOKrparen); + } + + f = new InvariantDeclaration(loc, 0); + f->fbody = parseStatement(PScurly); + return f; +} + +/***************************************** + * Parse a unittest definition: + * unittest { body } + * Current token is 'unittest'. + */ + +UnitTestDeclaration *Parser::parseUnitTest() +{ + UnitTestDeclaration *f; + Statement *body; + Loc loc = this->loc; + + nextToken(); + + body = parseStatement(PScurly); + + f = new UnitTestDeclaration(loc, this->loc); + f->fbody = body; + return f; +} + +/***************************************** + * Parse a new definition: + * new(arguments) { body } + * Current token is 'new'. + */ + +NewDeclaration *Parser::parseNew() +{ + NewDeclaration *f; + Parameters *arguments; + int varargs; + Loc loc = this->loc; + + nextToken(); + arguments = parseParameters(&varargs); + f = new NewDeclaration(loc, 0, arguments, varargs); + parseContracts(f); + return f; +} + +/***************************************** + * Parse a delete definition: + * delete(arguments) { body } + * Current token is 'delete'. + */ + +DeleteDeclaration *Parser::parseDelete() +{ + DeleteDeclaration *f; + Parameters *arguments; + int varargs; + Loc loc = this->loc; + + nextToken(); + arguments = parseParameters(&varargs); + if (varargs) + error("... not allowed in delete function parameter list"); + f = new DeleteDeclaration(loc, 0, arguments); + parseContracts(f); + return f; +} + +/********************************************** + * Parse parameter list. + */ + +Parameters *Parser::parseParameters(int *pvarargs, TemplateParameters **tpl) +{ + Parameters *arguments = new Parameters(); + int varargs = 0; + int hasdefault = 0; + + check(TOKlparen); + while (1) + { + Identifier *ai = NULL; + Type *at; + Parameter *a; + StorageClass storageClass = 0; + StorageClass stc; + Expression *ae; + + for (;1; nextToken()) + { + switch (token.value) + { + case TOKrparen: + break; + + case TOKdotdotdot: + varargs = 1; + nextToken(); + break; + + case TOKconst: + if (peek(&token)->value == TOKlparen) + goto Ldefault; + stc = STCconst; + goto L2; + + case TOKinvariant: + case TOKimmutable: + if (peek(&token)->value == TOKlparen) + goto Ldefault; + if (token.value == TOKinvariant && !global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); + stc = STCimmutable; + goto L2; + + case TOKshared: + if (peek(&token)->value == TOKlparen) + goto Ldefault; + stc = STCshared; + goto L2; + + case TOKwild: + if (peek(&token)->value == TOKlparen) + goto Ldefault; + stc = STCwild; + goto L2; + + case TOKin: stc = STCin; goto L2; + case TOKout: stc = STCout; goto L2; +#if D1INOUT + case TOKinout: +#endif + case TOKref: stc = STCref; goto L2; + case TOKlazy: stc = STClazy; goto L2; + case TOKscope: stc = STCscope; goto L2; + case TOKfinal: stc = STCfinal; goto L2; + case TOKauto: stc = STCauto; goto L2; + L2: + if (storageClass & stc || + (storageClass & STCin && stc & (STCconst | STCscope)) || + (stc & STCin && storageClass & (STCconst | STCscope)) + ) + error("redundant storage class %s", Token::toChars(token.value)); + storageClass |= stc; + composeStorageClass(storageClass); + continue; + +#if 0 + case TOKstatic: stc = STCstatic; goto L2; + case TOKauto: storageClass = STCauto; goto L4; + case TOKalias: storageClass = STCalias; goto L4; + L4: + nextToken(); + if (token.value == TOKidentifier) + { ai = token.ident; + nextToken(); + } + else + ai = NULL; + at = NULL; // no type + ae = NULL; // no default argument + if (token.value == TOKassign) // = defaultArg + { nextToken(); + ae = parseDefaultInitExp(); + hasdefault = 1; + } + else + { if (hasdefault) + error("default argument expected for alias %s", + ai ? ai->toChars() : ""); + } + goto L3; +#endif + + default: + Ldefault: + { stc = storageClass & (STCin | STCout | STCref | STClazy); + if (stc & (stc - 1)) // if stc is not a power of 2 + error("incompatible parameter storage classes"); + if ((storageClass & (STCconst | STCout)) == (STCconst | STCout)) + error("out cannot be const"); + if ((storageClass & (STCimmutable | STCout)) == (STCimmutable | STCout)) + error("out cannot be immutable"); + if ((storageClass & STCscope) && (storageClass & (STCref | STCout))) + error("scope cannot be ref or out"); + + Token *t; + if (tpl && !stc && token.value == TOKidentifier && + (t = peek(&token), (t->value == TOKcomma || t->value == TOKrparen))) + { Identifier *id = Lexer::uniqueId("__T"); + at = new TypeIdentifier(loc, id); + if (!*tpl) + *tpl = new TemplateParameters(); + TemplateParameter *tp = new TemplateTypeParameter(loc, id, NULL, NULL); + (*tpl)->push(tp); + + ai = token.ident; + nextToken(); + } + else + at = parseType(&ai); + + ae = NULL; + if (token.value == TOKassign) // = defaultArg + { nextToken(); + ae = parseDefaultInitExp(); + hasdefault = 1; + } + else + { if (hasdefault) + error("default argument expected for %s", + ai ? ai->toChars() : at->toChars()); + } + if (token.value == TOKdotdotdot) + { /* This is: + * at ai ... + */ + + if (storageClass & (STCout | STCref)) + error("variadic argument cannot be out or ref"); + varargs = 2; + a = new Parameter(storageClass, at, ai, ae); + arguments->push(a); + nextToken(); + break; + } + L3: + a = new Parameter(storageClass, at, ai, ae); + arguments->push(a); + if (token.value == TOKcomma) + { nextToken(); + goto L1; + } + break; + } + } + break; + } + break; + + L1: ; + } + check(TOKrparen); + *pvarargs = varargs; + return arguments; +} + + +/************************************* + */ + +EnumDeclaration *Parser::parseEnum() +{ EnumDeclaration *e; + Identifier *id; + Type *memtype; + Loc loc = this->loc; + + //printf("Parser::parseEnum()\n"); + nextToken(); + if (token.value == TOKidentifier) + { id = token.ident; + nextToken(); + } + else + id = NULL; + + if (token.value == TOKcolon) + { + nextToken(); + memtype = parseBasicType(); + memtype = parseDeclarator(memtype, NULL, NULL); + } + else + memtype = NULL; + + e = new EnumDeclaration(loc, id, memtype); + if (token.value == TOKsemicolon && id) + nextToken(); + else if (token.value == TOKlcurly) + { + //printf("enum definition\n"); + e->members = new Dsymbols(); + nextToken(); + unsigned char *comment = token.blockComment; + while (token.value != TOKrcurly) + { + /* Can take the following forms: + * 1. ident + * 2. ident = value + * 3. type ident = value + */ + + loc = this->loc; + + Type *type = NULL; + Identifier *ident; + Token *tp = peek(&token); + if (token.value == TOKidentifier && + (tp->value == TOKassign || tp->value == TOKcomma || tp->value == TOKrcurly)) + { + ident = token.ident; + type = NULL; + nextToken(); + } + else + { + type = parseType(&ident, NULL); + if (id || memtype) + error("type only allowed if anonymous enum and no enum type"); + } + + Expression *value; + if (token.value == TOKassign) + { + nextToken(); + value = parseAssignExp(); + } + else + { value = NULL; + if (type) + error("if type, there must be an initializer"); + } + + EnumMember *em = new EnumMember(loc, ident, value, type); + e->members->push(em); + + if (token.value == TOKrcurly) + ; + else + { addComment(em, comment); + comment = NULL; + check(TOKcomma); + } + addComment(em, comment); + comment = token.blockComment; + + if (token.value == TOKeof) + { error("premature end of file"); + break; + } + } + nextToken(); + } + else + error("enum declaration is invalid"); + + //printf("-parseEnum() %s\n", e->toChars()); + return e; +} + +/******************************** + * Parse struct, union, interface, class. + */ + +Dsymbol *Parser::parseAggregate() +{ AggregateDeclaration *a = NULL; + int anon = 0; + enum TOK tok; + Identifier *id; + TemplateParameters *tpl = NULL; + Expression *constraint = NULL; + + //printf("Parser::parseAggregate()\n"); + tok = token.value; + nextToken(); + if (token.value != TOKidentifier) + { id = NULL; + } + else + { id = token.ident; + nextToken(); + + if (token.value == TOKlparen) + { // Class template declaration. + + // Gather template parameter list + tpl = parseTemplateParameterList(); + constraint = parseConstraint(); + } + } + + Loc loc = this->loc; + switch (tok) + { case TOKclass: + case TOKinterface: + { + if (!id) + error("anonymous classes not allowed"); + + // Collect base class(es) + BaseClasses *baseclasses = NULL; + if (token.value == TOKcolon) + { + nextToken(); + baseclasses = parseBaseClasses(); + + if (token.value != TOKlcurly) + error("members expected"); + } + + if (tok == TOKclass) + a = new ClassDeclaration(loc, id, baseclasses); + else + a = new InterfaceDeclaration(loc, id, baseclasses); + break; + } + + case TOKstruct: + if (id) + a = new StructDeclaration(loc, id); + else + anon = 1; + break; + + case TOKunion: + if (id) + a = new UnionDeclaration(loc, id); + else + anon = 2; + break; + + default: + assert(0); + break; + } + if (a && token.value == TOKsemicolon) + { nextToken(); + } + else if (token.value == TOKlcurly) + { + //printf("aggregate definition\n"); + nextToken(); + Dsymbols *decl = parseDeclDefs(0); + if (token.value != TOKrcurly) + error("} expected following member declarations in aggregate"); + nextToken(); + if (anon) + { + /* Anonymous structs/unions are more like attributes. + */ + return new AnonDeclaration(loc, anon - 1, decl); + } + else + a->members = decl; + } + else + { + error("{ } expected following aggregate declaration"); + a = new StructDeclaration(loc, NULL); + } + + if (tpl) + { // Wrap a template around the aggregate declaration + + Dsymbols *decldefs = new Dsymbols(); + decldefs->push(a); + TemplateDeclaration *tempdecl = + new TemplateDeclaration(loc, id, tpl, constraint, decldefs, 0); + return tempdecl; + } + + return a; +} + +/******************************************* + */ + +BaseClasses *Parser::parseBaseClasses() +{ + BaseClasses *baseclasses = new BaseClasses(); + + for (; 1; nextToken()) + { + bool prot = false; + enum PROT protection = PROTpublic; + switch (token.value) + { + case TOKprivate: + prot = true; + protection = PROTprivate; + nextToken(); + break; + case TOKpackage: + prot = true; + protection = PROTpackage; + nextToken(); + break; + case TOKprotected: + prot = true; + protection = PROTprotected; + nextToken(); + break; + case TOKpublic: + prot = true; + protection = PROTpublic; + nextToken(); + break; + } + if (prot && !global.params.useDeprecated) + error("use of base class protection is deprecated"); + if (token.value == TOKidentifier) + { + BaseClass *b = new BaseClass(parseBasicType(), protection); + baseclasses->push(b); + if (token.value != TOKcomma) + break; + } + else + { + error("base classes expected instead of %s", token.toChars()); + return NULL; + } + } + return baseclasses; +} + +/************************************** + * Parse constraint. + * Constraint is of the form: + * if ( ConstraintExpression ) + */ + +#if DMDV2 +Expression *Parser::parseConstraint() +{ Expression *e = NULL; + + if (token.value == TOKif) + { + nextToken(); // skip over 'if' + check(TOKlparen); + e = parseExpression(); + check(TOKrparen); + } + return e; +} +#endif + +/************************************** + * Parse a TemplateDeclaration. + */ + +TemplateDeclaration *Parser::parseTemplateDeclaration(int ismixin) +{ + TemplateDeclaration *tempdecl; + Identifier *id; + TemplateParameters *tpl; + Dsymbols *decldefs; + Expression *constraint = NULL; + Loc loc = this->loc; + + nextToken(); + if (token.value != TOKidentifier) + { error("TemplateIdentifier expected following template"); + goto Lerr; + } + id = token.ident; + nextToken(); + tpl = parseTemplateParameterList(); + if (!tpl) + goto Lerr; + + constraint = parseConstraint(); + + if (token.value != TOKlcurly) + { error("members of template declaration expected"); + goto Lerr; + } + else + { + nextToken(); + decldefs = parseDeclDefs(0); + if (token.value != TOKrcurly) + { error("template member expected"); + goto Lerr; + } + nextToken(); + } + + tempdecl = new TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin); + return tempdecl; + +Lerr: + return NULL; +} + +/****************************************** + * Parse template parameter list. + * Input: + * flag 0: parsing "( list )" + * 1: parsing non-empty "list )" + */ + +TemplateParameters *Parser::parseTemplateParameterList(int flag) +{ + TemplateParameters *tpl = new TemplateParameters(); + + if (!flag && token.value != TOKlparen) + { error("parenthesized TemplateParameterList expected following TemplateIdentifier"); + goto Lerr; + } + nextToken(); + + // Get array of TemplateParameters + if (flag || token.value != TOKrparen) + { int isvariadic = 0; + + while (token.value != TOKrparen) + { TemplateParameter *tp; + Identifier *tp_ident = NULL; + Type *tp_spectype = NULL; + Type *tp_valtype = NULL; + Type *tp_defaulttype = NULL; + Expression *tp_specvalue = NULL; + Expression *tp_defaultvalue = NULL; + Token *t; + + // Get TemplateParameter + + // First, look ahead to see if it is a TypeParameter or a ValueParameter + t = peek(&token); + if (token.value == TOKalias) + { // AliasParameter + nextToken(); + Type *spectype = NULL; + if (isDeclaration(&token, 2, TOKreserved, NULL)) + { + spectype = parseType(&tp_ident); + } + else + { + if (token.value != TOKidentifier) + { error("identifier expected for template alias parameter"); + goto Lerr; + } + tp_ident = token.ident; + nextToken(); + } + Object *spec = NULL; + if (token.value == TOKcolon) // : Type + { + nextToken(); + if (isDeclaration(&token, 0, TOKreserved, NULL)) + spec = parseType(); + else + spec = parseCondExp(); + } + Object *def = NULL; + if (token.value == TOKassign) // = Type + { + nextToken(); + if (isDeclaration(&token, 0, TOKreserved, NULL)) + def = parseType(); + else + def = parseCondExp(); + } + tp = new TemplateAliasParameter(loc, tp_ident, spectype, spec, def); + } + else if (t->value == TOKcolon || t->value == TOKassign || + t->value == TOKcomma || t->value == TOKrparen) + { // TypeParameter + if (token.value != TOKidentifier) + { error("identifier expected for template type parameter"); + goto Lerr; + } + tp_ident = token.ident; + nextToken(); + if (token.value == TOKcolon) // : Type + { + nextToken(); + tp_spectype = parseType(); + } + if (token.value == TOKassign) // = Type + { + nextToken(); + tp_defaulttype = parseType(); + } + tp = new TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype); + } + else if (token.value == TOKidentifier && t->value == TOKdotdotdot) + { // ident... + if (isvariadic) + error("variadic template parameter must be last"); + isvariadic = 1; + tp_ident = token.ident; + nextToken(); + nextToken(); + tp = new TemplateTupleParameter(loc, tp_ident); + } +#if DMDV2 + else if (token.value == TOKthis) + { // ThisParameter + nextToken(); + if (token.value != TOKidentifier) + { error("identifier expected for template this parameter"); + goto Lerr; + } + tp_ident = token.ident; + nextToken(); + if (token.value == TOKcolon) // : Type + { + nextToken(); + tp_spectype = parseType(); + } + if (token.value == TOKassign) // = Type + { + nextToken(); + tp_defaulttype = parseType(); + } + tp = new TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype); + } +#endif + else + { // ValueParameter + tp_valtype = parseType(&tp_ident); + if (!tp_ident) + { + error("identifier expected for template value parameter"); + tp_ident = new Identifier("error", TOKidentifier); + } + if (token.value == TOKcolon) // : CondExpression + { + nextToken(); + tp_specvalue = parseCondExp(); + } + if (token.value == TOKassign) // = CondExpression + { + nextToken(); + tp_defaultvalue = parseDefaultInitExp(); + } + tp = new TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue); + } + tpl->push(tp); + if (token.value != TOKcomma) + break; + nextToken(); + } + } + check(TOKrparen); +Lerr: + return tpl; +} + +/****************************************** + * Parse template mixin. + * mixin Foo; + * mixin Foo!(args); + * mixin a.b.c!(args).Foo!(args); + * mixin Foo!(args) identifier; + * mixin typeof(expr).identifier!(args); + */ + +Dsymbol *Parser::parseMixin() +{ + TemplateMixin *tm; + Identifier *id; + Type *tqual; + Objects *tiargs; + Identifiers *idents; + + //printf("parseMixin()\n"); + nextToken(); + tqual = NULL; + if (token.value == TOKdot) + { + id = Id::empty; + } + else + { + if (token.value == TOKtypeof) + { + tqual = parseTypeof(); + check(TOKdot); + } + else if (token.value == TOKvector) + { + tqual = parseVector(); + check(TOKdot); + } + if (token.value != TOKidentifier) + { + error("identifier expected, not %s", token.toChars()); + id = Id::empty; + } + else + id = token.ident; + nextToken(); + } + + idents = new Identifiers(); + while (1) + { + tiargs = NULL; + if (token.value == TOKnot) + { + nextToken(); + if (token.value == TOKlparen) + tiargs = parseTemplateArgumentList(); + else + tiargs = parseTemplateArgument(); + } + + if (token.value != TOKdot) + break; + + if (tiargs) + { TemplateInstance *tempinst = new TemplateInstance(loc, id); + tempinst->tiargs = tiargs; + id = (Identifier *)tempinst; + tiargs = NULL; + } + idents->push(id); + + nextToken(); + if (token.value != TOKidentifier) + { error("identifier expected following '.' instead of '%s'", token.toChars()); + break; + } + id = token.ident; + nextToken(); + } + idents->push(id); + + if (token.value == TOKidentifier) + { + id = token.ident; + nextToken(); + } + else + id = NULL; + + tm = new TemplateMixin(loc, id, tqual, idents, tiargs); + if (token.value != TOKsemicolon) + error("';' expected after mixin"); + nextToken(); + + return tm; +} + +/****************************************** + * Parse template argument list. + * Input: + * current token is opening '(' + * Output: + * current token is one after closing ')' + */ + +Objects *Parser::parseTemplateArgumentList() +{ + //printf("Parser::parseTemplateArgumentList()\n"); + if (token.value != TOKlparen && token.value != TOKlcurly) + { error("!(TemplateArgumentList) expected following TemplateIdentifier"); + return new Objects(); + } + return parseTemplateArgumentList2(); +} + +Objects *Parser::parseTemplateArgumentList2() +{ + //printf("Parser::parseTemplateArgumentList2()\n"); + Objects *tiargs = new Objects(); + enum TOK endtok = TOKrparen; + nextToken(); + + // Get TemplateArgumentList + while (token.value != endtok) + { + // See if it is an Expression or a Type + if (isDeclaration(&token, 0, TOKreserved, NULL)) + { // Template argument is a type + Type *ta = parseType(); + tiargs->push(ta); + } + else + { // Template argument is an expression + Expression *ea = parseAssignExp(); + + if (ea->op == TOKfunction && ((FuncExp *)ea)->td) + tiargs->push(((FuncExp *)ea)->td); + else + tiargs->push(ea); + } + if (token.value != TOKcomma) + break; + nextToken(); + } + check(endtok, "template argument list"); + return tiargs; +} + +/***************************** + * Parse single template argument, to support the syntax: + * foo!arg + * Input: + * current token is the arg + */ + +Objects *Parser::parseTemplateArgument() +{ + //printf("parseTemplateArgument()\n"); + Objects *tiargs = new Objects(); + Type *ta; + switch (token.value) + { + case TOKidentifier: + ta = new TypeIdentifier(loc, token.ident); + goto LabelX; + + case TOKvector: + ta = parseVector(); + goto LabelX; + + case BASIC_TYPES_X(ta): + tiargs->push(ta); + nextToken(); + break; + + case TOKint32v: + case TOKuns32v: + case TOKint64v: + case TOKuns64v: + case TOKfloat32v: + case TOKfloat64v: + case TOKfloat80v: + case TOKimaginary32v: + case TOKimaginary64v: + case TOKimaginary80v: + case TOKnull: + case TOKtrue: + case TOKfalse: + case TOKcharv: + case TOKwcharv: + case TOKdcharv: + case TOKstring: + case TOKfile: + case TOKline: + case TOKthis: + { // Template argument is an expression + Expression *ea = parsePrimaryExp(); + tiargs->push(ea); + break; + } + + default: + error("template argument expected following !"); + break; + } + if (token.value == TOKnot) + error("multiple ! arguments are not allowed"); + return tiargs; +} + +Import *Parser::parseImport(Dsymbols *decldefs, int isstatic) +{ Import *s; + Identifier *id; + Identifier *aliasid = NULL; + Identifiers *a; + Loc loc; + + //printf("Parser::parseImport()\n"); + do + { + L1: + nextToken(); + if (token.value != TOKidentifier) + { error("Identifier expected following import"); + break; + } + + loc = this->loc; + a = NULL; + id = token.ident; + nextToken(); + if (!aliasid && token.value == TOKassign) + { + aliasid = id; + goto L1; + } + while (token.value == TOKdot) + { + if (!a) + a = new Identifiers(); + a->push(id); + nextToken(); + if (token.value != TOKidentifier) + { error("identifier expected following package"); + break; + } + id = token.ident; + nextToken(); + } + + s = new Import(loc, a, id, aliasid, isstatic); + decldefs->push(s); + + /* Look for + * : alias=name, alias=name; + * syntax. + */ + if (token.value == TOKcolon) + { + do + { Identifier *name; + + nextToken(); + if (token.value != TOKidentifier) + { error("Identifier expected following :"); + break; + } + Identifier *alias = token.ident; + nextToken(); + if (token.value == TOKassign) + { + nextToken(); + if (token.value != TOKidentifier) + { error("Identifier expected following %s=", alias->toChars()); + break; + } + name = token.ident; + nextToken(); + } + else + { name = alias; + alias = NULL; + } + s->addAlias(name, alias); + } while (token.value == TOKcomma); + break; // no comma-separated imports of this form + } + + aliasid = NULL; + } while (token.value == TOKcomma); + + if (token.value == TOKsemicolon) + nextToken(); + else + { + error("';' expected"); + nextToken(); + } + + return NULL; +} + +#if DMDV2 +Type *Parser::parseType(Identifier **pident, TemplateParameters **tpl) +{ Type *t; + + /* Take care of the storage class prefixes that + * serve as type attributes: + * const shared, shared const, const, invariant, shared + */ + if (token.value == TOKconst && peekNext() == TOKshared && peekNext2() != TOKlparen || + token.value == TOKshared && peekNext() == TOKconst && peekNext2() != TOKlparen) + { + nextToken(); + nextToken(); + /* shared const type + */ + t = parseType(pident, tpl); + t = t->makeSharedConst(); + return t; + } + else if (token.value == TOKwild && peekNext() == TOKshared && peekNext2() != TOKlparen || + token.value == TOKshared && peekNext() == TOKwild && peekNext2() != TOKlparen) + { + nextToken(); + nextToken(); + /* shared wild type + */ + t = parseType(pident, tpl); + t = t->makeSharedWild(); + return t; + } + else if (token.value == TOKconst && peekNext() != TOKlparen) + { + nextToken(); + /* const type + */ + t = parseType(pident, tpl); + t = t->makeConst(); + return t; + } + else if ((token.value == TOKinvariant || token.value == TOKimmutable) && + peekNext() != TOKlparen) + { + nextToken(); + /* invariant type + */ + t = parseType(pident, tpl); + t = t->makeInvariant(); + return t; + } + else if (token.value == TOKshared && peekNext() != TOKlparen) + { + nextToken(); + /* shared type + */ + t = parseType(pident, tpl); + t = t->makeShared(); + return t; + } + else if (token.value == TOKwild && peekNext() != TOKlparen) + { + nextToken(); + /* wild type + */ + t = parseType(pident, tpl); + t = t->makeWild(); + return t; + } + else + t = parseBasicType(); + t = parseDeclarator(t, pident, tpl); + return t; +} +#endif + +Type *Parser::parseBasicType() +{ Type *t; + Identifier *id; + TypeQualified *tid; + + //printf("parseBasicType()\n"); + switch (token.value) + { + case BASIC_TYPES_X(t): + nextToken(); + break; + + case TOKthis: + case TOKsuper: + case TOKidentifier: + id = token.ident; + nextToken(); + if (token.value == TOKnot) + { // ident!(template_arguments) + TemplateInstance *tempinst = new TemplateInstance(loc, id); + nextToken(); + if (token.value == TOKlparen) + // ident!(template_arguments) + tempinst->tiargs = parseTemplateArgumentList(); + else + // ident!template_argument + tempinst->tiargs = parseTemplateArgument(); + tid = new TypeInstance(loc, tempinst); + goto Lident2; + } + Lident: + tid = new TypeIdentifier(loc, id); + Lident2: + while (token.value == TOKdot) + { nextToken(); + if (token.value != TOKidentifier) + { error("identifier expected following '.' instead of '%s'", token.toChars()); + break; + } + id = token.ident; + nextToken(); + if (token.value == TOKnot) + { + TemplateInstance *tempinst = new TemplateInstance(loc, id); + nextToken(); + if (token.value == TOKlparen) + // ident!(template_arguments) + tempinst->tiargs = parseTemplateArgumentList(); + else + // ident!template_argument + tempinst->tiargs = parseTemplateArgument(); + tid->addIdent((Identifier *)tempinst); + } + else + tid->addIdent(id); + } + t = tid; + break; + + case TOKdot: + // Leading . as in .foo + id = Id::empty; + goto Lident; + + case TOKtypeof: + // typeof(expression) + tid = parseTypeof(); + goto Lident2; + + case TOKvector: + t = parseVector(); + break; + + case TOKconst: + // const(type) + nextToken(); + check(TOKlparen); + t = parseType(); + check(TOKrparen); + if (t->isImmutable()) + ; + else if (t->isShared()) + t = t->makeSharedConst(); + else + t = t->makeConst(); + break; + + case TOKinvariant: + if (!global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); + case TOKimmutable: + // invariant(type) + nextToken(); + check(TOKlparen); + t = parseType(); + check(TOKrparen); + t = t->makeInvariant(); + break; + + case TOKshared: + // shared(type) + nextToken(); + check(TOKlparen); + t = parseType(); + check(TOKrparen); + if (t->isImmutable()) + ; + else if (t->isConst()) + t = t->makeSharedConst(); + else if (t->isWild()) + t = t->makeSharedWild(); + else + t = t->makeShared(); + break; + + case TOKwild: + // wild(type) + nextToken(); + check(TOKlparen); + t = parseType(); + check(TOKrparen); + if (t->isImmutable()/* || t->isConst()*/) + ; + else if (t->isShared()) + t = t->makeSharedWild(); + else + t = t->makeWild(); + break; + + default: + error("basic type expected, not %s", token.toChars()); + t = Type::tint32; + break; + } + return t; +} + +/****************************************** + * Parse things that follow the initial type t. + * t * + * t [] + * t [type] + * t [expression] + * t [expression .. expression] + * t function + * t delegate + */ + +Type *Parser::parseBasicType2(Type *t) +{ + //printf("parseBasicType2()\n"); + while (1) + { + switch (token.value) + { + case TOKmul: + t = new TypePointer(t); + nextToken(); + continue; + + case TOKlbracket: + // Handle []. Make sure things like + // int[3][1] a; + // is (array[1] of array[3] of int) + nextToken(); + if (token.value == TOKrbracket) + { + t = new TypeDArray(t); // [] + nextToken(); + } + else if (isDeclaration(&token, 0, TOKrbracket, NULL)) + { // It's an associative array declaration + + //printf("it's an associative array\n"); + Type *index = parseType(); // [ type ] + t = new TypeAArray(t, index); + check(TOKrbracket); + } + else + { + //printf("it's type[expression]\n"); + inBrackets++; + Expression *e = parseAssignExp(); // [ expression ] + if (token.value == TOKslice) + { + nextToken(); + Expression *e2 = parseAssignExp(); // [ exp .. exp ] + t = new TypeSlice(t, e, e2); + } + else + t = new TypeSArray(t,e); + inBrackets--; + check(TOKrbracket); + } + continue; + + case TOKdelegate: + case TOKfunction: + { // Handle delegate declaration: + // t delegate(parameter list) nothrow pure + // t function(parameter list) nothrow pure + Parameters *arguments; + int varargs; + enum TOK save = token.value; + + nextToken(); + arguments = parseParameters(&varargs); + + StorageClass stc = parsePostfix(); + if (stc & (STCconst | STCimmutable | STCshared | STCwild)) + error("const/immutable/shared/inout attributes are only valid for non-static member functions"); + + TypeFunction *tf = new TypeFunction(arguments, t, varargs, linkage, stc); + + if (save == TOKdelegate) + t = new TypeDelegate(tf); + else + t = new TypePointer(tf); // pointer to function + continue; + } + + default: + return t; + } + assert(0); + } + assert(0); + return NULL; +} + +Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl, StorageClass storage_class, int* pdisable) +{ Type *ts; + + //printf("parseDeclarator(tpl = %p)\n", tpl); + t = parseBasicType2(t); + + switch (token.value) + { + + case TOKidentifier: + if (pident) + *pident = token.ident; + else + error("unexpected identifer '%s' in declarator", token.ident->toChars()); + ts = t; + nextToken(); + break; + + case TOKlparen: + if (peekNext() == TOKmul || // like: T (*fp)(); + peekNext() == TOKlparen // like: T ((*fp))(); + /* || peekNext() == TOKlbracket*/) // like: T ([] a) + { + /* Parse things with parentheses around the identifier, like: + * int (*ident[3])[] + * although the D style would be: + * int[]*[3] ident + */ + if (!global.params.useDeprecated) + { + error("C-style function pointer and pointer to array syntax is deprecated. Use 'function' to declare function pointers"); + } + nextToken(); + ts = parseDeclarator(t, pident); + check(TOKrparen); + break; + } + ts = t; + { + Token *peekt = &token; + /* Completely disallow C-style things like: + * T (a); + * Improve error messages for the common bug of a missing return type + * by looking to see if (a) looks like a parameter list. + */ + if (isParameters(&peekt)) { + error("function declaration without return type. " + "(Note that constructors are always named 'this')"); + } + else + error("unexpected ( in declarator"); + } + break; + + default: + ts = t; + break; + } + + // parse DeclaratorSuffixes + while (1) + { + switch (token.value) + { +#if CARRAYDECL + /* Support C style array syntax: + * int ident[] + * as opposed to D-style: + * int[] ident + */ + case TOKlbracket: + { // This is the old C-style post [] syntax. + TypeNext *ta; + nextToken(); + if (token.value == TOKrbracket) + { // It's a dynamic array + ta = new TypeDArray(t); // [] + nextToken(); + } + else if (isDeclaration(&token, 0, TOKrbracket, NULL)) + { // It's an associative array + + //printf("it's an associative array\n"); + Type *index = parseType(); // [ type ] + check(TOKrbracket); + ta = new TypeAArray(t, index); + } + else + { + //printf("It's a static array\n"); + Expression *e = parseAssignExp(); // [ expression ] + ta = new TypeSArray(t, e); + check(TOKrbracket); + } + + /* Insert ta into + * ts -> ... -> t + * so that + * ts -> ... -> ta -> t + */ + Type **pt; + for (pt = &ts; *pt != t; pt = &((TypeNext*)*pt)->next) + ; + *pt = ta; + continue; + } +#endif + case TOKlparen: + { + if (tpl) + { + /* Look ahead to see if this is (...)(...), + * i.e. a function template declaration + */ + if (peekPastParen(&token)->value == TOKlparen) + { + //printf("function template declaration\n"); + + // Gather template parameter list + *tpl = parseTemplateParameterList(); + } + } + + int varargs; + Parameters *arguments = parseParameters(&varargs); + + /* Parse const/immutable/shared/inout/nothrow/pure postfix + */ + StorageClass stc = parsePostfix(); + stc |= storage_class; // merge prefix storage classes + Type *tf = new TypeFunction(arguments, t, varargs, linkage, stc); + tf = tf->addSTC(stc); + if (pdisable) + *pdisable = stc & STCdisable ? 1 : 0; + + /* Insert tf into + * ts -> ... -> t + * so that + * ts -> ... -> tf -> t + */ + Type **pt; + for (pt = &ts; *pt != t; pt = &((TypeNext*)*pt)->next) + ; + *pt = tf; + break; + } + } + break; + } + + return ts; +} + +/********************************** + * Parse Declarations. + * These can be: + * 1. declarations at global/class level + * 2. declarations at statement level + * Return array of Declaration *'s. + */ + +Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *comment) +{ + StorageClass stc; + int disable; + Type *ts; + Type *t; + Type *tfirst; + Identifier *ident; + Dsymbols *a; + enum TOK tok = TOKreserved; + enum LINK link = linkage; + + //printf("parseDeclarations() %s\n", token.toChars()); + if (!comment) + comment = token.blockComment; + + if (storage_class) + { ts = NULL; // infer type + goto L2; + } + + switch (token.value) + { + case TOKalias: + /* Look for: + * alias identifier this; + */ + tok = token.value; + nextToken(); + if (token.value == TOKidentifier && peek(&token)->value == TOKthis) + { + AliasThis *s = new AliasThis(this->loc, token.ident); + nextToken(); + check(TOKthis); + check(TOKsemicolon); + a = new Dsymbols(); + a->push(s); + addComment(s, comment); + return a; + } + break; + case TOKtypedef: + if (!global.params.useDeprecated) + error("use of typedef is deprecated; use alias instead"); + tok = token.value; + nextToken(); + break; + } + + storage_class = STCundefined; + while (1) + { + switch (token.value) + { + case TOKconst: + if (peek(&token)->value == TOKlparen) + break; // const as type constructor + stc = STCconst; // const as storage class + goto L1; + + case TOKinvariant: + case TOKimmutable: + if (peek(&token)->value == TOKlparen) + break; + if (token.value == TOKinvariant && !global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); + stc = STCimmutable; + goto L1; + + case TOKshared: + if (peek(&token)->value == TOKlparen) + break; + stc = STCshared; + goto L1; + + case TOKwild: + if (peek(&token)->value == TOKlparen) + break; + stc = STCwild; + goto L1; + + case TOKstatic: stc = STCstatic; goto L1; + case TOKfinal: stc = STCfinal; goto L1; + case TOKauto: stc = STCauto; goto L1; + case TOKscope: stc = STCscope; goto L1; + case TOKoverride: stc = STCoverride; goto L1; + case TOKabstract: stc = STCabstract; goto L1; + case TOKsynchronized: stc = STCsynchronized; goto L1; + case TOKdeprecated: stc = STCdeprecated; goto L1; +#if DMDV2 + case TOKnothrow: stc = STCnothrow; goto L1; + case TOKpure: stc = STCpure; goto L1; + case TOKref: stc = STCref; goto L1; + case TOKtls: stc = STCtls; goto L1; + case TOKgshared: stc = STCgshared; goto L1; + case TOKenum: stc = STCmanifest; goto L1; + case TOKat: stc = parseAttribute(); goto L1; +#endif + L1: + if (storage_class & stc) + error("redundant storage class '%s'", token.toChars()); + storage_class = storage_class | stc; + composeStorageClass(storage_class); + nextToken(); + continue; + + case TOKextern: + if (peek(&token)->value != TOKlparen) + { stc = STCextern; + goto L1; + } + + link = parseLinkage(); + continue; + + default: + break; + } + break; + } + + /* Look for auto initializers: + * storage_class identifier = initializer; + */ + if (storage_class && + token.value == TOKidentifier && + peek(&token)->value == TOKassign) + { + return parseAutoDeclarations(storage_class, comment); + } + + if (token.value == TOKclass) + { + AggregateDeclaration *s = (AggregateDeclaration *)parseAggregate(); + s->storage_class |= storage_class; + Dsymbols *a = new Dsymbols(); + a->push(s); + addComment(s, comment); + return a; + } + + /* Look for return type inference for template functions. + */ + { + Token *tk; + if (storage_class && + token.value == TOKidentifier && + (tk = peek(&token))->value == TOKlparen && + skipParens(tk, &tk) && + ((tk = peek(tk)), 1) && + skipAttributes(tk, &tk) && + (tk->value == TOKlparen || + tk->value == TOKlcurly) + ) + { + ts = NULL; + } + else + { + ts = parseBasicType(); + ts = parseBasicType2(ts); + } + } + +L2: + tfirst = NULL; + a = new Dsymbols(); + + while (1) + { + Loc loc = this->loc; + TemplateParameters *tpl = NULL; + + ident = NULL; + t = parseDeclarator(ts, &ident, &tpl, storage_class, &disable); + assert(t); + if (!tfirst) + tfirst = t; + else if (t != tfirst) + error("multiple declarations must have the same type, not %s and %s", + tfirst->toChars(), t->toChars()); + if (!ident) + error("no identifier for declarator %s", t->toChars()); + + if (tok == TOKtypedef || tok == TOKalias) + { Declaration *v; + Initializer *init = NULL; + + /* Aliases can no longer have multiple declarators, storage classes, + * linkages, or auto declarations. + * These never made any sense, anyway. + * The code below needs to be fixed to reject them. + * The grammar has already been fixed to preclude them. + */ + + if (token.value == TOKassign) + { + nextToken(); + init = parseInitializer(); + } + if (tok == TOKtypedef) + { v = new TypedefDeclaration(loc, ident, t, init); + if (!global.params.useDeprecated) + error("use of typedef is deprecated; use alias instead"); + } + else + { if (init) + error("alias cannot have initializer"); + v = new AliasDeclaration(loc, ident, t); + } + v->storage_class = storage_class; + if (link == linkage) + a->push(v); + else + { + Dsymbols *ax = new Dsymbols(); + ax->push(v); + Dsymbol *s = new LinkDeclaration(link, ax); + a->push(s); + } + switch (token.value) + { case TOKsemicolon: + nextToken(); + addComment(v, comment); + break; + + case TOKcomma: + nextToken(); + addComment(v, comment); + continue; + + default: + error("semicolon expected to close %s declaration", Token::toChars(tok)); + break; + } + } + else if (t->ty == Tfunction) + { + TypeFunction *tf = (TypeFunction *)t; + Expression *constraint = NULL; +#if 0 + if (Parameter::isTPL(tf->parameters)) + { + if (!tpl) + tpl = new TemplateParameters(); + } +#endif + + //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t->toChars(), storage_class); + + FuncDeclaration *f = + new FuncDeclaration(loc, 0, ident, storage_class | (disable ? STCdisable : 0), t); + addComment(f, comment); + if (tpl) + constraint = parseConstraint(); + parseContracts(f); + addComment(f, NULL); + Dsymbol *s; + if (link == linkage) + { + s = f; + } + else + { + Dsymbols *ax = new Dsymbols(); + ax->push(f); + s = new LinkDeclaration(link, ax); + } + /* A template parameter list means it's a function template + */ + if (tpl) + { + // Wrap a template around the function declaration + Dsymbols *decldefs = new Dsymbols(); + decldefs->push(s); + TemplateDeclaration *tempdecl = + new TemplateDeclaration(loc, s->ident, tpl, constraint, decldefs, 0); + s = tempdecl; + } + addComment(s, comment); + a->push(s); + } + else + { + Initializer *init = NULL; + if (token.value == TOKassign) + { + nextToken(); + init = parseInitializer(); + } + + VarDeclaration *v = new VarDeclaration(loc, t, ident, init); + v->storage_class = storage_class; + if (link == linkage) + a->push(v); + else + { + Dsymbols *ax = new Dsymbols(); + ax->push(v); + Dsymbol *s = new LinkDeclaration(link, ax); + a->push(s); + } + switch (token.value) + { case TOKsemicolon: + nextToken(); + addComment(v, comment); + break; + + case TOKcomma: + nextToken(); + addComment(v, comment); + continue; + + default: + error("semicolon expected, not '%s'", token.toChars()); + break; + } + } + break; + } + return a; +} + +/***************************************** + * Parse auto declarations of the form: + * storageClass ident = init, ident = init, ... ; + * and return the array of them. + * Starts with token on the first ident. + * Ends with scanner past closing ';' + */ + +#if DMDV2 +Dsymbols *Parser::parseAutoDeclarations(StorageClass storageClass, unsigned char *comment) +{ + Dsymbols *a = new Dsymbols; + + while (1) + { + Identifier *ident = token.ident; + nextToken(); // skip over ident + assert(token.value == TOKassign); + nextToken(); // skip over '=' + Initializer *init = parseInitializer(); + VarDeclaration *v = new VarDeclaration(loc, NULL, ident, init); + v->storage_class = storageClass; + a->push(v); + if (token.value == TOKsemicolon) + { + nextToken(); + addComment(v, comment); + } + else if (token.value == TOKcomma) + { + nextToken(); + if (token.value == TOKidentifier && + peek(&token)->value == TOKassign) + { + addComment(v, comment); + continue; + } + else + error("Identifier expected following comma"); + } + else + error("semicolon expected following auto declaration, not '%s'", token.toChars()); + break; + } + return a; +} +#endif + +/***************************************** + * Parse contracts following function declaration. + */ + +void Parser::parseContracts(FuncDeclaration *f) +{ + enum LINK linksave = linkage; + + // The following is irrelevant, as it is overridden by sc->linkage in + // TypeFunction::semantic + linkage = LINKd; // nested functions have D linkage +L1: + switch (token.value) + { + case TOKlcurly: + if (f->frequire || f->fensure) + error("missing body { ... } after in or out"); + f->fbody = parseStatement(PSsemi); + f->endloc = endloc; + break; + + case TOKbody: + nextToken(); + f->fbody = parseStatement(PScurly); + f->endloc = endloc; + break; + + case TOKsemicolon: + if (f->frequire || f->fensure) + error("missing body { ... } after in or out"); + nextToken(); + break; + +#if 0 // Do we want this for function declarations, so we can do: + // int x, y, foo(), z; + case TOKcomma: + nextToken(); + continue; +#endif + +#if 0 // Dumped feature + case TOKthrow: + if (!f->fthrows) + f->fthrows = new Types(); + nextToken(); + check(TOKlparen); + while (1) + { + Type *tb = parseBasicType(); + f->fthrows->push(tb); + if (token.value == TOKcomma) + { nextToken(); + continue; + } + break; + } + check(TOKrparen); + goto L1; +#endif + + case TOKin: + nextToken(); + if (f->frequire) + error("redundant 'in' statement"); + f->frequire = parseStatement(PScurly | PSscope); + goto L1; + + case TOKout: + // parse: out (identifier) { statement } + nextToken(); + if (token.value != TOKlcurly) + { + check(TOKlparen); + if (token.value != TOKidentifier) + error("(identifier) following 'out' expected, not %s", token.toChars()); + f->outId = token.ident; + nextToken(); + check(TOKrparen); + } + if (f->fensure) + error("redundant 'out' statement"); + f->fensure = parseStatement(PScurly | PSscope); + goto L1; + + default: + if (!f->frequire && !f->fensure) // allow these even with no body + error("semicolon expected following function declaration"); + break; + } + linkage = linksave; +} + +/***************************************** + * Parse initializer for variable declaration. + */ + +Initializer *Parser::parseInitializer() +{ + StructInitializer *is; + ArrayInitializer *ia; + ExpInitializer *ie; + Expression *e; + Identifier *id; + Initializer *value; + int comma; + Loc loc = this->loc; + Token *t; + int braces; + int brackets; + + switch (token.value) + { + case TOKlcurly: + /* Scan ahead to see if it is a struct initializer or + * a function literal. + * If it contains a ';', it is a function literal. + * Treat { } as a struct initializer. + */ + braces = 1; + for (t = peek(&token); 1; t = peek(t)) + { + switch (t->value) + { + case TOKsemicolon: + case TOKreturn: + goto Lexpression; + + case TOKlcurly: + braces++; + continue; + + case TOKrcurly: + if (--braces == 0) + break; + continue; + + case TOKeof: + break; + + default: + continue; + } + break; + } + + is = new StructInitializer(loc); + nextToken(); + comma = 2; + while (1) + { + switch (token.value) + { + case TOKidentifier: + if (comma == 1) + error("comma expected separating field initializers"); + t = peek(&token); + if (t->value == TOKcolon) + { + id = token.ident; + nextToken(); + nextToken(); // skip over ':' + } + else + { id = NULL; + } + value = parseInitializer(); + is->addInit(id, value); + comma = 1; + continue; + + case TOKcomma: + if (comma == 2) + error("expression expected, not ','"); + nextToken(); + comma = 2; + continue; + + case TOKrcurly: // allow trailing comma's + nextToken(); + break; + + case TOKeof: + error("found EOF instead of initializer"); + break; + + default: + if (comma == 1) + error("comma expected separating field initializers"); + value = parseInitializer(); + is->addInit(NULL, value); + comma = 1; + continue; + //error("found '%s' instead of field initializer", token.toChars()); + //break; + } + break; + } + return is; + + case TOKlbracket: + /* Scan ahead to see if it is an array initializer or + * an expression. + * If it ends with a ';' ',' or '}', it is an array initializer. + */ + brackets = 1; + for (t = peek(&token); 1; t = peek(t)) + { + switch (t->value) + { + case TOKlbracket: + brackets++; + continue; + + case TOKrbracket: + if (--brackets == 0) + { t = peek(t); + if (t->value != TOKsemicolon && + t->value != TOKcomma && + t->value != TOKrbracket && + t->value != TOKrcurly) + goto Lexpression; + break; + } + continue; + + case TOKeof: + break; + + default: + continue; + } + break; + } + + ia = new ArrayInitializer(loc); + nextToken(); + comma = 2; + while (1) + { + switch (token.value) + { + default: + if (comma == 1) + { error("comma expected separating array initializers, not %s", token.toChars()); + nextToken(); + break; + } + e = parseAssignExp(); + if (!e) + break; + if (token.value == TOKcolon) + { + nextToken(); + value = parseInitializer(); + } + else + { value = new ExpInitializer(e->loc, e); + e = NULL; + } + ia->addInit(e, value); + comma = 1; + continue; + + case TOKlcurly: + case TOKlbracket: + if (comma == 1) + error("comma expected separating array initializers, not %s", token.toChars()); + value = parseInitializer(); + ia->addInit(NULL, value); + comma = 1; + continue; + + case TOKcomma: + if (comma == 2) + error("expression expected, not ','"); + nextToken(); + comma = 2; + continue; + + case TOKrbracket: // allow trailing comma's + nextToken(); + break; + + case TOKeof: + error("found '%s' instead of array initializer", token.toChars()); + break; + } + break; + } + return ia; + + case TOKvoid: + t = peek(&token); + if (t->value == TOKsemicolon || t->value == TOKcomma) + { + nextToken(); + return new VoidInitializer(loc); + } + goto Lexpression; + + default: + Lexpression: + e = parseAssignExp(); + ie = new ExpInitializer(loc, e); + return ie; + } +} + +/***************************************** + * Parses default argument initializer expression that is an assign expression, + * with special handling for __FILE__ and __LINE__. + */ + +#if DMDV2 +Expression *Parser::parseDefaultInitExp() +{ + if (token.value == TOKfile || + token.value == TOKline) + { + Token *t = peek(&token); + if (t->value == TOKcomma || t->value == TOKrparen) + { Expression *e; + + if (token.value == TOKfile) + e = new FileInitExp(loc); + else + e = new LineInitExp(loc); + nextToken(); + return e; + } + } + + Expression *e = parseAssignExp(); + return e; +} +#endif + +/***************************************** + */ + +void Parser::checkDanglingElse(Loc elseloc) +{ + if (token.value != TOKelse && + token.value != TOKcatch && + token.value != TOKfinally && + lookingForElse.linnum != 0) + { + warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars()); + } +} + +/***************************************** + * Input: + * flags PSxxxx + */ + +Statement *Parser::parseStatement(int flags) +{ Statement *s; + Token *t; + Condition *condition; + Statement *ifbody; + Statement *elsebody; + bool isfinal; + Loc loc = this->loc; + + //printf("parseStatement()\n"); + + if (flags & PScurly && token.value != TOKlcurly) + error("statement expected to be { }, not %s", token.toChars()); + + switch (token.value) + { + case TOKidentifier: + /* A leading identifier can be a declaration, label, or expression. + * The easiest case to check first is label: + */ + t = peek(&token); + if (t->value == TOKcolon) + { // It's a label + + Identifier *ident = token.ident; + nextToken(); + nextToken(); + s = parseStatement(PSsemi_ok); + s = new LabelStatement(loc, ident, s); + break; + } + // fallthrough to TOKdot + case TOKdot: + case TOKtypeof: + case TOKvector: + if (isDeclaration(&token, 2, TOKreserved, NULL)) + goto Ldeclaration; + else + goto Lexp; + break; + + case TOKassert: + case TOKthis: + case TOKsuper: + case TOKint32v: + case TOKuns32v: + case TOKint64v: + case TOKuns64v: + case TOKfloat32v: + case TOKfloat64v: + case TOKfloat80v: + case TOKimaginary32v: + case TOKimaginary64v: + case TOKimaginary80v: + case TOKcharv: + case TOKwcharv: + case TOKdcharv: + case TOKnull: + case TOKtrue: + case TOKfalse: + case TOKstring: + case TOKlparen: + case TOKcast: + case TOKmul: + case TOKmin: + case TOKadd: + case TOKtilde: + case TOKnot: + case TOKplusplus: + case TOKminusminus: + case TOKnew: + case TOKdelete: + case TOKdelegate: + case TOKfunction: + case TOKtypeid: + case TOKis: + case TOKlbracket: +#if DMDV2 + case TOKtraits: + case TOKfile: + case TOKline: +#endif + Lexp: + { + Expression *exp = parseExpression(); + check(TOKsemicolon, "statement"); + s = new ExpStatement(loc, exp); + break; + } + + case TOKstatic: + { // Look ahead to see if it's static assert() or static if() + + Token *t = peek(&token); + if (t->value == TOKassert) + { + nextToken(); + s = new StaticAssertStatement(parseStaticAssert()); + break; + } + if (t->value == TOKif) + { + nextToken(); + condition = parseStaticIfCondition(); + goto Lcondition; + } + if (t->value == TOKstruct || t->value == TOKunion || t->value == TOKclass) + { + nextToken(); + Dsymbols *a = parseBlock(); + Dsymbol *d = new StorageClassDeclaration(STCstatic, a); + s = new ExpStatement(loc, d); + if (flags & PSscope) + s = new ScopeStatement(loc, s); + break; + } + if (t->value == TOKimport) + { nextToken(); + Dsymbols *imports = new Dsymbols(); + parseImport(imports, 1); // static import ... + s = new ImportStatement(loc, imports); + break; + } + goto Ldeclaration; + } + + case TOKfinal: + if (peekNext() == TOKswitch) + { + nextToken(); + isfinal = TRUE; + goto Lswitch; + } + goto Ldeclaration; + + case BASIC_TYPES: + case TOKtypedef: + case TOKalias: + case TOKconst: + case TOKauto: + case TOKextern: + case TOKinvariant: +#if DMDV2 + case TOKimmutable: + case TOKshared: + case TOKwild: + case TOKnothrow: + case TOKpure: + case TOKref: + case TOKtls: + case TOKgshared: + case TOKat: +#endif + Ldeclaration: + { Dsymbols *a; + + a = parseDeclarations(STCundefined, NULL); + if (a->dim > 1) + { + Statements *as = new Statements(); + as->reserve(a->dim); + for (size_t i = 0; i < a->dim; i++) + { + Dsymbol *d = a->tdata()[i]; + s = new ExpStatement(loc, d); + as->push(s); + } + s = new CompoundDeclarationStatement(loc, as); + } + else if (a->dim == 1) + { + Dsymbol *d = a->tdata()[0]; + s = new ExpStatement(loc, d); + } + else + assert(0); + if (flags & PSscope) + s = new ScopeStatement(loc, s); + break; + } + + case TOKstruct: + case TOKunion: + case TOKclass: + case TOKinterface: + { Dsymbol *d; + + d = parseAggregate(); + s = new ExpStatement(loc, d); + break; + } + + case TOKenum: + { /* Determine if this is a manifest constant declaration, + * or a conventional enum. + */ + Dsymbol *d; + Token *t = peek(&token); + if (t->value == TOKlcurly || t->value == TOKcolon) + d = parseEnum(); + else if (t->value != TOKidentifier) + goto Ldeclaration; + else + { + t = peek(t); + if (t->value == TOKlcurly || t->value == TOKcolon || + t->value == TOKsemicolon) + d = parseEnum(); + else + goto Ldeclaration; + } + s = new ExpStatement(loc, d); + break; + } + + case TOKmixin: + { t = peek(&token); + if (t->value == TOKlparen) + { // mixin(string) + Expression *e = parseAssignExp(); + check(TOKsemicolon); + if (e->op == TOKmixin) + { + CompileExp *cpe = (CompileExp *)e; + s = new CompileStatement(loc, cpe->e1); + } + else + { + s = new ExpStatement(loc, e); + } + break; + } + Dsymbol *d = parseMixin(); + s = new ExpStatement(loc, d); + break; + } + + case TOKlcurly: + { + Loc lookingForElseSave = lookingForElse; + lookingForElse = 0; + + nextToken(); + //if (token.value == TOKsemicolon) + //error("use '{ }' for an empty statement, not a ';'"); + Statements *statements = new Statements(); + while (token.value != TOKrcurly && token.value != TOKeof) + { + statements->push(parseStatement(PSsemi | PScurlyscope)); + } + endloc = this->loc; + s = new CompoundStatement(loc, statements); + if (flags & (PSscope | PScurlyscope)) + s = new ScopeStatement(loc, s); + check(TOKrcurly, "compound statement"); + lookingForElse = lookingForElseSave; + break; + } + + case TOKwhile: + { Expression *condition; + Statement *body; + + nextToken(); + check(TOKlparen); + condition = parseExpression(); + check(TOKrparen); + body = parseStatement(PSscope); + s = new WhileStatement(loc, condition, body); + break; + } + + case TOKsemicolon: + if (!(flags & PSsemi_ok)) + { + if (flags & PSsemi) + { if (global.params.warnings) + warning(loc, "use '{ }' for an empty statement, not a ';'"); + } + else + error("use '{ }' for an empty statement, not a ';'"); + } + nextToken(); + s = new ExpStatement(loc, (Expression *)NULL); + break; + + case TOKdo: + { Statement *body; + Expression *condition; + + nextToken(); + Loc lookingForElseSave = lookingForElse; + lookingForElse = 0; + body = parseStatement(PSscope); + lookingForElse = lookingForElseSave; + check(TOKwhile); + check(TOKlparen); + condition = parseExpression(); + check(TOKrparen); + if (token.value == TOKsemicolon) + nextToken(); + else if (!global.params.useDeprecated) + error("do-while statement requires terminating ;"); + s = new DoStatement(loc, body, condition); + break; + } + + case TOKfor: + { + Statement *init; + Expression *condition; + Expression *increment; + Statement *body; + + nextToken(); + check(TOKlparen); + if (token.value == TOKsemicolon) + { init = NULL; + nextToken(); + } + else + { + Loc lookingForElseSave = lookingForElse; + lookingForElse = 0; + init = parseStatement(0); + lookingForElse = lookingForElseSave; + } + if (token.value == TOKsemicolon) + { + condition = NULL; + nextToken(); + } + else + { + condition = parseExpression(); + check(TOKsemicolon, "for condition"); + } + if (token.value == TOKrparen) + { increment = NULL; + nextToken(); + } + else + { increment = parseExpression(); + check(TOKrparen); + } + body = parseStatement(PSscope); + s = new ForStatement(loc, init, condition, increment, body); + if (init) + s = new ScopeStatement(loc, s); + break; + } + + case TOKforeach: + case TOKforeach_reverse: + { + enum TOK op = token.value; + + nextToken(); + check(TOKlparen); + + Parameters *arguments = new Parameters(); + + while (1) + { + Identifier *ai = NULL; + Type *at; + + StorageClass storageClass = 0; + if (token.value == TOKref +#if D1INOUT + || token.value == TOKinout +#endif + ) + { storageClass = STCref; + nextToken(); + } + if (token.value == TOKidentifier) + { + Token *t = peek(&token); + if (t->value == TOKcomma || t->value == TOKsemicolon) + { ai = token.ident; + at = NULL; // infer argument type + nextToken(); + goto Larg; + } + } + at = parseType(&ai); + if (!ai) + error("no identifier for declarator %s", at->toChars()); + Larg: + Parameter *a = new Parameter(storageClass, at, ai, NULL); + arguments->push(a); + if (token.value == TOKcomma) + { nextToken(); + continue; + } + break; + } + check(TOKsemicolon); + + Expression *aggr = parseExpression(); + if (token.value == TOKslice && arguments->dim == 1) + { + Parameter *a = arguments->tdata()[0]; + delete arguments; + nextToken(); + Expression *upr = parseExpression(); + check(TOKrparen); + Statement *body = parseStatement(0); + s = new ForeachRangeStatement(loc, op, a, aggr, upr, body); + } + else + { + check(TOKrparen); + Statement *body = parseStatement(0); + s = new ForeachStatement(loc, op, arguments, aggr, body); + } + break; + } + + case TOKif: + { Parameter *arg = NULL; + Expression *condition; + Statement *ifbody; + Statement *elsebody; + + nextToken(); + check(TOKlparen); + + if (token.value == TOKauto) + { + nextToken(); + if (token.value == TOKidentifier) + { + Token *t = peek(&token); + if (t->value == TOKassign) + { + arg = new Parameter(0, NULL, token.ident, NULL); + nextToken(); + nextToken(); + } + else + { error("= expected following auto identifier"); + goto Lerror; + } + } + else + { error("identifier expected following auto"); + goto Lerror; + } + } + else if (isDeclaration(&token, 2, TOKassign, NULL)) + { + Type *at; + Identifier *ai; + + at = parseType(&ai); + check(TOKassign); + arg = new Parameter(0, at, ai, NULL); + } + + // Check for " ident;" + else if (token.value == TOKidentifier) + { + Token *t = peek(&token); + if (t->value == TOKsemicolon) + { + arg = new Parameter(0, NULL, token.ident, NULL); + nextToken(); + nextToken(); + if (!global.params.useDeprecated) + error("if (v%s e) is deprecated, use if (auto v = e)", t->toChars()); + } + } + + condition = parseExpression(); + check(TOKrparen); + { + Loc lookingForElseSave = lookingForElse; + lookingForElse = loc; + ifbody = parseStatement(PSscope); + lookingForElse = lookingForElseSave; + } + if (token.value == TOKelse) + { + Loc elseloc = this->loc; + nextToken(); + elsebody = parseStatement(PSscope); + checkDanglingElse(elseloc); + } + else + elsebody = NULL; + if (condition && ifbody) + s = new IfStatement(loc, arg, condition, ifbody, elsebody); + else + s = NULL; // don't propagate parsing errors + break; + } + + case TOKscope: + if (peek(&token)->value != TOKlparen) + goto Ldeclaration; // scope used as storage class + nextToken(); + check(TOKlparen); + if (token.value != TOKidentifier) + { error("scope identifier expected"); + goto Lerror; + } + else + { TOK t = TOKon_scope_exit; + Identifier *id = token.ident; + + if (id == Id::exit) + t = TOKon_scope_exit; + else if (id == Id::failure) + t = TOKon_scope_failure; + else if (id == Id::success) + t = TOKon_scope_success; + else + error("valid scope identifiers are exit, failure, or success, not %s", id->toChars()); + nextToken(); + check(TOKrparen); + Statement *st = parseStatement(PScurlyscope); + s = new OnScopeStatement(loc, t, st); + break; + } + + case TOKdebug: + nextToken(); + condition = parseDebugCondition(); + goto Lcondition; + + case TOKversion: + nextToken(); + condition = parseVersionCondition(); + goto Lcondition; + + Lcondition: + { + Loc lookingForElseSave = lookingForElse; + lookingForElse = loc; + ifbody = parseStatement(0 /*PSsemi*/); + lookingForElse = lookingForElseSave; + } + elsebody = NULL; + if (token.value == TOKelse) + { + Loc elseloc = this->loc; + nextToken(); + elsebody = parseStatement(0 /*PSsemi*/); + checkDanglingElse(elseloc); + } + s = new ConditionalStatement(loc, condition, ifbody, elsebody); + break; + + case TOKpragma: + { Identifier *ident; + Expressions *args = NULL; + Statement *body; + + nextToken(); + check(TOKlparen); + if (token.value != TOKidentifier) + { error("pragma(identifier expected"); + goto Lerror; + } + ident = token.ident; + nextToken(); + if (token.value == TOKcomma && peekNext() != TOKrparen) + args = parseArguments(); // pragma(identifier, args...); + else + check(TOKrparen); // pragma(identifier); + if (token.value == TOKsemicolon) + { nextToken(); + body = NULL; + } + else + body = parseStatement(PSsemi); + s = new PragmaStatement(loc, ident, args, body); + break; + } + + case TOKswitch: + isfinal = FALSE; + goto Lswitch; + + Lswitch: + { + nextToken(); + check(TOKlparen); + Expression *condition = parseExpression(); + check(TOKrparen); + Statement *body = parseStatement(PSscope); + s = new SwitchStatement(loc, condition, body, isfinal); + break; + } + + case TOKcase: + { Expression *exp; + Statements *statements; + Expressions cases; // array of Expression's + Expression *last = NULL; + + while (1) + { + nextToken(); + exp = parseAssignExp(); + cases.push(exp); + if (token.value != TOKcomma) + break; + } + check(TOKcolon); + +#if DMDV2 + /* case exp: .. case last: + */ + if (token.value == TOKslice) + { + if (cases.dim > 1) + error("only one case allowed for start of case range"); + nextToken(); + check(TOKcase); + last = parseAssignExp(); + check(TOKcolon); + } +#endif + + statements = new Statements(); + while (token.value != TOKcase && + token.value != TOKdefault && + token.value != TOKeof && + token.value != TOKrcurly) + { + statements->push(parseStatement(PSsemi | PScurlyscope)); + } + s = new CompoundStatement(loc, statements); + s = new ScopeStatement(loc, s); + +#if DMDV2 + if (last) + { + s = new CaseRangeStatement(loc, exp, last, s); + } + else +#endif + { + // Keep cases in order by building the case statements backwards + for (size_t i = cases.dim; i; i--) + { + exp = cases.tdata()[i - 1]; + s = new CaseStatement(loc, exp, s); + } + } + break; + } + + case TOKdefault: + { + Statements *statements; + + nextToken(); + check(TOKcolon); + + statements = new Statements(); + while (token.value != TOKcase && + token.value != TOKdefault && + token.value != TOKeof && + token.value != TOKrcurly) + { + statements->push(parseStatement(PSsemi | PScurlyscope)); + } + s = new CompoundStatement(loc, statements); + s = new ScopeStatement(loc, s); + s = new DefaultStatement(loc, s); + break; + } + + case TOKreturn: + { Expression *exp; + + nextToken(); + if (token.value == TOKsemicolon) + exp = NULL; + else + exp = parseExpression(); + check(TOKsemicolon, "return statement"); + s = new ReturnStatement(loc, exp); + break; + } + + case TOKbreak: + { Identifier *ident; + + nextToken(); + if (token.value == TOKidentifier) + { ident = token.ident; + nextToken(); + } + else + ident = NULL; + check(TOKsemicolon, "break statement"); + s = new BreakStatement(loc, ident); + break; + } + + case TOKcontinue: + { Identifier *ident; + + nextToken(); + if (token.value == TOKidentifier) + { ident = token.ident; + nextToken(); + } + else + ident = NULL; + check(TOKsemicolon, "continue statement"); + s = new ContinueStatement(loc, ident); + break; + } + + case TOKgoto: + { Identifier *ident; + + nextToken(); + if (token.value == TOKdefault) + { + nextToken(); + s = new GotoDefaultStatement(loc); + } + else if (token.value == TOKcase) + { + Expression *exp = NULL; + + nextToken(); + if (token.value != TOKsemicolon) + exp = parseExpression(); + s = new GotoCaseStatement(loc, exp); + } + else + { + if (token.value != TOKidentifier) + { error("Identifier expected following goto"); + ident = NULL; + } + else + { ident = token.ident; + nextToken(); + } + s = new GotoStatement(loc, ident); + } + check(TOKsemicolon, "goto statement"); + break; + } + + case TOKsynchronized: + { Expression *exp; + Statement *body; + + nextToken(); + if (token.value == TOKlparen) + { + nextToken(); + exp = parseExpression(); + check(TOKrparen); + } + else + exp = NULL; + body = parseStatement(PSscope); + s = new SynchronizedStatement(loc, exp, body); + break; + } + + case TOKwith: + { Expression *exp; + Statement *body; + + nextToken(); + check(TOKlparen); + exp = parseExpression(); + check(TOKrparen); + body = parseStatement(PSscope); + s = new WithStatement(loc, exp, body); + break; + } + + case TOKtry: + { Statement *body; + Catches *catches = NULL; + Statement *finalbody = NULL; + + nextToken(); + Loc lookingForElseSave = lookingForElse; + lookingForElse = 0; + body = parseStatement(PSscope); + lookingForElse = lookingForElseSave; + while (token.value == TOKcatch) + { + Statement *handler; + Catch *c; + Type *t; + Identifier *id; + Loc loc = this->loc; + + nextToken(); + if (token.value == TOKlcurly) + { + t = NULL; + id = NULL; + } + else + { + check(TOKlparen); + id = NULL; + t = parseType(&id); + check(TOKrparen); + } + handler = parseStatement(0); + c = new Catch(loc, t, id, handler); + if (!catches) + catches = new Catches(); + catches->push(c); + } + + if (token.value == TOKfinally) + { nextToken(); + finalbody = parseStatement(0); + } + + s = body; + if (!catches && !finalbody) + error("catch or finally expected following try"); + else + { if (catches) + s = new TryCatchStatement(loc, body, catches); + if (finalbody) + s = new TryFinallyStatement(loc, s, finalbody); + } + break; + } + + case TOKthrow: + { Expression *exp; + + nextToken(); + exp = parseExpression(); + check(TOKsemicolon, "throw statement"); + s = new ThrowStatement(loc, exp); + break; + } + + case TOKvolatile: + nextToken(); + s = parseStatement(PSsemi | PScurlyscope); +#if DMDV2 + if (!global.params.useDeprecated) + error("volatile statements deprecated; use synchronized statements instead"); +#endif + s = new VolatileStatement(loc, s); + break; + + case TOKasm: + { Statements *statements; + Identifier *label; + Loc labelloc; + Token *toklist; + Token **ptoklist; + + // Parse the asm block into a sequence of AsmStatements, + // each AsmStatement is one instruction. + // Separate out labels. + // Defer parsing of AsmStatements until semantic processing. + + nextToken(); + check(TOKlcurly); + toklist = NULL; + ptoklist = &toklist; + label = NULL; + statements = new Statements(); + while (1) + { + switch (token.value) + { + case TOKidentifier: + if (!toklist) + { + // Look ahead to see if it is a label + t = peek(&token); + if (t->value == TOKcolon) + { // It's a label + label = token.ident; + labelloc = this->loc; + nextToken(); + nextToken(); + continue; + } + } + goto Ldefault; + + case TOKrcurly: + if (toklist || label) + { + error("asm statements must end in ';'"); + } + break; + + case TOKsemicolon: + s = NULL; + if (toklist || label) + { // Create AsmStatement from list of tokens we've saved + s = new AsmStatement(this->loc, toklist); + toklist = NULL; + ptoklist = &toklist; + if (label) + { s = new LabelStatement(labelloc, label, s); + label = NULL; + } + statements->push(s); + } + nextToken(); + continue; + + case TOKeof: + /* { */ + error("matching '}' expected, not end of file"); + break; + + default: + Ldefault: + *ptoklist = new Token(); + memcpy(*ptoklist, &token, sizeof(Token)); + ptoklist = &(*ptoklist)->next; + *ptoklist = NULL; + + nextToken(); + continue; + } + break; + } + s = new CompoundStatement(loc, statements); + nextToken(); + break; + } + + case TOKimport: + { Dsymbols *imports = new Dsymbols(); + parseImport(imports, 0); + s = new ImportStatement(loc, imports); + break; + } + + default: + error("found '%s' instead of statement", token.toChars()); + goto Lerror; + + Lerror: + while (token.value != TOKrcurly && + token.value != TOKsemicolon && + token.value != TOKeof) + nextToken(); + if (token.value == TOKsemicolon) + nextToken(); + s = NULL; + break; + } + + return s; +} + +void Parser::check(enum TOK value) +{ + check(loc, value); +} + +void Parser::check(Loc loc, enum TOK value) +{ + if (token.value != value) + error(loc, "found '%s' when expecting '%s'", token.toChars(), Token::toChars(value)); + nextToken(); +} + +void Parser::check(enum TOK value, const char *string) +{ + if (token.value != value) + error("found '%s' when expecting '%s' following %s", + token.toChars(), Token::toChars(value), string); + nextToken(); +} + +void Parser::checkParens(enum TOK value, Expression *e) +{ + if (precedence[e->op] == PREC_rel && !e->parens) + error(loc, "%s must be parenthesized when next to operator %s", e->toChars(), Token::toChars(value)); +} + +/************************************ + * Determine if the scanner is sitting on the start of a declaration. + * Input: + * needId 0 no identifier + * 1 identifier optional + * 2 must have identifier + * Output: + * if *pt is not NULL, it is set to the ending token, which would be endtok + */ + +int Parser::isDeclaration(Token *t, int needId, enum TOK endtok, Token **pt) +{ + //printf("isDeclaration(needId = %d)\n", needId); + int haveId = 0; + +#if DMDV2 + if ((t->value == TOKconst || + t->value == TOKinvariant || + t->value == TOKimmutable || + t->value == TOKwild || + t->value == TOKshared) && + peek(t)->value != TOKlparen) + { /* const type + * immutable type + * shared type + * wild type + */ + t = peek(t); + } +#endif + + if (!isBasicType(&t)) + { + goto Lisnot; + } + if (!isDeclarator(&t, &haveId, endtok)) + goto Lisnot; + if ( needId == 1 || + (needId == 0 && !haveId) || + (needId == 2 && haveId)) + { if (pt) + *pt = t; + goto Lis; + } + else + goto Lisnot; + +Lis: + //printf("\tis declaration, t = %s\n", t->toChars()); + return TRUE; + +Lisnot: + //printf("\tis not declaration\n"); + return FALSE; +} + +int Parser::isBasicType(Token **pt) +{ + // This code parallels parseBasicType() + Token *t = *pt; + int haveId = 0; + + switch (t->value) + { + case BASIC_TYPES: + t = peek(t); + break; + + case TOKidentifier: + L5: + t = peek(t); + if (t->value == TOKnot) + { + goto L4; + } + goto L3; + while (1) + { + L2: + t = peek(t); + L3: + if (t->value == TOKdot) + { + Ldot: + t = peek(t); + if (t->value != TOKidentifier) + goto Lfalse; + t = peek(t); + if (t->value != TOKnot) + goto L3; + L4: + /* Seen a ! + * Look for: + * !( args ), !identifier, etc. + */ + t = peek(t); + switch (t->value) + { case TOKidentifier: + goto L5; + case TOKlparen: + if (!skipParens(t, &t)) + goto Lfalse; + break; + case BASIC_TYPES: + case TOKint32v: + case TOKuns32v: + case TOKint64v: + case TOKuns64v: + case TOKfloat32v: + case TOKfloat64v: + case TOKfloat80v: + case TOKimaginary32v: + case TOKimaginary64v: + case TOKimaginary80v: + case TOKnull: + case TOKtrue: + case TOKfalse: + case TOKcharv: + case TOKwcharv: + case TOKdcharv: + case TOKstring: + case TOKfile: + case TOKline: + goto L2; + default: + goto Lfalse; + } + } + else + break; + } + break; + + case TOKdot: + goto Ldot; + + case TOKtypeof: + case TOKvector: + /* typeof(exp).identifier... + */ + t = peek(t); + if (t->value != TOKlparen) + goto Lfalse; + if (!skipParens(t, &t)) + goto Lfalse; + goto L2; + + case TOKconst: + case TOKinvariant: + case TOKimmutable: + case TOKshared: + case TOKwild: + // const(type) or immutable(type) or shared(type) or wild(type) + t = peek(t); + if (t->value != TOKlparen) + goto Lfalse; + t = peek(t); + if (!isDeclaration(t, 0, TOKrparen, &t)) + { + goto Lfalse; + } + t = peek(t); + break; + + default: + goto Lfalse; + } + *pt = t; + //printf("is\n"); + return TRUE; + +Lfalse: + //printf("is not\n"); + return FALSE; +} + +int Parser::isDeclarator(Token **pt, int *haveId, enum TOK endtok) +{ // This code parallels parseDeclarator() + Token *t = *pt; + int parens; + + //printf("Parser::isDeclarator()\n"); + //t->print(); + if (t->value == TOKassign) + return FALSE; + + while (1) + { + parens = FALSE; + switch (t->value) + { + case TOKmul: + //case TOKand: + t = peek(t); + continue; + + case TOKlbracket: + t = peek(t); + if (t->value == TOKrbracket) + { + t = peek(t); + } + else if (isDeclaration(t, 0, TOKrbracket, &t)) + { // It's an associative array declaration + t = peek(t); + } + else + { + // [ expression ] + // [ expression .. expression ] + if (!isExpression(&t)) + return FALSE; + if (t->value == TOKslice) + { t = peek(t); + if (!isExpression(&t)) + return FALSE; + } + if (t->value != TOKrbracket) + return FALSE; + t = peek(t); + } + continue; + + case TOKidentifier: + if (*haveId) + return FALSE; + *haveId = TRUE; + t = peek(t); + break; + + case TOKlparen: + t = peek(t); + + if (t->value == TOKrparen) + return FALSE; // () is not a declarator + + /* Regard ( identifier ) as not a declarator + * BUG: what about ( *identifier ) in + * f(*p)(x); + * where f is a class instance with overloaded () ? + * Should we just disallow C-style function pointer declarations? + */ + if (t->value == TOKidentifier) + { Token *t2 = peek(t); + if (t2->value == TOKrparen) + return FALSE; + } + + + if (!isDeclarator(&t, haveId, TOKrparen)) + return FALSE; + t = peek(t); + parens = TRUE; + break; + + case TOKdelegate: + case TOKfunction: + t = peek(t); + if (!isParameters(&t)) + return FALSE; + continue; + } + break; + } + + while (1) + { + switch (t->value) + { +#if CARRAYDECL + case TOKlbracket: + parens = FALSE; + t = peek(t); + if (t->value == TOKrbracket) + { + t = peek(t); + } + else if (isDeclaration(t, 0, TOKrbracket, &t)) + { // It's an associative array declaration + t = peek(t); + } + else + { + // [ expression ] + if (!isExpression(&t)) + return FALSE; + if (t->value != TOKrbracket) + return FALSE; + t = peek(t); + } + continue; +#endif + + case TOKlparen: + parens = FALSE; + if (!isParameters(&t)) + return FALSE; +#if DMDV2 + while (1) + { + switch (t->value) + { + case TOKconst: + case TOKinvariant: + case TOKimmutable: + case TOKshared: + case TOKwild: + case TOKpure: + case TOKnothrow: + t = peek(t); + continue; + case TOKat: + t = peek(t); // skip '@' + t = peek(t); // skip identifier + continue; + default: + break; + } + break; + } +#endif + continue; + + // Valid tokens that follow a declaration + case TOKrparen: + case TOKrbracket: + case TOKassign: + case TOKcomma: + case TOKdotdotdot: + case TOKsemicolon: + case TOKlcurly: + case TOKin: + case TOKout: + case TOKbody: + // The !parens is to disallow unnecessary parentheses + if (!parens && (endtok == TOKreserved || endtok == t->value)) + { *pt = t; + return TRUE; + } + return FALSE; + + default: + return FALSE; + } + } +} + + +int Parser::isParameters(Token **pt) +{ // This code parallels parseParameters() + Token *t = *pt; + + //printf("isParameters()\n"); + if (t->value != TOKlparen) + return FALSE; + + t = peek(t); + for (;1; t = peek(t)) + { + L1: + switch (t->value) + { + case TOKrparen: + break; + + case TOKdotdotdot: + t = peek(t); + break; + +#if D1INOUT + case TOKinout: +#endif + case TOKin: + case TOKout: + case TOKref: + case TOKlazy: + case TOKfinal: + case TOKauto: + continue; + + case TOKconst: + case TOKinvariant: + case TOKimmutable: + case TOKshared: + case TOKwild: + t = peek(t); + if (t->value == TOKlparen) + { + t = peek(t); + if (!isDeclaration(t, 0, TOKrparen, &t)) + return FALSE; + t = peek(t); // skip past closing ')' + goto L2; + } + goto L1; + +#if 0 + case TOKstatic: + continue; + case TOKauto: + case TOKalias: + t = peek(t); + if (t->value == TOKidentifier) + t = peek(t); + if (t->value == TOKassign) + { t = peek(t); + if (!isExpression(&t)) + return FALSE; + } + goto L3; +#endif + + default: + { if (!isBasicType(&t)) + return FALSE; + L2: + int tmp = FALSE; + if (t->value != TOKdotdotdot && + !isDeclarator(&t, &tmp, TOKreserved)) + return FALSE; + if (t->value == TOKassign) + { t = peek(t); + if (!isExpression(&t)) + return FALSE; + } + if (t->value == TOKdotdotdot) + { + t = peek(t); + break; + } + } + if (t->value == TOKcomma) + { + continue; + } + break; + } + break; + } + if (t->value != TOKrparen) + return FALSE; + t = peek(t); + *pt = t; + return TRUE; +} + +int Parser::isExpression(Token **pt) +{ + // This is supposed to determine if something is an expression. + // What it actually does is scan until a closing right bracket + // is found. + + Token *t = *pt; + int brnest = 0; + int panest = 0; + int curlynest = 0; + + for (;; t = peek(t)) + { + switch (t->value) + { + case TOKlbracket: + brnest++; + continue; + + case TOKrbracket: + if (--brnest >= 0) + continue; + break; + + case TOKlparen: + panest++; + continue; + + case TOKcomma: + if (brnest || panest) + continue; + break; + + case TOKrparen: + if (--panest >= 0) + continue; + break; + + case TOKlcurly: + curlynest++; + continue; + + case TOKrcurly: + if (--curlynest >= 0) + continue; + return FALSE; + + case TOKslice: + if (brnest) + continue; + break; + + case TOKsemicolon: + if (curlynest) + continue; + return FALSE; + + case TOKeof: + return FALSE; + + default: + continue; + } + break; + } + + *pt = t; + return TRUE; +} + +/********************************************** + * Skip over + * instance foo.bar(parameters...) + * Output: + * if (pt), *pt is set to the token following the closing ) + * Returns: + * 1 it's valid instance syntax + * 0 invalid instance syntax + */ + +int Parser::isTemplateInstance(Token *t, Token **pt) +{ + t = peek(t); + if (t->value != TOKdot) + { + if (t->value != TOKidentifier) + goto Lfalse; + t = peek(t); + } + while (t->value == TOKdot) + { + t = peek(t); + if (t->value != TOKidentifier) + goto Lfalse; + t = peek(t); + } + if (t->value != TOKlparen) + goto Lfalse; + + // Skip over the template arguments + while (1) + { + while (1) + { + t = peek(t); + switch (t->value) + { + case TOKlparen: + if (!skipParens(t, &t)) + goto Lfalse; + continue; + case TOKrparen: + break; + case TOKcomma: + break; + case TOKeof: + case TOKsemicolon: + goto Lfalse; + default: + continue; + } + break; + } + + if (t->value != TOKcomma) + break; + } + if (t->value != TOKrparen) + goto Lfalse; + t = peek(t); + if (pt) + *pt = t; + return 1; + +Lfalse: + return 0; +} + +/******************************************* + * Skip parens, brackets. + * Input: + * t is on opening ( + * Output: + * *pt is set to closing token, which is ')' on success + * Returns: + * !=0 successful + * 0 some parsing error + */ + +int Parser::skipParens(Token *t, Token **pt) +{ + int parens = 0; + + while (1) + { + switch (t->value) + { + case TOKlparen: + parens++; + break; + + case TOKrparen: + parens--; + if (parens < 0) + goto Lfalse; + if (parens == 0) + goto Ldone; + break; + + case TOKeof: + goto Lfalse; + + default: + break; + } + t = peek(t); + } + + Ldone: + if (*pt) + *pt = t; + return 1; + + Lfalse: + return 0; +} + +/******************************************* + * Skip attributes. + * Input: + * t is on a candidate attribute + * Output: + * *pt is set to first non-attribute token on success + * Returns: + * !=0 successful + * 0 some parsing error + */ + +int Parser::skipAttributes(Token *t, Token **pt) +{ + while (1) + { + switch (t->value) + { + case TOKconst: + case TOKinvariant: + case TOKimmutable: + case TOKshared: + case TOKwild: + case TOKfinal: + case TOKauto: + case TOKscope: + case TOKoverride: + case TOKabstract: + case TOKsynchronized: + case TOKdeprecated: + case TOKnothrow: + case TOKpure: + case TOKref: + case TOKtls: + case TOKgshared: + //case TOKmanifest: + break; + case TOKat: + t = peek(t); + if (t->value == TOKidentifier) + break; + goto Lerror; + default: + goto Ldone; + } + t = peek(t); + } + + Ldone: + if (*pt) + *pt = t; + return 1; + + Lerror: + return 0; +} + +/********************************* Expression Parser ***************************/ + +Expression *Parser::parsePrimaryExp() +{ Expression *e; + Type *t; + Identifier *id; + enum TOK save; + Loc loc = this->loc; + + //printf("parsePrimaryExp(): loc = %d\n", loc.linnum); + switch (token.value) + { + case TOKidentifier: + if (peekNext() == TOKgoesto) + goto case_delegate; + + id = token.ident; + nextToken(); + if (token.value == TOKnot && (save = peekNext()) != TOKis && save != TOKin) + { // identifier!(template-argument-list) + TemplateInstance *tempinst; + + tempinst = new TemplateInstance(loc, id); + nextToken(); + if (token.value == TOKlparen) + // ident!(template_arguments) + tempinst->tiargs = parseTemplateArgumentList(); + else + // ident!template_argument + tempinst->tiargs = parseTemplateArgument(); + e = new ScopeExp(loc, tempinst); + } + else + e = new IdentifierExp(loc, id); + break; + + case TOKdollar: + if (!inBrackets) + error("'$' is valid only inside [] of index or slice"); + e = new DollarExp(loc); + nextToken(); + break; + + case TOKdot: + // Signal global scope '.' operator with "" identifier + e = new IdentifierExp(loc, Id::empty); + break; + + case TOKthis: + e = new ThisExp(loc); + nextToken(); + break; + + case TOKsuper: + e = new SuperExp(loc); + nextToken(); + break; + + case TOKint32v: + e = new IntegerExp(loc, token.int32value, Type::tint32); + nextToken(); + break; + + case TOKuns32v: + e = new IntegerExp(loc, token.uns32value, Type::tuns32); + nextToken(); + break; + + case TOKint64v: + e = new IntegerExp(loc, token.int64value, Type::tint64); + nextToken(); + break; + + case TOKuns64v: + e = new IntegerExp(loc, token.uns64value, Type::tuns64); + nextToken(); + break; + + case TOKfloat32v: + e = new RealExp(loc, token.float80value, Type::tfloat32); + nextToken(); + break; + + case TOKfloat64v: + e = new RealExp(loc, token.float80value, Type::tfloat64); + nextToken(); + break; + + case TOKfloat80v: + e = new RealExp(loc, token.float80value, Type::tfloat80); + nextToken(); + break; + + case TOKimaginary32v: + e = new RealExp(loc, token.float80value, Type::timaginary32); + nextToken(); + break; + + case TOKimaginary64v: + e = new RealExp(loc, token.float80value, Type::timaginary64); + nextToken(); + break; + + case TOKimaginary80v: + e = new RealExp(loc, token.float80value, Type::timaginary80); + nextToken(); + break; + + case TOKnull: + e = new NullExp(loc); + nextToken(); + break; + +#if DMDV2 + case TOKfile: + { const char *s = loc.filename ? loc.filename : mod->ident->toChars(); + e = new StringExp(loc, (char *)s, strlen(s), 0); + nextToken(); + break; + } + + case TOKline: + e = new IntegerExp(loc, loc.linnum, Type::tint32); + nextToken(); + break; +#endif + + case TOKtrue: + e = new IntegerExp(loc, 1, Type::tbool); + nextToken(); + break; + + case TOKfalse: + e = new IntegerExp(loc, 0, Type::tbool); + nextToken(); + break; + + case TOKcharv: + e = new IntegerExp(loc, token.uns32value, Type::tchar); + nextToken(); + break; + + case TOKwcharv: + e = new IntegerExp(loc, token.uns32value, Type::twchar); + nextToken(); + break; + + case TOKdcharv: + e = new IntegerExp(loc, token.uns32value, Type::tdchar); + nextToken(); + break; + + case TOKstring: + { unsigned char *s; + unsigned len; + unsigned char postfix; + + // cat adjacent strings + s = token.ustring; + len = token.len; + postfix = token.postfix; + while (1) + { + nextToken(); + if (token.value == TOKstring) + { unsigned len1; + unsigned len2; + unsigned char *s2; + + if (token.postfix) + { if (token.postfix != postfix) + error("mismatched string literal postfixes '%c' and '%c'", postfix, token.postfix); + postfix = token.postfix; + } + + len1 = len; + len2 = token.len; + len = len1 + len2; + s2 = (unsigned char *)mem.malloc((len + 1) * sizeof(unsigned char)); + memcpy(s2, s, len1 * sizeof(unsigned char)); + memcpy(s2 + len1, token.ustring, (len2 + 1) * sizeof(unsigned char)); + s = s2; + } + else + break; + } + e = new StringExp(loc, s, len, postfix); + break; + } + + case BASIC_TYPES_X(t): + nextToken(); + check(TOKdot, t->toChars()); + if (token.value != TOKidentifier) + { error("found '%s' when expecting identifier following '%s.'", token.toChars(), t->toChars()); + goto Lerr; + } + e = typeDotIdExp(loc, t, token.ident); + nextToken(); + break; + + case TOKtypeof: + { + t = parseTypeof(); + e = new TypeExp(loc, t); + break; + } + + case TOKvector: + { + t = parseVector(); + e = new TypeExp(loc, t); + break; + } + + case TOKtypeid: + { + nextToken(); + check(TOKlparen, "typeid"); + Object *o; + if (isDeclaration(&token, 0, TOKreserved, NULL)) + { // argument is a type + o = parseType(); + } + else + { // argument is an expression + o = parseAssignExp(); + } + check(TOKrparen); + e = new TypeidExp(loc, o); + break; + } + +#if DMDV2 + case TOKtraits: + { /* __traits(identifier, args...) + */ + Identifier *ident; + Objects *args = NULL; + + nextToken(); + check(TOKlparen); + if (token.value != TOKidentifier) + { error("__traits(identifier, args...) expected"); + goto Lerr; + } + ident = token.ident; + nextToken(); + if (token.value == TOKcomma) + args = parseTemplateArgumentList2(); // __traits(identifier, args...) + else + check(TOKrparen); // __traits(identifier) + + e = new TraitsExp(loc, ident, args); + break; + } +#endif + + case TOKis: + { Type *targ; + Identifier *ident = NULL; + Type *tspec = NULL; + enum TOK tok = TOKreserved; + enum TOK tok2 = TOKreserved; + TemplateParameters *tpl = NULL; + Loc loc = this->loc; + + nextToken(); + if (token.value == TOKlparen) + { + nextToken(); + targ = parseType(&ident); + if (token.value == TOKcolon || token.value == TOKequal) + { + tok = token.value; + nextToken(); + if (tok == TOKequal && + (token.value == TOKtypedef || + token.value == TOKstruct || + token.value == TOKunion || + token.value == TOKclass || + token.value == TOKsuper || + token.value == TOKenum || + token.value == TOKinterface || + token.value == TOKargTypes || +#if DMDV2 + token.value == TOKconst && peek(&token)->value == TOKrparen || + token.value == TOKinvariant && peek(&token)->value == TOKrparen || + token.value == TOKimmutable && peek(&token)->value == TOKrparen || + token.value == TOKshared && peek(&token)->value == TOKrparen || + token.value == TOKwild && peek(&token)->value == TOKrparen || +#endif + token.value == TOKfunction || + token.value == TOKdelegate || + token.value == TOKreturn)) + { + if (token.value == TOKinvariant && !global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); + tok2 = token.value; + nextToken(); + } + else + { + tspec = parseType(); + } + } + if (ident && tspec) + { + if (token.value == TOKcomma) + tpl = parseTemplateParameterList(1); + else + { tpl = new TemplateParameters(); + check(TOKrparen); + } + TemplateParameter *tp = new TemplateTypeParameter(loc, ident, NULL, NULL); + tpl->insert(0, tp); + } + else + check(TOKrparen); + } + else + { error("(type identifier : specialization) expected following is"); + goto Lerr; + } + e = new IsExp(loc, targ, ident, tok, tspec, tok2, tpl); + break; + } + + case TOKassert: + { Expression *msg = NULL; + + nextToken(); + check(TOKlparen, "assert"); + e = parseAssignExp(); + if (token.value == TOKcomma) + { nextToken(); + msg = parseAssignExp(); + } + check(TOKrparen); + e = new AssertExp(loc, e, msg); + break; + } + + case TOKmixin: + { + nextToken(); + check(TOKlparen, "mixin"); + e = parseAssignExp(); + check(TOKrparen); + e = new CompileExp(loc, e); + break; + } + + case TOKimport: + { + nextToken(); + check(TOKlparen, "import"); + e = parseAssignExp(); + check(TOKrparen); + e = new FileExp(loc, e); + break; + } + + case TOKlparen: + { enum TOK past = peekPastParen(&token)->value; + + if (past == TOKgoesto) + { // (arguments) => expression + goto case_delegate; + } + else if (past == TOKlcurly) + { // (arguments) { statements... } + goto case_delegate; + } + // ( expression ) + nextToken(); + e = parseExpression(); + e->parens = 1; + check(loc, TOKrparen); + break; + } + + case TOKlbracket: + { /* Parse array literals and associative array literals: + * [ value, value, value ... ] + * [ key:value, key:value, key:value ... ] + */ + Expressions *values = new Expressions(); + Expressions *keys = NULL; + + nextToken(); + while (token.value != TOKrbracket && token.value != TOKeof) + { + Expression *e = parseAssignExp(); + if (token.value == TOKcolon && (keys || values->dim == 0)) + { nextToken(); + if (!keys) + keys = new Expressions(); + keys->push(e); + e = parseAssignExp(); + } + else if (keys) + { error("'key:value' expected for associative array literal"); + delete keys; + keys = NULL; + } + values->push(e); + if (token.value == TOKrbracket) + break; + check(TOKcomma); + } + check(loc, TOKrbracket); + + if (keys) + e = new AssocArrayLiteralExp(loc, keys, values); + else + e = new ArrayLiteralExp(loc, values); + break; + } + + case TOKlcurly: + case TOKfunction: + case TOKdelegate: + case_delegate: + { + TemplateParameters *tpl = NULL; + Parameters *parameters = NULL; + int varargs = 0; + Type *tret = NULL; + StorageClass stc = 0; + enum TOK save = TOKreserved; + Loc loc = this->loc; + + switch (token.value) + { + case TOKfunction: + case TOKdelegate: + save = token.value; + nextToken(); + if (token.value != TOKlparen && token.value != TOKlcurly) + { // function type (parameters) { statements... } + // delegate type (parameters) { statements... } + tret = parseBasicType(); + tret = parseBasicType2(tret); // function return type + } + + if (token.value == TOKlparen) + { // function (parameters) { statements... } + // delegate (parameters) { statements... } + } + else + { // function { statements... } + // delegate { statements... } + break; + } + /* fall through to TOKlparen */ + + case TOKlparen: + Lparen: + { // (parameters) => expression + // (parameters) { statements... } + parameters = parseParameters(&varargs, &tpl); + stc = parsePostfix(); + if (stc & (STCconst | STCimmutable | STCshared | STCwild)) + error("const/immutable/shared/inout attributes are only valid for non-static member functions"); + break; + } + case TOKlcurly: + // { statements... } + break; + + case TOKidentifier: + { // identifier => expression + parameters = new Parameters(); + Identifier *id = Lexer::uniqueId("__T"); + Type *t = new TypeIdentifier(loc, id); + parameters->push(new Parameter(0, t, token.ident, NULL)); + + tpl = new TemplateParameters(); + TemplateParameter *tp = new TemplateTypeParameter(loc, id, NULL, NULL); + tpl->push(tp); + + nextToken(); + break; + } + default: + assert(0); + } + + if (!parameters) + parameters = new Parameters(); + TypeFunction *tf = new TypeFunction(parameters, tret, varargs, linkage, stc); + FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, 0, tf, save, NULL); + + if (token.value == TOKgoesto) + { + check(TOKgoesto); + Loc loc = this->loc; + Expression *ae = parseAssignExp(); + fd->fbody = new ReturnStatement(loc, ae); + fd->endloc = this->loc; + } + else + { + parseContracts(fd); + } + + TemplateDeclaration *td = NULL; + if (tpl) + { // Wrap a template around function fd + Dsymbols *decldefs = new Dsymbols(); + decldefs->push(fd); + td = new TemplateDeclaration(fd->loc, fd->ident, tpl, NULL, decldefs, 0); + td->literal = 1; // it's a template 'literal' + } + + e = new FuncExp(loc, fd, td); + break; + } + + default: + error("expression expected, not '%s'", token.toChars()); + Lerr: + // Anything for e, as long as it's not NULL + e = new IntegerExp(loc, 0, Type::tint32); + nextToken(); + break; + } + return e; +} + +Expression *Parser::parsePostExp(Expression *e) +{ + Loc loc; + + while (1) + { + loc = this->loc; + switch (token.value) + { + case TOKdot: + nextToken(); + if (token.value == TOKidentifier) + { Identifier *id = token.ident; + + nextToken(); + if (token.value == TOKnot && peekNext() != TOKis && peekNext() != TOKin) + { // identifier!(template-argument-list) + TemplateInstance *tempinst = new TemplateInstance(loc, id); + Objects *tiargs; + nextToken(); + if (token.value == TOKlparen) + // ident!(template_arguments) + tiargs = parseTemplateArgumentList(); + else + // ident!template_argument + tiargs = parseTemplateArgument(); + e = new DotTemplateInstanceExp(loc, e, id, tiargs); + } + else + e = new DotIdExp(loc, e, id); + continue; + } + else if (token.value == TOKnew) + { + e = parseNewExp(e); + continue; + } + else + error("identifier expected following '.', not '%s'", token.toChars()); + break; + + case TOKplusplus: + e = new PostExp(TOKplusplus, loc, e); + break; + + case TOKminusminus: + e = new PostExp(TOKminusminus, loc, e); + break; + + case TOKlparen: + e = new CallExp(loc, e, parseArguments()); + continue; + + case TOKlbracket: + { // array dereferences: + // array[index] + // array[] + // array[lwr .. upr] + Expression *index; + Expression *upr; + + inBrackets++; + nextToken(); + if (token.value == TOKrbracket) + { // array[] + inBrackets--; + e = new SliceExp(loc, e, NULL, NULL); + nextToken(); + } + else + { + index = parseAssignExp(); + if (token.value == TOKslice) + { // array[lwr .. upr] + nextToken(); + upr = parseAssignExp(); + e = new SliceExp(loc, e, index, upr); + } + else + { // array[index, i2, i3, i4, ...] + Expressions *arguments = new Expressions(); + arguments->push(index); + if (token.value == TOKcomma) + { + nextToken(); + while (token.value != TOKrbracket && token.value != TOKeof) + { + Expression *arg = parseAssignExp(); + arguments->push(arg); + if (token.value == TOKrbracket) + break; + check(TOKcomma); + } + } + e = new ArrayExp(loc, e, arguments); + } + check(TOKrbracket); + inBrackets--; + } + continue; + } + + default: + return e; + } + nextToken(); + } +} + +Expression *Parser::parseUnaryExp() +{ Expression *e; + Loc loc = this->loc; + + switch (token.value) + { + case TOKand: + nextToken(); + e = parseUnaryExp(); + e = new AddrExp(loc, e); + break; + + case TOKplusplus: + nextToken(); + e = parseUnaryExp(); + //e = new AddAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32)); + e = new PreExp(TOKpreplusplus, loc, e); + break; + + case TOKminusminus: + nextToken(); + e = parseUnaryExp(); + //e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32)); + e = new PreExp(TOKpreminusminus, loc, e); + break; + + case TOKmul: + nextToken(); + e = parseUnaryExp(); + e = new PtrExp(loc, e); + break; + + case TOKmin: + nextToken(); + e = parseUnaryExp(); + e = new NegExp(loc, e); + break; + + case TOKadd: + nextToken(); + e = parseUnaryExp(); + e = new UAddExp(loc, e); + break; + + case TOKnot: + nextToken(); + e = parseUnaryExp(); + e = new NotExp(loc, e); + break; + + case TOKtilde: + nextToken(); + e = parseUnaryExp(); + e = new ComExp(loc, e); + break; + + case TOKdelete: + nextToken(); + e = parseUnaryExp(); + e = new DeleteExp(loc, e); + break; + + case TOKnew: + e = parseNewExp(NULL); + break; + + case TOKcast: // cast(type) expression + { + nextToken(); + check(TOKlparen); + /* Look for cast(), cast(const), cast(immutable), + * cast(shared), cast(shared const), cast(wild), cast(shared wild) + */ + unsigned m; + if (token.value == TOKrparen) + { + m = 0; + goto Lmod1; + } + else if (token.value == TOKconst && peekNext() == TOKrparen) + { + m = MODconst; + goto Lmod2; + } + else if ((token.value == TOKimmutable || token.value == TOKinvariant) && peekNext() == TOKrparen) + { + if (token.value == TOKinvariant && !global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); + m = MODimmutable; + goto Lmod2; + } + else if (token.value == TOKshared && peekNext() == TOKrparen) + { + m = MODshared; + goto Lmod2; + } + else if (token.value == TOKwild && peekNext() == TOKrparen) + { + m = MODwild; + goto Lmod2; + } + else if (token.value == TOKwild && peekNext() == TOKshared && peekNext2() == TOKrparen || + token.value == TOKshared && peekNext() == TOKwild && peekNext2() == TOKrparen) + { + m = MODshared | MODwild; + goto Lmod3; + } + else if (token.value == TOKconst && peekNext() == TOKshared && peekNext2() == TOKrparen || + token.value == TOKshared && peekNext() == TOKconst && peekNext2() == TOKrparen) + { + m = MODshared | MODconst; + Lmod3: + nextToken(); + Lmod2: + nextToken(); + Lmod1: + nextToken(); + e = parseUnaryExp(); + e = new CastExp(loc, e, m); + } + else + { + Type *t = parseType(); // ( type ) + check(TOKrparen); + e = parseUnaryExp(); + e = new CastExp(loc, e, t); + } + break; + } + + case TOKwild: + case TOKshared: + case TOKconst: + case TOKinvariant: + case TOKimmutable: // immutable(type)(arguments) + { + Type *t = parseBasicType(); + e = new TypeExp(loc, t); + if (token.value != TOKlparen) + { + error("(arguments) expected following %s", t->toChars()); + return e; + } + e = new CallExp(loc, e, parseArguments()); + break; + } + + + case TOKlparen: + { Token *tk; + + tk = peek(&token); +#if CCASTSYNTAX + // If cast + if (isDeclaration(tk, 0, TOKrparen, &tk)) + { + tk = peek(tk); // skip over right parenthesis + switch (tk->value) + { + case TOKnot: + tk = peek(tk); + if (tk->value == TOKis || tk->value == TOKin) // !is or !in + break; + case TOKdot: + case TOKplusplus: + case TOKminusminus: + case TOKdelete: + case TOKnew: + case TOKlparen: + case TOKidentifier: + case TOKthis: + case TOKsuper: + case TOKint32v: + case TOKuns32v: + case TOKint64v: + case TOKuns64v: + case TOKfloat32v: + case TOKfloat64v: + case TOKfloat80v: + case TOKimaginary32v: + case TOKimaginary64v: + case TOKimaginary80v: + case TOKnull: + case TOKtrue: + case TOKfalse: + case TOKcharv: + case TOKwcharv: + case TOKdcharv: + case TOKstring: +#if 0 + case TOKtilde: + case TOKand: + case TOKmul: + case TOKmin: + case TOKadd: +#endif + case TOKfunction: + case TOKdelegate: + case TOKtypeof: + case TOKvector: +#if DMDV2 + case TOKfile: + case TOKline: +#endif + case BASIC_TYPES: // (type)int.size + { // (type) una_exp + Type *t; + + nextToken(); + t = parseType(); + check(TOKrparen); + + // if .identifier + if (token.value == TOKdot) + { + nextToken(); + if (token.value != TOKidentifier) + { error("Identifier expected following (type)."); + return NULL; + } + e = typeDotIdExp(loc, t, token.ident); + nextToken(); + e = parsePostExp(e); + } + else + { + e = parseUnaryExp(); + e = new CastExp(loc, e, t); + error("C style cast illegal, use %s", e->toChars()); + } + return e; + } + } + } +#endif + e = parsePrimaryExp(); + e = parsePostExp(e); + break; + } + default: + e = parsePrimaryExp(); + e = parsePostExp(e); + break; + } + assert(e); + + // ^^ is right associative and has higher precedence than the unary operators + while (token.value == TOKpow) + { + nextToken(); + Expression *e2 = parseUnaryExp(); + e = new PowExp(loc, e, e2); + } + + return e; +} + +Expression *Parser::parseMulExp() +{ Expression *e; + Expression *e2; + Loc loc = this->loc; + + e = parseUnaryExp(); + while (1) + { + switch (token.value) + { + case TOKmul: nextToken(); e2 = parseUnaryExp(); e = new MulExp(loc,e,e2); continue; + case TOKdiv: nextToken(); e2 = parseUnaryExp(); e = new DivExp(loc,e,e2); continue; + case TOKmod: nextToken(); e2 = parseUnaryExp(); e = new ModExp(loc,e,e2); continue; + + default: + break; + } + break; + } + return e; +} + +Expression *Parser::parseAddExp() +{ Expression *e; + Expression *e2; + Loc loc = this->loc; + + e = parseMulExp(); + while (1) + { + switch (token.value) + { + case TOKadd: nextToken(); e2 = parseMulExp(); e = new AddExp(loc,e,e2); continue; + case TOKmin: nextToken(); e2 = parseMulExp(); e = new MinExp(loc,e,e2); continue; + case TOKtilde: nextToken(); e2 = parseMulExp(); e = new CatExp(loc,e,e2); continue; + + default: + break; + } + break; + } + return e; +} + +Expression *Parser::parseShiftExp() +{ Expression *e; + Expression *e2; + Loc loc = this->loc; + + e = parseAddExp(); + while (1) + { + switch (token.value) + { + case TOKshl: nextToken(); e2 = parseAddExp(); e = new ShlExp(loc,e,e2); continue; + case TOKshr: nextToken(); e2 = parseAddExp(); e = new ShrExp(loc,e,e2); continue; + case TOKushr: nextToken(); e2 = parseAddExp(); e = new UshrExp(loc,e,e2); continue; + + default: + break; + } + break; + } + return e; +} + +#if DMDV1 +Expression *Parser::parseRelExp() +{ Expression *e; + Expression *e2; + enum TOK op; + Loc loc = this->loc; + + e = parseShiftExp(); + while (1) + { + switch (token.value) + { + case TOKlt: + case TOKle: + case TOKgt: + case TOKge: + case TOKunord: + case TOKlg: + case TOKleg: + case TOKule: + case TOKul: + case TOKuge: + case TOKug: + case TOKue: + op = token.value; + nextToken(); + e2 = parseShiftExp(); + e = new CmpExp(op, loc, e, e2); + continue; + + case TOKnot: // could be !in + if (peekNext() == TOKin) + { + nextToken(); + nextToken(); + e2 = parseShiftExp(); + e = new InExp(loc, e, e2); + e = new NotExp(loc, e); + continue; + } + break; + + case TOKin: + nextToken(); + e2 = parseShiftExp(); + e = new InExp(loc, e, e2); + continue; + + default: + break; + } + break; + } + return e; +} +#endif + +#if DMDV1 +Expression *Parser::parseEqualExp() +{ Expression *e; + Expression *e2; + Token *t; + Loc loc = this->loc; + + e = parseRelExp(); + while (1) + { enum TOK value = token.value; + + switch (value) + { + case TOKequal: + case TOKnotequal: + nextToken(); + e2 = parseRelExp(); + e = new EqualExp(value, loc, e, e2); + continue; + + case TOKidentity: + error("'===' is no longer legal, use 'is' instead"); + goto L1; + + case TOKnotidentity: + error("'!==' is no longer legal, use '!is' instead"); + goto L1; + + case TOKis: + value = TOKidentity; + goto L1; + + case TOKnot: + // Attempt to identify '!is' + t = peek(&token); + if (t->value != TOKis) + break; + nextToken(); + value = TOKnotidentity; + goto L1; + + L1: + nextToken(); + e2 = parseRelExp(); + e = new IdentityExp(value, loc, e, e2); + continue; + + default: + break; + } + break; + } + return e; +} +#endif + +Expression *Parser::parseCmpExp() +{ Expression *e; + Expression *e2; + Token *t; + Loc loc = this->loc; + + e = parseShiftExp(); + enum TOK op = token.value; + + switch (op) + { + case TOKequal: + case TOKnotequal: + nextToken(); + e2 = parseShiftExp(); + e = new EqualExp(op, loc, e, e2); + break; + + case TOKis: + op = TOKidentity; + goto L1; + + case TOKnot: + // Attempt to identify '!is' + t = peek(&token); + if (t->value == TOKin) + { + nextToken(); + nextToken(); + e2 = parseShiftExp(); + e = new InExp(loc, e, e2); + e = new NotExp(loc, e); + break; + } + if (t->value != TOKis) + break; + nextToken(); + op = TOKnotidentity; + goto L1; + + L1: + nextToken(); + e2 = parseShiftExp(); + e = new IdentityExp(op, loc, e, e2); + break; + + case TOKlt: + case TOKle: + case TOKgt: + case TOKge: + case TOKunord: + case TOKlg: + case TOKleg: + case TOKule: + case TOKul: + case TOKuge: + case TOKug: + case TOKue: + nextToken(); + e2 = parseShiftExp(); + e = new CmpExp(op, loc, e, e2); + break; + + case TOKin: + nextToken(); + e2 = parseShiftExp(); + e = new InExp(loc, e, e2); + break; + + default: + break; + } + return e; +} + +Expression *Parser::parseAndExp() +{ + Loc loc = this->loc; + + Expression *e = parseCmpExp(); + while (token.value == TOKand) + { + checkParens(TOKand, e); + nextToken(); + Expression *e2 = parseCmpExp(); + checkParens(TOKand, e2); + e = new AndExp(loc,e,e2); + loc = this->loc; + } + return e; +} + +Expression *Parser::parseXorExp() +{ + Loc loc = this->loc; + + Expression *e = parseAndExp(); + while (token.value == TOKxor) + { + checkParens(TOKxor, e); + nextToken(); + Expression *e2 = parseAndExp(); + checkParens(TOKxor, e2); + e = new XorExp(loc, e, e2); + } + return e; +} + +Expression *Parser::parseOrExp() +{ + Loc loc = this->loc; + + Expression *e = parseXorExp(); + while (token.value == TOKor) + { + checkParens(TOKor, e); + nextToken(); + Expression *e2 = parseXorExp(); + checkParens(TOKor, e2); + e = new OrExp(loc, e, e2); + } + return e; +} + +Expression *Parser::parseAndAndExp() +{ Expression *e; + Expression *e2; + Loc loc = this->loc; + + e = parseOrExp(); + while (token.value == TOKandand) + { + nextToken(); + e2 = parseOrExp(); + e = new AndAndExp(loc, e, e2); + } + return e; +} + +Expression *Parser::parseOrOrExp() +{ Expression *e; + Expression *e2; + Loc loc = this->loc; + + e = parseAndAndExp(); + while (token.value == TOKoror) + { + nextToken(); + e2 = parseAndAndExp(); + e = new OrOrExp(loc, e, e2); + } + return e; +} + +Expression *Parser::parseCondExp() +{ Expression *e; + Expression *e1; + Expression *e2; + Loc loc = this->loc; + + e = parseOrOrExp(); + if (token.value == TOKquestion) + { + nextToken(); + e1 = parseExpression(); + check(TOKcolon); + e2 = parseCondExp(); + e = new CondExp(loc, e, e1, e2); + } + return e; +} + +Expression *Parser::parseAssignExp() +{ Expression *e; + Expression *e2; + Loc loc; + + e = parseCondExp(); + while (1) + { + loc = this->loc; + switch (token.value) + { +#define X(tok,ector) \ + case tok: nextToken(); e2 = parseAssignExp(); e = new ector(loc,e,e2); continue; + + X(TOKassign, AssignExp); + X(TOKaddass, AddAssignExp); + X(TOKminass, MinAssignExp); + X(TOKmulass, MulAssignExp); + X(TOKdivass, DivAssignExp); + X(TOKmodass, ModAssignExp); + X(TOKpowass, PowAssignExp); + X(TOKandass, AndAssignExp); + X(TOKorass, OrAssignExp); + X(TOKxorass, XorAssignExp); + X(TOKshlass, ShlAssignExp); + X(TOKshrass, ShrAssignExp); + X(TOKushrass, UshrAssignExp); + X(TOKcatass, CatAssignExp); + +#undef X + default: + break; + } + break; + } + return e; +} + +Expression *Parser::parseExpression() +{ Expression *e; + Expression *e2; + Loc loc = this->loc; + + //printf("Parser::parseExpression() loc = %d\n", loc.linnum); + e = parseAssignExp(); + while (token.value == TOKcomma) + { + nextToken(); + e2 = parseAssignExp(); + e = new CommaExp(loc, e, e2); + loc = this->loc; + } + return e; +} + + +/************************* + * Collect argument list. + * Assume current token is ',', '(' or '['. + */ + +Expressions *Parser::parseArguments() +{ // function call + Expressions *arguments; + Expression *arg; + enum TOK endtok; + + arguments = new Expressions(); + if (token.value == TOKlbracket) + endtok = TOKrbracket; + else + endtok = TOKrparen; + + { + nextToken(); + while (token.value != endtok && token.value != TOKeof) + { + arg = parseAssignExp(); + arguments->push(arg); + if (token.value == endtok) + break; + check(TOKcomma); + } + check(endtok); + } + return arguments; +} + +/******************************************* + */ + +Expression *Parser::parseNewExp(Expression *thisexp) +{ Type *t; + Expressions *newargs; + Expressions *arguments = NULL; + Expression *e; + Loc loc = this->loc; + + nextToken(); + newargs = NULL; + if (token.value == TOKlparen) + { + newargs = parseArguments(); + } + + // An anonymous nested class starts with "class" + if (token.value == TOKclass) + { + nextToken(); + if (token.value == TOKlparen) + arguments = parseArguments(); + + BaseClasses *baseclasses = NULL; + if (token.value != TOKlcurly) + baseclasses = parseBaseClasses(); + + Identifier *id = NULL; + ClassDeclaration *cd = new ClassDeclaration(loc, id, baseclasses); + + if (token.value != TOKlcurly) + { error("{ members } expected for anonymous class"); + cd->members = NULL; + } + else + { + nextToken(); + Dsymbols *decl = parseDeclDefs(0); + if (token.value != TOKrcurly) + error("class member expected"); + nextToken(); + cd->members = decl; + } + + e = new NewAnonClassExp(loc, thisexp, newargs, cd, arguments); + + return e; + } + + t = parseBasicType(); + t = parseBasicType2(t); + if (t->ty == Taarray) + { TypeAArray *taa = (TypeAArray *)t; + Type *index = taa->index; + + Expression *e = index->toExpression(); + if (e) + { arguments = new Expressions(); + arguments->push(e); + t = new TypeDArray(taa->next); + } + else + { + error("need size of rightmost array, not type %s", index->toChars()); + return new NullExp(loc); + } + } + else if (t->ty == Tsarray) + { + TypeSArray *tsa = (TypeSArray *)t; + Expression *e = tsa->dim; + + arguments = new Expressions(); + arguments->push(e); + t = new TypeDArray(tsa->next); + } + else if (token.value == TOKlparen) + { + arguments = parseArguments(); + } + e = new NewExp(loc, thisexp, newargs, t, arguments); + return e; +} + +/********************************************** + */ + +void Parser::addComment(Dsymbol *s, unsigned char *blockComment) +{ + s->addComment(combineComments(blockComment, token.lineComment)); + token.lineComment = NULL; +} + + +/********************************** + * Set operator precedence for each operator. + */ + +enum PREC precedence[TOKMAX]; + +void initPrecedence() +{ + for (size_t i = 0; i < TOKMAX; i++) + precedence[i] = PREC_zero; + + precedence[TOKtype] = PREC_expr; + precedence[TOKerror] = PREC_expr; + + precedence[TOKtypeof] = PREC_primary; + precedence[TOKmixin] = PREC_primary; + + precedence[TOKdotvar] = PREC_primary; + precedence[TOKimport] = PREC_primary; + precedence[TOKidentifier] = PREC_primary; + precedence[TOKthis] = PREC_primary; + precedence[TOKsuper] = PREC_primary; + precedence[TOKint64] = PREC_primary; + precedence[TOKfloat64] = PREC_primary; + precedence[TOKcomplex80] = PREC_primary; + precedence[TOKnull] = PREC_primary; + precedence[TOKstring] = PREC_primary; + precedence[TOKarrayliteral] = PREC_primary; + precedence[TOKassocarrayliteral] = PREC_primary; +#if DMDV2 + precedence[TOKfile] = PREC_primary; + precedence[TOKline] = PREC_primary; +#endif + precedence[TOKtypeid] = PREC_primary; + precedence[TOKis] = PREC_primary; + precedence[TOKassert] = PREC_primary; + precedence[TOKhalt] = PREC_primary; + precedence[TOKtemplate] = PREC_primary; + precedence[TOKdsymbol] = PREC_primary; + precedence[TOKfunction] = PREC_primary; + precedence[TOKvar] = PREC_primary; + precedence[TOKsymoff] = PREC_primary; + precedence[TOKstructliteral] = PREC_primary; + precedence[TOKarraylength] = PREC_primary; + precedence[TOKremove] = PREC_primary; + precedence[TOKtuple] = PREC_primary; +#if DMDV2 + precedence[TOKtraits] = PREC_primary; + precedence[TOKdefault] = PREC_primary; + precedence[TOKoverloadset] = PREC_primary; +#endif + + // post + precedence[TOKdotti] = PREC_primary; + precedence[TOKdot] = PREC_primary; + precedence[TOKdottd] = PREC_primary; + precedence[TOKdotexp] = PREC_primary; + precedence[TOKdottype] = PREC_primary; +// precedence[TOKarrow] = PREC_primary; + precedence[TOKplusplus] = PREC_primary; + precedence[TOKminusminus] = PREC_primary; +#if DMDV2 + precedence[TOKpreplusplus] = PREC_primary; + precedence[TOKpreminusminus] = PREC_primary; +#endif + precedence[TOKcall] = PREC_primary; + precedence[TOKslice] = PREC_primary; + precedence[TOKarray] = PREC_primary; + precedence[TOKindex] = PREC_primary; + + precedence[TOKdelegate] = PREC_unary; + precedence[TOKaddress] = PREC_unary; + precedence[TOKstar] = PREC_unary; + precedence[TOKneg] = PREC_unary; + precedence[TOKuadd] = PREC_unary; + precedence[TOKnot] = PREC_unary; + precedence[TOKtobool] = PREC_add; + precedence[TOKtilde] = PREC_unary; + precedence[TOKdelete] = PREC_unary; + precedence[TOKnew] = PREC_unary; + precedence[TOKnewanonclass] = PREC_unary; + precedence[TOKcast] = PREC_unary; + +#if DMDV2 + precedence[TOKvector] = PREC_unary; + precedence[TOKpow] = PREC_pow; +#endif + + precedence[TOKmul] = PREC_mul; + precedence[TOKdiv] = PREC_mul; + precedence[TOKmod] = PREC_mul; + + precedence[TOKadd] = PREC_add; + precedence[TOKmin] = PREC_add; + precedence[TOKcat] = PREC_add; + + precedence[TOKshl] = PREC_shift; + precedence[TOKshr] = PREC_shift; + precedence[TOKushr] = PREC_shift; + + precedence[TOKlt] = PREC_rel; + precedence[TOKle] = PREC_rel; + precedence[TOKgt] = PREC_rel; + precedence[TOKge] = PREC_rel; + precedence[TOKunord] = PREC_rel; + precedence[TOKlg] = PREC_rel; + precedence[TOKleg] = PREC_rel; + precedence[TOKule] = PREC_rel; + precedence[TOKul] = PREC_rel; + precedence[TOKuge] = PREC_rel; + precedence[TOKug] = PREC_rel; + precedence[TOKue] = PREC_rel; + precedence[TOKin] = PREC_rel; + +#if 0 + precedence[TOKequal] = PREC_equal; + precedence[TOKnotequal] = PREC_equal; + precedence[TOKidentity] = PREC_equal; + precedence[TOKnotidentity] = PREC_equal; +#else + /* Note that we changed precedence, so that < and != have the same + * precedence. This change is in the parser, too. + */ + precedence[TOKequal] = PREC_rel; + precedence[TOKnotequal] = PREC_rel; + precedence[TOKidentity] = PREC_rel; + precedence[TOKnotidentity] = PREC_rel; +#endif + + precedence[TOKand] = PREC_and; + + precedence[TOKxor] = PREC_xor; + + precedence[TOKor] = PREC_or; + + precedence[TOKandand] = PREC_andand; + + precedence[TOKoror] = PREC_oror; + + precedence[TOKquestion] = PREC_cond; + + precedence[TOKassign] = PREC_assign; + precedence[TOKconstruct] = PREC_assign; + precedence[TOKblit] = PREC_assign; + precedence[TOKaddass] = PREC_assign; + precedence[TOKminass] = PREC_assign; + precedence[TOKcatass] = PREC_assign; + precedence[TOKmulass] = PREC_assign; + precedence[TOKdivass] = PREC_assign; + precedence[TOKmodass] = PREC_assign; +#if DMDV2 + precedence[TOKpowass] = PREC_assign; +#endif + precedence[TOKshlass] = PREC_assign; + precedence[TOKshrass] = PREC_assign; + precedence[TOKushrass] = PREC_assign; + precedence[TOKandass] = PREC_assign; + precedence[TOKorass] = PREC_assign; + precedence[TOKxorass] = PREC_assign; + + precedence[TOKcomma] = PREC_expr; + precedence[TOKdeclaration] = PREC_expr; +} + + diff --git a/parse.h b/parse.h new file mode 100644 index 00000000..716e775a --- /dev/null +++ b/parse.h @@ -0,0 +1,187 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_PARSE_H +#define DMD_PARSE_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "arraytypes.h" +#include "lexer.h" +#include "enum.h" + +struct Type; +struct TypeQualified; +struct Expression; +struct Declaration; +struct Statement; +struct Import; +struct Initializer; +struct FuncDeclaration; +struct CtorDeclaration; +struct PostBlitDeclaration; +struct DtorDeclaration; +struct StaticCtorDeclaration; +struct StaticDtorDeclaration; +struct SharedStaticCtorDeclaration; +struct SharedStaticDtorDeclaration; +struct ConditionalDeclaration; +struct InvariantDeclaration; +struct UnitTestDeclaration; +struct NewDeclaration; +struct DeleteDeclaration; +struct Condition; +struct Module; +struct ModuleDeclaration; +struct TemplateDeclaration; +struct TemplateInstance; +struct StaticAssert; + +/************************************ + * These control how parseStatement() works. + */ + +enum ParseStatementFlags +{ + PSsemi = 1, // empty ';' statements are allowed, but deprecated + PSscope = 2, // start a new scope + PScurly = 4, // { } statement is required + PScurlyscope = 8, // { } starts a new scope + PSsemi_ok = 0x10, // empty ';' are really ok +}; + + +struct Parser : Lexer +{ + ModuleDeclaration *md; + enum LINK linkage; + Loc endloc; // set to location of last right curly + int inBrackets; // inside [] of array index or slice + Loc lookingForElse; // location of lonely if looking for an else + + Parser(Module *module, unsigned char *base, unsigned length, int doDocComment); + + Dsymbols *parseModule(); + Dsymbols *parseDeclDefs(int once); + Dsymbols *parseAutoDeclarations(StorageClass storageClass, unsigned char *comment); + Dsymbols *parseBlock(); + void composeStorageClass(StorageClass stc); + StorageClass parseAttribute(); + StorageClass parsePostfix(); + Expression *parseConstraint(); + TemplateDeclaration *parseTemplateDeclaration(int ismixin); + TemplateParameters *parseTemplateParameterList(int flag = 0); + Dsymbol *parseMixin(); + Objects *parseTemplateArgumentList(); + Objects *parseTemplateArgumentList2(); + Objects *parseTemplateArgument(); + StaticAssert *parseStaticAssert(); + TypeQualified *parseTypeof(); + Type *parseVector(); + enum LINK parseLinkage(); + Condition *parseDebugCondition(); + Condition *parseVersionCondition(); + Condition *parseStaticIfCondition(); + Dsymbol *parseCtor(); + PostBlitDeclaration *parsePostBlit(); + DtorDeclaration *parseDtor(); + StaticCtorDeclaration *parseStaticCtor(); + StaticDtorDeclaration *parseStaticDtor(); + SharedStaticCtorDeclaration *parseSharedStaticCtor(); + SharedStaticDtorDeclaration *parseSharedStaticDtor(); + InvariantDeclaration *parseInvariant(); + UnitTestDeclaration *parseUnitTest(); + NewDeclaration *parseNew(); + DeleteDeclaration *parseDelete(); + Parameters *parseParameters(int *pvarargs, TemplateParameters **tpl = NULL); + EnumDeclaration *parseEnum(); + Dsymbol *parseAggregate(); + BaseClasses *parseBaseClasses(); + Import *parseImport(Dsymbols *decldefs, int isstatic); + Type *parseType(Identifier **pident = NULL, TemplateParameters **tpl = NULL); + Type *parseBasicType(); + Type *parseBasicType2(Type *t); + Type *parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl = NULL, StorageClass storage_class = 0, int* pdisable = NULL); + Dsymbols *parseDeclarations(StorageClass storage_class, unsigned char *comment); + void parseContracts(FuncDeclaration *f); + void checkDanglingElse(Loc elseloc); + Statement *parseStatement(int flags); + Initializer *parseInitializer(); + Expression *parseDefaultInitExp(); + void check(Loc loc, enum TOK value); + void check(enum TOK value); + void check(enum TOK value, const char *string); + void checkParens(enum TOK value, Expression *e); + int isDeclaration(Token *t, int needId, enum TOK endtok, Token **pt); + int isBasicType(Token **pt); + int isDeclarator(Token **pt, int *haveId, enum TOK endtok); + int isParameters(Token **pt); + int isExpression(Token **pt); + int isTemplateInstance(Token *t, Token **pt); + int skipParens(Token *t, Token **pt); + int skipAttributes(Token *t, Token **pt); + + Expression *parseExpression(); + Expression *parsePrimaryExp(); + Expression *parseUnaryExp(); + Expression *parsePostExp(Expression *e); + Expression *parseMulExp(); + Expression *parseAddExp(); + Expression *parseShiftExp(); +#if DMDV1 + Expression *parseRelExp(); + Expression *parseEqualExp(); +#endif + Expression *parseCmpExp(); + Expression *parseAndExp(); + Expression *parseXorExp(); + Expression *parseOrExp(); + Expression *parseAndAndExp(); + Expression *parseOrOrExp(); + Expression *parseCondExp(); + Expression *parseAssignExp(); + + Expressions *parseArguments(); + + Expression *parseNewExp(Expression *thisexp); + + void addComment(Dsymbol *s, unsigned char *blockComment); +}; + +// Operator precedence - greater values are higher precedence + +enum PREC +{ + PREC_zero, + PREC_expr, + PREC_assign, + PREC_cond, + PREC_oror, + PREC_andand, + PREC_or, + PREC_xor, + PREC_and, + PREC_equal, + PREC_rel, + PREC_shift, + PREC_add, + PREC_mul, + PREC_pow, + PREC_unary, + PREC_primary, +}; + +extern enum PREC precedence[TOKMAX]; + +void initPrecedence(); + +#endif /* DMD_PARSE_H */ diff --git a/ph.c b/ph.c new file mode 100644 index 00000000..2cfb8fef --- /dev/null +++ b/ph.c @@ -0,0 +1,346 @@ + + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include + +#if __DMC__ +#include +#else +#include +#endif + +#include "cc.h" +#include "global.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +/********************************************** + * Do our own storage allocator, a replacement + * for malloc/free. + */ + +struct Heap +{ + Heap *prev; // previous heap + unsigned char *buf; // buffer + unsigned char *p; // high water mark + unsigned nleft; // number of bytes left +}; + +Heap *heap=NULL; + +void ph_init() +{ + if (!heap) { + heap = (Heap *)calloc(1,sizeof(Heap)); + } + assert(heap); +} + + + +void ph_term() +{ + //printf("ph_term()\n"); +#if _WINDLL || DEBUG + Heap *h; + Heap *hprev; + + for (h = heap; h; h = hprev) + { + hprev = h->prev; + free(h->buf); + free(h); + } +#endif +} + +void ph_newheap(size_t nbytes) +{ unsigned newsize; + Heap *h; + + h = (Heap *) malloc(sizeof(Heap)); + if (!h) + err_nomem(); + + newsize = (nbytes > 0xFF00) ? nbytes : 0xFF00; + h->buf = (unsigned char *) malloc(newsize); + if (!h->buf) + { + free(h); + err_nomem(); + } + h->nleft = newsize; + h->p = h->buf; + h->prev = heap; + heap = h; +} + +void *ph_malloc(size_t nbytes) +{ unsigned char *p; + +#ifdef DEBUG + util_progress(); +#endif + nbytes += sizeof(unsigned) * 2; + nbytes &= ~(sizeof(unsigned) - 1); + + if (nbytes >= heap->nleft) + ph_newheap(nbytes); + p = heap->p; + heap->p += nbytes; + heap->nleft -= nbytes; + *(unsigned *)p = nbytes - sizeof(unsigned); + p += sizeof(unsigned); + return p; +} + +#if ASM86 +__declspec(naked) void *ph_calloc(size_t nbytes) +{ + _asm + { + push dword ptr 4[ESP] + call ph_malloc + test EAX,EAX + je L25 + push dword ptr 4[ESP] + push 0 + push EAX + call memset + add ESP,0Ch +L25: ret 4 + } +} +#else +void *ph_calloc(size_t nbytes) +{ void *p; + + p = ph_malloc(nbytes); + return p ? memset(p,0,nbytes) : p; +} +#endif + +void ph_free(void *p) +{ +} + +void *ph_realloc(void *p,size_t nbytes) +{ + //dbg_printf("ph_realloc(%p,%d)\n",p,nbytes); + if (!p) + return ph_malloc(nbytes); + if (!nbytes) + { ph_free(p); + return NULL; + } + void *newp = ph_malloc(nbytes); + if (newp) + { unsigned oldsize = ((unsigned *)p)[-1]; + memcpy(newp,p,oldsize); + ph_free(p); + } + return newp; +} + +void err_nomem() +{ + printf("Error: out of memory\n"); + err_exit(); +} + +#if !MEM_DEBUG + + +/*********************** + * Replacement for the standard C++ library operator delete(). + */ + +#if 0 +#undef delete +void __cdecl operator delete(void *p) +{ +} +#endif + +#if 0 + +/***************************************** + * Using this for array allocations gives + * us an easy way to get at the array dimension. + * Overloading operator new[]() doesn't work because that + * gives us the array allocated size, but we need the dimension. + */ + +#ifdef DEBUG +#define ARRAY_PROLOG 'prol' +#define ARRAY_EPILOG 'epil' +#define ARRAY_FILL 'f' +static int array_max_dim; + +/********************************* + * Run "reasonableness" checks on array. + */ + +void array_debug(void *a) +{ size_t *p = (size_t *)a; + + assert(p); + assert(p[-2] == ARRAY_PROLOG); + int length = p[-1]; + assert(length >= 0 && length <= array_max_dim); + assert(p[length] == ARRAY_EPILOG); + + // Since array contents are aligned pointers or NULL... + for (int i = 0; i < length; i++) + assert((p[i] & 3) == 0); +} + +#endif + +#undef array_new +void *array_new(int sizelem, int dim) +{ size_t *p; + size_t sz; + +#ifdef DEBUG + assert(sizelem == sizeof(void *)); // must be array of pointers + if (!(dim >= 0 && dim < 10000)) + printf("dim = %d\n",dim); + assert(dim >= 0 && dim < 10000); + if (dim > array_max_dim) + array_max_dim = dim; + + sz = sizeof(size_t) * (3 + dim); + p = ph_calloc(sz); + if (p) + { p[0] = ARRAY_PROLOG; // leading sentinel + p[1] = dim; + p[2 + dim] = ARRAY_EPILOG; // trailing sentinel + p += 2; + array_debug(p); + } +#else + + sz = sizeof(size_t) * (1 + dim); + p = ph_calloc(sz); + if (p) + *p++ = dim; +#endif + return (void *)p; +} + +#undef array_delete +void array_delete(void *a, int sizelem) +{ + size_t *p = (size_t *)a; +#ifdef DEBUG + + array_debug(p); + assert(sizelem == sizeof(size_t)); + memset(p - 2,ARRAY_FILL,sizeof(size_t *) * (3 + p[-1])); + ph_free(p - 2); +#else + ((size_t *)p)--; + ph_free(p); +#endif +} + +size_t array_length(void *p) +{ + array_debug(p); + return ((size_t *)p)[-1]; +} + +/******************************** + * Same as System.arraycopy() + */ + +void array_copy(void *f,int fi,void *t,int ti,int length) +{ +#ifdef DEBUG + assert(length >= 0 && length <= array_max_dim); + int f_length = array_length(f); + int t_length = array_length(t); + assert(fi >= 0 && fi + length <= f_length); + assert(ti >= 0 && ti + length <= t_length); +#endif + memcpy(&((void **)t)[ti],&((void **)f)[fi],length * sizeof(void *)); +} + +/************************************ + * Reallocate. + */ + +#undef array_renew +void **array_renew(void *a,int newlength) +{ int sz = sizeof(void *); + int hsz = sizeof(void *); + + if (!a) + a = array_new(sz,newlength); + else + { + int oldlength = array_length(a); +#ifdef DEBUG + void *b = array_new(sizeof(void *),newlength); + int len = (oldlength < newlength) ? oldlength : newlength; + array_copy(a,0,b,0,len); + array_delete(a,sizeof(void *)); + a = b; +#else + if (oldlength < newlength) + { + (char *)a -= hsz; + a = ph_realloc(a,hsz + newlength * sz); + if (!a) + goto Lret; + (char *)a += hsz; + memset(&((void **)a)[oldlength],0,(newlength - oldlength) * sz); + } + else if (oldlength > newlength) + { + ; + } + ((size_t *)a)[-1] = newlength; +#endif + } +Lret: + return a; +} + +/****************************************** + * Sort an array. + */ + +#if MACINTOSH +extern "C" int acompare(const void *e1,const void *e2) +#else +int __cdecl acompare(const void *e1,const void *e2) +#endif +{ + Object *o1 = *(Object **)e1; + Object *o2 = *(Object **)e2; + + return o1->compare(o2); +} + +void array_sort(void *a) +{ + qsort(a,array_length(a),sizeof(void *),acompare); +} + +#endif +#endif diff --git a/posix.mak b/posix.mak new file mode 100644 index 00000000..b35596eb --- /dev/null +++ b/posix.mak @@ -0,0 +1,674 @@ +# NOTE: need to validate solaris behavior +ifeq (,$(TARGET)) + OS:=$(shell uname) + OSVER:=$(shell uname -r) + ifeq (Darwin,$(OS)) + TARGET=OSX + else + ifeq (Linux,$(OS)) + TARGET=LINUX + else + ifeq (FreeBSD,$(OS)) + TARGET=FREEBSD + else + ifeq (OpenBSD,$(OS)) + TARGET=OPENBSD + else + ifeq (Solaris,$(OS)) + TARGET=SOLARIS + else + $(error Unrecognized or unsupported OS for uname: $(OS)) + endif + endif + endif + endif + endif +endif + +C=backend +TK=tk +ROOT=root + +MODEL=32 + +ifeq (OSX,$(TARGET)) + ## See: http://developer.apple.com/documentation/developertools/conceptual/cross_development/Using/chapter_3_section_2.html#//apple_ref/doc/uid/20002000-1114311-BABGCAAB + ENVP= MACOSX_DEPLOYMENT_TARGET=10.3 + #SDK=/Developer/SDKs/MacOSX10.4u.sdk #doesn't work because can't find + #SDK=/Developer/SDKs/MacOSX10.5.sdk + #SDK=/Developer/SDKs/MacOSX10.6.sdk + SDK:=$(if $(filter 11.%, $(OSVER)), /Developer/SDKs/MacOSX10.6.sdk, /Developer/SDKs/MacOSX10.5.sdk) + TARGET_CFLAGS=-isysroot ${SDK} + #-syslibroot is only passed to libtool, not ld. + #if gcc sees -isysroot it should pass -syslibroot to the linker when needed + #LDFLAGS=-lstdc++ -isysroot ${SDK} -Wl,-syslibroot,${SDK} -framework CoreServices + LDFLAGS=-lstdc++ -isysroot ${SDK} -Wl -framework CoreServices +else + LDFLAGS=-lm -lstdc++ -lpthread +endif + +HOST_CC=g++ +CC=$(HOST_CC) -m$(MODEL) $(TARGET_CFLAGS) + +#OPT=-g -g3 +#OPT=-O2 + +#COV=-fprofile-arcs -ftest-coverage + +WARNINGS=-Wno-deprecated -Wstrict-aliasing + +#GFLAGS = $(WARNINGS) -D__near= -D__pascal= -fno-exceptions -g -DDEBUG=1 -DUNITTEST $(COV) +GFLAGS = $(WARNINGS) -D__near= -D__pascal= -fno-exceptions -O2 + +CFLAGS = $(GFLAGS) -I$(ROOT) -DMARS=1 -DTARGET_$(TARGET)=1 +MFLAGS = $(GFLAGS) -I$C -I$(TK) -DMARS=1 -DTARGET_$(TARGET)=1 + +CH= $C/cc.h $C/global.h $C/oper.h $C/code.h $C/type.h \ + $C/dt.h $C/cgcv.h $C/el.h $C/iasm.h + +DMD_OBJS = \ + access.o array.o attrib.o bcomplex.o blockopt.o \ + cast.o code.o cg.o cg87.o cgxmm.o cgcod.o cgcs.o cgelem.o cgen.o \ + cgreg.o cgsched.o class.o cod1.o cod2.o cod3.o cod4.o cod5.o \ + constfold.o irstate.o dchar.o cond.o debug.o \ + declaration.o dsymbol.o dt.o dump.o e2ir.o ee.o eh.o el.o \ + dwarf.o enum.o evalu8.o expression.o func.o gdag.o gflow.o \ + glocal.o gloop.o glue.o gnuc.o go.o gother.o html.o iasm.o id.o \ + identifier.o impcnvtab.o import.o inifile.o init.o inline.o \ + lexer.o link.o lstring.o mangle.o mars.o rmem.o module.o msc.o mtype.o \ + nteh.o cppmangle.o opover.o optimize.o os.o out.o outbuf.o \ + parse.o ph.o ptrntab.o root.o rtlsym.o s2ir.o scope.o statement.o \ + stringtable.o struct.o csymbol.o template.o tk.o tocsym.o todt.o \ + type.o typinf.o util.o var.o version.o strtold.o utf.o staticassert.o \ + unialpha.o toobj.o toctype.o toelfdebug.o entity.o doc.o macro.o \ + hdrgen.o delegatize.o aa.o ti_achar.o toir.o interpret.o traits.o \ + builtin.o clone.o aliasthis.o intrange.o \ + man.o arrayop.o port.o response.o async.o json.o speller.o aav.o unittests.o \ + imphint.o argtypes.o ti_pvoid.o apply.o canthrow.o sideeffect.o + +ifeq (OSX,$(TARGET)) + DMD_OBJS += libmach.o machobj.o +else + DMD_OBJS += libelf.o elfobj.o +endif + +SRC = win32.mak posix.mak \ + mars.c enum.c struct.c dsymbol.c import.c idgen.c impcnvgen.c \ + identifier.c mtype.c expression.c optimize.c template.h \ + template.c lexer.c declaration.c cast.c cond.h cond.c link.c \ + aggregate.h parse.c statement.c constfold.c version.h version.c \ + inifile.c iasm.c module.c scope.c dump.c init.h init.c attrib.h \ + attrib.c opover.c class.c mangle.c tocsym.c func.c inline.c \ + access.c complex_t.h irstate.h irstate.c glue.c msc.c ph.c tk.c \ + s2ir.c todt.c e2ir.c util.c identifier.h parse.h intrange.h \ + scope.h enum.h import.h mars.h module.h mtype.h dsymbol.h \ + declaration.h lexer.h expression.h irstate.h statement.h eh.c \ + utf.h utf.c staticassert.h staticassert.c unialpha.c \ + typinf.c toobj.c toctype.c tocvdebug.c toelfdebug.c entity.c \ + doc.h doc.c macro.h macro.c hdrgen.h hdrgen.c arraytypes.h \ + delegatize.c toir.h toir.c interpret.c traits.c cppmangle.c \ + builtin.c clone.c lib.h libomf.c libelf.c libmach.c arrayop.c \ + aliasthis.h aliasthis.c json.h json.c unittests.c imphint.c \ + argtypes.c intrange.c apply.c canthrow.c sideeffect.c \ + $C/cdef.h $C/cc.h $C/oper.h $C/ty.h $C/optabgen.c \ + $C/global.h $C/code.h $C/type.h $C/dt.h $C/cgcv.h \ + $C/el.h $C/iasm.h $C/rtlsym.h $C/html.h \ + $C/bcomplex.c $C/blockopt.c $C/cg.c $C/cg87.c $C/cgxmm.c \ + $C/cgcod.c $C/cgcs.c $C/cgcv.c $C/cgelem.c $C/cgen.c $C/cgobj.c \ + $C/cgreg.c $C/var.c $C/strtold.c \ + $C/cgsched.c $C/cod1.c $C/cod2.c $C/cod3.c $C/cod4.c $C/cod5.c \ + $C/code.c $C/symbol.c $C/debug.c $C/dt.c $C/ee.c $C/el.c \ + $C/evalu8.c $C/go.c $C/gflow.c $C/gdag.c \ + $C/gother.c $C/glocal.c $C/gloop.c $C/html.c $C/newman.c \ + $C/nteh.c $C/os.c $C/out.c $C/outbuf.c $C/ptrntab.c $C/rtlsym.c \ + $C/type.c $C/melf.h $C/mach.h $C/bcomplex.h \ + $C/cdeflnx.h $C/outbuf.h $C/token.h $C/tassert.h \ + $C/elfobj.c $C/cv4.h $C/dwarf2.h $C/exh.h $C/go.h \ + $C/dwarf.c $C/dwarf.h $C/aa.h $C/aa.c $C/tinfo.h $C/ti_achar.c \ + $C/ti_pvoid.c \ + $C/machobj.c \ + $C/xmm.h \ + $(TK)/filespec.h $(TK)/mem.h $(TK)/list.h $(TK)/vec.h \ + $(TK)/filespec.c $(TK)/mem.c $(TK)/vec.c $(TK)/list.c \ + $(ROOT)/dchar.h $(ROOT)/dchar.c $(ROOT)/lstring.h \ + $(ROOT)/lstring.c $(ROOT)/root.h $(ROOT)/root.c $(ROOT)/array.c \ + $(ROOT)/rmem.h $(ROOT)/rmem.c $(ROOT)/port.h $(ROOT)/port.c \ + $(ROOT)/gnuc.h $(ROOT)/gnuc.c $(ROOT)/man.c \ + $(ROOT)/stringtable.h $(ROOT)/stringtable.c \ + $(ROOT)/response.c $(ROOT)/async.h $(ROOT)/async.c \ + $(ROOT)/aav.h $(ROOT)/aav.c \ + $(ROOT)/speller.h $(ROOT)/speller.c + + +all: dmd + +dmd: $(DMD_OBJS) + $(ENVP) $(HOST_CC) -o dmd -m$(MODEL) $(COV) $(DMD_OBJS) $(LDFLAGS) + +clean: + rm -f $(DMD_OBJS) dmd optab.o id.o impcnvgen idgen id.c id.h \ + impcnvtab.c optabgen debtab.c optab.c cdxxx.c elxxx.c fltables.c \ + tytab.c core \ + *.cov *.gcda *.gcno + +######## optabgen generates some source + +optabgen: $C/optabgen.c $C/cc.h $C/oper.h + $(ENVP) $(CC) $(MFLAGS) $< -o optabgen + ./optabgen + +optabgen_output = debtab.c optab.c cdxxx.c elxxx.c fltables.c tytab.c +$(optabgen_output) : optabgen + +######## idgen generates some source + +idgen_output = id.h id.c +$(idgen_output) : idgen + +idgen : idgen.c + $(ENVP) $(CC) idgen.c -o idgen + ./idgen + +######### impcnvgen generates some source + +impcnvtab_output = impcnvtab.c +$(impcnvtab_output) : impcnvgen + +impcnvgen : mtype.h impcnvgen.c + $(ENVP) $(CC) $(CFLAGS) impcnvgen.c -o impcnvgen + ./impcnvgen + +######### + +$(DMD_OBJS) : $(idgen_output) $(optabgen_output) $(impcnvgen_output) + +aa.o: $C/aa.c $C/aa.h $C/tinfo.h + $(CC) -c $(MFLAGS) -I. $< + +aav.o: $(ROOT)/aav.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +access.o: access.c + $(CC) -c $(CFLAGS) $< + +aliasthis.o: aliasthis.c + $(CC) -c $(CFLAGS) $< + +apply.o: apply.c + $(CC) -c $(CFLAGS) $< + +argtypes.o: argtypes.c + $(CC) -c $(CFLAGS) $< + +array.o: $(ROOT)/array.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +arrayop.o: arrayop.c + $(CC) -c $(CFLAGS) $< + +async.o: $(ROOT)/async.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +attrib.o: attrib.c + $(CC) -c $(CFLAGS) $< + +bcomplex.o: $C/bcomplex.c + $(CC) -c $(MFLAGS) $< + +blockopt.o: $C/blockopt.c + $(CC) -c $(MFLAGS) $< + +builtin.o: builtin.c + $(CC) -c $(CFLAGS) $< + +canthrow.o: canthrow.c + $(CC) -c $(CFLAGS) $< + +cast.o: cast.c + $(CC) -c $(CFLAGS) $< + +cg.o: $C/cg.c fltables.c + $(CC) -c $(MFLAGS) -I. $< + +cg87.o: $C/cg87.c + $(CC) -c $(MFLAGS) $< + +cgcod.o: $C/cgcod.c + $(CC) -c $(MFLAGS) -I. $< + +cgcs.o: $C/cgcs.c + $(CC) -c $(MFLAGS) $< + +cgcv.o: $C/cgcv.c + $(CC) -c $(MFLAGS) $< + +cgelem.o: $C/cgelem.c $C/rtlsym.h + $(CC) -c $(MFLAGS) -I. $< + +cgen.o: $C/cgen.c $C/rtlsym.h + $(CC) -c $(MFLAGS) $< + +cgobj.o: $C/cgobj.c + $(CC) -c $(MFLAGS) $< + +cgreg.o: $C/cgreg.c + $(CC) -c $(MFLAGS) $< + +cgsched.o: $C/cgsched.c $C/rtlsym.h + $(CC) -c $(MFLAGS) $< + +cgxmm.o: $C/cgxmm.c + $(CC) -c $(MFLAGS) $< + +class.o: class.c + $(CC) -c $(CFLAGS) $< + +clone.o: clone.c + $(CC) -c $(CFLAGS) $< + +cod1.o: $C/cod1.c $C/rtlsym.h + $(CC) -c $(MFLAGS) $< + +cod2.o: $C/cod2.c $C/rtlsym.h + $(CC) -c $(MFLAGS) $< + +cod3.o: $C/cod3.c $C/rtlsym.h + $(CC) -c $(MFLAGS) $< + +cod4.o: $C/cod4.c + $(CC) -c $(MFLAGS) $< + +cod5.o: $C/cod5.c + $(CC) -c $(MFLAGS) $< + +code.o: $C/code.c + $(CC) -c $(MFLAGS) $< + +constfold.o: constfold.c + $(CC) -c $(CFLAGS) $< + +irstate.o: irstate.c irstate.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +csymbol.o: $C/symbol.c + $(CC) -c $(MFLAGS) $< -o $@ + +dchar.o: $(ROOT)/dchar.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +cond.o: cond.c + $(CC) -c $(CFLAGS) $< + +cppmangle.o: cppmangle.c + $(CC) -c $(CFLAGS) $< + +debug.o: $C/debug.c + $(CC) -c $(MFLAGS) -I. $< + +declaration.o: declaration.c + $(CC) -c $(CFLAGS) $< + +delegatize.o: delegatize.c + $(CC) -c $(CFLAGS) $< + +doc.o: doc.c + $(CC) -c $(CFLAGS) $< + +dsymbol.o: dsymbol.c + $(CC) -c $(CFLAGS) $< + +dt.o: $C/dt.c $C/dt.h + $(CC) -c $(MFLAGS) $< + +dump.o: dump.c + $(CC) -c $(CFLAGS) $< + +dwarf.o: $C/dwarf.c $C/dwarf.h + $(CC) -c $(MFLAGS) -I. $< + +e2ir.o: e2ir.c $C/rtlsym.h expression.h toir.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +ee.o: $C/ee.c + $(CC) -c $(MFLAGS) $< + +eh.o: eh.c $C/cc.h $C/code.h $C/type.h $C/dt.h + $(CC) -c $(MFLAGS) $< + +el.o: $C/el.c $C/rtlsym.h $C/el.h + $(CC) -c $(MFLAGS) $< + +elfobj.o: $C/elfobj.c + $(CC) -c $(MFLAGS) $< + +entity.o: entity.c + $(CC) -c $(CFLAGS) $< + +enum.o: enum.c + $(CC) -c $(CFLAGS) $< + +evalu8.o: $C/evalu8.c + $(CC) -c $(MFLAGS) $< + +expression.o: expression.c expression.h + $(CC) -c $(CFLAGS) $< + +func.o: func.c + $(CC) -c $(CFLAGS) $< + +gdag.o: $C/gdag.c + $(CC) -c $(MFLAGS) $< + +gflow.o: $C/gflow.c + $(CC) -c $(MFLAGS) $< + +#globals.o: globals.c +# $(CC) -c $(CFLAGS) $< + +glocal.o: $C/glocal.c $C/rtlsym.h + $(CC) -c $(MFLAGS) $< + +gloop.o: $C/gloop.c + $(CC) -c $(MFLAGS) $< + +glue.o: glue.c $(CH) $C/rtlsym.h mars.h module.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +gnuc.o: $(ROOT)/gnuc.c $(ROOT)/gnuc.h + $(CC) -c $(GFLAGS) $< + +go.o: $C/go.c + $(CC) -c $(MFLAGS) $< + +gother.o: $C/gother.c + $(CC) -c $(MFLAGS) $< + +hdrgen.o: hdrgen.c + $(CC) -c $(CFLAGS) $< + +html.o: $C/html.c $(CH) $C/html.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +iasm.o: iasm.c $(CH) $C/iasm.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +id.o: id.c id.h + $(CC) -c $(CFLAGS) $< + +identifier.o: identifier.c + $(CC) -c $(CFLAGS) $< + +impcnvtab.o: impcnvtab.c mtype.h + $(CC) -c $(CFLAGS) -I$(ROOT) $< + +imphint.o: imphint.c + $(CC) -c $(CFLAGS) $< + +import.o: import.c + $(CC) -c $(CFLAGS) $< + +inifile.o: inifile.c + $(CC) -c $(CFLAGS) $< + +init.o: init.c + $(CC) -c $(CFLAGS) $< + +inline.o: inline.c + $(CC) -c $(CFLAGS) $< + +interpret.o: interpret.c + $(CC) -c $(CFLAGS) $< + +intrange.o: intrange.h intrange.c + $(CC) -c $(CFLAGS) intrange.c + +json.o: json.c + $(CC) -c $(CFLAGS) $< + +lexer.o: lexer.c + $(CC) -c $(CFLAGS) $< + +libelf.o: libelf.c $C/melf.h + $(CC) -c $(CFLAGS) -I$C $< + +libmach.o: libmach.c $C/mach.h + $(CC) -c $(CFLAGS) -I$C $< + +link.o: link.c + $(CC) -c $(CFLAGS) $< + +lstring.o: $(ROOT)/lstring.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +machobj.o: $C/machobj.c + $(CC) -c $(MFLAGS) -I. $< + +macro.o: macro.c + $(CC) -c $(CFLAGS) $< + +man.o: $(ROOT)/man.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +mangle.o: mangle.c + $(CC) -c $(CFLAGS) $< + +mars.o: mars.c + $(CC) -c $(CFLAGS) $< + +rmem.o: $(ROOT)/rmem.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +module.o: module.c $C/html.h + $(CC) -c $(CFLAGS) -I$C $< + +msc.o: msc.c $(CH) mars.h + $(CC) -c $(MFLAGS) $< + +mtype.o: mtype.c + $(CC) -c $(CFLAGS) $< + +nteh.o: $C/nteh.c $C/rtlsym.h + $(CC) -c $(MFLAGS) $< + +opover.o: opover.c + $(CC) -c $(CFLAGS) $< + +optimize.o: optimize.c + $(CC) -c $(CFLAGS) $< + +os.o: $C/os.c + $(CC) -c $(MFLAGS) $< + +out.o: $C/out.c + $(CC) -c $(MFLAGS) $< + +outbuf.o: $C/outbuf.c $C/outbuf.h + $(CC) -c $(MFLAGS) $< + +parse.o: parse.c + $(CC) -c $(CFLAGS) $< + +ph.o: ph.c + $(CC) -c $(MFLAGS) $< + +port.o: $(ROOT)/port.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +ptrntab.o: $C/ptrntab.c $C/iasm.h + $(CC) -c $(MFLAGS) $< + +response.o: $(ROOT)/response.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +root.o: $(ROOT)/root.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +rtlsym.o: $C/rtlsym.c $C/rtlsym.h + $(CC) -c $(MFLAGS) $< + +s2ir.o: s2ir.c $C/rtlsym.h statement.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +scope.o: scope.c + $(CC) -c $(CFLAGS) $< + +sideeffect.o: sideeffect.c + $(CC) -c $(CFLAGS) $< + +speller.o: $(ROOT)/speller.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +statement.o: statement.c + $(CC) -c $(CFLAGS) $< + +staticassert.o: staticassert.c staticassert.h + $(CC) -c $(CFLAGS) $< + +stringtable.o: $(ROOT)/stringtable.c + $(CC) -c $(GFLAGS) -I$(ROOT) $< + +strtold.o: $C/strtold.c + gcc -m$(MODEL) -c $< + +struct.o: struct.c + $(CC) -c $(CFLAGS) $< + +template.o: template.c + $(CC) -c $(CFLAGS) $< + +ti_achar.o: $C/ti_achar.c $C/tinfo.h + $(CC) -c $(MFLAGS) -I. $< + +ti_pvoid.o: $C/ti_pvoid.c $C/tinfo.h + $(CC) -c $(MFLAGS) -I. $< + +tk.o: tk.c + $(CC) -c $(MFLAGS) $< + +tocsym.o: tocsym.c $(CH) mars.h module.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +toctype.o: toctype.c $(CH) $C/rtlsym.h mars.h module.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +todt.o: todt.c mtype.h expression.h $C/dt.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +toelfdebug.o: toelfdebug.c $(CH) mars.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +toir.o: toir.c $C/rtlsym.h expression.h toir.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +toobj.o: toobj.c $(CH) mars.h module.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +traits.o: traits.c + $(CC) -c $(CFLAGS) $< + +type.o: $C/type.c + $(CC) -c $(MFLAGS) $< + +typinf.o: typinf.c $(CH) mars.h module.h mtype.h + $(CC) -c $(MFLAGS) -I$(ROOT) $< + +util.o: util.c + $(CC) -c $(MFLAGS) $< + +utf.o: utf.c utf.h + $(CC) -c $(CFLAGS) $< + +unialpha.o: unialpha.c + $(CC) -c $(CFLAGS) $< + +unittests.o: unittests.c + $(CC) -c $(CFLAGS) $< + +var.o: $C/var.c optab.c + $(CC) -c $(MFLAGS) -I. $< + +version.o: version.c + $(CC) -c $(CFLAGS) $< + +###################################################### + +gcov: + gcov access.c + gcov aliasthis.c + gcov apply.c + gcov arrayop.c + gcov attrib.c + gcov builtin.c + gcov canthrow.c + gcov cast.c + gcov class.c + gcov clone.c + gcov cond.c + gcov constfold.c + gcov declaration.c + gcov delegatize.c + gcov doc.c + gcov dsymbol.c + gcov dump.c + gcov e2ir.c + gcov eh.c + gcov entity.c + gcov enum.c + gcov expression.c + gcov func.c + gcov glue.c + gcov iasm.c + gcov identifier.c + gcov imphint.c + gcov import.c + gcov inifile.c + gcov init.c + gcov inline.c + gcov interpret.c + gcov irstate.c + gcov json.c + gcov lexer.c +ifeq (OSX,$(TARGET)) + gcov libmach.c +else + gcov libelf.c +endif + gcov link.c + gcov macro.c + gcov mangle.c + gcov mars.c + gcov module.c + gcov msc.c + gcov mtype.c + gcov opover.c + gcov optimize.c + gcov parse.c + gcov ph.c + gcov scope.c + gcov sideeffect.c + gcov statement.c + gcov staticassert.c + gcov s2ir.c + gcov struct.c + gcov template.c + gcov tk.c + gcov tocsym.c + gcov todt.c + gcov toobj.c + gcov toctype.c + gcov toelfdebug.c + gcov typinf.c + gcov unialpha.c + gcov utf.c + gcov util.c + gcov version.c + gcov intrange.c + +# gcov hdrgen.c +# gcov tocvdebug.c + +###################################################### + +zip: + -rm -f dmdsrc.zip + zip dmdsrc $(SRC) diff --git a/readme.txt b/readme.txt new file mode 100644 index 00000000..e8109070 --- /dev/null +++ b/readme.txt @@ -0,0 +1,24 @@ + + The D Programming Language + Compiler Front End Source + Copyright (c) 1999-2009, by Digital Mars + http://www.digitalmars.com + All Rights Reserved + + +This is the source code to the front end Digital Mars D compiler. +It covers the lexical analysis, parsing, and semantic analysis +of the D Programming Language defined in the documents at +http://www.digitalmars.com/d/ + +These sources are free, they are redistributable and modifiable +under the terms of the GNU General Public License (attached as gpl.txt), +or the Artistic License (attached as artistic.txt). + +The optimizer and code generator sources are +covered under a separate license, backendlicense.txt. + +It does not apply to anything else distributed by Digital Mars, +including D compiler executables. + +-Walter Bright diff --git a/root/aav.c b/root/aav.c new file mode 100644 index 00000000..ca685d42 --- /dev/null +++ b/root/aav.c @@ -0,0 +1,188 @@ +/** + * Implementation of associative arrays. + * + */ + +#include +#include +#include + +#include "aav.h" + +static const size_t prime_list[] = { + 31UL, + 97UL, 389UL, + 1543UL, 6151UL, + 24593UL, 98317UL, + 393241UL, 1572869UL, + 6291469UL, 25165843UL, + 100663319UL, 402653189UL, + 1610612741UL, 4294967291UL, +}; + +struct aaA +{ + aaA *next; + Key key; + Value value; +}; + +struct AA +{ + aaA* *b; + size_t b_length; + size_t nodes; // total number of aaA nodes + aaA* binit[4]; // initial value of b[] +}; + +static const AA bbinit = { NULL, }; + +/**************************************************** + * Determine number of entries in associative array. + */ + +size_t _aaLen(AA* aa) +{ + return aa ? aa->nodes : 0; +} + + +/************************************************* + * Get pointer to value in associative array indexed by key. + * Add entry for key if it is not already there. + */ + +Value* _aaGet(AA** paa, Key key) +{ + //printf("paa = %p\n", paa); + + if (!*paa) + { AA *a = new AA(); + *a = bbinit; + a->b = a->binit; + a->b_length = sizeof(a->binit) / sizeof(a->binit[0]); + *paa = a; + assert((*paa)->b_length == 4); + } + //printf("paa = %p, *paa = %p\n", paa, *paa); + + assert((*paa)->b_length); + size_t i = (size_t)key % (*paa)->b_length; + aaA** pe = &(*paa)->b[i]; + aaA *e; + while ((e = *pe) != NULL) + { + if (key == e->key) + return &e->value; + pe = &e->next; + } + + // Not found, create new elem + //printf("create new one\n"); + e = new aaA(); + e->next = NULL; + e->key = key; + e->value = NULL; + *pe = e; + + size_t nodes = ++(*paa)->nodes; + //printf("length = %d, nodes = %d\n", paa.a.b.length, nodes); + if (nodes > (*paa)->b_length * 4) + { + //printf("rehash\n"); + _aaRehash(paa); + } + + return &e->value; +} + + +/************************************************* + * Get value in associative array indexed by key. + * Returns NULL if it is not already there. + */ + +Value _aaGetRvalue(AA* aa, Key key) +{ + //printf("_aaGetRvalue(key = %p)\n", key); + if (!aa) + return NULL; + + size_t len = aa->b_length; + + if (len) + { + size_t i = (size_t)key % len; + aaA* e = aa->b[i]; + while (e) + { + if (key == e->key) + return e->value; + e = e->next; + } + } + return NULL; // not found +} + + +/******************************************** + * Rehash an array. + */ + +void _aaRehash(AA** paa) +{ + //printf("Rehash\n"); + if (*paa) + { + AA newb = bbinit; + AA *aa = *paa; + size_t len = _aaLen(*paa); + if (len) + { size_t i; + + for (i = 0; i < sizeof(prime_list)/sizeof(prime_list[0]) - 1; i++) + { + if (len <= prime_list[i]) + break; + } + len = prime_list[i]; + newb.b = new aaA*[len]; + memset(newb.b, 0, len * sizeof(aaA*)); + newb.b_length = len; + + for (size_t k = 0; k < aa->b_length; k++) + { aaA *e = aa->b[k]; + while (e) + { aaA* enext = e->next; + size_t j = (size_t)e->key % len; + e->next = newb.b[j]; + newb.b[j] = e; + e = enext; + } + } + if (aa->b != aa->binit) + delete[] aa->b; + + newb.nodes = aa->nodes; + } + + **paa = newb; + } +} + + +#if UNITTEST + +void unittest_aa() +{ + AA* aa = NULL; + Value v = _aaGetRvalue(aa, NULL); + assert(!v); + Value *pv = _aaGet(&aa, NULL); + assert(pv); + *pv = (void *)3; + v = _aaGetRvalue(aa, NULL); + assert(v == (void *)3); +} + +#endif diff --git a/root/aav.h b/root/aav.h new file mode 100644 index 00000000..266b5a83 --- /dev/null +++ b/root/aav.h @@ -0,0 +1,11 @@ + +typedef void* Value; +typedef void* Key; + +struct AA; + +size_t _aaLen(AA* aa); +Value* _aaGet(AA** aa, Key key); +Value _aaGetRvalue(AA* aa, Key key); +void _aaRehash(AA** paa); + diff --git a/root/array.c b/root/array.c new file mode 100644 index 00000000..f3440445 --- /dev/null +++ b/root/array.c @@ -0,0 +1,257 @@ + +// Copyright (c) 1999-2010 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include +#include + +#if (defined (__SVR4) && defined (__sun)) +#include +#endif + +#if _MSC_VER || __MINGW32__ +#include +#endif + +#if IN_GCC +#include "gdc_alloca.h" +#endif + +#if _WIN32 +#include +#endif + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#endif + +#include "port.h" +#include "root.h" +#include "dchar.h" +#include "rmem.h" + + +/********************************* Array ****************************/ + +Array::Array() +{ + data = SMALLARRAYCAP ? &smallarray[0] : NULL; + dim = 0; + allocdim = SMALLARRAYCAP; +} + +Array::~Array() +{ + if (data != &smallarray[0]) + mem.free(data); +} + +void Array::mark() +{ unsigned u; + + mem.mark(data); + for (u = 0; u < dim; u++) + mem.mark(data[u]); // BUG: what if arrays of Object's? +} + +void Array::reserve(unsigned nentries) +{ + //printf("Array::reserve: dim = %d, allocdim = %d, nentries = %d\n", dim, allocdim, nentries); + if (allocdim - dim < nentries) + { + if (allocdim == 0) + { // Not properly initialized, someone memset it to zero + if (nentries <= SMALLARRAYCAP) + { allocdim = SMALLARRAYCAP; + data = SMALLARRAYCAP ? &smallarray[0] : NULL; + } + else + { allocdim = nentries; + data = (void **)mem.malloc(allocdim * sizeof(*data)); + } + } + else if (allocdim == SMALLARRAYCAP) + { + allocdim = dim + nentries; + data = (void **)mem.malloc(allocdim * sizeof(*data)); + memcpy(data, &smallarray[0], dim * sizeof(*data)); + } + else + { allocdim = dim + nentries; + data = (void **)mem.realloc(data, allocdim * sizeof(*data)); + } + } +} + +void Array::setDim(unsigned newdim) +{ + if (dim < newdim) + { + reserve(newdim - dim); + } + dim = newdim; +} + +void Array::fixDim() +{ + if (dim != allocdim) + { + if (allocdim >= SMALLARRAYCAP) + { + if (dim <= SMALLARRAYCAP) + { + memcpy(&smallarray[0], data, dim * sizeof(*data)); + mem.free(data); + } + else + data = (void **)mem.realloc(data, dim * sizeof(*data)); + } + allocdim = dim; + } +} + +void Array::push(void *ptr) +{ + reserve(1); + data[dim++] = ptr; +} + +void *Array::pop() +{ + return data[--dim]; +} + +void Array::shift(void *ptr) +{ + reserve(1); + memmove(data + 1, data, dim * sizeof(*data)); + data[0] = ptr; + dim++; +} + +void Array::insert(unsigned index, void *ptr) +{ + reserve(1); + memmove(data + index + 1, data + index, (dim - index) * sizeof(*data)); + data[index] = ptr; + dim++; +} + + +void Array::insert(unsigned index, Array *a) +{ + if (a) + { unsigned d; + + d = a->dim; + reserve(d); + if (dim != index) + memmove(data + index + d, data + index, (dim - index) * sizeof(*data)); + memcpy(data + index, a->data, d * sizeof(*data)); + dim += d; + } +} + + +/*********************************** + * Append array a to this array. + */ + +void Array::append(Array *a) +{ + insert(dim, a); +} + +void Array::remove(unsigned i) +{ + if (dim - i - 1) + memmove(data + i, data + i + 1, (dim - i - 1) * sizeof(data[0])); + dim--; +} + +char *Array::toChars() +{ + unsigned len; + unsigned u; + char **buf; + char *str; + char *p; + + buf = (char **)malloc(dim * sizeof(char *)); + assert(buf); + len = 2; + for (u = 0; u < dim; u++) + { + buf[u] = ((Object *)data[u])->toChars(); + len += strlen(buf[u]) + 1; + } + str = (char *)mem.malloc(len); + + str[0] = '['; + p = str + 1; + for (u = 0; u < dim; u++) + { + if (u) + *p++ = ','; + len = strlen(buf[u]); + memcpy(p,buf[u],len); + p += len; + } + *p++ = ']'; + *p = 0; + free(buf); + return str; +} + +void Array::zero() +{ + memset(data,0,dim * sizeof(data[0])); +} + +void *Array::tos() +{ + return dim ? data[dim - 1] : NULL; +} + +int +#if _WIN32 + __cdecl +#endif + Array_sort_compare(const void *x, const void *y) +{ + Object *ox = *(Object **)x; + Object *oy = *(Object **)y; + + return ox->compare(oy); +} + +void Array::sort() +{ + if (dim) + { + qsort(data, dim, sizeof(Object *), Array_sort_compare); + } +} + +Array *Array::copy() +{ + Array *a = new Array(); + + a->setDim(dim); + memcpy(a->data, data, dim * sizeof(void *)); + return a; +} + diff --git a/root/async.c b/root/async.c new file mode 100644 index 00000000..78f17c68 --- /dev/null +++ b/root/async.c @@ -0,0 +1,325 @@ + +#define _MT 1 + +#include +#include +#include + +#if _WIN32 + +#include +#include +#include + +#include "root.h" + +static unsigned __stdcall startthread(void *p); + +struct FileData +{ + File *file; + int result; + HANDLE event; +}; + +struct AsyncRead +{ + static AsyncRead *create(size_t nfiles); + void addFile(File *file); + void start(); + int read(size_t i); + static void dispose(AsyncRead *); + + HANDLE hThread; + + size_t filesdim; + size_t filesmax; + FileData files[1]; +}; + + +AsyncRead *AsyncRead::create(size_t nfiles) +{ + AsyncRead *aw = (AsyncRead *)calloc(1, sizeof(AsyncRead) + + (nfiles - 1) * sizeof(FileData)); + aw->filesmax = nfiles; + return aw; +} + +void AsyncRead::addFile(File *file) +{ + //printf("addFile(file = %p)\n", file); + //printf("filesdim = %d, filesmax = %d\n", filesdim, filesmax); + assert(filesdim < filesmax); + files[filesdim].file = file; + files[filesdim].event = CreateEvent(NULL, TRUE, FALSE, NULL); + ResetEvent(files[filesdim].event); + filesdim++; +} + +void AsyncRead::start() +{ + //printf("aw->filesdim = %p %d\n", this, filesdim); + if (filesdim) + { + unsigned threadaddr; + hThread = (HANDLE) _beginthreadex(NULL, + 0, + &startthread, + this, + 0, + (unsigned *)&threadaddr); + + if (hThread) + { + SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST); + } + else + { + assert(0); + } + } +} + +int AsyncRead::read(size_t i) +{ + FileData *f = &files[i]; + WaitForSingleObject(f->event, INFINITE); + Sleep(0); // give up time slice + return f->result; +} + +void AsyncRead::dispose(AsyncRead *aw) +{ + free(aw); +} + + + +unsigned __stdcall startthread(void *p) +{ + AsyncRead *aw = (AsyncRead *)p; + + //printf("aw->filesdim = %p %d\n", aw, aw->filesdim); + for (size_t i = 0; i < aw->filesdim; i++) + { FileData *f = &aw->files[i]; + + f->result = f->file->read(); + SetEvent(f->event); + } + _endthreadex(EXIT_SUCCESS); + return EXIT_SUCCESS; // if skidding +} + +#elif linux // Posix + +#include +#include +#include + +#include "root.h" + +void *startthread(void *arg); + +void err_abort(int status, const char *msg) +{ + fprintf(stderr, "fatal error = %d, %s\n", status, msg); + exit(EXIT_FAILURE); +} + +struct FileData +{ + File *file; + int result; + + pthread_mutex_t mutex; + pthread_cond_t cond; + int value; +}; + +struct AsyncRead +{ + static AsyncRead *create(size_t nfiles); + void addFile(File *file); + void start(); + int read(size_t i); + static void dispose(AsyncRead *); + + size_t filesdim; + size_t filesmax; + FileData files[1]; +}; + + +AsyncRead *AsyncRead::create(size_t nfiles) +{ + AsyncRead *aw = (AsyncRead *)calloc(1, sizeof(AsyncRead) + + (nfiles - 1) * sizeof(FileData)); + aw->filesmax = nfiles; + return aw; +} + +void AsyncRead::addFile(File *file) +{ + //printf("addFile(file = %p)\n", file); + //printf("filesdim = %d, filesmax = %d\n", filesdim, filesmax); + assert(filesdim < filesmax); + FileData *f = &files[filesdim]; + f->file = file; + + int status = pthread_mutex_init(&f->mutex, NULL); + if (status != 0) + err_abort(status, "init mutex"); + status = pthread_cond_init(&f->cond, NULL); + if (status != 0) + err_abort(status, "init cond"); + + filesdim++; +} + +void AsyncRead::start() +{ + //printf("aw->filesdim = %p %d\n", this, filesdim); + if (filesdim) + { + pthread_t thread_id; + int status = pthread_create(&thread_id, + NULL, + &startthread, + this); + if (status != 0) + err_abort(status, "create thread"); + } +} + +int AsyncRead::read(size_t i) +{ + FileData *f = &files[i]; + + // Wait for the event + int status = pthread_mutex_lock(&f->mutex); + if (status != 0) + err_abort(status, "lock mutex"); + while (f->value == 0) + { + status = pthread_cond_wait(&f->cond, &f->mutex); + if (status != 0) + err_abort(status, "wait on condition"); + } + status = pthread_mutex_unlock(&f->mutex); + if (status != 0) + err_abort(status, "unlock mutex"); + + return f->result; +} + +void AsyncRead::dispose(AsyncRead *aw) +{ + //printf("AsyncRead::dispose()\n"); + for (int i = 0; i < aw->filesdim; i++) + { + FileData *f = &aw->files[i]; + int status = pthread_cond_destroy(&f->cond); + if (status != 0) + err_abort(status, "cond destroy"); + status = pthread_mutex_destroy(&f->mutex); + if (status != 0) + err_abort(status, "mutex destroy"); + } + free(aw); +} + + +void *startthread(void *p) +{ + AsyncRead *aw = (AsyncRead *)p; + + //printf("startthread: aw->filesdim = %p %d\n", aw, aw->filesdim); + size_t dim = aw->filesdim; + for (size_t i = 0; i < dim; i++) + { FileData *f = &aw->files[i]; + + f->result = f->file->read(); + + // Set event + int status = pthread_mutex_lock(&f->mutex); + if (status != 0) + err_abort(status, "lock mutex"); + f->value = 1; + status = pthread_cond_signal(&f->cond); + if (status != 0) + err_abort(status, "signal condition"); + status = pthread_mutex_unlock(&f->mutex); + if (status != 0) + err_abort(status, "unlock mutex"); + } + + return NULL; // end thread +} + +#else + +#include +#include + +#include "root.h" + +struct FileData +{ + File *file; + int result; + //HANDLE event; +}; + +struct AsyncRead +{ + static AsyncRead *create(size_t nfiles); + void addFile(File *file); + void start(); + int read(size_t i); + static void dispose(AsyncRead *); + + //HANDLE hThread; + + size_t filesdim; + size_t filesmax; + FileData files[1]; +}; + + +AsyncRead *AsyncRead::create(size_t nfiles) +{ + AsyncRead *aw = (AsyncRead *)calloc(1, sizeof(AsyncRead) + + (nfiles - 1) * sizeof(FileData)); + aw->filesmax = nfiles; + return aw; +} + +void AsyncRead::addFile(File *file) +{ + //printf("addFile(file = %p)\n", file); + //printf("filesdim = %d, filesmax = %d\n", filesdim, filesmax); + assert(filesdim < filesmax); + files[filesdim].file = file; + //files[filesdim].event = CreateEvent(NULL, TRUE, FALSE, NULL); + //ResetEvent(files[filesdim].event); + filesdim++; +} + +void AsyncRead::start() +{ +} + +int AsyncRead::read(size_t i) +{ + FileData *f = &files[i]; + f->result = f->file->read(); + return f->result; +} + +void AsyncRead::dispose(AsyncRead *aw) +{ + free(aw); +} + +#endif diff --git a/root/async.h b/root/async.h new file mode 100644 index 00000000..6f25f367 --- /dev/null +++ b/root/async.h @@ -0,0 +1,33 @@ + +// Copyright (c) 2009-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef ASYNC_H +#define ASYNC_H + +#if __DMC__ +#pragma once +#endif + + +/******************* + * Simple interface to read files asynchronously in another + * thread. + */ + +struct AsyncRead +{ + static AsyncRead *create(size_t nfiles); + void addFile(File *file); + void start(); + int read(size_t i); + static void dispose(AsyncRead *); +}; + + +#endif diff --git a/root/dchar.c b/root/dchar.c new file mode 100644 index 00000000..0b11a8a4 --- /dev/null +++ b/root/dchar.c @@ -0,0 +1,482 @@ + +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#include +#include +#include +#include + +#include "dchar.h" +#include "rmem.h" + +#if M_UNICODE + +// Converts a char string to Unicode + +dchar *Dchar::dup(char *p) +{ + dchar *s; + size_t len; + + if (!p) + return NULL; + len = strlen(p); + s = (dchar *)mem.malloc((len + 1) * sizeof(dchar)); + for (unsigned i = 0; i < len; i++) + { + s[i] = (dchar)(p[i] & 0xFF); + } + s[len] = 0; + return s; +} + +dchar *Dchar::memchr(dchar *p, int c, int count) +{ + int u; + + for (u = 0; u < count; u++) + { + if (p[u] == c) + return p + u; + } + return NULL; +} + +#if _WIN32 && __DMC__ +__declspec(naked) +unsigned Dchar::calcHash(const dchar *str, unsigned len) +{ + __asm + { + mov ECX,4[ESP] + mov EDX,8[ESP] + xor EAX,EAX + test EDX,EDX + je L92 + +LC8: cmp EDX,1 + je L98 + cmp EDX,2 + je LAE + + add EAX,[ECX] +// imul EAX,EAX,025h + lea EAX,[EAX][EAX*8] + add ECX,4 + sub EDX,2 + jmp LC8 + +L98: mov DX,[ECX] + and EDX,0FFFFh + add EAX,EDX + ret + +LAE: add EAX,[ECX] +L92: ret + } +} +#else +hash_t Dchar::calcHash(const dchar *str, size_t len) +{ + unsigned hash = 0; + + for (;;) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash += *(const uint16_t *)str; + return hash; + + case 2: + hash += *(const uint32_t *)str; + return hash; + + default: + hash += *(const uint32_t *)str; + hash *= 37; + str += 2; + len -= 2; + break; + } + } +} +#endif + +hash_t Dchar::icalcHash(const dchar *str, size_t len) +{ + hash_t hash = 0; + + for (;;) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash += *(const uint16_t *)str | 0x20; + return hash; + + case 2: + hash += *(const uint32_t *)str | 0x200020; + return hash; + + default: + hash += *(const uint32_t *)str | 0x200020; + hash *= 37; + str += 2; + len -= 2; + break; + } + } +} + +#elif MCBS + +hash_t Dchar::calcHash(const dchar *str, size_t len) +{ + hash_t hash = 0; + + while (1) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash *= 37; + hash += *(const uint8_t *)str; + return hash; + + case 2: + hash *= 37; + hash += *(const uint16_t *)str; + return hash; + + case 3: + hash *= 37; + hash += (*(const uint16_t *)str << 8) + + ((const uint8_t *)str)[2]; + return hash; + + default: + hash *= 37; + hash += *(const uint32_t *)str; + str += 4; + len -= 4; + break; + } + } +} + +#elif UTF8 + +// Specification is: http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335 + +char Dchar::mblen[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1, +}; + +dchar *Dchar::dec(dchar *pstart, dchar *p) +{ + while ((p[-1] & 0xC0) == 0x80) + p--; + return p; +} + +int Dchar::get(dchar *p) +{ + unsigned c; + unsigned char *q = (unsigned char *)p; + + c = q[0]; + switch (mblen[c]) + { + case 2: + c = ((c - 0xC0) << 6) | + (q[1] - 0x80); + break; + + case 3: + c = ((c - 0xE0) << 12) | + ((q[1] - 0x80) << 6) | + (q[2] - 0x80); + break; + + case 4: + c = ((c - 0xF0) << 18) | + ((q[1] - 0x80) << 12) | + ((q[2] - 0x80) << 6) | + (q[3] - 0x80); + break; + + case 5: + c = ((c - 0xF8) << 24) | + ((q[1] - 0x80) << 18) | + ((q[2] - 0x80) << 12) | + ((q[3] - 0x80) << 6) | + (q[4] - 0x80); + break; + + case 6: + c = ((c - 0xFC) << 30) | + ((q[1] - 0x80) << 24) | + ((q[2] - 0x80) << 18) | + ((q[3] - 0x80) << 12) | + ((q[4] - 0x80) << 6) | + (q[5] - 0x80); + break; + } + return c; +} + +dchar *Dchar::put(dchar *p, unsigned c) +{ + if (c <= 0x7F) + { + *p++ = c; + } + else if (c <= 0x7FF) + { + p[0] = 0xC0 + (c >> 6); + p[1] = 0x80 + (c & 0x3F); + p += 2; + } + else if (c <= 0xFFFF) + { + p[0] = 0xE0 + (c >> 12); + p[1] = 0x80 + ((c >> 6) & 0x3F); + p[2] = 0x80 + (c & 0x3F); + p += 3; + } + else if (c <= 0x1FFFFF) + { + p[0] = 0xF0 + (c >> 18); + p[1] = 0x80 + ((c >> 12) & 0x3F); + p[2] = 0x80 + ((c >> 6) & 0x3F); + p[3] = 0x80 + (c & 0x3F); + p += 4; + } + else if (c <= 0x3FFFFFF) + { + p[0] = 0xF8 + (c >> 24); + p[1] = 0x80 + ((c >> 18) & 0x3F); + p[2] = 0x80 + ((c >> 12) & 0x3F); + p[3] = 0x80 + ((c >> 6) & 0x3F); + p[4] = 0x80 + (c & 0x3F); + p += 5; + } + else if (c <= 0x7FFFFFFF) + { + p[0] = 0xFC + (c >> 30); + p[1] = 0x80 + ((c >> 24) & 0x3F); + p[2] = 0x80 + ((c >> 18) & 0x3F); + p[3] = 0x80 + ((c >> 12) & 0x3F); + p[4] = 0x80 + ((c >> 6) & 0x3F); + p[5] = 0x80 + (c & 0x3F); + p += 6; + } + else + assert(0); // not a UCS-4 character + return p; +} + +hash_t Dchar::calcHash(const dchar *str, size_t len) +{ + hash_t hash = 0; + + while (1) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash *= 37; + hash += *(const uint8_t *)str; + return hash; + + case 2: + hash *= 37; +#if LITTLE_ENDIAN + hash += *(const uint16_t *)str; +#else + hash += str[0] * 256 + str[1]; +#endif + return hash; + + case 3: + hash *= 37; +#if LITTLE_ENDIAN + hash += (*(const uint16_t *)str << 8) + + ((const uint8_t *)str)[2]; +#else + hash += (str[0] * 256 + str[1]) * 256 + str[2]; +#endif + return hash; + + default: + hash *= 37; +#if LITTLE_ENDIAN + hash += *(const uint32_t *)str; +#else + hash += ((str[0] * 256 + str[1]) * 256 + str[2]) * 256 + str[3]; +#endif + + str += 4; + len -= 4; + break; + } + } +} + +#else // ascii + +hash_t Dchar::calcHash(const dchar *str, size_t len) +{ + hash_t hash = 0; + + while (1) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash *= 37; + hash += *(const uint8_t *)str; + return hash; + + case 2: + hash *= 37; +#if LITTLE_ENDIAN + hash += *(const uint16_t *)str; +#else + hash += str[0] * 256 + str[1]; +#endif + return hash; + + case 3: + hash *= 37; +#if LITTLE_ENDIAN + hash += (*(const uint16_t *)str << 8) + + ((const uint8_t *)str)[2]; +#else + hash += (str[0] * 256 + str[1]) * 256 + str[2]; +#endif + return hash; + + default: + hash *= 37; +#if LITTLE_ENDIAN + hash += *(const uint32_t *)str; +#else + hash += ((str[0] * 256 + str[1]) * 256 + str[2]) * 256 + str[3]; +#endif + str += 4; + len -= 4; + break; + } + } +} + +hash_t Dchar::icalcHash(const dchar *str, size_t len) +{ + hash_t hash = 0; + + while (1) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash *= 37; + hash += *(const uint8_t *)str | 0x20; + return hash; + + case 2: + hash *= 37; + hash += *(const uint16_t *)str | 0x2020; + return hash; + + case 3: + hash *= 37; + hash += ((*(const uint16_t *)str << 8) + + ((const uint8_t *)str)[2]) | 0x202020; + return hash; + + default: + hash *= 37; + hash += *(const uint32_t *)str | 0x20202020; + str += 4; + len -= 4; + break; + } + } +} + +#endif + +#if 0 +#include + +void main() +{ + // Print out values to hardcode into Dchar::mblen[] + int c; + int s; + + for (c = 0; c < 256; c++) + { + s = 1; + if (c >= 0xC0 && c <= 0xDF) + s = 2; + if (c >= 0xE0 && c <= 0xEF) + s = 3; + if (c >= 0xF0 && c <= 0xF7) + s = 4; + if (c >= 0xF8 && c <= 0xFB) + s = 5; + if (c >= 0xFC && c <= 0xFD) + s = 6; + + printf("%d", s); + if ((c & 15) == 15) + printf(",\n"); + else + printf(","); + } +} +#endif diff --git a/root/dchar.h b/root/dchar.h new file mode 100644 index 00000000..2b8df523 --- /dev/null +++ b/root/dchar.h @@ -0,0 +1,194 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#ifndef DCHAR_H +#define DCHAR_H + +#if __GNUC__ && !_WIN32 +#include "gnuc.h" +#endif + +#if _MSC_VER + // Disable useless warnings about unreferenced functions + #pragma warning (disable : 4514) +#endif + +//#include "root.h" +typedef size_t hash_t; + +#undef TEXT + +// NOTE: All functions accepting pointer arguments must not be NULL + +#if M_UNICODE + +#include +#include + +typedef wchar_t dchar; +#define TEXT(x) L##x + +#define Dchar_mbmax 1 + +struct Dchar +{ + static dchar *inc(dchar *p) { return p + 1; } + static dchar *dec(dchar *pstart, dchar *p) { (void)pstart; return p - 1; } + static int len(const dchar *p) { return wcslen(p); } + static dchar get(dchar *p) { return *p; } + static dchar getprev(dchar *pstart, dchar *p) { (void)pstart; return p[-1]; } + static dchar *put(dchar *p, dchar c) { *p = c; return p + 1; } + static int cmp(dchar *s1, dchar *s2) + { +#if __DMC__ + if (!*s1 && !*s2) // wcscmp is broken + return 0; +#endif + return wcscmp(s1, s2); +#if 0 + return (*s1 == *s2) + ? wcscmp(s1, s2) + : ((int)*s1 - (int)*s2); +#endif + } + static int memcmp(const dchar *s1, const dchar *s2, int nchars) { return ::memcmp(s1, s2, nchars * sizeof(dchar)); } + static int isDigit(dchar c) { return '0' <= c && c <= '9'; } + static int isAlpha(dchar c) { return iswalpha(c); } + static int isUpper(dchar c) { return iswupper(c); } + static int isLower(dchar c) { return iswlower(c); } + static int isLocaleUpper(dchar c) { return isUpper(c); } + static int isLocaleLower(dchar c) { return isLower(c); } + static int toLower(dchar c) { return isUpper(c) ? towlower(c) : c; } + static int toLower(dchar *p) { return toLower(*p); } + static int toUpper(dchar c) { return isLower(c) ? towupper(c) : c; } + static dchar *dup(dchar *p) { return ::_wcsdup(p); } // BUG: out of memory? + static dchar *dup(char *p); + static dchar *chr(dchar *p, unsigned c) { return wcschr(p, (dchar)c); } + static dchar *rchr(dchar *p, unsigned c) { return wcsrchr(p, (dchar)c); } + static dchar *memchr(dchar *p, int c, int count); + static dchar *cpy(dchar *s1, dchar *s2) { return wcscpy(s1, s2); } + static dchar *str(dchar *s1, dchar *s2) { return wcsstr(s1, s2); } + static hash_t calcHash(const dchar *str, size_t len); + + // Case insensitive versions + static int icmp(dchar *s1, dchar *s2) { return wcsicmp(s1, s2); } + static int memicmp(const dchar *s1, const dchar *s2, int nchars) { return ::wcsnicmp(s1, s2, nchars); } + static hash_t icalcHash(const dchar *str, size_t len); +}; + +#elif MCBS + +#include +#include + +typedef char dchar; +#define TEXT(x) x + +#define Dchar_mbmax MB_LEN_MAX + +#elif UTF8 + +typedef char dchar; +#define TEXT(x) x + +#define Dchar_mbmax 6 + +struct Dchar +{ + static char mblen[256]; + + static dchar *inc(dchar *p) { return p + mblen[*p & 0xFF]; } + static dchar *dec(dchar *pstart, dchar *p); + static int len(const dchar *p) { return strlen(p); } + static int get(dchar *p); + static int getprev(dchar *pstart, dchar *p) + { return *dec(pstart, p) & 0xFF; } + static dchar *put(dchar *p, unsigned c); + static int cmp(dchar *s1, dchar *s2) { return strcmp(s1, s2); } + static int memcmp(const dchar *s1, const dchar *s2, int nchars) { return ::memcmp(s1, s2, nchars); } + static int isDigit(dchar c) { return '0' <= c && c <= '9'; } + static int isAlpha(dchar c) { return c <= 0x7F ? isalpha(c) : 0; } + static int isUpper(dchar c) { return c <= 0x7F ? isupper(c) : 0; } + static int isLower(dchar c) { return c <= 0x7F ? islower(c) : 0; } + static int isLocaleUpper(dchar c) { return isUpper(c); } + static int isLocaleLower(dchar c) { return isLower(c); } + static int toLower(dchar c) { return isUpper(c) ? tolower(c) : c; } + static int toLower(dchar *p) { return toLower(*p); } + static int toUpper(dchar c) { return isLower(c) ? toupper(c) : c; } + static dchar *dup(dchar *p) { return ::strdup(p); } // BUG: out of memory? + static dchar *chr(dchar *p, int c) { return strchr(p, c); } + static dchar *rchr(dchar *p, int c) { return strrchr(p, c); } + static dchar *memchr(dchar *p, int c, int count) + { return (dchar *)::memchr(p, c, count); } + static dchar *cpy(dchar *s1, dchar *s2) { return strcpy(s1, s2); } + static dchar *str(dchar *s1, dchar *s2) { return strstr(s1, s2); } + static hash_t calcHash(const dchar *str, size_t len); + + // Case insensitive versions + static int icmp(dchar *s1, dchar *s2) { return _mbsicmp(s1, s2); } + static int memicmp(const dchar *s1, const dchar *s2, int nchars) { return ::_mbsnicmp(s1, s2, nchars); } +}; + +#else + +#include + +#ifndef GCC_SAFE_DMD +#include +#endif + +typedef char dchar; +#define TEXT(x) x + +#define Dchar_mbmax 1 + +struct Dchar +{ + static dchar *inc(dchar *p) { return p + 1; } + static dchar *dec(dchar *pstart, dchar *p) { return p - 1; } + static int len(const dchar *p) { return strlen(p); } + static int get(dchar *p) { return *p & 0xFF; } + static int getprev(dchar *pstart, dchar *p) { return p[-1] & 0xFF; } + static dchar *put(dchar *p, unsigned c) { *p = c; return p + 1; } + static int cmp(dchar *s1, dchar *s2) { return strcmp(s1, s2); } + static int memcmp(const dchar *s1, const dchar *s2, int nchars) { return ::memcmp(s1, s2, nchars); } + static int isDigit(dchar c) { return '0' <= c && c <= '9'; } +#ifndef GCC_SAFE_DMD + static int isAlpha(dchar c) { return isalpha((unsigned char)c); } + static int isUpper(dchar c) { return isupper((unsigned char)c); } + static int isLower(dchar c) { return islower((unsigned char)c); } + static int isLocaleUpper(dchar c) { return isupper((unsigned char)c); } + static int isLocaleLower(dchar c) { return islower((unsigned char)c); } + static int toLower(dchar c) { return isupper((unsigned char)c) ? tolower(c) : c; } + static int toLower(dchar *p) { return toLower(*p); } + static int toUpper(dchar c) { return islower((unsigned char)c) ? toupper(c) : c; } + static dchar *dup(dchar *p) { return ::strdup(p); } // BUG: out of memory? +#endif + static dchar *chr(dchar *p, int c) { return strchr(p, c); } + static dchar *rchr(dchar *p, int c) { return strrchr(p, c); } + static dchar *memchr(dchar *p, int c, int count) + { return (dchar *)::memchr(p, c, count); } + static dchar *cpy(dchar *s1, dchar *s2) { return strcpy(s1, s2); } + static dchar *str(dchar *s1, dchar *s2) { return strstr(s1, s2); } + static hash_t calcHash(const dchar *str, size_t len); + + // Case insensitive versions +#ifdef __GNUC__ + static int icmp(dchar *s1, dchar *s2) { return strcasecmp(s1, s2); } +#else + static int icmp(dchar *s1, dchar *s2) { return stricmp(s1, s2); } +#endif + static int memicmp(const dchar *s1, const dchar *s2, int nchars) { return ::memicmp(s1, s2, nchars); } + static hash_t icalcHash(const dchar *str, size_t len); +}; + +#endif +#endif + diff --git a/root/dmgcmem.c b/root/dmgcmem.c new file mode 100644 index 00000000..9a283890 --- /dev/null +++ b/root/dmgcmem.c @@ -0,0 +1,499 @@ + + +// Copyright (c) 2000-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ +#include +#include +#endif + +#include "rmem.h" +#include "gc/gc.h" +//#include "printf.h" + +/* This implementation of the storage allocator uses the Digital Mars gc. + */ + +Mem mem; + +//static int nuncollectable; + +extern "C" +{ + void gc_init(); + GC *gc_get(); +} + +void Mem::init() +{ + gc_init(); +} + +char *Mem::strdup(const char *s) +{ + return gc_get()->strdup(s); +} + +void *Mem::malloc(size_t size) +{ + if (gc) // if cached allocator + { +// PRINTF("Using cached gc for size %d, file = '%s', line = %d\n", size, GC::file, GC::line); +// GC::file = NULL; +// GC::line = 0; + return ((GC *)gc)->malloc(size); + } + if (this == &mem) // don't cache global mem + { +// PRINTF("Using global gc for size %d, file = '%s', line = %d\n", size, GC::file, GC::line); +// GC::file = NULL; +// GC::line = 0; + return gc_get()->malloc(size); + } +// PRINTF("Generating cached gc for size %d, file = '%s', line = %d\n", size, GC::file, GC::line); + gc = gc_get(); + return gc->malloc(size); +} + +void *Mem::malloc_uncollectable(size_t size) +{ void *p; + + p = ::malloc(size); + if (!p) + error(); + addroots((char *)p, (char *)p + size); + +#if 0 + ++nuncollectable; + WPRINTF(L"malloc_uncollectable(%u) = %x, n=%d\n", size, p, nuncollectable); +#endif + + return p; +} + +void *Mem::calloc(size_t size, size_t n) +{ + return gc_get()->calloc(size, n); +} + +void *Mem::realloc(void *p, size_t size) +{ + return gc_get()->realloc(p, size); +} + +void Mem::free(void *p) +{ + gc_get()->free(p); +} + +void Mem::free_uncollectable(void *p) +{ + if (p) + { removeroots((char *)p); + ::free(p); + +#if 0 + --nuncollectable; + WPRINTF(L"free_uncollectable(%x) n=%d\n", p, nuncollectable); +#endif + +#if 0 + gc_get()->fullcollect(); + + GCStats stats; + + getStats(&stats); + WPRINTF(L"poolsize = %x, usedsize = %x, freelistsize = %x\n", + stats.poolsize, stats.usedsize, stats.freelistsize); +#endif + } +} + +void *Mem::mallocdup(void *o, size_t size) +{ + return gc_get()->mallocdup(o, size); +} + +void Mem::check(void *p) +{ + if (gc) + gc->check(p); + else + gc_get()->check(p); +} + +void Mem::error() +{ +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ + assert(0); +#endif + printf("Error: out of memory\n"); + exit(EXIT_FAILURE); +} + +void Mem::fullcollect() +{ + gc_get()->fullcollect(); + +#if 0 + { + GCStats stats; + + gc_get()->getStats(&stats); + WPRINTF(L"Thread %x ", Thread::getId()); + WPRINTF(L"poolsize=x%x, usedsize=x%x, freelistsize=x%x, freeblocks=%d, pageblocks=%d\n", + stats.poolsize, stats.usedsize, stats.freelistsize, stats.freeblocks, stats.pageblocks); + } +#endif +} + + +void Mem::fullcollectNoStack() +{ + gc_get()->fullcollectNoStack(); + +#if 0 + { + GCStats stats; + + gc_get()->getStats(&stats); + WPRINTF(L"Thread %x ", Thread::getId()); + WPRINTF(L"poolsize=x%x, usedsize=x%x, freelistsize=x%x, freeblocks=%d, pageblocks=%d\n", + stats.poolsize, stats.usedsize, stats.freelistsize, stats.freeblocks, stats.pageblocks); + } +#endif +} + + +void Mem::mark(void *pointer) +{ + (void) pointer; // for VC /W4 compatibility +} + + +void Mem::addroots(char* pStart, char* pEnd) +{ + gc_get()->addRange(pStart, pEnd); +} + + +void Mem::removeroots(char* pStart) +{ + gc_get()->removeRange(pStart); +} + + +void Mem::setFinalizer(void* pObj, FINALIZERPROC pFn, void* pClientData) +{ + (void)pClientData; + gc_get()->setFinalizer(pObj, pFn); +} + + +void Mem::setStackBottom(void *stackbottom) +{ + gc_get()->setStackBottom(stackbottom); +} + + +GC *Mem::getThreadGC() +{ + return gc_get(); +} + + +/* =================================================== */ + +#if 1 +void * operator new(size_t m_size) +{ + //PRINTF("Call to global operator new(%d), file = '%s', line = %d\n", m_size, GC::file ? GC::file : "(null)", GC::line); + GC::file = NULL; + GC::line = 0; + return mem.malloc(m_size); +} + +void operator delete(void *p) +{ + //WPRINTF(L"Call to global operator delete\n"); + mem.free(p); +} + +void* operator new[](size_t size) +{ + return operator new(size); +} + +void operator delete[](void *pv) +{ + operator delete(pv); +} +#endif + +void * Mem::operator new(size_t m_size) +{ void *p; + + p = gc_get()->malloc(m_size); + //printf("Mem::operator new(%d) = %p\n", m_size, p); + if (!p) + mem.error(); + return p; +} + +void * Mem::operator new(size_t m_size, Mem *mem) +{ void *p; + + p = mem->malloc(m_size); + //printf("Mem::operator new(%d) = %p\n", m_size, p); + if (!p) + ::mem.error(); + return p; +} + +void * Mem::operator new(size_t m_size, GC *gc) +{ void *p; + +// if (!gc) +// WPRINTF(L"gc is NULL\n"); + p = gc->malloc(m_size); + //printf("Mem::operator new(%d) = %p\n", m_size, p); + if (!p) + ::mem.error(); + return p; +} + +void Mem::operator delete(void *p) +{ +// printf("Mem::operator delete(%p)\n", p); + gc_get()->free(p); +} + +/* ============================================================ */ + +/* The following section of code exists to find the right + * garbage collector for this thread. There is one independent instance + * of the collector per thread. + */ + +/* ===================== linux ================================ */ + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ + +#include + +#define LOG 0 // log thread creation / destruction + +extern "C" +{ + +// Key identifying the thread-specific data +static pthread_key_t gc_key; + +/* "Once" variable ensuring that the key for gc_alloc will be allocated + * exactly once. + */ +static pthread_once_t gc_alloc_key_once = PTHREAD_ONCE_INIT; + +/* Forward functions */ +static void gc_alloc_key(); +static void gc_alloc_destroy_gc(void * accu); + + +void gc_init() +{ +#if LOG + WPRINTF(L"Thread %lx: gc_init()\n", pthread_self()); +#endif + pthread_once(&gc_alloc_key_once, gc_alloc_key); +#if LOG + WPRINTF(L"Thread %lx: gc_init() return\n", pthread_self()); +#endif +} + +GC *gc_get() +{ + GC *gc; + + // Get the thread-specific data associated with the key + gc = (GC *) pthread_getspecific(gc_key); + + // It's initially NULL, meaning that we must allocate the buffer first. + if (gc == NULL) + { + GC_LOG(); + gc = new GC(); + gc->init(); + + // Store the buffer pointer in the thread-specific data. + pthread_setspecific(gc_key, (void *) gc); +#if LOG + WPRINTF(L"Thread %lx: allocating gc at %x\n", pthread_self(), gc); +#endif + } + return gc; +} + +// Function to allocate the key for gc_alloc thread-specific data. + +static void gc_alloc_key() +{ + pthread_key_create(&gc_key, gc_alloc_destroy_gc); +#if LOG + WPRINTF(L"Thread %lx: allocated gc key %d\n", pthread_self(), gc_key); +#endif +} + +// Function to free the buffer when the thread exits. +// Called only when the thread-specific data is not NULL. + +static void gc_alloc_destroy_gc(void *gc) +{ +#if LOG + WPRINTF(L"Thread %x: freeing gc at %x\n", pthread_self(), gc); +#endif + delete (GC *)gc; +} + +} + +#endif + +/* ===================== win32 ================================ */ + +#if !defined(linux) && defined(_WIN32) + +#if 1 // single threaded version + +extern "C" +{ + +static GC *gc; + +void gc_init() +{ + if (!gc) + { gc = (GC *)::malloc(sizeof(GC)); + gc->init(); + } +} + +GC *gc_get() +{ + return gc; +} + +} + +#else // multi threaded version + +#include "mutex.h" +#include "thread.h" + +/* This is the win32 version. It suffers from the bug that + * when the thread exits the data structure is not cleared, + * but the memory pool it points to is free'd. + * Thus, if a new thread comes along with the same thread id, + * the data will look initialized, but will point to garbage. + * + * What needs to happen is when a thread exits, the associated + * GC_context data struct is cleared. + */ + +struct GC_context +{ + ThreadId threadid; // identifier of current thread + GC *gc; +}; + +Mutex gc_mutex; + +static GC_context array[64]; + +// Array of pointers to GC_context objects, one per threadid +GC_context *gccontext = array; +unsigned gccontext_allocdim = 64; +unsigned gccontext_dim; + +ThreadId gc_cache_ti; +GC_context *gc_cache_cc; + +extern "C" void gc_init() +{ +} + + +extern "C" GC *gc_get() +{ + /* This works by creating an array of GC_context's, one + * for each thread. We match up by thread id. + */ + + ThreadId ti; + GC_context *cc; + + //PRINTF("gc_get()\n"); + + ti = Thread::getId(); + gc_mutex.acquire(); + + // Used cached version if we can + if (ti == gc_cache_ti) + { + cc = gc_cache_cc; + //exception(L"getGC_context(): cache x%x", ti); + } + else + { + // This does a linear search through gccontext[]. + // A hash table might be faster if there are more + // than a dozen threads. + GC_context *ccp; + GC_context *ccptop = &gccontext[gccontext_dim]; + for (ccp = gccontext; ccp < ccptop; ccp++) + { + cc = ccp; + if (cc->threadid == ti) + { + WPRINTF(L"getGC_context(): existing x%x", ti); + goto Lret; + } + } + + // Do not allocate with garbage collector, as this must reside + // global to all threads. + + assert(gccontext_dim < gccontext_allocdim); + cc = ccp; + memset(cc, 0, sizeof(*cc)); + cc->threadid = ti; + cc->gc = new GC(); + cc->gc->init(); + + gccontext_dim++; + WPRINTF(L"getGC_context(): new x%x\n", ti); + + Lret: + // Cache for next time + gc_cache_ti = ti; + gc_cache_cc = cc; + } + + gc_mutex.release(); + return cc->gc; +} + +#endif + + +#endif diff --git a/root/gc/bits.c b/root/gc/bits.c new file mode 100644 index 00000000..bbf3a324 --- /dev/null +++ b/root/gc/bits.c @@ -0,0 +1,43 @@ +// Copyright (c) 2000-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#include +#include + +#include "bits.h" + +GCBits::GCBits() +{ + data = NULL; + nwords = 0; + nbits = 0; +} + +GCBits::~GCBits() +{ + if (data) + ::free(data); + data = NULL; +} + +void GCBits::invariant() +{ + if (data) + { + assert(nwords * sizeof(*data) * 8 >= nbits); + } +} + +void GCBits::alloc(unsigned nbits) +{ + this->nbits = nbits; + nwords = (nbits + (BITS_PER_WORD - 1)) >> BITS_SHIFT; + data = (unsigned *)::calloc(nwords + 2, sizeof(unsigned)); + assert(data); +} diff --git a/root/gc/bits.h b/root/gc/bits.h new file mode 100644 index 00000000..df414392 --- /dev/null +++ b/root/gc/bits.h @@ -0,0 +1,83 @@ +// Copyright (c) 2000-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include + +#if __DMC__ +// Inline bit operations +#include +#endif + +#ifdef linux +#include "gccbitops.h" +#endif + +#if _MSC_VER + // Disable useless warnings about unreferenced functions + #pragma warning (disable : 4514) +#endif // _MSC_VER + + +#define BITS_PER_WORD 32 +#define BITS_SHIFT 5 +#define BITS_MASK 31 + +struct Mem; + +struct GCBits +{ + unsigned *data; + unsigned nwords; // allocated words in data[] excluding sentinals + unsigned nbits; // number of bits in data[] excluding sentinals + + GCBits(); + ~GCBits(); + void invariant(); + + void alloc(unsigned nbits); + +#if __DMC__ + unsigned test(unsigned i) { return _inline_bt(data + 1, i); } + void set(unsigned i) { _inline_bts(data + 1, i); } + void clear(unsigned i) { _inline_btr(data + 1, i); } + unsigned testClear(unsigned i) { return _inline_btr(data + 1, i); } + unsigned testSet(unsigned i) { return _inline_bts(data + 1, i); } +#elif 0 //defined linux + // for unknown reasons, GCC does badly with this + unsigned test(unsigned i) { return _inline_bt(data + 1, i); } + void set(unsigned i) { _inline_bts(data + 1, i); } + void clear(unsigned i) { _inline_btr(data + 1, i); } + unsigned testClear(unsigned i) { return _inline_btr(data + 1, i); } + unsigned testSet(unsigned i) { return _inline_bts(data + 1, i); } +#else + unsigned test(unsigned i) { return data[1 + (i >> BITS_SHIFT)] & (1 << (i & BITS_MASK)); } + void set(unsigned i) { data[1 + (i >> BITS_SHIFT)] |= (1 << (i & BITS_MASK)); } + void clear(unsigned i) { data[1 + (i >> BITS_SHIFT)] &= ~(1 << (i & BITS_MASK)); } + unsigned testClear(unsigned i) + { + unsigned *p = &data[1 + (i >> BITS_SHIFT)]; + unsigned mask = (1 << (i & BITS_MASK)); + unsigned result = *p & mask; + *p &= ~mask; + return result; + } + unsigned testSet(unsigned i) + { + unsigned *p = &data[1 + (i >> BITS_SHIFT)]; + unsigned mask = (1 << (i & BITS_MASK)); + unsigned result = *p & mask; + *p |= mask; + return result; + } +#endif + + void zero() { memset(data + 1, 0, nwords * sizeof(unsigned)); } + void copy(GCBits *f) { memcpy(data + 1, f->data + 1, nwords * sizeof(unsigned)); } + + unsigned *base() { return data + 1; } +}; diff --git a/root/gc/gc.c b/root/gc/gc.c new file mode 100644 index 00000000..7c412566 --- /dev/null +++ b/root/gc/gc.c @@ -0,0 +1,2223 @@ +// Copyright (c) 2000-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +/************** Debugging ***************************/ + +#define THREADINVARIANT 0 // check thread integrity +#define INVARIANT 0 // check class invariants +#define LOGGING 0 // log allocations / frees +#define MEMSTOMP 0 // stomp on memory +#define SENTINEL 0 // add underrun/overrrun protection +#define PTRCHECK 0 // 0: fast pointer checking + // 1: more pointer checking + // 2: thorough but slow pointer checking + +/*************** Configuration *********************/ + +#define USEROOT 0 // use root for printf +#define STACKGROWSDOWN 1 // 1: growing the stack means subtracting from the stack pointer + // (use 1 for Intel X86 CPUs) + // 0: growing the stack means adding to the stack pointer +#define ATOMIC 1 // mark some mallocs as "atomic" +#define LISTPREV 1 // use List prev pointers + +/***************************************************/ + + +#include +#include +#include +#include + +#include "gc.h" +#include "os.h" +#include "bits.h" + +#if CHECK_OUT_OF_MEM +#include +extern jmp_buf g_setjmp_buf; +#endif + +//#include "../root/perftimer.h" + +#if USEROOT +#if defined linux + +#include "../root/printf.h" + +#elif defined _WIN32 + +#include "..\root\printf.h" + +#endif +#else +#include +#define WPRINTF wprintf +#define PRINTF printf +#endif + +#ifdef linux +#include "gccbitops.h" +#endif + +#ifdef _MSC_VER +#include "mscbitops.h" +#endif + +#define PAGESIZE 4096 +#define COMMITSIZE (4096*16) +#define POOLSIZE (4096*256*2) // 2 megs + +//#define printf 1 || printf + +#undef assert +#define assert(e) if (!(e)) _gc_assert(__LINE__) +void _gc_assert(unsigned line); + +static int zero = 0; // used to avoid complaints about assert(0) by MSVC + +#if LOGGING + +struct Log; + +struct LogArray +{ + unsigned dim; + unsigned allocdim; + Log *data; + + LogArray(); + ~LogArray(); + + void reserve(unsigned nentries); + void push(Log foo); + void remove(unsigned i); + unsigned find(void *p); + void copy(LogArray *from); +}; + +#endif + +enum Bins +{ + B_16, + B_32, + B_64, + B_128, + B_256, + B_512, + B_1024, + B_2048, + B_PAGE, // start of large alloc + B_PAGEPLUS, // continuation of large alloc + B_FREE, // free page + B_UNCOMMITTED, // memory not committed for this page + B_MAX +}; + + +struct List +{ + List *next; + List *prev; +}; + +struct Range +{ + void *pbot; + void *ptop; +}; + +struct Pool +{ + char *baseAddr; + char *topAddr; + GCBits mark; + GCBits scan; + GCBits finals; + GCBits freebits; +#if ATOMIC + GCBits atomic; +#endif + + unsigned npages; + unsigned ncommitted; // ncommitted <= npages + unsigned char *pagetable; + + void init(unsigned npages); + ~Pool(); + void invariant(); + + unsigned allocPages(unsigned n); + void freePages(unsigned pagenum, unsigned npages); + int cmp(Pool *); +}; + +struct Gcx +{ +#if THREADINVARIANT + pthread_t self; +# define thread_invariant(gcx) assert(gcx->self == pthread_self()) +#else +# define thread_invariant(gcx) ((void)0) +#endif + + unsigned nroots; + unsigned rootdim; + void **roots; + + unsigned nranges; + unsigned rangedim; + Range *ranges; + + unsigned noStack; // !=0 means don't scan stack + unsigned log; + unsigned anychanges; + char *stackBottom; + + char *minAddr; // min(baseAddr) + char *maxAddr; // max(topAddr) + + unsigned npages; // total number of pages in all the pools + unsigned npools; + Pool **pooltable; + + List *bucket[B_MAX]; // free list for each size + + GC_FINALIZER finalizer; // finalizer function (one per GC) + + void init(); + ~Gcx(); + void invariant(); + + void addRoot(void *p); + void removeRoot(void *p); + + void addRange(void *pbot, void *ptop); // add range to scan for roots + void removeRange(void *pbot); // remove range + + Pool *findPool(void *p); + unsigned findSize(void *p); + static Bins findBin(unsigned size); + void *bigAlloc(unsigned size); + Pool *newPool(unsigned npages); + int allocPage(Bins bin); + void mark(void *pbot, void *ptop); + unsigned fullcollectshell(); + unsigned fullcollect(void *stackTop); + void doFinalize(void *p); + + + /***** Leak Detector ******/ +#if LOGGING + LogArray current; + LogArray prev; + + void log_init(); + void log_malloc(void *p, unsigned size); + void log_free(void *p); + void log_collect(); + void log_parent(void *p, void *parent); +#else + void log_init() { } + void log_malloc(void *p, unsigned size) { (void)p; (void)size; } + void log_free(void *p) { (void)p; } + void log_collect() { } + void log_parent(void *p, void *parent) { (void)p; (void)parent; } +#endif +}; + +void _gc_assert(unsigned line) +{ + (void)line; +#if USEROOT + WPRINTF(L"GC assert fail: gc.c(%d)\n", line); +#else + printf("GC assert fail: gc.c(%d)\n", line); +#endif + *(char *)0 = 0; + exit(0); +} + +const unsigned binsize[B_MAX] = { 16,32,64,128,256,512,1024,2048,4096 }; +const unsigned notbinsize[B_MAX] = { ~(16u-1),~(32u-1),~(64u-1),~(128u-1),~(256u-1), + ~(512u-1),~(1024u-1),~(2048u-1),~(4096u-1) }; + +unsigned binset[B_PAGE][PAGESIZE / (16 * 32)]; + +/******************************** + * Initialize binset[][]. + */ + +void binset_init() +{ + int bin; + + for (bin = 0; bin < B_PAGE; bin++) + { + unsigned bitstride = binsize[bin] / 16; + + for (unsigned bit = 0; bit < (PAGESIZE / 16); bit += bitstride) + { + unsigned u = bit / 32; + unsigned m = bit % 32; + binset[bin][u] |= 1 << m; + } + } +} + +/* ============================ SENTINEL =============================== */ + + +#if SENTINEL +# define SENTINEL_PRE 0xF4F4F4F4 // 32 bits +# define SENTINEL_POST 0xF5 // 8 bits +# define SENTINEL_EXTRA (2 * sizeof(unsigned) + 1) +# define sentinel_size(p) (((unsigned *)(p))[-2]) +# define sentinel_pre(p) (((unsigned *)(p))[-1]) +# define sentinel_post(p) (((unsigned char *)(p))[sentinel_size(p)]) + +void sentinel_init(void *p, unsigned size) +{ + sentinel_size(p) = size; + sentinel_pre(p) = SENTINEL_PRE; + sentinel_post(p) = SENTINEL_POST; +} + +void sentinel_invariant(void *p, int line) +{ + //WPRINTF(L"pre = %x, line = %d\n", sentinel_pre(p), line); + if (sentinel_pre(p) != SENTINEL_PRE) + WPRINTF(L"p = %x, pre = %x, size = %x line = %d\n", p, sentinel_pre(p), sentinel_size(p), line); + assert(sentinel_pre(p) == SENTINEL_PRE); + assert(sentinel_post(p) == SENTINEL_POST); +} +#define sentinel_invariant(p) sentinel_invariant(p, __LINE__) + +inline void *sentinel_add(void *p) +{ + //assert(((unsigned)p & 3) == 0); + return (void *)((char *)p + 2 * sizeof(unsigned)); +} + +inline void *sentinel_sub(void *p) +{ + return (void *)((char *)p - 2 * sizeof(unsigned)); +} + +#else + +# define SENTINEL_EXTRA 0 +# define sentinel_init(p,size) (void)(p) +# define sentinel_invariant(p) (void)(p) +# define sentinel_add(p) (void *)(p) +# define sentinel_sub(p) (void *)(p) + +#endif + +/* ============================ GC =============================== */ + +unsigned GC::line = 0; +char *GC::file = NULL; + +GC::~GC() +{ +#if defined linux + //WPRINTF(L"Thread %x ", pthread_self()); + //WPRINTF(L"GC::~GC()\n"); +#endif + if (gcx) + { + Gcx *g = (Gcx *)gcx; + delete g; + } +} + +void GC::init() +{ + //printf("GC::init()\n"); + if (!binset[0][0]) + binset_init(); + gcx = (Gcx *)::malloc(sizeof(Gcx)); + gcx->init(); +} + +void GC::setStackBottom(void *p) +{ + thread_invariant(gcx); +#if STACKGROWSDOWN + p = (void *)((unsigned *)p + 4); + if (p > gcx->stackBottom) +#else + p = (void *)((unsigned *)p - 4); + if (p < gcx->stackBottom) +#endif + { + //WPRINTF(L"setStackBottom(%x)\n", p); + gcx->stackBottom = (char *)p; + } +} + +char *GC::strdup(const char *s) +{ + unsigned len; + char *p = NULL; + + thread_invariant(gcx); + if (s) + { + len = strlen(s) + 1; + p = (char *)malloc(len); +#if CHECK_OUT_OF_MEM + if (p) +#endif + memcpy(p, s, len); + } + return p; +} + +void *GC::calloc(size_t size, size_t n) +{ + unsigned len; + void *p; + + thread_invariant(gcx); + len = size * n; + p = malloc(len); + if (p) + { //printf("calloc: %x len %d\n", p, len); + memset(p, 0, len); + } + return p; +} + +void *GC::realloc(void *p, size_t size) +{ + thread_invariant(gcx); + if (!size) + { if (p) + { free(p); + p = NULL; + } + } + else if (!p) + { + p = malloc(size); + } + else + { void *p2; + unsigned psize; + +//WPRINTF(L"GC::realloc(p = %x, size = %u)\n", p, size); + sentinel_invariant(p); +#if SENTINEL + psize = sentinel_size(p); + if (psize != size) +#else + psize = gcx->findSize(p); // find allocated size + if (psize < size || // if new size is bigger + psize > size * 2) // or less than half +#endif + { + p2 = malloc(size); +#if CHECK_OUT_OF_MEM + if (!p2) + return NULL; +#endif + if (psize < size) + size = psize; + //WPRINTF(L"\tcopying %d bytes\n",size); + memcpy(p2, p, size); + //free(p); // causes 507 crash + p = p2; + } + } + return p; +} + +void *GC::malloc_atomic(size_t size) +{ +#if ATOMIC + //WPRINTF(L"GC::malloc_atomic(size = %d)\n", size); + void *p; + + p = malloc(size); + if (p) + { + Pool *pool = gcx->findPool(p); + pool->atomic.set(((char *)p - pool->baseAddr) / 16); + } + return p; +#else + return malloc(size); +#endif +} + +void *GC::malloc(size_t size) +{ void *p; + Bins bin; + + //WPRINTF(L"GC::malloc(size = %d)\n", size); + //PRINTF("GC::malloc(size = %d, file = '%s', line = %d)\n", size, GC::file, GC::line); + //printf("gcx->self = %x, pthread_self() = %x\n", gcx->self, pthread_self()); + thread_invariant(gcx); + if (size) + { + size += SENTINEL_EXTRA; + + // Compute size bin + bin = gcx->findBin(size); + + if (bin < B_PAGE) + { + p = gcx->bucket[bin]; + if (p == NULL) + { + if (!gcx->allocPage(bin)) // try to find a new page + { unsigned freedpages; + + freedpages = gcx->fullcollectshell(); // collect to find a new page + //if (freedpages < gcx->npools * 16) + if (freedpages < gcx->npages / 20 + 1) + { + gcx->newPool(1); + } + } + if (!gcx->bucket[bin] && !gcx->allocPage(bin)) + { int result; + + gcx->newPool(1); // allocate new pool to find a new page + result = gcx->allocPage(bin); +#if CHECK_OUT_OF_MEM + if (!result) + //return NULL; + longjmp(g_setjmp_buf, 1); +#else + assert(result); +#endif + } + p = gcx->bucket[bin]; + } + + // Return next item from free list + gcx->bucket[bin] = ((List *)p)->next; + memset((char *)p + size, 0, binsize[bin] - size); + #if MEMSTOMP + memset(p, 0xF0, size); + #endif + } + else + { + p = gcx->bigAlloc(size); + if (!p) +#if CHECK_OUT_OF_MEM + longjmp(g_setjmp_buf, 1); +#else + return NULL; +#endif + } + size -= SENTINEL_EXTRA; + p = sentinel_add(p); + sentinel_init(p, size); + //WPRINTF(L"\tmalloc => %x, %x\n", sentinel_sub(p), *(unsigned *)sentinel_sub(p)); + gcx->log_malloc(p, size); + return p; + } + return NULL; +} + +void GC::free(void *p) +{ + Pool *pool; + unsigned pagenum; + Bins bin; + unsigned bit; + + thread_invariant(gcx); + if (!p) + return; + + // Find which page it is in + pool = gcx->findPool(p); + if (!pool) // if not one of ours + return; // ignore + sentinel_invariant(p); + p = sentinel_sub(p); + pagenum = ((char *)p - pool->baseAddr) / PAGESIZE; + + if (pool->finals.nbits && gcx->finalizer) + { + bit = (unsigned)((char *)p - pool->baseAddr) / 16; + if (pool->finals.testClear(bit)) + { + (*gcx->finalizer)(sentinel_add(p), NULL); + } + } + + bin = (Bins)pool->pagetable[pagenum]; + if (bin == B_PAGE) // if large alloc + { int npages; + unsigned n; + + // Free pages + npages = 1; + n = pagenum; + while (++n < pool->ncommitted && pool->pagetable[n] == B_PAGEPLUS) + npages++; + #if MEMSTOMP + memset(p, 0xF2, npages * PAGESIZE); + #endif + pool->freePages(pagenum, npages); + } + else + { // Add to free list + List *list = (List *)p; + + #if MEMSTOMP + memset(p, 0xF2, binsize[bin]); + #endif + + list->next = gcx->bucket[bin]; + gcx->bucket[bin] = list; + } + gcx->log_free(sentinel_add(p)); +} + +void *GC::mallocdup(void *o, size_t size) +{ + void *p; + + thread_invariant(gcx); + p = malloc(size); +#if CHECK_OUT_OF_MEM + if (!p) + return NULL; +#endif + return memcpy(p, o, size); +} + +/**************************************** + * Verify that pointer p: + * 1) belongs to this memory pool + * 2) points to the start of an allocated piece of memory + * 3) is not on a free list + */ + +void GC::check(void *p) +{ + if (p) + { + sentinel_invariant(p); +#if PTRCHECK >= 1 + Pool *pool; + unsigned pagenum; + Bins bin; + unsigned size; + + p = sentinel_sub(p); + pool = gcx->findPool(p); + assert(pool); + pagenum = ((char *)p - pool->baseAddr) / PAGESIZE; + bin = (Bins)pool->pagetable[pagenum]; + assert(bin <= B_PAGE); + size = binsize[bin]; + assert(((unsigned)p & (size - 1)) == 0); + +#if PTRCHECK >= 2 + if (bin < B_PAGE) + { + // Check that p is not on a free list + List *list; + + for (list = gcx->bucket[bin]; list; list = list->next) + { + assert((void *)list != p); + } + } +#endif +#endif + } +} + +void GC::error() +{ int i = 0; + + assert(i); +} + +void GC::addRoot(void *p) +{ + thread_invariant(gcx); + gcx->addRoot(p); +} + +void GC::removeRoot(void *p) +{ + gcx->removeRoot(p); +} + +void GC::addRange(void *pbot, void *ptop) +{ + thread_invariant(gcx); + gcx->addRange(pbot, ptop); +} + +void GC::removeRange(void *pbot) +{ + gcx->removeRange(pbot); +} + +void GC::fullcollect() +{ + thread_invariant(gcx); + + gcx->fullcollectshell(); + +#if 0 + { + GCStats stats; + + getStats(&stats); + PRINTF("poolsize = %x, usedsize = %x, freelistsize = %x\n", + stats.poolsize, stats.usedsize, stats.freelistsize); + } +#endif +} + +void GC::fullcollectNoStack() +{ + //WPRINTF(L"fullcollectNoStack()\n"); + gcx->noStack++; + fullcollect(); + gcx->log_collect(); + gcx->noStack--; +} + +void GC::gencollect() +{ + gcx->fullcollectshell(); +} + +void GC::minimize() +{ + // Not implemented, ignore +} + + +void GC::setFinalizer(void *p, GC_FINALIZER pFn) +{ + thread_invariant(gcx); + + gcx->finalizer = pFn; + gcx->doFinalize(p); +} + +/***************************************** + * Retrieve statistics about garbage collection. + * Useful for debugging and tuning. + */ + +void GC::getStats(GCStats *stats) +{ + unsigned psize = 0; + unsigned usize = 0; + unsigned flsize = 0; + + unsigned n; + unsigned bsize = 0; + +//WPRINTF(L"getStats()\n"); + memset(stats, 0, sizeof(*stats)); + for (n = 0; n < gcx->npools; n++) + { Pool *pool = gcx->pooltable[n]; + + psize += pool->ncommitted * PAGESIZE; + for (unsigned j = 0; j < pool->ncommitted; j++) + { + Bins bin = (Bins)pool->pagetable[j]; + if (bin == B_FREE) + stats->freeblocks++; + else if (bin == B_PAGE) + stats->pageblocks++; + else if (bin < B_PAGE) + bsize += PAGESIZE; + } + } + + for (n = 0; n < B_PAGE; n++) + { +//WPRINTF(L"bin %d\n", n); + for (List *list = gcx->bucket[n]; list; list = list->next) + { +//WPRINTF(L"\tlist %x\n", list); + flsize += binsize[n]; + } + } + + usize = bsize - flsize; + + stats->poolsize = psize; + stats->usedsize = bsize - flsize; + stats->freelistsize = flsize; +} + +/* ============================ Gcx =============================== */ + +void Gcx::init() +{ int dummy; + + memset(this, 0, sizeof(Gcx)); + stackBottom = (char *)&dummy; + log_init(); +#if THREADINVARIANT + self = pthread_self(); +#endif + invariant(); +} + +Gcx::~Gcx() +{ + invariant(); + + for (unsigned i = 0; i < npools; i++) + { Pool *pool = pooltable[i]; + + delete pool; + } + if (pooltable) + ::free(pooltable); + + if (roots) + ::free(roots); + + if (ranges) + ::free(ranges); +} + +void Gcx::invariant() +{ +#if INVARIANT + unsigned i; + + thread_invariant(this); // assure we're called on the right thread + for (i = 0; i < npools; i++) + { Pool *pool = pooltable[i]; + + pool->invariant(); + if (i == 0) + { + assert(minAddr == pool->baseAddr); + } + if (i + 1 < npools) + { + assert(pool->cmp(pooltable[i + 1]) < 0); + } + else if (i + 1 == npools) + { + assert(maxAddr == pool->topAddr); + } + } + + if (roots) + { + assert(rootdim != 0); + assert(nroots <= rootdim); + } + + if (ranges) + { + assert(rangedim != 0); + assert(nranges <= rangedim); + + for (i = 0; i < nranges; i++) + { + assert(ranges[i].pbot); + assert(ranges[i].ptop); + assert(ranges[i].pbot <= ranges[i].ptop); + } + } + + for (i = 0; i < B_PAGE; i++) + { + for (List *list = bucket[i]; list; list = list->next) + { + } + } +#endif +} + +/*************************************** + */ + +void Gcx::addRoot(void *p) +{ + if (nroots == rootdim) + { + unsigned newdim = rootdim * 2 + 16; + void **newroots; + + newroots = (void **)::malloc(newdim * sizeof(newroots[0])); +#if CHECK_OUT_OF_MEM + if (!newroots) + longjmp(g_setjmp_buf, 1); +#else + assert(newroots); +#endif + if (roots) + { memcpy(newroots, roots, nroots * sizeof(newroots[0])); + ::free(roots); + } + roots = newroots; + rootdim = newdim; + } + roots[nroots] = p; + nroots++; +} + +void Gcx::removeRoot(void *p) +{ + unsigned i; + for (i = nroots; i--;) + { + if (roots[i] == p) + { + nroots--; + memmove(roots + i, roots + i + 1, (nroots - i) * sizeof(roots[0])); + return; + } + } + assert(zero); +} + + +/*************************************** + */ + +void Gcx::addRange(void *pbot, void *ptop) +{ + //WPRINTF(L"Thread %x ", pthread_self()); + //WPRINTF(L"%x->Gcx::addRange(%x, %x), nranges = %d\n", this, pbot, ptop, nranges); + if (nranges == rangedim) + { + unsigned newdim = rangedim * 2 + 16; + Range *newranges; + + newranges = (Range *)::malloc(newdim * sizeof(newranges[0])); +#if CHECK_OUT_OF_MEM + if (!newranges) + longjmp(g_setjmp_buf, 1); +#else + assert(newranges); +#endif + if (ranges) + { memcpy(newranges, ranges, nranges * sizeof(newranges[0])); + ::free(ranges); + } + ranges = newranges; + rangedim = newdim; + } + ranges[nranges].pbot = pbot; + ranges[nranges].ptop = ptop; + nranges++; +} + +void Gcx::removeRange(void *pbot) +{ + //WPRINTF(L"Thread %x ", pthread_self()); + //WPRINTF(L"%x->Gcx::removeRange(%x), nranges = %d\n", this, pbot, nranges); + for (unsigned i = nranges; i--;) + { + if (ranges[i].pbot == pbot) + { + nranges--; + memmove(ranges + i, ranges + i + 1, (nranges - i) * sizeof(ranges[0])); + return; + } + } + //WPRINTF(L"Wrong thread\n"); + + // This is a fatal error, but ignore it at Sun's request. + // The problem is that we can get a Close() call on a thread + // other than the one the range was allocated on. + //assert(zero); +} + + +/*********************************** + * Allocate a new pool with at least npages in it. + * Sort it into pooltable[]. + * Return NULL if failed. + */ + +Pool *Gcx::newPool(unsigned npages) +{ + Pool *pool; + Pool **newpooltable; + unsigned newnpools; + unsigned i; + + //WPRINTF(L"************Gcx::newPool(npages = %d)****************\n", npages); + + // Round up to COMMITSIZE pages + npages = (npages + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1); + + // Minimum of POOLSIZE + if (npages < POOLSIZE/PAGESIZE) + npages = POOLSIZE/PAGESIZE; + + // Allocate successively larger pools up to 8 megs + if (npools) + { unsigned n; + + n = npools; + if (n > 8) + n = 8; // cap pool size at 8 megs + n *= (POOLSIZE / PAGESIZE); + if (npages < n) + npages = n; + } + + pool = (Pool *)::malloc(sizeof(Pool)); + if (pool) + { + pool->init(npages); + if (!pool->baseAddr) + goto Lerr; + + newnpools = npools + 1; + newpooltable = (Pool **)::realloc(pooltable, newnpools * sizeof(Pool *)); + if (!newpooltable) + goto Lerr; + + // Sort pool into newpooltable[] + for (i = 0; i < npools; i++) + { + if (pool->cmp(newpooltable[i]) < 0) + break; + } + memmove(newpooltable + i + 1, newpooltable + i, (npools - i) * sizeof(Pool *)); + newpooltable[i] = pool; + + pooltable = newpooltable; + npools = newnpools; + this->npages += npages; + + minAddr = pooltable[0]->baseAddr; + maxAddr = pooltable[npools - 1]->topAddr; + } + return pool; + + Lerr: + delete pool; + return NULL; +} + +/**************************************** + * Allocate a chunk of memory that is larger than a page. + * Return NULL if out of memory. + */ + +void *Gcx::bigAlloc(unsigned size) +{ + Pool *pool; + unsigned npages; + unsigned n; + unsigned pn; + unsigned freedpages; + void *p; + int state; + + npages = (size + PAGESIZE - 1) / PAGESIZE; + + for (state = 0; ; ) + { + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pn = pool->allocPages(npages); + if (pn != ~0u) + goto L1; + } + + // Failed + switch (state) + { + case 0: + // Try collecting + freedpages = fullcollectshell(); + if (freedpages >= npools * ((POOLSIZE / PAGESIZE) / 2)) + { state = 1; + continue; + } + // Allocate new pool + pool = newPool(npages); + if (!pool) + { state = 2; + continue; + } + pn = pool->allocPages(npages); + assert(pn != ~0u); + goto L1; + + case 1: + // Allocate new pool + pool = newPool(npages); + if (!pool) + goto Lnomemory; + pn = pool->allocPages(npages); + assert(pn != ~0u); + goto L1; + + case 2: + goto Lnomemory; + } + } + + L1: + pool->pagetable[pn] = B_PAGE; + if (npages > 1) + memset(&pool->pagetable[pn + 1], B_PAGEPLUS, npages - 1); + p = pool->baseAddr + pn * PAGESIZE; + memset((char *)p + size, 0, npages * PAGESIZE - size); + #if MEMSTOMP + memset(p, 0xF1, size); + #endif + //printf("\tp = %x\n", p); + return p; + + Lnomemory: + //assert(zero); + return NULL; +} + +/******************************* + * Allocate a page of bin's. + * Returns: + * 0 failed + */ + +int Gcx::allocPage(Bins bin) +{ + Pool *pool; + unsigned n; + unsigned pn; + char *p; + char *ptop; + + //printf("Gcx::allocPage(bin = %d)\n", bin); + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pn = pool->allocPages(1); + if (pn != ~0u) + goto L1; + } + return 0; // failed + + L1: + pool->pagetable[pn] = (unsigned char)bin; + + // Convert page to free list + unsigned size = binsize[bin]; + List **b = &bucket[bin]; + + p = pool->baseAddr + pn * PAGESIZE; + ptop = p + PAGESIZE; + for (; p < ptop; p += size) + { List *list = (List *)p; + + list->next = *b; + *b = list; + } + return 1; +} + +/******************************* + * Find Pool that pointer is in. + * Return NULL if not in a Pool. + * Assume pooltable[] is sorted. + */ + +Pool *Gcx::findPool(void *p) +{ + if (p >= minAddr && p < maxAddr) + { + if (npools == 1) + { + return pooltable[0]; + } + + for (unsigned i = 0; i < npools; i++) + { Pool *pool; + + pool = pooltable[i]; + if (p < pool->topAddr) + { if (pool->baseAddr <= p) + return pool; + break; + } + } + } + return NULL; +} + +/******************************* + * Find size of pointer p. + * Returns 0 if not a gc'd pointer + */ + +unsigned Gcx::findSize(void *p) +{ + Pool *pool; + unsigned size = 0; + + pool = findPool(p); + if (pool) + { + unsigned pagenum; + Bins bin; + + pagenum = ((unsigned)((char *)p - pool->baseAddr)) / PAGESIZE; + bin = (Bins)pool->pagetable[pagenum]; + size = binsize[bin]; + if (bin == B_PAGE) + { unsigned npages = pool->ncommitted; + unsigned char *pt; + unsigned i; + + pt = &pool->pagetable[0]; + for (i = pagenum + 1; i < npages; i++) + { + if (pt[i] != B_PAGEPLUS) + break; + } + size = (i - pagenum) * PAGESIZE; + } + } + return size; +} + + +/******************************* + * Compute bin for size. + */ + +Bins Gcx::findBin(unsigned size) +{ Bins bin; + + if (size <= 256) + { + if (size <= 64) + { + if (size <= 16) + bin = B_16; + else if (size <= 32) + bin = B_32; + else + bin = B_64; + } + else + { + if (size <= 128) + bin = B_128; + else + bin = B_256; + } + } + else + { + if (size <= 1024) + { + if (size <= 512) + bin = B_512; + else + bin = B_1024; + } + else + { + if (size <= 2048) + bin = B_2048; + else + bin = B_PAGE; + } + } + return bin; +} + +/************************************ + * Search a range of memory values and mark any pointers into the GC pool. + */ + +void Gcx::mark(void *pbot, void *ptop) +{ + void **p1 = (void **)pbot; + void **p2 = (void **)ptop; + unsigned changes = 0; + Pool *pool; + + //if (log) printf("Gcx::mark(%p .. %p)\n", pbot, ptop); + if (npools == 1) + pool = pooltable[0]; + + for (; p1 < p2; p1++) + { + char *p = (char *)(*p1); + + //if (log) WPRINTF(L"\tmark %x\n", p); + if (p >= minAddr && p < maxAddr /*&& ((int)p & 3) == 0*/) + { + if (npools != 1) + { pool = findPool(p); + if (!pool) + continue; + } + + unsigned offset = (unsigned)(p - pool->baseAddr); + unsigned bit; + unsigned pn = offset / PAGESIZE; + Bins bin = (Bins)pool->pagetable[pn]; + + //printf("\t\tfound pool %x, base=%x, pn = %d, bin = %d, bit = x%x\n", pool, pool->baseAddr, pn, bin, bit); + + // Adjust bit to be at start of allocated memory block + if (bin <= B_PAGE) + { + bit = (offset & notbinsize[bin]) >> 4; + //printf("\t\tbit = x%x\n", bit); + } + else if (bin == B_PAGEPLUS) + { + do + { --pn; + } while ((Bins)pool->pagetable[pn] == B_PAGEPLUS); + bit = pn * (PAGESIZE / 16); + } + else + { + // Don't mark bits in B_FREE or B_UNCOMMITTED pages + continue; + } + + //printf("\t\tmark(x%x) = %d\n", bit, pool->mark.test(bit)); + if (!pool->mark.testSet(bit)) + { + //if (log) PRINTF("\t\tmarking %p\n", p); + //pool->mark.set(bit); +#if ATOMIC + if (!pool->atomic.test(bit)) +#endif + { + pool->scan.set(bit); + changes += 1; + } + log_parent(sentinel_add(pool->baseAddr + bit * 16), sentinel_add(pbot)); + } + } + } + anychanges += changes; +} + +/********************************* + * Return number of full pages free'd. + */ + +unsigned Gcx::fullcollectshell() +{ +#if __GCC__ + asm("pushl %eax"); + asm("pushl %ebx"); + asm("pushl %ecx"); + asm("pushl %edx"); + asm("pushl %ebp"); + asm("pushl %esi"); + asm("pushl %edi"); + // This function must ensure that all register variables are on the stack + // before &dummy + unsigned dummy; + dummy = fullcollect(&dummy - 7); + asm("addl $28,%esp"); +#elif _MSC_VER || __DMC__ + __asm push eax; + __asm push ebx; + __asm push ecx; + __asm push edx; + __asm push ebp; + __asm push esi; + __asm push edi; + // This function must ensure that all register variables are on the stack + // before &dummy + unsigned dummy; + dummy = fullcollect(&dummy - 7); + __asm add esp,28; +#else + // This function must ensure that all register variables are on the stack + // before &dummy + unsigned dummy; + dummy = fullcollect(&dummy); +#endif + return dummy; +} + +unsigned Gcx::fullcollect(void *stackTop) +{ + unsigned n; + Pool *pool; + unsigned freedpages = 0; + unsigned freed = 0; + unsigned recoveredpages = 0; + +int x1 = 0; +int x2 = 0; +int x3 = 0; + + //PRINTF("Gcx::fullcollect() npools = %d\n", npools); +//PerfTimer pf(L"fullcollect"); + invariant(); + anychanges = 0; + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pool->mark.zero(); + pool->scan.zero(); + pool->freebits.zero(); + } + + // Mark each free entry, so it doesn't get scanned + for (n = 0; n < B_PAGE; n++) + { + List *list = bucket[n]; + List *prev = NULL; + + //WPRINTF(L"bin = %d\n", n); + for (; list; list = list->next) + { + if (list->prev != prev) + list->prev = prev; + prev = list; + pool = findPool(list); + assert(pool); + //WPRINTF(L" list = %x, bit = %d\n", list, (unsigned)((char *)list - pool->baseAddr) / 16); + pool->freebits.set((unsigned)((char *)list - pool->baseAddr) / 16); + assert(pool->freebits.test((unsigned)((char *)list - pool->baseAddr) / 16)); + } + } + +#if SENTINEL + // Every memory item should either be on the free list or have the sentinel + // set. + for (n = 0; n < npools; n++) + { unsigned pn; + unsigned ncommitted; + + pool = pooltable[n]; + ncommitted = pool->ncommitted; + for (pn = 0; pn < ncommitted; pn++) + { + char *p; + char *ptop; + Bins bin = (Bins)pool->pagetable[pn]; + unsigned bit; + + p = pool->baseAddr + pn * PAGESIZE; + ptop = p + PAGESIZE; + + //WPRINTF(L"pn = %d, bin = %d\n", pn, bin); + if (bin < B_PAGE) + { + unsigned size = binsize[bin]; + unsigned bitstride = size / 16; + bit = pn * (PAGESIZE/16); + + for (; p < ptop; p += size, bit += bitstride) + { + if (pool->freebits.test(bit)) + ; + else + { + //WPRINTF(L"p = %x, *p = %x\n", p, *(unsigned *)p); + sentinel_invariant(sentinel_add(p)); + } + } + } + } + } +#endif + + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pool->mark.copy(&pool->freebits); + } + + if (!noStack) + { + // Scan stack + //WPRINTF(L"scan stack bot = %x, top = %x\n", stackTop, stackBottom); +#if STACKGROWSDOWN + mark(stackTop, stackBottom); +#else + mark(stackBottom, stackTop); +#endif + } + + // Scan roots[] + //WPRINTF(L"scan roots[]\n"); + mark(roots, roots + nroots); + + // Scan ranges[] + //WPRINTF(L"scan ranges[]\n"); + //log++; + for (n = 0; n < nranges; n++) + { + //WPRINTF(L"\t%x .. %x\n", ranges[n].pbot, ranges[n].ptop); + mark(ranges[n].pbot, ranges[n].ptop); + } + //log--; + + //WPRINTF(L"\tscan heap\n"); +{ +//PerfTimer pf(L"fullcollect: scanning "); + while (anychanges) + { + //WPRINTF(L"anychanges = %d\n", anychanges); + anychanges = 0; + for (n = 0; n < npools; n++) + { + unsigned *bbase; + unsigned *b; + unsigned *btop; + + pool = pooltable[n]; + + bbase = pool->scan.base(); + btop = bbase + pool->scan.nwords; + for (b = bbase; b < btop;) + { Bins bin; + unsigned pn; + unsigned u; + unsigned bitm; + char *o; + char *ostart; + + bitm = *b; + if (!bitm) + { b++; + continue; + } + //WPRINTF(L"bitm = x%08x, b = %x\n", bitm, b); + pn = (b - bbase) / (PAGESIZE / (32 * 16)); + bin = (Bins)pool->pagetable[pn]; + o = pool->baseAddr + (b - bbase) * 32 * 16; + *b = 0; + + if (bin < B_PAGE) + { int size = binsize[bin]; + unsigned index; + + do + { + index = _inline_bsf(bitm); + o += index * 16; + mark(o, o + size); + o += 16; + + // Cannot combine these two, because 0x80000000<<32 is + // still 0x80000000 + bitm >>= 1; + } while ((bitm >>= index) != 0); + } + else if (bin == B_PAGE) + { + u = 1; + while (pn + u < pool->ncommitted && + pool->pagetable[pn + u] == B_PAGEPLUS) + u++; + o = pool->baseAddr + pn * PAGESIZE; + mark(o, o + u * PAGESIZE); + b = bbase + (pn + u) * (PAGESIZE / (32 * 16)); + } + else + { + assert(0); + } + } + } + } +} + + // Free up everything not marked +{ +//PerfTimer pf(L"fullcollect: freeing "); + //WPRINTF(L"\tfree'ing\n"); + for (n = 0; n < npools; n++) + { unsigned pn; + unsigned ncommitted; + unsigned *bbase; + unsigned *fbase; + int delta; + + pool = pooltable[n]; + bbase = pool->mark.base(); + delta = pool->freebits.base() - bbase; + ncommitted = pool->ncommitted; + for (pn = 0; pn < ncommitted; pn++, bbase += PAGESIZE / (32 * 16)) + { + Bins bin = (Bins)pool->pagetable[pn]; + + if (bin < B_PAGE) + { char *p; + char *ptop; + unsigned bit; + unsigned bitstride; + unsigned size = binsize[bin]; + + p = pool->baseAddr + pn * PAGESIZE; + ptop = p + PAGESIZE; + bit = pn * (PAGESIZE/16); + bitstride = size / 16; + +#if 1 + // If free'd entire page + fbase = bbase + delta; +#if INVARIANT + assert(bbase == pool->mark.base() + pn * 8); +#endif +#if 1 + if ((bbase[0] ^ fbase[0]) == 0 && + (bbase[1] ^ fbase[1]) == 0 && + (bbase[2] ^ fbase[2]) == 0 && + (bbase[3] ^ fbase[3]) == 0 && + (bbase[4] ^ fbase[4]) == 0 && + (bbase[5] ^ fbase[5]) == 0 && + (bbase[6] ^ fbase[6]) == 0 && + (bbase[7] ^ fbase[7]) == 0) +#else + if ((bbase[0]) == 0 && + (bbase[1]) == 0 && + (bbase[2]) == 0 && + (bbase[3]) == 0 && + (bbase[4]) == 0 && + (bbase[5]) == 0 && + (bbase[6]) == 0 && + (bbase[7]) == 0) +#endif + { +x1++; + for (; p < ptop; p += size, bit += bitstride) + { +#if LISTPREV + if (pool->freebits.test(bit)) + { // Remove from free list + List *list = (List *)p; + + //WPRINTF(L"bbase = %x, bbase[0] = %x, mark = %d\n", bbase, bbase[0], pool->mark.test(bit)); + //WPRINTF(L"p = %x, bin = %d, size = %d, bit = %d\n", p, bin, size, bit); + + if (bucket[bin] == list) + bucket[bin] = list->next; + if (list->next) + list->next->prev = list->prev;; + if (list->prev) + list->prev->next = list->next; + continue; + } +#endif +#if ATOMIC + pool->atomic.clear(bit); +#endif + if (finalizer && pool->finals.nbits && + pool->finals.testClear(bit)) + { + (*finalizer)((List *)sentinel_add(p), NULL); + } + + List *list = (List *)p; + //printf("\tcollecting %x\n", list); + log_free(sentinel_add(list)); + + #if MEMSTOMP + memset(p, 0xF3, size); + #endif + } + pool->pagetable[pn] = B_FREE; + recoveredpages++; + //printf("freeing entire page %d\n", pn); + continue; + } +#endif + if (bbase[0] == binset[bin][0] && + bbase[1] == binset[bin][1] && + bbase[2] == binset[bin][2] && + bbase[3] == binset[bin][3] && + bbase[4] == binset[bin][4] && + bbase[5] == binset[bin][5] && + bbase[6] == binset[bin][6] && + bbase[7] == binset[bin][7]) + { +x2++; + continue; + } +x3++; + for (; p < ptop; p += size, bit += bitstride) + { + if (!pool->mark.test(bit)) + { + sentinel_invariant(sentinel_add(p)); + +#if ATOMIC + pool->atomic.clear(bit); +#endif + pool->freebits.set(bit); + if (finalizer && pool->finals.nbits && + pool->finals.testClear(bit)) + { + (*finalizer)((List *)sentinel_add(p), NULL); + } + + List *list = (List *)p; + //WPRINTF(L"\tcollecting %x, bin = %d\n", list, bin); + log_free(sentinel_add(list)); + + #if MEMSTOMP + memset(p, 0xF3, size); + #endif + +#if LISTPREV + // Add to free list + list->next = bucket[bin]; + list->prev = NULL; + if (list->next) + list->next->prev = list; + bucket[bin] = list; +#endif + freed += size; + } + } + } + else if (bin == B_PAGE) + { unsigned bit = pn * (PAGESIZE / 16); + + if (!pool->mark.test(bit)) + { char *p = pool->baseAddr + pn * PAGESIZE; + + sentinel_invariant(sentinel_add(p)); +#if ATOMIC + pool->atomic.clear(bit); +#endif + if (finalizer && pool->finals.nbits && + pool->finals.testClear(bit)) + { + (*finalizer)(sentinel_add(p), NULL); + } + + //printf("\tcollecting big %x\n", p); + log_free(sentinel_add(p)); + pool->pagetable[pn] = B_FREE; + freedpages++; + #if MEMSTOMP + memset(p, 0xF3, PAGESIZE); + #endif + while (pn + 1 < ncommitted && pool->pagetable[pn + 1] == B_PAGEPLUS) + { + pn++; + bbase += PAGESIZE / (32 * 16); + pool->pagetable[pn] = B_FREE; + freedpages++; + + #if MEMSTOMP + p += PAGESIZE; + memset(p, 0xF3, PAGESIZE); + #endif + } + } + } + } + } +} + +#if !LISTPREV + // Zero buckets + memset(bucket, 0, sizeof(bucket)); + + // Free complete pages, rebuild free list + //WPRINTF(L"\tfree complete pages\n"); +PerfTimer pf(L"fullcollect: recoverpages"); + recoveredpages = 0; + for (n = 0; n < npools; n++) + { unsigned pn; + unsigned ncommitted; + + pool = pooltable[n]; + ncommitted = pool->ncommitted; + for (pn = 0; pn < ncommitted; pn++) + { + Bins bin = (Bins)pool->pagetable[pn]; + unsigned bit; + unsigned u; + + if (bin < B_PAGE) + { + unsigned size = binsize[bin]; + unsigned bitstride = size / 16; + unsigned bitbase = pn * (PAGESIZE / 16); + unsigned bittop = bitbase + (PAGESIZE / 16); + char *p; + + bit = bitbase; + for (bit = bitbase; bit < bittop; bit += bitstride) + { if (!pool->freebits.test(bit)) + goto Lnotfree; + } + pool->pagetable[pn] = B_FREE; + recoveredpages++; + continue; + + Lnotfree: + p = pool->baseAddr + pn * PAGESIZE; + for (bit = bitbase; bit < bittop; bit += bitstride) + { if (pool->freebits.test(bit)) + { List *list; + + u = (bit - bitbase) * 16; + list = (List *)(p + u); + if (list->next != bucket[bin]) // avoid unnecessary writes + list->next = bucket[bin]; + bucket[bin] = list; + } + } + } + } + } +#endif + +#undef printf +// printf("recovered pages = %d\n", recoveredpages); +// printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, npools); +#define printf 1 || printf + + //WPRINTF(L"\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, npools); + invariant(); + +#if 0 +if (noStack) +{ + WPRINTF(L"\tdone, freedpages = %d, recoveredpages = %d, freed = %d, total = %d\n", freedpages, recoveredpages, freed / PAGESIZE, freedpages + recoveredpages + freed / PAGESIZE); + WPRINTF(L"\tx1 = %d, x2 = %d, x3 = %d\n", x1, x2, x3); + int psize = 0; + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + psize += pool->topAddr - pool->baseAddr; + } + WPRINTF(L"total memory = x%x (npages=%d) in %d pools\n", psize, npages, npools); + + psize = 0; + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + for (unsigned i = 0; i < pool->ncommitted; i++) + { + if (pool->pagetable[i] < B_FREE) + { psize++; + if (psize <= 25) + WPRINTF(L"\tbin = %d\n", pool->pagetable[i]); + } + } + } + WPRINTF(L"total used pages = %d\n", psize); +} +#endif + return freedpages + recoveredpages + freed / PAGESIZE; +} + +/********************************* + * Run finalizer on p when it is free'd. + */ + +void Gcx::doFinalize(void *p) +{ + Pool *pool = findPool(p); + assert(pool); + + // Only allocate finals[] if we actually need it + if (!pool->finals.nbits) + pool->finals.alloc(pool->mark.nbits); + + pool->finals.set(((char *)p - pool->baseAddr) / 16); +} + +/* ============================ Pool =============================== */ + +void Pool::init(unsigned npages) +{ + unsigned poolsize; + + //printf("Pool::Pool(%u)\n", npages); + poolsize = npages * PAGESIZE; + assert(poolsize >= POOLSIZE); + baseAddr = (char *)os_mem_map(poolsize); + + if (!baseAddr) + { +#if CHECK_OUT_OF_MEM + longjmp(g_setjmp_buf, 1); +#endif + WPRINTF(L"GC fail: poolsize = x%x, errno = %d\n", poolsize, errno); +#if USEROOT + PRINTF("message = '%s'\n", sys_errlist[errno]); +#else + printf("message = '%s'\n", sys_errlist[errno]); +#endif + npages = 0; + poolsize = 0; + } + //assert(baseAddr); + topAddr = baseAddr + poolsize; + + mark.alloc(poolsize / 16); + scan.alloc(poolsize / 16); + freebits.alloc(poolsize / 16); +#if ATOMIC + atomic.alloc(poolsize / 16); +#endif + + pagetable = (unsigned char *)::malloc(npages); + memset(pagetable, B_UNCOMMITTED, npages); + + this->npages = npages; + ncommitted = 0; + + invariant(); +} + +Pool::~Pool() +{ + invariant(); + if (baseAddr) + { + int result; + + if (ncommitted) + { + result = os_mem_decommit(baseAddr, 0, ncommitted * PAGESIZE); + assert(result == 0); + } + + if (npages) + { + result = os_mem_unmap(baseAddr, npages * PAGESIZE); + assert(result == 0); + } + } + if (pagetable) + ::free(pagetable); +} + +void Pool::invariant() +{ +#if INVARIANT + mark.invariant(); + scan.invariant(); + + assert(baseAddr < topAddr); + assert(baseAddr + npages * PAGESIZE == topAddr); + assert(ncommitted <= npages); + + for (unsigned i = 0; i < npages; i++) + { Bins bin = (Bins)pagetable[i]; + + assert(bin < B_MAX); +#if 0 + // Buggy GCC doesn't compile this right with -O + if (i < ncommitted) + assert(bin != B_UNCOMMITTED); + else + assert(bin == B_UNCOMMITTED); +#endif + } +#endif +} + +/*************************** + * Used for sorting pooltable[] + */ + +int Pool::cmp(Pool *p2) +{ + return baseAddr - p2->baseAddr; +} + +/************************************** + * Allocate n pages from Pool. + * Returns ~0u on failure. + */ + +unsigned Pool::allocPages(unsigned n) +{ + unsigned i; + unsigned n2; + + //printf("Pool::allocPages(n = %d)\n", n); + n2 = n; + for (i = 0; i < ncommitted; i++) + { + if (pagetable[i] == B_FREE) + { + if (--n2 == 0) + { //printf("\texisting pn = %d\n", i - n + 1); + return i - n + 1; + } + } + else + n2 = n; + } + if (ncommitted + n < npages) + { + unsigned tocommit; + + tocommit = (n + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1); + if (ncommitted + tocommit > npages) + tocommit = npages - ncommitted; + //printf("\tlooking to commit %d more pages\n", tocommit); + //fflush(stdout); + if (os_mem_commit(baseAddr, ncommitted * PAGESIZE, tocommit * PAGESIZE) == 0) + { + memset(pagetable + ncommitted, B_FREE, tocommit); + i = ncommitted; + ncommitted += tocommit; + + while (i && pagetable[i - 1] == B_FREE) + i--; + + return i; + } + //printf("\tfailed to commit %d pages\n", tocommit); + } + + return ~0u; +} + +/********************************** + * Free npages pages starting with pagenum. + */ + +void Pool::freePages(unsigned pagenum, unsigned npages) +{ + memset(&pagetable[pagenum], B_FREE, npages); +} + +/* ======================= Leak Detector =========================== */ + +#if LOGGING + +struct Log +{ + void *p; + unsigned size; + unsigned line; + char *file; + void *parent; + + void print(); +}; + +void Log::print() +{ + WPRINTF(L" p = %x, size = %d, parent = %x ", p, size, parent); + if (file) + { + PRINTF("%s(%u)", file, line); + } + WPRINTF(L"\n"); +} + +LogArray::LogArray() +{ + data = NULL; + dim = 0; + allocdim = 0; +} + +LogArray::~LogArray() +{ + if (data) + ::free(data); + data = NULL; +} + +void LogArray::reserve(unsigned nentries) +{ + //WPRINTF(L"LogArray::reserve(%d)\n", nentries); + assert(dim <= allocdim); + if (allocdim - dim < nentries) + { + allocdim = (dim + nentries) * 2; + assert(dim + nentries <= allocdim); + if (!data) + { + data = (Log *)::malloc(allocdim * sizeof(*data)); + } + else + { Log *newdata; + + newdata = (Log *)::malloc(allocdim * sizeof(*data)); + assert(newdata); + memcpy(newdata, data, dim * sizeof(Log)); + ::free(data); + data = newdata; + } + assert(!allocdim || data); + } +} + +void LogArray::push(Log log) +{ + reserve(1); + data[dim++] = log; +} + +void LogArray::remove(unsigned i) +{ + memmove(data + i, data + i + 1, (dim - i) * sizeof(data[0])); + dim--; +} + +unsigned LogArray::find(void *p) +{ + for (unsigned i = 0; i < dim; i++) + { + if (data[i].p == p) + return i; + } + return ~0u; // not found +} + +void LogArray::copy(LogArray *from) +{ + if (from->dim > dim) + { reserve(from->dim - dim); + assert(from->dim <= allocdim); + } + memcpy(data, from->data, from->dim * sizeof(data[0])); + dim = from->dim; +} + + +/****************************/ + +void Gcx::log_init() +{ + //WPRINTF(L"+log_init()\n"); + current.reserve(1000); + prev.reserve(1000); + //WPRINTF(L"-log_init()\n"); +} + +void Gcx::log_parent(void *p, void *parent) +{ + //WPRINTF(L"+log_parent()\n"); + unsigned i; + + i = current.find(p); + if (i == ~0u) + { + WPRINTF(L"parent'ing unallocated memory %x, parent = %x\n", p, parent); + Pool *pool; + pool = findPool(p); + assert(pool); + unsigned offset = (unsigned)((char *)p - pool->baseAddr); + unsigned bit; + unsigned pn = offset / PAGESIZE; + Bins bin = (Bins)pool->pagetable[pn]; + bit = (offset & notbinsize[bin]); + WPRINTF(L"\tbin = %d, offset = x%x, bit = x%x\n", bin, offset, bit); + } + else + { + current.data[i].parent = parent; + } + //WPRINTF(L"-log_parent()\n"); +} + +void Gcx::log_malloc(void *p, unsigned size) +{ + //WPRINTF(L"+log_malloc(p = %x, size = %d)\n", p, size); + Log log; + + log.p = p; + log.size = size; + log.line = GC::line; + log.file = GC::file; + log.parent = NULL; + + GC::line = 0; + GC::file = NULL; + + current.push(log); + //WPRINTF(L"-log_malloc()\n"); +} + +void Gcx::log_free(void *p) +{ + //WPRINTF(L"+log_free(%x)\n", p); + unsigned i; + + i = current.find(p); + if (i == ~0u) + { + WPRINTF(L"free'ing unallocated memory %x\n", p); + } + else + current.remove(i); + //WPRINTF(L"-log_free()\n"); +} + +void Gcx::log_collect() +{ + //WPRINTF(L"+log_collect()\n"); + // Print everything in current that is not in prev + + WPRINTF(L"New pointers this cycle: --------------------------------\n"); + int used = 0; + for (unsigned i = 0; i < current.dim; i++) + { + unsigned j; + + j = prev.find(current.data[i].p); + if (j == ~0u) + current.data[i].print(); + else + used++; + } + + WPRINTF(L"All roots this cycle: --------------------------------\n"); + for (unsigned i = 0; i < current.dim; i++) + { + void *p; + unsigned j; + + p = current.data[i].p; + if (!findPool(current.data[i].parent)) + { + j = prev.find(current.data[i].p); + if (j == ~0u) + WPRINTF(L"N"); + else + WPRINTF(L" ");; + current.data[i].print(); + } + } + + WPRINTF(L"Used = %d-------------------------------------------------\n", used); + prev.copy(¤t); + + WPRINTF(L"-log_collect()\n"); +} + +#endif diff --git a/root/gc/gc.h b/root/gc/gc.h new file mode 100644 index 00000000..9f5e926b --- /dev/null +++ b/root/gc/gc.h @@ -0,0 +1,68 @@ +// Copyright (c) 2000-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef GC_H +#define GC_H + +struct Gcx; // private data + +typedef void (*GC_FINALIZER)(void *p, void *dummy); + +struct GCStats +{ + unsigned poolsize; // total size of pool + unsigned usedsize; // bytes allocated + unsigned freeblocks; // number of blocks marked FREE + unsigned freelistsize; // total of memory on free lists + unsigned pageblocks; // number of blocks marked PAGE +}; + +struct GC +{ + // For passing to debug code + static unsigned line; + static char *file; +// #define GC_LOG() ((GC::line = __LINE__), (GC::file = __FILE__)) + #define GC_LOG() ((void)0) + + Gcx *gcx; // implementation + + ~GC(); + + void init(); + + char *strdup(const char *s); + void *malloc(size_t size); + void *malloc_atomic(size_t size); + void *calloc(size_t size, size_t n); + void *realloc(void *p, size_t size); + void free(void *p); + void *mallocdup(void *o, size_t size); + void check(void *p); + void error(); + + void setStackBottom(void *p); + + void addRoot(void *p); // add p to list of roots + void removeRoot(void *p); // remove p from list of roots + + void addRange(void *pbot, void *ptop); // add range to scan for roots + void removeRange(void *pbot); // remove range + + void fullcollect(); // do full garbage collection + void fullcollectNoStack(); // do full garbage collection; no scan stack + void gencollect(); // do generational garbage collection + void minimize(); // minimize physical memory usage + + void setFinalizer(void *p, GC_FINALIZER pFn); + + void getStats(GCStats *stats); +}; + +#endif + diff --git a/root/gc/gccbitops.h b/root/gc/gccbitops.h new file mode 100644 index 00000000..89fd82b7 --- /dev/null +++ b/root/gc/gccbitops.h @@ -0,0 +1,69 @@ +// Copyright (c) 2000-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +// Bit operations for GCC and I386 + +#ifndef GCCBITOPS_H +#define GCCBITOPS_H 1 + +inline int _inline_bsf(int w) +{ int index; + + __asm__ __volatile__ + ( + "bsfl %1, %0 \n\t" + : "=r" (index) + : "r" (w) + ); + return index; +} + + +inline int _inline_bt(unsigned *p, int i) +{ + char result; + + __asm__ __volatile__ + ( + "btl %2,%1 \n\t" + "setc %0 \n\t" + :"=r" (result) + :"m" (*p), "r" (i) + ); + return result; +} + +inline int _inline_bts(unsigned *p, int i) +{ + char result; + + __asm__ __volatile__ + ( + "btsl %2,%1 \n\t" + "setc %0 \n\t" + :"=r" (result) + :"m" (*p), "r" (i) + ); + return result; +} + +inline int _inline_btr(unsigned *p, int i) +{ + char result; + + __asm__ __volatile__ + ( + "btrl %2,%1 \n\t" + "setc %0 \n\t" + :"=r" (result) + :"m" (*p), "r" (i) + ); + return result; +} + +#endif diff --git a/root/gc/linux.c b/root/gc/linux.c new file mode 100644 index 00000000..5d50ad51 --- /dev/null +++ b/root/gc/linux.c @@ -0,0 +1,96 @@ +// Copyright (c) 2000-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#include +#include +#include + + +/************************************* + * This is all necessary to get fd initialized at startup. + */ + +#define FDMAP 0 + +#if FDMAP +#include + +struct OS_INIT +{ + static int fd; + + OS_INIT(); +}; + +OS_INIT os_init; + +int OS_INIT::fd = 0; + +OS_INIT::OS_INIT() +{ + fd = open("/dev/zero", O_RDONLY); +} +#endif + +/*********************************** + * Map memory. + */ + +void *os_mem_map(unsigned nbytes) +{ void *p; + + errno = 0; +#if FDMAP + p = mmap(NULL, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE, OS_INIT::fd, 0); +#else + p = mmap(NULL, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); +#endif + return (p == MAP_FAILED) ? NULL : p; +} + +/*********************************** + * Commit memory. + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_commit(void *base, unsigned offset, unsigned nbytes) +{ + return 0; +} + + +/*********************************** + * Decommit memory. + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_decommit(void *base, unsigned offset, unsigned nbytes) +{ + return 0; +} + +/*********************************** + * Unmap memory allocated with os_mem_map(). + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_unmap(void *base, unsigned nbytes) +{ + return munmap(base, nbytes); +} + + + + diff --git a/root/gc/mscbitops.h b/root/gc/mscbitops.h new file mode 100644 index 00000000..bc84a3da --- /dev/null +++ b/root/gc/mscbitops.h @@ -0,0 +1,25 @@ +// Copyright (c) 2000-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +// Bit operations for MSC and I386 + +#ifndef MSCBITOPS_H +#define MSCBITOPS_H 1 + +inline int _inline_bsf(int w) +{ int index; + + index = 0; + while (!(w & 1)) + { index++; + w >>= 1; + } + return index; +} + +#endif diff --git a/root/gc/os.h b/root/gc/os.h new file mode 100644 index 00000000..4ee7d8f1 --- /dev/null +++ b/root/gc/os.h @@ -0,0 +1,25 @@ +// Copyright (c) 2000-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +// OS specific routines + +void *os_mem_map(unsigned nbytes); +int os_mem_commit(void *base, unsigned offset, unsigned nbytes); +int os_mem_decommit(void *base, unsigned offset, unsigned nbytes); +int os_mem_unmap(void *base, unsigned nbytes); + + +// Threading + +#if defined linux +#include +#else +typedef long pthread_t; +pthread_t pthread_self(); +#endif diff --git a/root/gc/testgc.c b/root/gc/testgc.c new file mode 100644 index 00000000..683c1f86 --- /dev/null +++ b/root/gc/testgc.c @@ -0,0 +1,300 @@ +/* Digital Mars DMDScript source code. + * Copyright (c) 2001-2007 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com/dscript/cppscript.html + * + * This software is for evaluation purposes only. + * It may not be redistributed in binary or source form, + * incorporated into any product or program, + * or used for any purpose other than evaluating it. + * To purchase a license, + * see http://www.digitalmars.com/dscript/cpplicense.html + * + * Use at your own risk. There is no warranty, express or implied. + */ + +// GC tester program + +#include +#include +#include +#include + +#include "gc.h" + +void *stackbottom; + +void printStats(GC *gc) +{ + GCStats stats; + + gc->getStats(&stats); + printf("poolsize = x%x, usedsize = x%x, freelistsize = x%x, freeblocks = %d, pageblocks = %d\n", + stats.poolsize, stats.usedsize, stats.freelistsize, stats.freeblocks, stats.pageblocks); +} + +#define PERMUTE(key) (key + 1) + +void fill(void *p, unsigned key, unsigned size) +{ + unsigned i; + char *q = (char *)p; + + for (i = 0; i < size; i++) + { + key = PERMUTE(key); + q[i] = (char)key; + } +} + +void verify(void *p, unsigned key, unsigned size) +{ + unsigned i; + char *q = (char *)p; + + for (i = 0; i < size; i++) + { + key = PERMUTE(key); + assert(q[i] == (char)key); + } +} + +long desregs() +{ + return strlen("foo"); +} + +/* ---------------------------- */ + +void smoke() +{ + GC *gc; + + printf("--------------------------smoke()\n"); + + gc = new GC(); + delete gc; + + gc = new GC(); + gc->init(); + delete gc; + + gc = new GC(); + gc->init(); + gc->setStackBottom(stackbottom); + char *p = (char *)gc->malloc(10); + assert(p); + strcpy(p, "Hello!"); + char *p2 = gc->strdup(p); + printf("p2 = %x, '%s'\n", p2, p2); + int result = strcmp(p, p2); + assert(result == 0); + gc->strdup(p); + + printf("p = %x\n", p); + p = NULL; + gc->fullcollect(); + printStats(gc); + + delete gc; +} + +/* ---------------------------- */ + +void finalizer(void *p, void *dummy) +{ + (void)p; + (void)dummy; +} + +void smoke2() +{ + GC *gc; + int *p; + int i; + + #define SMOKE2_SIZE 100 + int *foo[SMOKE2_SIZE]; + + printf("--------------------------smoke2()\n"); + + gc = new GC(); + gc->init(); + gc->setStackBottom(stackbottom); + + for (i = 0; i < SMOKE2_SIZE; i++) + { + p = (int *)gc->calloc(i + 1, 500); + p[0] = i * 3; + foo[i] = p; + gc->setFinalizer(p, finalizer); + } + + for (i = 0; i < SMOKE2_SIZE; i += 2) + { + p = foo[i]; + if (p[0] != i * 3) + { + printf("p = %x, i = %d, p[0] = %d\n", p, i, p[0]); + fflush(stdout); + } + assert(p[0] == i * 3); + gc->free(p); + } + + p = NULL; + memset(foo, 0, sizeof(foo)); + + gc->fullcollect(); + printStats(gc); + + delete gc; +} + +/* ---------------------------- */ + +void smoke3() +{ + GC *gc; + int *p; + int i; + + printf("--------------------------smoke3()\n"); + + gc = new GC(); + gc->init(); + gc->setStackBottom(stackbottom); + + for (i = 0; i < 1000000; i++) +// for (i = 0; i < 1000; i++) + { + unsigned size = rand() % 2048; + p = (int *)gc->malloc(size); + memset(p, i, size); + + size = rand() % 2048; + p = (int *)gc->realloc(p, size); + memset(p, i + 1, size); + } + + p = NULL; + desregs(); + gc->fullcollect(); + printStats(gc); + + delete gc; +} + +/* ---------------------------- */ + +void smoke4() +{ + GC *gc; + int *p; + int i; + + printf("--------------------------smoke4()\n"); + + gc = new GC(); + gc->init(); + gc->setStackBottom(stackbottom); + + for (i = 0; i < 80000; i++) + { + unsigned size = i; + p = (int *)gc->malloc(size); + memset(p, i, size); + + size = rand() % 2048; + gc->check(p); + p = (int *)gc->realloc(p, size); + memset(p, i + 1, size); + } + + p = NULL; + desregs(); + gc->fullcollect(); + printStats(gc); + + delete gc; +} + +/* ---------------------------- */ + +void smoke5(GC *gc) +{ + char *p; + int i; + int j; + #define SMOKE5_SIZE 1000 + char *array[SMOKE5_SIZE]; + unsigned offset[SMOKE5_SIZE]; + + printf("--------------------------smoke5()\n"); + + memset(array, 0, sizeof(array)); + memset(offset, 0, sizeof(offset)); + + for (j = 0; j < 20; j++) + { + for (i = 0; i < 4000; i++) + { + unsigned size = (rand() % 2048) + 1; + unsigned index = rand() % SMOKE5_SIZE; + + //printf("index = %d, size = %d\n", index, size); + p = array[index] - offset[index]; + p = (char *)gc->realloc(p, size); + if (array[index]) + { unsigned s; + + //printf("\tverify = %d\n", p[0]); + s = offset[index]; + if (size < s) + s = size; + verify(p, index, s); + } + array[index] = p; + fill(p, index, size); + offset[index] = rand() % size; + array[index] += offset[index]; + + //printf("p[0] = %d\n", p[0]); + } + gc->fullcollect(); + } + + p = NULL; + memset(array, 0, sizeof(array)); + gc->fullcollect(); + printStats(gc); +} + +/* ---------------------------- */ + +/* ---------------------------- */ + +int main(int argc, char *argv[]) +{ + GC *gc; + + printf("GC test start\n"); + + (void)argc; + stackbottom = &argv; + + gc = new GC(); + gc->init(); + gc->setStackBottom(stackbottom); + + smoke(); + smoke2(); + smoke3(); + smoke4(); + smoke5(gc); + + delete gc; + + printf("GC test success\n"); + return EXIT_SUCCESS; +} diff --git a/root/gc/win32.c b/root/gc/win32.c new file mode 100644 index 00000000..ddff10d5 --- /dev/null +++ b/root/gc/win32.c @@ -0,0 +1,72 @@ +// Copyright (c) 2000-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#include + +#include "os.h" + +/*********************************** + * Map memory. + */ + +void *os_mem_map(unsigned nbytes) +{ + return VirtualAlloc(NULL, nbytes, MEM_RESERVE, PAGE_READWRITE); +} + +/*********************************** + * Commit memory. + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_commit(void *base, unsigned offset, unsigned nbytes) +{ + void *p; + + p = VirtualAlloc((char *)base + offset, nbytes, MEM_COMMIT, PAGE_READWRITE); + return (p == NULL); +} + + +/*********************************** + * Decommit memory. + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_decommit(void *base, unsigned offset, unsigned nbytes) +{ + return VirtualFree((char *)base + offset, nbytes, MEM_DECOMMIT) == 0; +} + +/*********************************** + * Unmap memory allocated with os_mem_map(). + * Memory must have already been decommitted. + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_unmap(void *base, unsigned nbytes) +{ + (void)nbytes; + return VirtualFree(base, 0, MEM_RELEASE) == 0; +} + + +/******************************************** + */ + +pthread_t pthread_self() +{ + return (pthread_t) GetCurrentThreadId(); +} diff --git a/root/gnuc.c b/root/gnuc.c new file mode 100644 index 00000000..8f33d839 --- /dev/null +++ b/root/gnuc.c @@ -0,0 +1,55 @@ + +// Put functions in here missing from gnu C + +#include "gnuc.h" + +int memicmp(const char *s1, const char *s2, int n) +{ + int result = 0; + + for (int i = 0; i < n; i++) + { char c1 = s1[i]; + char c2 = s2[i]; + + result = c1 - c2; + if (result) + { + if ('A' <= c1 && c1 <= 'Z') + c1 += 'a' - 'A'; + if ('A' <= c2 && c2 <= 'Z') + c2 += 'a' - 'A'; + result = c1 - c2; + if (result) + break; + } + } + return result; +} + +int stricmp(const char *s1, const char *s2) +{ + int result = 0; + + for (;;) + { char c1 = *s1; + char c2 = *s2; + + result = c1 - c2; + if (result) + { + if ('A' <= c1 && c1 <= 'Z') + c1 += 'a' - 'A'; + if ('A' <= c2 && c2 <= 'Z') + c2 += 'a' - 'A'; + result = c1 - c2; + if (result) + break; + } + if (!c1) + break; + s1++; + s2++; + } + return result; +} + diff --git a/root/gnuc.h b/root/gnuc.h new file mode 100644 index 00000000..00c9851d --- /dev/null +++ b/root/gnuc.h @@ -0,0 +1,8 @@ + +#ifndef _GNUC_H +#define _GNUC_H 1 + +int memicmp(const char *s1, const char *s2, int n); +int stricmp(const char *s1, const char *s2); + +#endif diff --git a/root/lstring.c b/root/lstring.c new file mode 100644 index 00000000..a4e41ed5 --- /dev/null +++ b/root/lstring.c @@ -0,0 +1,63 @@ +// lstring.c + +// Copyright (c) 1999-2002 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include + +#include "dchar.h" +#include "rmem.h" +#include "lstring.h" + +#ifdef _MSC_VER // prevent compiler internal crash +Lstring Lstring::zero; +#else +Lstring Lstring::zero = LSTRING_EMPTY(); +#endif + +Lstring *Lstring::ctor(const dchar *p, unsigned length) +{ + Lstring *s; + + s = alloc(length); + memcpy(s->string, p, length * sizeof(dchar)); + return s; +} + +Lstring *Lstring::alloc(unsigned length) +{ + Lstring *s; + + s = (Lstring *)mem.malloc(size(length)); + s->length = length; + s->string[length] = 0; + return s; +} + +Lstring *Lstring::append(const Lstring *s) +{ + Lstring *t; + + if (!s->length) + return this; + t = alloc(length + s->length); + memcpy(t->string, string, length * sizeof(dchar)); + memcpy(t->string + length, s->string, s->length * sizeof(dchar)); + return t; +} + +Lstring *Lstring::substring(int start, int end) +{ + Lstring *t; + + if (start == end) + return &zero; + t = alloc(end - start); + memcpy(t->string, string + start, (end - start) * sizeof(dchar)); + return t; +} diff --git a/root/lstring.h b/root/lstring.h new file mode 100644 index 00000000..17a8e447 --- /dev/null +++ b/root/lstring.h @@ -0,0 +1,72 @@ + +// lstring.h +// length-prefixed strings + +// Copyright (c) 1999-2002 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef LSTRING_H +#define LSTRING_H 1 + +#include "dchar.h" + +struct Lstring +{ + unsigned length; + + // Disable warning about nonstandard extension + #pragma warning (disable : 4200) + dchar string[]; + + static Lstring zero; // 0 length string + + // No constructors because we want to be able to statically + // initialize Lstring's, and Lstrings are of variable size. + + #if M_UNICODE + #define LSTRING(p,length) { length, L##p } + #else + #define LSTRING(p,length) { length, p } + #endif + +#if __GNUC__ + #define LSTRING_EMPTY() { 0 } +#else + #define LSTRING_EMPTY() LSTRING("", 0) +#endif + + static Lstring *ctor(const dchar *p) { return ctor(p, Dchar::len(p)); } + static Lstring *ctor(const dchar *p, unsigned length); + static unsigned size(unsigned length) { return sizeof(Lstring) + (length + 1) * sizeof(dchar); } + static Lstring *alloc(unsigned length); + Lstring *clone(); + + unsigned len() { return length; } + + dchar *toDchars() { return string; } + + hash_t hash() { return Dchar::calcHash(string, length); } + hash_t ihash() { return Dchar::icalcHash(string, length); } + + static int cmp(const Lstring *s1, const Lstring *s2) + { + int c = s2->length - s1->length; + return c ? c : Dchar::memcmp(s1->string, s2->string, s1->length); + } + + static int icmp(const Lstring *s1, const Lstring *s2) + { + int c = s2->length - s1->length; + return c ? c : Dchar::memicmp(s1->string, s2->string, s1->length); + } + + Lstring *append(const Lstring *s); + Lstring *substring(int start, int end); +}; + +#endif diff --git a/root/man.c b/root/man.c new file mode 100644 index 00000000..3770aa96 --- /dev/null +++ b/root/man.c @@ -0,0 +1,100 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 2008-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include + +#if _WIN32 + +#include + +#pragma comment(lib,"shell32.lib") + +void browse(const char *url) +{ + ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL); +} + +#endif + +#if linux || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + +#include +#include +#include + +void browse(const char *url) +{ + pid_t childpid; + const char *args[3]; + + const char *browser = getenv("BROWSER"); + if (browser) + browser = strdup(browser); + else + browser = "x-www-browser"; + + args[0] = browser; + args[1] = url; + args[2] = NULL; + + childpid = fork(); + if (childpid == 0) + { + execvp(args[0], (char**)args); + perror(args[0]); // failed to execute + return; + } +} + +#endif + +#if __APPLE__ + +#include +#include +#include + +void browse(const char *url) +{ + pid_t childpid; + const char *args[5]; + + char *browser = getenv("BROWSER"); + if (browser) + { browser = strdup(browser); + args[0] = browser; + args[1] = url; + args[2] = NULL; + } + else + { + //browser = "/Applications/Safari.app/Contents/MacOS/Safari"; + args[0] = "open"; + args[1] = "-a"; + args[2] = "/Applications/Safari.app"; + args[3] = url; + args[4] = NULL; + } + + childpid = fork(); + if (childpid == 0) + { + execvp(args[0], (char**)args); + perror(args[0]); // failed to execute + return; + } +} + +#endif + + diff --git a/root/port.c b/root/port.c new file mode 100644 index 00000000..59698cba --- /dev/null +++ b/root/port.c @@ -0,0 +1,792 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com + +#include "port.h" +#if __DMC__ +#include +#include +#include +#include +#include +#include + +double Port::nan = NAN; +double Port::infinity = INFINITY; +double Port::dbl_max = DBL_MAX; +double Port::dbl_min = DBL_MIN; +long double Port::ldbl_max = LDBL_MAX; + +int Port::isNan(double r) +{ + return ::isnan(r); +} + +int Port::isNan(long double r) +{ + return ::isnan(r); +} + +int Port::isSignallingNan(double r) +{ + /* A signalling NaN is a NaN with 0 as the most significant bit of + * its significand, which is bit 51 of 0..63 for 64 bit doubles. + */ + return isNan(r) && !((((unsigned char*)&r)[6]) & 8); +} + +int Port::isSignallingNan(long double r) +{ + /* A signalling NaN is a NaN with 0 as the most significant bit of + * its significand, which is bit 62 of 0..79 for 80 bit reals. + */ + return isNan(r) && !((((unsigned char*)&r)[7]) & 0x40); +} + +int Port::isFinite(double r) +{ + return ::isfinite(r); +} + +int Port::isInfinity(double r) +{ + return (::fpclassify(r) == FP_INFINITE); +} + +int Port::Signbit(double r) +{ + return ::signbit(r); +} + +double Port::floor(double d) +{ + return ::floor(d); +} + +double Port::pow(double x, double y) +{ + return ::pow(x, y); +} + +long double Port::fmodl(long double x, long double y) +{ + return ::fmodl(x, y); +} + +unsigned long long Port::strtoull(const char *p, char **pend, int base) +{ + return ::strtoull(p, pend, base); +} + +char *Port::ull_to_string(char *buffer, ulonglong ull) +{ + sprintf(buffer, "%llu", ull); + return buffer; +} + +wchar_t *Port::ull_to_string(wchar_t *buffer, ulonglong ull) +{ + swprintf(buffer, sizeof(ulonglong) * 3 + 1, L"%llu", ull); + return buffer; +} + +double Port::ull_to_double(ulonglong ull) +{ + return (double) ull; +} + +const char *Port::list_separator() +{ + // LOCALE_SLIST for Windows + return ","; +} + +const wchar_t *Port::wlist_separator() +{ + // LOCALE_SLIST for Windows + return L","; +} + +char *Port::strupr(char *s) +{ + return ::strupr(s); +} + +#endif + +#if _MSC_VER + +// Disable useless warnings about unreferenced functions +#pragma warning (disable : 4514) + +#include +#include +#include +#include +#include +#include +#include +#include // for std::numeric_limits + +static unsigned long nanarray[2]= { 0xFFFFFFFF, 0x7FFFFFFF }; +//static unsigned long nanarray[2] = {0,0x7FF80000 }; +double Port::nan = (*(double *)nanarray); + +//static unsigned long infinityarray[2] = {0,0x7FF00000 }; +static double zero = 0; +double Port::infinity = 1 / zero; + +double Port::dbl_max = DBL_MAX; +double Port::dbl_min = DBL_MIN; +long double Port::ldbl_max = LDBL_MAX; + +struct PortInitializer +{ + PortInitializer(); +}; + +static PortInitializer portinitializer; + +PortInitializer::PortInitializer() +{ + Port::infinity = std::numeric_limits::infinity(); +} + +int Port::isNan(double r) +{ + return ::_isnan(r); +} + +int Port::isNan(long double r) +{ + return ::_isnan(r); +} + +int Port::isSignallingNan(double r) +{ + /* A signalling NaN is a NaN with 0 as the most significant bit of + * its significand, which is bit 51 of 0..63 for 64 bit doubles. + */ + return isNan(r) && !((((unsigned char*)&r)[6]) & 8); +} + +int Port::isSignallingNan(long double r) +{ + /* MSVC doesn't have 80 bit long doubles + */ + return isSignallingNan((double) r); +} + +int Port::isFinite(double r) +{ + return ::_finite(r); +} + +int Port::isInfinity(double r) +{ + return (::_fpclass(r) & (_FPCLASS_NINF | _FPCLASS_PINF)); +} + +int Port::Signbit(double r) +{ + return (long)(((long *)&(r))[1] & 0x80000000); +} + +double Port::floor(double d) +{ + return ::floor(d); +} + +double Port::pow(double x, double y) +{ + if (y == 0) + return 1; // even if x is NAN + return ::pow(x, y); +} + +long double Port::fmodl(long double x, long double y) +{ + return ::fmodl(x, y); +} + +unsigned _int64 Port::strtoull(const char *p, char **pend, int base) +{ + unsigned _int64 number = 0; + int c; + int error; +#ifndef ULLONG_MAX + #define ULLONG_MAX ((unsigned _int64)~0I64) +#endif + + while (isspace((unsigned char)*p)) /* skip leading white space */ + p++; + if (*p == '+') + p++; + switch (base) + { case 0: + base = 10; /* assume decimal base */ + if (*p == '0') + { base = 8; /* could be octal */ + p++; + switch (*p) + { case 'x': + case 'X': + base = 16; /* hex */ + p++; + break; +#if BINARY + case 'b': + case 'B': + base = 2; /* binary */ + p++; + break; +#endif + } + } + break; + case 16: /* skip over '0x' and '0X' */ + if (*p == '0' && (p[1] == 'x' || p[1] == 'X')) + p += 2; + break; +#if BINARY + case 2: /* skip over '0b' and '0B' */ + if (*p == '0' && (p[1] == 'b' || p[1] == 'B')) + p += 2; + break; +#endif + } + error = 0; + for (;;) + { c = *p; + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c = (c & ~0x20) - ('A' - 10); + else /* unrecognized character */ + break; + if (c >= base) /* not in number base */ + break; + if ((ULLONG_MAX - c) / base < number) + error = 1; + number = number * base + c; + p++; + } + if (pend) + *pend = (char *)p; + if (error) + { number = ULLONG_MAX; + errno = ERANGE; + } + return number; +} + +char *Port::ull_to_string(char *buffer, ulonglong ull) +{ + _ui64toa(ull, buffer, 10); + return buffer; +} + +wchar_t *Port::ull_to_string(wchar_t *buffer, ulonglong ull) +{ + _ui64tow(ull, buffer, 10); + return buffer; +} + +double Port::ull_to_double(ulonglong ull) +{ double d; + + if ((__int64) ull < 0) + { + // MSVC doesn't implement the conversion + d = (double) (__int64)(ull - 0x8000000000000000i64); + d += (double)(signed __int64)(0x7FFFFFFFFFFFFFFFi64) + 1.0; + } + else + d = (double)(__int64)ull; + return d; +} + +const char *Port::list_separator() +{ + // LOCALE_SLIST for Windows + return ","; +} + +const wchar_t *Port::wlist_separator() +{ + // LOCALE_SLIST for Windows + return L","; +} + +char *Port::strupr(char *s) +{ + return ::strupr(s); +} + +#endif + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ + +#include +#if linux +#include +#include +#endif +#if __FreeBSD__ && __i386__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +static double zero = 0; +double Port::nan = copysign(NAN, 1.0); +double Port::infinity = 1 / zero; +double Port::dbl_max = 1.7976931348623157e308; +double Port::dbl_min = 5e-324; +long double Port::ldbl_max = LDBL_MAX; + +struct PortInitializer +{ + PortInitializer(); +}; + +static PortInitializer portinitializer; + +PortInitializer::PortInitializer() +{ + assert(!signbit(Port::nan)); + +#if __FreeBSD__ && __i386__ + // LDBL_MAX comes out as infinity. Fix. + static unsigned char x[sizeof(long double)] = + { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x7F }; + Port::ldbl_max = *(long double *)&x[0]; + // FreeBSD defaults to double precision. Switch to extended precision. + fpsetprec(FP_PE); +#endif +} + +int Port::isNan(double r) +{ +#if __APPLE__ + return __inline_isnan(r); +#elif __OpenBSD__ + return isnan(r); +#else + #undef isnan + return ::isnan(r); +#endif +} + +int Port::isNan(long double r) +{ +#if __APPLE__ + return __inline_isnan(r); +#elif __OpenBSD__ + return isnan(r); +#else + #undef isnan + return ::isnan(r); +#endif +} + +int Port::isSignallingNan(double r) +{ + /* A signalling NaN is a NaN with 0 as the most significant bit of + * its significand, which is bit 51 of 0..63 for 64 bit doubles. + */ + return isNan(r) && !((((unsigned char*)&r)[6]) & 8); +} + +int Port::isSignallingNan(long double r) +{ + /* A signalling NaN is a NaN with 0 as the most significant bit of + * its significand, which is bit 62 of 0..79 for 80 bit reals. + */ + return isNan(r) && !((((unsigned char*)&r)[7]) & 0x40); +} + +#undef isfinite +int Port::isFinite(double r) +{ + return ::finite(r); +} + +int Port::isInfinity(double r) +{ +#if __APPLE__ + return fpclassify(r) == FP_INFINITE; +#elif __OpenBSD__ + return isinf(r); +#else + #undef isinf + return ::isinf(r); +#endif +} + +#undef signbit +int Port::Signbit(double r) +{ + union { double d; long long ll; } u; + u.d = r; + return u.ll < 0; +} + +double Port::floor(double d) +{ + return ::floor(d); +} + +double Port::pow(double x, double y) +{ + return ::pow(x, y); +} + +long double Port::fmodl(long double x, long double y) +{ +#if __FreeBSD__ || __OpenBSD__ + return ::fmod(x, y); // hack for now, fix later +#else + return ::fmodl(x, y); +#endif +} + +unsigned long long Port::strtoull(const char *p, char **pend, int base) +{ + return ::strtoull(p, pend, base); +} + +char *Port::ull_to_string(char *buffer, ulonglong ull) +{ + sprintf(buffer, "%llu", ull); + return buffer; +} + +wchar_t *Port::ull_to_string(wchar_t *buffer, ulonglong ull) +{ +#if __OpenBSD__ + assert(0); +#else + swprintf(buffer, sizeof(ulonglong) * 3 + 1, L"%llu", ull); +#endif + return buffer; +} + +double Port::ull_to_double(ulonglong ull) +{ + return (double) ull; +} + +const char *Port::list_separator() +{ + return ","; +} + +const wchar_t *Port::wlist_separator() +{ + return L","; +} + +char *Port::strupr(char *s) +{ + char *t = s; + + while (*s) + { + *s = toupper(*s); + s++; + } + + return t; +} + +#endif + +#if __sun&&__SVR4 + +#define __C99FEATURES__ 1 // Needed on Solaris for NaN and more +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static double zero = 0; +double Port::nan = NAN; +double Port::infinity = 1 / zero; +double Port::dbl_max = 1.7976931348623157e308; +double Port::dbl_min = 5e-324; +long double Port::ldbl_max = LDBL_MAX; + +struct PortInitializer +{ + PortInitializer(); +}; + +static PortInitializer portinitializer; + +PortInitializer::PortInitializer() +{ + // gcc nan's have the sign bit set by default, so turn it off + // Need the volatile to prevent gcc from doing incorrect + // constant folding. + volatile long double foo; + foo = NAN; + if (signbit(foo)) // signbit sometimes, not always, set + foo = -foo; // turn off sign bit + Port::nan = foo; +} + +int Port::isNan(double r) +{ + return isnan(r); +} + +int Port::isNan(long double r) +{ + return isnan(r); +} + +int Port::isSignallingNan(double r) +{ + /* A signalling NaN is a NaN with 0 as the most significant bit of + * its significand, which is bit 51 of 0..63 for 64 bit doubles. + */ + return isNan(r) && !((((unsigned char*)&r)[6]) & 8); +} + +int Port::isSignallingNan(long double r) +{ + /* A signalling NaN is a NaN with 0 as the most significant bit of + * its significand, which is bit 62 of 0..79 for 80 bit reals. + */ + return isNan(r) && !((((unsigned char*)&r)[7]) & 0x40); +} + +#undef isfinite +int Port::isFinite(double r) +{ + return finite(r); +} + +int Port::isInfinity(double r) +{ + return isinf(r); +} + +#undef signbit +int Port::Signbit(double r) +{ + return (long)(((long *)&r)[1] & 0x80000000); +} + +double Port::floor(double d) +{ + return ::floor(d); +} + +double Port::pow(double x, double y) +{ + return ::pow(x, y); +} + +unsigned long long Port::strtoull(const char *p, char **pend, int base) +{ + return ::strtoull(p, pend, base); +} + +char *Port::ull_to_string(char *buffer, ulonglong ull) +{ + sprintf(buffer, "%llu", ull); + return buffer; +} + +wchar_t *Port::ull_to_string(wchar_t *buffer, ulonglong ull) +{ + swprintf(buffer, sizeof(ulonglong) * 3 + 1, L"%llu", ull); + return buffer; +} + +double Port::ull_to_double(ulonglong ull) +{ + return (double) ull; +} + +const char *Port::list_separator() +{ + return ","; +} + +const wchar_t *Port::wlist_separator() +{ + return L","; +} + +char *Port::strupr(char *s) +{ + char *t = s; + + while (*s) + { + *s = toupper(*s); + s++; + } + + return t; +} + +#endif + +#if IN_GCC + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static double zero = 0; +double Port::nan = NAN; +double Port::infinity = 1 / zero; +double Port::dbl_max = 1.7976931348623157e308; +double Port::dbl_min = 5e-324; +long double Port::ldbl_max = LDBL_MAX; + +#include "d-gcc-real.h" +extern "C" bool real_isnan (const real_t *); + +struct PortInitializer +{ + PortInitializer(); +}; + +static PortInitializer portinitializer; + +PortInitializer::PortInitializer() +{ + Port::infinity = real_t::getinfinity(); + Port::nan = real_t::getnan(real_t::LongDouble); +} + +#undef isnan +int Port::isNan(double r) +{ +#if __APPLE__ + return __inline_isnan(r); +#else + return ::isnan(r); +#endif +} + +int Port::isNan(long double r) +{ + return real_isnan(&r); +} + +int Port::isSignallingNan(double r) +{ + /* A signalling NaN is a NaN with 0 as the most significant bit of + * its significand, which is bit 51 of 0..63 for 64 bit doubles. + */ + return isNan(r) && !((((unsigned char*)&r)[6]) & 8); +} + +int Port::isSignallingNan(long double r) +{ + /* A signalling NaN is a NaN with 0 as the most significant bit of + * its significand, which is bit 62 of 0..79 for 80 bit reals. + */ + return isNan(r) && !((((unsigned char*)&r)[7]) & 0x40); +} + +#undef isfinite +int Port::isFinite(double r) +{ + return ::finite(r); +} + +#undef isinf +int Port::isInfinity(double r) +{ + return ::isinf(r); +} + +#undef signbit +int Port::Signbit(double r) +{ + return (long)(((long *)&r)[1] & 0x80000000); +} + +double Port::floor(double d) +{ + return ::floor(d); +} + +double Port::pow(double x, double y) +{ + return ::pow(x, y); +} + +unsigned long long Port::strtoull(const char *p, char **pend, int base) +{ + return ::strtoull(p, pend, base); +} + +char *Port::ull_to_string(char *buffer, ulonglong ull) +{ + sprintf(buffer, "%llu", ull); + return buffer; +} + +wchar_t *Port::ull_to_string(wchar_t *buffer, ulonglong ull) +{ + swprintf(buffer, L"%llu", ull); + return buffer; +} + +double Port::ull_to_double(ulonglong ull) +{ + return (double) ull; +} + +const char *Port::list_separator() +{ + return ","; +} + +const wchar_t *Port::wlist_separator() +{ + return L","; +} + +char *Port::strupr(char *s) +{ + char *t = s; + + while (*s) + { + *s = toupper(*s); + s++; + } + + return t; +} + +#endif + diff --git a/root/port.h b/root/port.h new file mode 100644 index 00000000..40c07bb9 --- /dev/null +++ b/root/port.h @@ -0,0 +1,81 @@ + +// Copyright (c) 1999-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com + +#ifndef PORT_H +#define PORT_H + +// Portable wrapper around compiler/system specific things. +// The idea is to minimize #ifdef's in the app code. + +#ifndef TYPEDEFS +#define TYPEDEFS + +#include + +#if _MSC_VER +typedef __int64 longlong; +typedef unsigned __int64 ulonglong; + +// According to VC 8.0 docs, long double is the same as double +#define strtold strtod +#define strtof strtod + +#else +typedef long long longlong; +typedef unsigned long long ulonglong; +#endif + +#endif + +typedef double d_time; + +struct Port +{ + static double nan; + static double infinity; + static double dbl_max; + static double dbl_min; + static long double ldbl_max; + +#if __OpenBSD__ +#elif __GNUC__ + // These conflict with macros in math.h, should rename them + #undef isnan + #undef isfinite + #undef isinfinity + #undef signbit +#endif + static int isNan(double); + static int isNan(long double); + + static int isSignallingNan(double); + static int isSignallingNan(long double); + + static int isFinite(double); + static int isInfinity(double); + static int Signbit(double); + + static double floor(double); + static double pow(double x, double y); + + static long double fmodl(long double x, long double y); + + static ulonglong strtoull(const char *p, char **pend, int base); + + static char *ull_to_string(char *buffer, ulonglong ull); + static wchar_t *ull_to_string(wchar_t *buffer, ulonglong ull); + + // Convert ulonglong to double + static double ull_to_double(ulonglong ull); + + // Get locale-dependent list separator + static const char *list_separator(); + static const wchar_t *wlist_separator(); + + static char *strupr(char *); +}; + +#endif diff --git a/root/response.c b/root/response.c new file mode 100644 index 00000000..2096f11b --- /dev/null +++ b/root/response.c @@ -0,0 +1,290 @@ +// Copyright (C) 1990-1998 by Symantec +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved +// http://www.digitalmars.com +// Written by Walter Bright +/* + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +#include +#include +#include +#include +#include + +#if _WIN32 +#include +#include +#endif + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#include +#include +#include +#include +#include +#include +#endif + +/********************************* + * #include + * int response_expand(int *pargc,char ***pargv); + * + * Expand any response files in command line. + * Response files are arguments that look like: + * @NAME + * The name is first searched for in the environment. If it is not + * there, it is searched for as a file name. + * Arguments are separated by spaces, tabs, or newlines. These can be + * imbedded within arguments by enclosing the argument in '' or "". + * Recursively expands nested response files. + * + * To use, put the line: + * response_expand(&argc,&argv); + * as the first executable statement in main(int argc, char **argv). + * argc and argv are adjusted to be the new command line arguments + * after response file expansion. + * + * Digital Mars's MAKE program can be notified that a program can accept + * long command lines via environment variables by preceding the rule + * line for the program with a *. + * + * Returns: + * 0 success + * !=0 failure (argc, argv unchanged) + */ + +struct Narg +{ + int argc; /* arg count */ + int argvmax; /* dimension of nargv[] */ + char **argv; +}; + +static int addargp(struct Narg *n, char *p) +{ + /* The 2 is to always allow room for a NULL argp at the end */ + if (n->argc + 2 > n->argvmax) + { + n->argvmax = n->argc + 2; + char **ap = n->argv; + ap = (char **) realloc(ap,n->argvmax * sizeof(char *)); + if (!ap) + { if (n->argv) + free(n->argv); + memset(n, 0, sizeof(*n)); + return 1; + } + n->argv = ap; + } + n->argv[n->argc++] = p; + return 0; +} + +int response_expand(int *pargc, char ***pargv) +{ + struct Narg n; + int i; + char *cp; + int recurse = 0; + + n.argc = 0; + n.argvmax = 0; /* dimension of n.argv[] */ + n.argv = NULL; + for(i=0; i<*pargc; ++i) + { + cp = (*pargv)[i]; + if (*cp == '@') + { + char *buffer; + char *bufend; + char *p; + + cp++; + p = getenv(cp); + if (p) + { + buffer = strdup(p); + if (!buffer) + goto noexpand; + bufend = buffer + strlen(buffer); + } + else + { + long length; + int fd; + int nread; + size_t len; + +#if __DMC__ + length = filesize(cp); +#else + struct stat statbuf; + if (stat(cp, &statbuf)) + goto noexpand; + length = statbuf.st_size; +#endif + if (length & 0xF0000000) /* error or file too big */ + goto noexpand; + len = length; + buffer = (char *)malloc(len + 1); + if (!buffer) + goto noexpand; + bufend = &buffer[len]; + /* Read file into buffer */ +#if _WIN32 + fd = _open(cp,O_RDONLY|O_BINARY); +#else + fd = open(cp,O_RDONLY); +#endif + if (fd == -1) + goto noexpand; + nread = read(fd,buffer,len); + close(fd); + + if (nread != len) + goto noexpand; + } + + // The logic of this should match that in setargv() + + for (p = buffer; p < bufend; p++) + { + char *d; + char c,lastc; + unsigned char instring; + int num_slashes,non_slashes; + + switch (*p) + { + case 26: /* ^Z marks end of file */ + goto L2; + + case 0xD: + case 0: + case ' ': + case '\t': + case '\n': + continue; // scan to start of argument + + case '@': + recurse = 1; + default: /* start of new argument */ + if (addargp(&n,p)) + goto noexpand; + instring = 0; + c = 0; + num_slashes = 0; + for (d = p; 1; p++) + { + lastc = c; + if (p >= bufend) + goto Lend; + c = *p; + switch (c) + { + case '"': + /* + Yes this looks strange,but this is so that we are + MS Compatible, tests have shown that: + \\\\"foo bar" gets passed as \\foo bar + \\\\foo gets passed as \\\\foo + \\\"foo gets passed as \"foo + and \"foo gets passed as "foo in VC! + */ + non_slashes = num_slashes % 2; + num_slashes = num_slashes / 2; + for (; num_slashes > 0; num_slashes--) + { + d--; + *d = '\0'; + } + + if (non_slashes) + { + *(d-1) = c; + } + else + { + instring ^= 1; + } + break; + case 26: + Lend: + *d = 0; // terminate argument + goto L2; + + case 0xD: // CR + c = lastc; + continue; // ignore + + case '@': + recurse = 1; + goto Ladd; + + case ' ': + case '\t': + if (!instring) + { + case '\n': + case 0: + *d = 0; // terminate argument + goto Lnextarg; + } + default: + Ladd: + if (c == '\\') + num_slashes++; + else + num_slashes = 0; + *d++ = c; + break; + } +#ifdef _MBCS + if (_istlead (c)) { + *d++ = *++p; + if (*(d - 1) == '\0') { + d--; + goto Lnextarg; + } + } +#endif + } + break; + } + Lnextarg: + ; + } + L2: + ; + } + else if (addargp(&n,(*pargv)[i])) + goto noexpand; + } + if (n.argvmax == 0) + { + n.argvmax = 1; + n.argv = (char **) calloc(n.argvmax, sizeof(char *)); + if (!n.argv) + return 1; + } + else + n.argv[n.argc] = NULL; + if (recurse) + { + /* Recursively expand @filename */ + if (response_expand(&n.argc,&n.argv)) + goto noexpand; + } + *pargc = n.argc; + *pargv = n.argv; + return 0; /* success */ + +noexpand: /* error */ + free(n.argv); + /* BUG: any file buffers are not free'd */ + return 1; +} diff --git a/root/rmem.c b/root/rmem.c new file mode 100644 index 00000000..2504ab93 --- /dev/null +++ b/root/rmem.c @@ -0,0 +1,155 @@ + +/* Copyright (c) 2000 Digital Mars */ +/* All Rights Reserved */ + +#include +#include +#include + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#include "../root/rmem.h" +#else +#include "rmem.h" +#endif + +/* This implementation of the storage allocator uses the standard C allocation package. + */ + +Mem mem; + +void Mem::init() +{ +} + +char *Mem::strdup(const char *s) +{ + char *p; + + if (s) + { + p = ::strdup(s); + if (p) + return p; + error(); + } + return NULL; +} + +void *Mem::malloc(size_t size) +{ void *p; + + if (!size) + p = NULL; + else + { + p = ::malloc(size); + if (!p) + error(); + } + return p; +} + +void *Mem::calloc(size_t size, size_t n) +{ void *p; + + if (!size || !n) + p = NULL; + else + { + p = ::calloc(size, n); + if (!p) + error(); + } + return p; +} + +void *Mem::realloc(void *p, size_t size) +{ + if (!size) + { if (p) + { ::free(p); + p = NULL; + } + } + else if (!p) + { + p = ::malloc(size); + if (!p) + error(); + } + else + { + void *psave = p; + p = ::realloc(psave, size); + if (!p) + { free(psave); + error(); + } + } + return p; +} + +void Mem::free(void *p) +{ + if (p) + ::free(p); +} + +void *Mem::mallocdup(void *o, size_t size) +{ void *p; + + if (!size) + p = NULL; + else + { + p = ::malloc(size); + if (!p) + error(); + else + memcpy(p,o,size); + } + return p; +} + +void Mem::error() +{ + printf("Error: out of memory\n"); + exit(EXIT_FAILURE); +} + +void Mem::fullcollect() +{ +} + +void Mem::mark(void *pointer) +{ + (void) pointer; // necessary for VC /W4 +} + +void Mem::setStackBottom(void *bottom) +{ +} + +void Mem::addroots(char* pStart, char* pEnd) +{ +} + + +/* =================================================== */ + +void * operator new(size_t m_size) +{ + void *p = malloc(m_size); + if (p) + return p; + printf("Error: out of memory\n"); + exit(EXIT_FAILURE); + return p; +} + +void operator delete(void *p) +{ + free(p); +} + + diff --git a/root/rmem.h b/root/rmem.h new file mode 100644 index 00000000..d22145a9 --- /dev/null +++ b/root/rmem.h @@ -0,0 +1,51 @@ +// Copyright (C) 2000-2011 by Digital Mars +// All Rights Reserved + +#ifndef ROOT_MEM_H +#define ROOT_MEM_H + +#include // for size_t + +typedef void (*FINALIZERPROC)(void* pObj, void* pClientData); + +struct GC; // thread specific allocator + +struct Mem +{ + GC *gc; // pointer to our thread specific allocator + Mem() { gc = NULL; } + + void init(); + + // Derive from Mem to get these storage allocators instead of global new/delete + void * operator new(size_t m_size); + void * operator new(size_t m_size, Mem *mem); + void * operator new(size_t m_size, GC *gc); + void operator delete(void *p); + + void * operator new[](size_t m_size); + void operator delete[](void *p); + + char *strdup(const char *s); + void *malloc(size_t size); + void *malloc_uncollectable(size_t size); + void *calloc(size_t size, size_t n); + void *realloc(void *p, size_t size); + void free(void *p); + void free_uncollectable(void *p); + void *mallocdup(void *o, size_t size); + void error(); + void check(void *p); // validate pointer + void fullcollect(); // do full garbage collection + void fullcollectNoStack(); // do full garbage collection, no scan stack + void mark(void *pointer); + void addroots(char* pStart, char* pEnd); + void removeroots(char* pStart); + void setFinalizer(void* pObj, FINALIZERPROC pFn, void* pClientData); + void setStackBottom(void *bottom); + GC *getThreadGC(); // get apartment allocator for this thread +}; + +extern Mem mem; + +#endif /* ROOT_MEM_H */ diff --git a/root/root.c b/root/root.c new file mode 100644 index 00000000..f0d02426 --- /dev/null +++ b/root/root.c @@ -0,0 +1,2089 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#define POSIX (linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4) + +#include +#include +#include +#include +#include +#include +#include + +#if (defined (__SVR4) && defined (__sun)) +#include +#endif + +#if _MSC_VER ||__MINGW32__ +#include +#include +#endif + +#if _WIN32 +#include +#include +#include +#endif + +#if POSIX +#include +#include +#include +#include +#include +#include +#endif + +#include "port.h" +#include "root.h" +#include "dchar.h" +#include "rmem.h" + +#if 0 //__SC__ //def DEBUG +extern "C" void __cdecl _assert(void *e, void *f, unsigned line) +{ + printf("Assert('%s','%s',%d)\n",e,f,line); + fflush(stdout); + *(char *)0 = 0; +} +#endif + + +/************************************* + * Convert wchar string to ascii string. + */ + +char *wchar2ascii(wchar_t *us) +{ + return wchar2ascii(us, wcslen(us)); +} + +char *wchar2ascii(wchar_t *us, unsigned len) +{ + unsigned i; + char *p; + + p = (char *)mem.malloc(len + 1); + for (i = 0; i <= len; i++) + p[i] = (char) us[i]; + return p; +} + +int wcharIsAscii(wchar_t *us) +{ + return wcharIsAscii(us, wcslen(us)); +} + +int wcharIsAscii(wchar_t *us, unsigned len) +{ + unsigned i; + + for (i = 0; i <= len; i++) + { + if (us[i] & ~0xFF) // if high bits set + return 0; // it's not ascii + } + return 1; +} + + +/*********************************** + * Compare length-prefixed strings (bstr). + */ + +int bstrcmp(unsigned char *b1, unsigned char *b2) +{ + return (*b1 == *b2 && memcmp(b1 + 1, b2 + 1, *b2) == 0) ? 0 : 1; +} + +/*************************************** + * Convert bstr into a malloc'd string. + */ + +char *bstr2str(unsigned char *b) +{ + char *s; + unsigned len; + + len = *b; + s = (char *) mem.malloc(len + 1); + s[len] = 0; + return (char *)memcpy(s,b + 1,len); +} + +/************************************** + * Print error message and exit. + */ + +void error(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + printf("Error: "); + vprintf(format, ap); + va_end( ap ); + printf("\n"); + fflush(stdout); + + exit(EXIT_FAILURE); +} + +#if M_UNICODE +void error(const dchar *format, ...) +{ + va_list ap; + + va_start(ap, format); + printf("Error: "); + vwprintf(format, ap); + va_end( ap ); + printf("\n"); + fflush(stdout); + + exit(EXIT_FAILURE); +} +#endif + +void error_mem() +{ + error("out of memory"); +} + +/************************************** + * Print warning message. + */ + +void warning(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + printf("Warning: "); + vprintf(format, ap); + va_end( ap ); + printf("\n"); + fflush(stdout); +} + +/****************************** Object ********************************/ + +int Object::equals(Object *o) +{ + return o == this; +} + +hash_t Object::hashCode() +{ + return (hash_t) this; +} + +int Object::compare(Object *obj) +{ + return this - obj; +} + +void Object::print() +{ + printf("%s %p\n", toChars(), this); +} + +char *Object::toChars() +{ + return (char *)"Object"; +} + +dchar *Object::toDchars() +{ +#if M_UNICODE + return L"Object"; +#else + return toChars(); +#endif +} + +int Object::dyncast() +{ + return 0; +} + +void Object::toBuffer(OutBuffer *b) +{ + b->writestring("Object"); +} + +void Object::mark() +{ +} + +/****************************** String ********************************/ + +String::String(char *str, int ref) +{ + this->str = ref ? str : mem.strdup(str); + this->ref = ref; +} + +String::~String() +{ + mem.free(str); +} + +void String::mark() +{ + mem.mark(str); +} + +hash_t String::calcHash(const char *str, size_t len) +{ + hash_t hash = 0; + + for (;;) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash *= 37; + hash += *(uint8_t *)str; + return hash; + + case 2: + hash *= 37; + hash += *(uint16_t *)str; + return hash; + + case 3: + hash *= 37; + hash += (*(uint16_t *)str << 8) + + ((uint8_t *)str)[2]; + return hash; + + default: + hash *= 37; + hash += *(uint32_t *)str; + str += 4; + len -= 4; + break; + } + } +} + +hash_t String::calcHash(const char *str) +{ + return calcHash(str, strlen(str)); +} + +hash_t String::hashCode() +{ + return calcHash(str, strlen(str)); +} + +unsigned String::len() +{ + return strlen(str); +} + +int String::equals(Object *obj) +{ + return strcmp(str,((String *)obj)->str) == 0; +} + +int String::compare(Object *obj) +{ + return strcmp(str,((String *)obj)->str); +} + +char *String::toChars() +{ + return str; +} + +void String::print() +{ + printf("String '%s'\n",str); +} + + +/****************************** FileName ********************************/ + +FileName::FileName(char *str, int ref) + : String(str,ref) +{ +} + +char *FileName::combine(const char *path, const char *name) +{ char *f; + size_t pathlen; + size_t namelen; + + if (!path || !*path) + return (char *)name; + pathlen = strlen(path); + namelen = strlen(name); + f = (char *)mem.malloc(pathlen + 1 + namelen + 1); + memcpy(f, path, pathlen); +#if POSIX + if (path[pathlen - 1] != '/') + { f[pathlen] = '/'; + pathlen++; + } +#elif _WIN32 + if (path[pathlen - 1] != '\\' && + path[pathlen - 1] != '/' && + path[pathlen - 1] != ':') + { f[pathlen] = '\\'; + pathlen++; + } +#else + assert(0); +#endif + memcpy(f + pathlen, name, namelen + 1); + return f; +} + +FileName::FileName(char *path, char *name) + : String(combine(path,name),1) +{ +} + +// Split a path into an Array of paths +Strings *FileName::splitPath(const char *path) +{ + char c = 0; // unnecessary initializer is for VC /W4 + const char *p; + OutBuffer buf; + Strings *array; + + array = new Strings(); + if (path) + { + p = path; + do + { char instring = 0; + + while (isspace((unsigned char)*p)) // skip leading whitespace + p++; + buf.reserve(strlen(p) + 1); // guess size of path + for (; ; p++) + { + c = *p; + switch (c) + { + case '"': + instring ^= 1; // toggle inside/outside of string + continue; + +#if MACINTOSH + case ',': +#endif +#if _WIN32 + case ';': +#endif +#if POSIX + case ':': +#endif + p++; + break; // note that ; cannot appear as part + // of a path, quotes won't protect it + + case 0x1A: // ^Z means end of file + case 0: + break; + + case '\r': + continue; // ignore carriage returns + +#if POSIX + case '~': + buf.writestring(getenv("HOME")); + continue; +#endif + +#if 0 + case ' ': + case '\t': // tabs in filenames? + if (!instring) // if not in string + break; // treat as end of path +#endif + default: + buf.writeByte(c); + continue; + } + break; + } + if (buf.offset) // if path is not empty + { + buf.writeByte(0); // to asciiz + array->push(buf.extractData()); + } + } while (c); + } + return array; +} + +hash_t FileName::hashCode() +{ +#if _WIN32 + // We need a different hashCode because it must be case-insensitive + size_t len = strlen(str); + hash_t hash = 0; + unsigned char *s = (unsigned char *)str; + + for (;;) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash *= 37; + hash += *(uint8_t *)s | 0x20; + return hash; + + case 2: + hash *= 37; + hash += *(uint16_t *)s | 0x2020; + return hash; + + case 3: + hash *= 37; + hash += ((*(uint16_t *)s << 8) + + ((uint8_t *)s)[2]) | 0x202020; + break; + + default: + hash *= 37; + hash += *(uint32_t *)s | 0x20202020; + s += 4; + len -= 4; + break; + } + } +#else + // darwin HFS is case insensitive, though... + return String::hashCode(); +#endif +} + +int FileName::compare(Object *obj) +{ + return compare(str, ((FileName *)obj)->str); +} + +int FileName::compare(const char *name1, const char *name2) +{ +#if _WIN32 + return stricmp(name1, name2); +#else + return strcmp(name1, name2); +#endif +} + +int FileName::equals(Object *obj) +{ + return compare(obj) == 0; +} + +int FileName::equals(const char *name1, const char *name2) +{ + return compare(name1, name2) == 0; +} + +/************************************ + * Return !=0 if absolute path name. + */ + +int FileName::absolute(const char *name) +{ +#if _WIN32 + return (*name == '\\') || + (*name == '/') || + (*name && name[1] == ':'); +#elif POSIX + return (*name == '/'); +#else + assert(0); +#endif +} + +/******************************** + * Return filename extension (read-only). + * Points past '.' of extension. + * If there isn't one, return NULL. + */ + +char *FileName::ext(const char *str) +{ + char *e; + size_t len = strlen(str); + + e = (char *)str + len; + for (;;) + { + switch (*e) + { case '.': + return e + 1; +#if POSIX + case '/': + break; +#endif +#if _WIN32 + case '\\': + case ':': + case '/': + break; +#endif + default: + if (e == str) + break; + e--; + continue; + } + return NULL; + } +} + +char *FileName::ext() +{ + return ext(str); +} + +/******************************** + * Return mem.malloc'd filename with extension removed. + */ + +char *FileName::removeExt(const char *str) +{ + const char *e = ext(str); + if (e) + { size_t len = (e - str) - 1; + char *n = (char *)mem.malloc(len + 1); + memcpy(n, str, len); + n[len] = 0; + return n; + } + return mem.strdup(str); +} + +/******************************** + * Return filename name excluding path (read-only). + */ + +char *FileName::name(const char *str) +{ + char *e; + size_t len = strlen(str); + + e = (char *)str + len; + for (;;) + { + switch (*e) + { +#if POSIX + case '/': + return e + 1; +#endif +#if _WIN32 + case '/': + case '\\': + return e + 1; + case ':': + /* The ':' is a drive letter only if it is the second + * character or the last character, + * otherwise it is an ADS (Alternate Data Stream) separator. + * Consider ADS separators as part of the file name. + */ + if (e == str + 1 || e == str + len - 1) + return e + 1; +#endif + default: + if (e == str) + break; + e--; + continue; + } + return e; + } +} + +char *FileName::name() +{ + return name(str); +} + +/************************************** + * Return path portion of str. + * Path will does not include trailing path separator. + */ + +char *FileName::path(const char *str) +{ + char *n = name(str); + char *path; + size_t pathlen; + + if (n > str) + { +#if POSIX + if (n[-1] == '/') + n--; +#elif _WIN32 + if (n[-1] == '\\' || n[-1] == '/') + n--; +#else + assert(0); +#endif + } + pathlen = n - str; + path = (char *)mem.malloc(pathlen + 1); + memcpy(path, str, pathlen); + path[pathlen] = 0; + return path; +} + +/************************************** + * Replace filename portion of path. + */ + +const char *FileName::replaceName(const char *path, const char *name) +{ char *f; + char *n; + size_t pathlen; + size_t namelen; + + if (absolute(name)) + return name; + + n = FileName::name(path); + if (n == path) + return name; + pathlen = n - path; + namelen = strlen(name); + f = (char *)mem.malloc(pathlen + 1 + namelen + 1); + memcpy(f, path, pathlen); +#if POSIX + if (path[pathlen - 1] != '/') + { f[pathlen] = '/'; + pathlen++; + } +#elif _WIN32 + if (path[pathlen - 1] != '\\' && + path[pathlen - 1] != '/' && + path[pathlen - 1] != ':') + { f[pathlen] = '\\'; + pathlen++; + } +#else + assert(0); +#endif + memcpy(f + pathlen, name, namelen + 1); + return f; +} + +/*************************** + */ + +FileName *FileName::defaultExt(const char *name, const char *ext) +{ + char *e; + char *s; + size_t len; + size_t extlen; + + e = FileName::ext(name); + if (e) // if already has an extension + return new FileName((char *)name, 0); + + len = strlen(name); + extlen = strlen(ext); + s = (char *)alloca(len + 1 + extlen + 1); + memcpy(s,name,len); + s[len] = '.'; + memcpy(s + len + 1, ext, extlen + 1); + return new FileName(s, 0); +} + +/*************************** + */ + +FileName *FileName::forceExt(const char *name, const char *ext) +{ + char *e; + char *s; + size_t len; + size_t extlen; + + e = FileName::ext(name); + if (e) // if already has an extension + { + len = e - name; + extlen = strlen(ext); + + s = (char *)alloca(len + extlen + 1); + memcpy(s,name,len); + memcpy(s + len, ext, extlen + 1); + return new FileName(s, 0); + } + else + return defaultExt(name, ext); // doesn't have one +} + +/****************************** + * Return !=0 if extensions match. + */ + +int FileName::equalsExt(const char *ext) +{ const char *e; + + e = FileName::ext(); + if (!e && !ext) + return 1; + if (!e || !ext) + return 0; +#if POSIX + return strcmp(e,ext) == 0; +#elif _WIN32 + return stricmp(e,ext) == 0; +#else + assert(0); +#endif +} + +/************************************* + * Copy file from this to to. + */ + +void FileName::CopyTo(FileName *to) +{ + File file(this); + +#if _WIN32 + file.touchtime = mem.malloc(sizeof(WIN32_FIND_DATAA)); // keep same file time +#elif POSIX + file.touchtime = mem.malloc(sizeof(struct stat)); // keep same file time +#else + assert(0); +#endif + file.readv(); + file.name = to; + file.writev(); +} + +/************************************* + * Search Path for file. + * Input: + * cwd if !=0, search current directory before searching path + */ + +char *FileName::searchPath(Strings *path, const char *name, int cwd) +{ + if (absolute(name)) + { + return exists(name) ? (char *)name : NULL; + } + if (cwd) + { + if (exists(name)) + return (char *)name; + } + if (path) + { unsigned i; + + for (i = 0; i < path->dim; i++) + { + char *p = path->tdata()[i]; + char *n = combine(p, name); + + if (exists(n)) + return n; + } + } + return NULL; +} + + +/************************************* + * Search Path for file in a safe manner. + * + * Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory + * ('Path Traversal') attacks. + * http://cwe.mitre.org/data/definitions/22.html + * More info: + * https://www.securecoding.cert.org/confluence/display/seccode/FIO02-C.+Canonicalize+path+names+originating+from+untrusted+sources + * Returns: + * NULL file not found + * !=NULL mem.malloc'd file name + */ + +char *FileName::safeSearchPath(Strings *path, const char *name) +{ +#if _WIN32 + /* Disallow % / \ : and .. in name characters + */ + for (const char *p = name; *p; p++) + { + char c = *p; + if (c == '\\' || c == '/' || c == ':' || c == '%' || + (c == '.' && p[1] == '.')) + { + return NULL; + } + } + + return FileName::searchPath(path, name, 0); +#elif POSIX + /* Even with realpath(), we must check for // and disallow it + */ + for (const char *p = name; *p; p++) + { + char c = *p; + if (c == '/' && p[1] == '/') + { + return NULL; + } + } + + if (path) + { unsigned i; + + /* Each path is converted to a cannonical name and then a check is done to see + * that the searched name is really a child one of the the paths searched. + */ + for (i = 0; i < path->dim; i++) + { + char *cname = NULL; + char *cpath = canonicalName(path->tdata()[i]); + //printf("FileName::safeSearchPath(): name=%s; path=%s; cpath=%s\n", + // name, (char *)path->data[i], cpath); + if (cpath == NULL) + goto cont; + cname = canonicalName(combine(cpath, name)); + //printf("FileName::safeSearchPath(): cname=%s\n", cname); + if (cname == NULL) + goto cont; + //printf("FileName::safeSearchPath(): exists=%i " + // "strncmp(cpath, cname, %i)=%i\n", exists(cname), + // strlen(cpath), strncmp(cpath, cname, strlen(cpath))); + // exists and name is *really* a "child" of path + if (exists(cname) && strncmp(cpath, cname, strlen(cpath)) == 0) + { + free(cpath); + char *p = mem.strdup(cname); + free(cname); + return p; + } +cont: + if (cpath) + free(cpath); + if (cname) + free(cname); + } + } + return NULL; +#else + assert(0); +#endif +} + + +int FileName::exists(const char *name) +{ +#if POSIX + struct stat st; + + if (stat(name, &st) < 0) + return 0; + if (S_ISDIR(st.st_mode)) + return 2; + return 1; +#elif _WIN32 + DWORD dw; + int result; + + dw = GetFileAttributesA(name); + if (dw == -1L) + result = 0; + else if (dw & FILE_ATTRIBUTE_DIRECTORY) + result = 2; + else + result = 1; + return result; +#else + assert(0); +#endif +} + +void FileName::ensurePathExists(const char *path) +{ + //printf("FileName::ensurePathExists(%s)\n", path ? path : ""); + if (path && *path) + { + if (!exists(path)) + { + char *p = FileName::path(path); + if (*p) + { +#if _WIN32 + size_t len = strlen(path); + if (len > 2 && p[-1] == ':' && path + 2 == p) + { mem.free(p); + return; + } +#endif + ensurePathExists(p); + mem.free(p); + } +#if _WIN32 + if (path[strlen(path) - 1] != '\\') +#endif +#if POSIX + if (path[strlen(path) - 1] != '\\') +#endif + { + //printf("mkdir(%s)\n", path); +#if _WIN32 + if (_mkdir(path)) +#endif +#if POSIX + if (mkdir(path, 0777)) +#endif + { + /* Don't error out if another instance of dmd just created + * this directory + */ + if (errno != EEXIST) + error("cannot create directory %s", path); + } + } + } + } +} + + +/****************************************** + * Return canonical version of name in a malloc'd buffer. + * This code is high risk. + */ +char *FileName::canonicalName(const char *name) +{ +#if linux + // Lovely glibc extension to do it for us + return canonicalize_file_name(name); +#elif POSIX + #if _POSIX_VERSION >= 200809L || defined (linux) + // NULL destination buffer is allowed and preferred + return realpath(name, NULL); + #else + char *cname = NULL; + #if PATH_MAX + /* PATH_MAX must be defined as a constant in , + * otherwise using it is unsafe due to TOCTOU + */ + size_t path_max = (size_t)PATH_MAX; + if (path_max > 0) + { + /* Need to add one to PATH_MAX because of realpath() buffer overflow bug: + * http://isec.pl/vulnerabilities/isec-0011-wu-ftpd.txt + */ + cname = (char *)malloc(path_max + 1); + if (cname == NULL) + return NULL; + } + #endif + return realpath(name, cname); + #endif +#elif _WIN32 + /* Apparently, there is no good way to do this on Windows. + * GetFullPathName isn't it. + */ + assert(0); + return NULL; +#else + assert(0); + return NULL; +#endif +} + + +/****************************** File ********************************/ + +File::File(FileName *n) +{ + ref = 0; + buffer = NULL; + len = 0; + touchtime = NULL; + name = n; +} + +File::File(char *n) +{ + ref = 0; + buffer = NULL; + len = 0; + touchtime = NULL; + name = new FileName(n, 0); +} + +File::~File() +{ + if (buffer) + { + if (ref == 0) + mem.free(buffer); +#if _WIN32 + else if (ref == 2) + UnmapViewOfFile(buffer); +#endif + } + if (touchtime) + mem.free(touchtime); +} + +void File::mark() +{ + mem.mark(buffer); + mem.mark(touchtime); + mem.mark(name); +} + +/************************************* + */ + +int File::read() +{ +#if POSIX + off_t size; + ssize_t numread; + int fd; + struct stat buf; + int result = 0; + char *name; + + name = this->name->toChars(); + //printf("File::read('%s')\n",name); + fd = open(name, O_RDONLY); + if (fd == -1) + { + //printf("\topen error, errno = %d\n",errno); + goto err1; + } + + if (!ref) + ::free(buffer); + ref = 0; // we own the buffer now + + //printf("\tfile opened\n"); + if (fstat(fd, &buf)) + { + printf("\tfstat error, errno = %d\n",errno); + goto err2; + } + size = buf.st_size; + buffer = (unsigned char *) ::malloc(size + 2); + if (!buffer) + { + printf("\tmalloc error, errno = %d\n",errno); + goto err2; + } + + numread = ::read(fd, buffer, size); + if (numread != size) + { + printf("\tread error, errno = %d\n",errno); + goto err2; + } + + if (touchtime) + memcpy(touchtime, &buf, sizeof(buf)); + + if (close(fd) == -1) + { + printf("\tclose error, errno = %d\n",errno); + goto err; + } + + len = size; + + // Always store a wchar ^Z past end of buffer so scanner has a sentinel + buffer[size] = 0; // ^Z is obsolete, use 0 + buffer[size + 1] = 0; + return 0; + +err2: + close(fd); +err: + ::free(buffer); + buffer = NULL; + len = 0; + +err1: + result = 1; + return result; +#elif _WIN32 + DWORD size; + DWORD numread; + HANDLE h; + int result = 0; + char *name; + + name = this->name->toChars(); + h = CreateFileA(name,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,0); + if (h == INVALID_HANDLE_VALUE) + goto err1; + + if (!ref) + ::free(buffer); + ref = 0; + + size = GetFileSize(h,NULL); + buffer = (unsigned char *) ::malloc(size + 2); + if (!buffer) + goto err2; + + if (ReadFile(h,buffer,size,&numread,NULL) != TRUE) + goto err2; + + if (numread != size) + goto err2; + + if (touchtime) + { + if (!GetFileTime(h, NULL, NULL, &((WIN32_FIND_DATAA *)touchtime)->ftLastWriteTime)) + goto err2; + } + + if (!CloseHandle(h)) + goto err; + + len = size; + + // Always store a wchar ^Z past end of buffer so scanner has a sentinel + buffer[size] = 0; // ^Z is obsolete, use 0 + buffer[size + 1] = 0; + return 0; + +err2: + CloseHandle(h); +err: + ::free(buffer); + buffer = NULL; + len = 0; + +err1: + result = 1; + return result; +#else + assert(0); +#endif +} + +/***************************** + * Read a file with memory mapped file I/O. + */ + +int File::mmread() +{ +#if POSIX + return read(); +#elif _WIN32 + HANDLE hFile; + HANDLE hFileMap; + DWORD size; + char *name; + + name = this->name->toChars(); + hFile = CreateFile(name, GENERIC_READ, + FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + goto Lerr; + size = GetFileSize(hFile, NULL); + //printf(" file created, size %d\n", size); + + hFileMap = CreateFileMapping(hFile,NULL,PAGE_READONLY,0,size,NULL); + if (CloseHandle(hFile) != TRUE) + goto Lerr; + + if (hFileMap == NULL) + goto Lerr; + + //printf(" mapping created\n"); + + if (!ref) + mem.free(buffer); + ref = 2; + buffer = (unsigned char *)MapViewOfFileEx(hFileMap, FILE_MAP_READ,0,0,size,NULL); + if (CloseHandle(hFileMap) != TRUE) + goto Lerr; + if (buffer == NULL) // mapping view failed + goto Lerr; + + len = size; + //printf(" buffer = %p\n", buffer); + + return 0; + +Lerr: + return GetLastError(); // failure +#else + assert(0); +#endif +} + +/********************************************* + * Write a file. + * Returns: + * 0 success + */ + +int File::write() +{ +#if POSIX + int fd; + ssize_t numwritten; + char *name; + + name = this->name->toChars(); + fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, 0644); + if (fd == -1) + goto err; + + numwritten = ::write(fd, buffer, len); + if (len != numwritten) + goto err2; + + if (close(fd) == -1) + goto err; + + if (touchtime) + { struct utimbuf ubuf; + + ubuf.actime = ((struct stat *)touchtime)->st_atime; + ubuf.modtime = ((struct stat *)touchtime)->st_mtime; + if (utime(name, &ubuf)) + goto err; + } + return 0; + +err2: + close(fd); + ::remove(name); +err: + return 1; +#elif _WIN32 + HANDLE h; + DWORD numwritten; + char *name; + + name = this->name->toChars(); + h = CreateFileA(name,GENERIC_WRITE,0,NULL,CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,NULL); + if (h == INVALID_HANDLE_VALUE) + goto err; + + if (WriteFile(h,buffer,len,&numwritten,NULL) != TRUE) + goto err2; + + if (len != numwritten) + goto err2; + + if (touchtime) { + SetFileTime(h, NULL, NULL, &((WIN32_FIND_DATAA *)touchtime)->ftLastWriteTime); + } + if (!CloseHandle(h)) + goto err; + return 0; + +err2: + CloseHandle(h); + DeleteFileA(name); +err: + return 1; +#else + assert(0); +#endif +} + +/********************************************* + * Append to a file. + * Returns: + * 0 success + */ + +int File::append() +{ +#if POSIX + return 1; +#elif _WIN32 + HANDLE h; + DWORD numwritten; + char *name; + + name = this->name->toChars(); + h = CreateFileA(name,GENERIC_WRITE,0,NULL,OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,NULL); + if (h == INVALID_HANDLE_VALUE) + goto err; + +#if 1 + SetFilePointer(h, 0, NULL, FILE_END); +#else // INVALID_SET_FILE_POINTER doesn't seem to have a definition + if (SetFilePointer(h, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) + goto err; +#endif + + if (WriteFile(h,buffer,len,&numwritten,NULL) != TRUE) + goto err2; + + if (len != numwritten) + goto err2; + + if (touchtime) { + SetFileTime(h, NULL, NULL, &((WIN32_FIND_DATAA *)touchtime)->ftLastWriteTime); + } + if (!CloseHandle(h)) + goto err; + return 0; + +err2: + CloseHandle(h); +err: + return 1; +#else + assert(0); +#endif +} + +/************************************** + */ + +void File::readv() +{ + if (read()) + error("Error reading file '%s'\n",name->toChars()); +} + +/************************************** + */ + +void File::mmreadv() +{ + if (mmread()) + readv(); +} + +void File::writev() +{ + if (write()) + error("Error writing file '%s'\n",name->toChars()); +} + +void File::appendv() +{ + if (write()) + error("Error appending to file '%s'\n",name->toChars()); +} + +/******************************************* + * Return !=0 if file exists. + * 0: file doesn't exist + * 1: normal file + * 2: directory + */ + +int File::exists() +{ +#if POSIX + return 0; +#elif _WIN32 + DWORD dw; + int result; + char *name; + + name = this->name->toChars(); + if (touchtime) + dw = ((WIN32_FIND_DATAA *)touchtime)->dwFileAttributes; + else + dw = GetFileAttributesA(name); + if (dw == -1L) + result = 0; + else if (dw & FILE_ATTRIBUTE_DIRECTORY) + result = 2; + else + result = 1; + return result; +#else + assert(0); +#endif +} + +void File::remove() +{ +#if POSIX + ::remove(this->name->toChars()); +#elif _WIN32 + DeleteFileA(this->name->toChars()); +#else + assert(0); +#endif +} + +Files *File::match(char *n) +{ + return match(new FileName(n, 0)); +} + +Files *File::match(FileName *n) +{ +#if POSIX + return NULL; +#elif _WIN32 + HANDLE h; + WIN32_FIND_DATAA fileinfo; + Files *a; + char *c; + char *name; + + a = new Files(); + c = n->toChars(); + name = n->name(); + h = FindFirstFileA(c,&fileinfo); + if (h != INVALID_HANDLE_VALUE) + { + do + { + // Glue path together with name + char *fn; + File *f; + + fn = (char *)mem.malloc(name - c + strlen(fileinfo.cFileName) + 1); + memcpy(fn, c, name - c); + strcpy(fn + (name - c), fileinfo.cFileName); + f = new File(fn); + f->touchtime = mem.malloc(sizeof(WIN32_FIND_DATAA)); + memcpy(f->touchtime, &fileinfo, sizeof(fileinfo)); + a->push(f); + } while (FindNextFileA(h,&fileinfo) != FALSE); + FindClose(h); + } + return a; +#else + assert(0); +#endif +} + +int File::compareTime(File *f) +{ +#if POSIX + return 0; +#elif _WIN32 + if (!touchtime) + stat(); + if (!f->touchtime) + f->stat(); + return CompareFileTime(&((WIN32_FIND_DATAA *)touchtime)->ftLastWriteTime, &((WIN32_FIND_DATAA *)f->touchtime)->ftLastWriteTime); +#else + assert(0); +#endif +} + +void File::stat() +{ +#if POSIX + if (!touchtime) + { + touchtime = mem.calloc(1, sizeof(struct stat)); + } +#elif _WIN32 + HANDLE h; + + if (!touchtime) + { + touchtime = mem.calloc(1, sizeof(WIN32_FIND_DATAA)); + } + h = FindFirstFileA(name->toChars(),(WIN32_FIND_DATAA *)touchtime); + if (h != INVALID_HANDLE_VALUE) + { + FindClose(h); + } +#else + assert(0); +#endif +} + +void File::checkoffset(size_t offset, size_t nbytes) +{ + if (offset > len || offset + nbytes > len) + error("Corrupt file '%s': offset x%zx off end of file",toChars(),offset); +} + +char *File::toChars() +{ + return name->toChars(); +} + + +/************************* OutBuffer *************************/ + +OutBuffer::OutBuffer() +{ + data = NULL; + offset = 0; + size = 0; +} + +OutBuffer::~OutBuffer() +{ + mem.free(data); +} + +char *OutBuffer::extractData() +{ + char *p; + + p = (char *)data; + data = NULL; + offset = 0; + size = 0; + return p; +} + +void OutBuffer::mark() +{ + mem.mark(data); +} + +void OutBuffer::reserve(unsigned nbytes) +{ + //printf("OutBuffer::reserve: size = %d, offset = %d, nbytes = %d\n", size, offset, nbytes); + if (size - offset < nbytes) + { + size = (offset + nbytes) * 2; + data = (unsigned char *)mem.realloc(data, size); + } +} + +void OutBuffer::reset() +{ + offset = 0; +} + +void OutBuffer::setsize(unsigned size) +{ + offset = size; +} + +void OutBuffer::write(const void *data, unsigned nbytes) +{ + reserve(nbytes); + memcpy(this->data + offset, data, nbytes); + offset += nbytes; +} + +void OutBuffer::writebstring(unsigned char *string) +{ + write(string,*string + 1); +} + +void OutBuffer::writestring(const char *string) +{ + write(string,strlen(string)); +} + +void OutBuffer::writedstring(const char *string) +{ +#if M_UNICODE + for (; *string; string++) + { + writedchar(*string); + } +#else + write(string,strlen(string)); +#endif +} + +void OutBuffer::writedstring(const wchar_t *string) +{ +#if M_UNICODE + write(string,wcslen(string) * sizeof(wchar_t)); +#else + for (; *string; string++) + { + writedchar(*string); + } +#endif +} + +void OutBuffer::prependstring(const char *string) +{ unsigned len; + + len = strlen(string); + reserve(len); + memmove(data + len, data, offset); + memcpy(data, string, len); + offset += len; +} + +void OutBuffer::writenl() +{ +#if _WIN32 +#if M_UNICODE + write4(0x000A000D); // newline is CR,LF on Microsoft OS's +#else + writeword(0x0A0D); // newline is CR,LF on Microsoft OS's +#endif +#else +#if M_UNICODE + writeword('\n'); +#else + writeByte('\n'); +#endif +#endif +} + +void OutBuffer::writeByte(unsigned b) +{ + reserve(1); + this->data[offset] = (unsigned char)b; + offset++; +} + +void OutBuffer::writeUTF8(unsigned b) +{ + reserve(6); + if (b <= 0x7F) + { + this->data[offset] = (unsigned char)b; + offset++; + } + else if (b <= 0x7FF) + { + this->data[offset + 0] = (unsigned char)((b >> 6) | 0xC0); + this->data[offset + 1] = (unsigned char)((b & 0x3F) | 0x80); + offset += 2; + } + else if (b <= 0xFFFF) + { + this->data[offset + 0] = (unsigned char)((b >> 12) | 0xE0); + this->data[offset + 1] = (unsigned char)(((b >> 6) & 0x3F) | 0x80); + this->data[offset + 2] = (unsigned char)((b & 0x3F) | 0x80); + offset += 3; + } + else if (b <= 0x1FFFFF) + { + this->data[offset + 0] = (unsigned char)((b >> 18) | 0xF0); + this->data[offset + 1] = (unsigned char)(((b >> 12) & 0x3F) | 0x80); + this->data[offset + 2] = (unsigned char)(((b >> 6) & 0x3F) | 0x80); + this->data[offset + 3] = (unsigned char)((b & 0x3F) | 0x80); + offset += 4; + } + else if (b <= 0x3FFFFFF) + { + this->data[offset + 0] = (unsigned char)((b >> 24) | 0xF8); + this->data[offset + 1] = (unsigned char)(((b >> 18) & 0x3F) | 0x80); + this->data[offset + 2] = (unsigned char)(((b >> 12) & 0x3F) | 0x80); + this->data[offset + 3] = (unsigned char)(((b >> 6) & 0x3F) | 0x80); + this->data[offset + 4] = (unsigned char)((b & 0x3F) | 0x80); + offset += 5; + } + else if (b <= 0x7FFFFFFF) + { + this->data[offset + 0] = (unsigned char)((b >> 30) | 0xFC); + this->data[offset + 1] = (unsigned char)(((b >> 24) & 0x3F) | 0x80); + this->data[offset + 2] = (unsigned char)(((b >> 18) & 0x3F) | 0x80); + this->data[offset + 3] = (unsigned char)(((b >> 12) & 0x3F) | 0x80); + this->data[offset + 4] = (unsigned char)(((b >> 6) & 0x3F) | 0x80); + this->data[offset + 5] = (unsigned char)((b & 0x3F) | 0x80); + offset += 6; + } + else + assert(0); +} + +void OutBuffer::writedchar(unsigned b) +{ + reserve(Dchar_mbmax * sizeof(dchar)); + offset = (unsigned char *)Dchar::put((dchar *)(this->data + offset), (dchar)b) - + this->data; +} + +void OutBuffer::prependbyte(unsigned b) +{ + reserve(1); + memmove(data + 1, data, offset); + data[0] = (unsigned char)b; + offset++; +} + +void OutBuffer::writeword(unsigned w) +{ + reserve(2); + *(unsigned short *)(this->data + offset) = (unsigned short)w; + offset += 2; +} + +void OutBuffer::writeUTF16(unsigned w) +{ + reserve(4); + if (w <= 0xFFFF) + { + *(unsigned short *)(this->data + offset) = (unsigned short)w; + offset += 2; + } + else if (w <= 0x10FFFF) + { + *(unsigned short *)(this->data + offset) = (unsigned short)((w >> 10) + 0xD7C0); + *(unsigned short *)(this->data + offset + 2) = (unsigned short)((w & 0x3FF) | 0xDC00); + offset += 4; + } + else + assert(0); +} + +void OutBuffer::write4(unsigned w) +{ + reserve(4); + *(unsigned *)(this->data + offset) = w; + offset += 4; +} + +void OutBuffer::write(OutBuffer *buf) +{ + if (buf) + { reserve(buf->offset); + memcpy(data + offset, buf->data, buf->offset); + offset += buf->offset; + } +} + +void OutBuffer::write(Object *obj) +{ + if (obj) + { + writestring(obj->toChars()); + } +} + +void OutBuffer::fill0(unsigned nbytes) +{ + reserve(nbytes); + memset(data + offset,0,nbytes); + offset += nbytes; +} + +void OutBuffer::align(unsigned size) +{ unsigned nbytes; + + nbytes = ((offset + size - 1) & ~(size - 1)) - offset; + fill0(nbytes); +} + + +//////////////////////////////////////////////////////////////// +// The compiler shipped with Visual Studio 2005 (and possible +// other versions) does not support C99 printf format specfiers +// such as %z and %j +#if _MSC_VER +using std::string; +using std::wstring; + +template +inline void +search_and_replace(S& str, const S& what, const S& replacement) +{ + assert(!what.empty()); + size_t pos = str.find(what); + while (pos != S::npos) + { + str.replace(pos, what.size(), replacement); + pos = str.find(what, pos + replacement.size()); + } +} +#define WORKAROUND_C99_SPECIFIERS_BUG(S,tmp,f) \ + S tmp = f; \ + search_and_replace(fmt, S("%z"), S("%l")); \ + search_and_replace(fmt, S("%j"), S("%i")); \ + f = tmp.c_str(); +#else +#define WORKAROUND_C99_SPECIFIERS_BUG(S,tmp,f) +#endif + +void OutBuffer::vprintf(const char *format, va_list args) +{ + char buffer[128]; + char *p; + unsigned psize; + int count; + + WORKAROUND_C99_SPECIFIERS_BUG(string, fmt, format); + + p = buffer; + psize = sizeof(buffer); + for (;;) + { +#if _WIN32 + count = _vsnprintf(p,psize,format,args); + if (count != -1) + break; + psize *= 2; +#elif POSIX + va_list va; + va_copy(va, args); +/* + The functions vprintf(), vfprintf(), vsprintf(), vsnprintf() + are equivalent to the functions printf(), fprintf(), sprintf(), + snprintf(), respectively, except that they are called with a + va_list instead of a variable number of arguments. These + functions do not call the va_end macro. Consequently, the value + of ap is undefined after the call. The application should call + va_end(ap) itself afterwards. + */ + count = vsnprintf(p,psize,format,va); + va_end(va); + if (count == -1) + psize *= 2; + else if (count >= psize) + psize = count + 1; + else + break; +#else + assert(0); +#endif + p = (char *) alloca(psize); // buffer too small, try again with larger size + } + write(p,count); +} + +#if M_UNICODE +void OutBuffer::vprintf(const wchar_t *format, va_list args) +{ + dchar buffer[128]; + dchar *p; + unsigned psize; + int count; + + WORKAROUND_C99_SPECIFIERS_BUG(wstring, fmt, format); + + p = buffer; + psize = sizeof(buffer) / sizeof(buffer[0]); + for (;;) + { +#if _WIN32 + count = _vsnwprintf(p,psize,format,args); + if (count != -1) + break; + psize *= 2; +#elif POSIX + va_list va; + va_copy(va, args); + count = vsnwprintf(p,psize,format,va); + va_end(va); + + if (count == -1) + psize *= 2; + else if (count >= psize) + psize = count + 1; + else + break; +#else + assert(0); +#endif + p = (dchar *) alloca(psize * 2); // buffer too small, try again with larger size + } + write(p,count * 2); +} +#endif + +void OutBuffer::printf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vprintf(format,ap); + va_end(ap); +} + +#if M_UNICODE +void OutBuffer::printf(const wchar_t *format, ...) +{ + va_list ap; + va_start(ap, format); + vprintf(format,ap); + va_end(ap); +} +#endif + +void OutBuffer::bracket(char left, char right) +{ + reserve(2); + memmove(data + 1, data, offset); + data[0] = left; + data[offset + 1] = right; + offset += 2; +} + +/****************** + * Insert left at i, and right at j. + * Return index just past right. + */ + +unsigned OutBuffer::bracket(unsigned i, const char *left, unsigned j, const char *right) +{ + size_t leftlen = strlen(left); + size_t rightlen = strlen(right); + reserve(leftlen + rightlen); + insert(i, left, leftlen); + insert(j + leftlen, right, rightlen); + return j + leftlen + rightlen; +} + +void OutBuffer::spread(unsigned offset, unsigned nbytes) +{ + reserve(nbytes); + memmove(data + offset + nbytes, data + offset, + this->offset - offset); + this->offset += nbytes; +} + +/**************************************** + * Returns: offset + nbytes + */ + +unsigned OutBuffer::insert(unsigned offset, const void *p, unsigned nbytes) +{ + spread(offset, nbytes); + memmove(data + offset, p, nbytes); + return offset + nbytes; +} + +void OutBuffer::remove(unsigned offset, unsigned nbytes) +{ + memmove(data + offset, data + offset + nbytes, this->offset - (offset + nbytes)); + this->offset -= nbytes; +} + +char *OutBuffer::toChars() +{ + writeByte(0); + return (char *)data; +} + +/********************************* Bits ****************************/ + +Bits::Bits() +{ + data = NULL; + bitdim = 0; + allocdim = 0; +} + +Bits::~Bits() +{ + mem.free(data); +} + +void Bits::mark() +{ + mem.mark(data); +} + +void Bits::resize(unsigned bitdim) +{ + unsigned allocdim; + unsigned mask; + + allocdim = (bitdim + 31) / 32; + data = (unsigned *)mem.realloc(data, allocdim * sizeof(data[0])); + if (this->allocdim < allocdim) + memset(data + this->allocdim, 0, (allocdim - this->allocdim) * sizeof(data[0])); + + // Clear other bits in last word + mask = (1 << (bitdim & 31)) - 1; + if (mask) + data[allocdim - 1] &= ~mask; + + this->bitdim = bitdim; + this->allocdim = allocdim; +} + +void Bits::set(unsigned bitnum) +{ + data[bitnum / 32] |= 1 << (bitnum & 31); +} + +void Bits::clear(unsigned bitnum) +{ + data[bitnum / 32] &= ~(1 << (bitnum & 31)); +} + +int Bits::test(unsigned bitnum) +{ + return data[bitnum / 32] & (1 << (bitnum & 31)); +} + +void Bits::set() +{ unsigned mask; + + memset(data, ~0, allocdim * sizeof(data[0])); + + // Clear other bits in last word + mask = (1 << (bitdim & 31)) - 1; + if (mask) + data[allocdim - 1] &= mask; +} + +void Bits::clear() +{ + memset(data, 0, allocdim * sizeof(data[0])); +} + +void Bits::copy(Bits *from) +{ + assert(bitdim == from->bitdim); + memcpy(data, from->data, allocdim * sizeof(data[0])); +} + +Bits *Bits::clone() +{ + Bits *b; + + b = new Bits(); + b->resize(bitdim); + b->copy(this); + return b; +} + +void Bits::sub(Bits *b) +{ + unsigned u; + + for (u = 0; u < allocdim; u++) + data[u] &= ~b->data[u]; +} + + + + + + + + + + + + + + + diff --git a/root/root.h b/root/root.h new file mode 100644 index 00000000..bac6036e --- /dev/null +++ b/root/root.h @@ -0,0 +1,417 @@ + + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef ROOT_H +#define ROOT_H + +#include +#include +#ifdef DEBUG +#include +#endif + +#if __DMC__ +#pragma once +#endif + +typedef size_t hash_t; + +#include "dchar.h" + +char *wchar2ascii(wchar_t *); +int wcharIsAscii(wchar_t *); +char *wchar2ascii(wchar_t *, unsigned len); +int wcharIsAscii(wchar_t *, unsigned len); + +int bstrcmp(unsigned char *s1, unsigned char *s2); +char *bstr2str(unsigned char *b); +void error(const char *format, ...); +void error(const wchar_t *format, ...); +void warning(const char *format, ...); + +#ifndef TYPEDEFS +#define TYPEDEFS + +#if _MSC_VER +#include // for _isnan +#include // for alloca +// According to VC 8.0 docs, long double is the same as double +#define strtold strtod +#define strtof strtod +#define isnan _isnan + +typedef __int64 longlong; +typedef unsigned __int64 ulonglong; +#else +typedef long long longlong; +typedef unsigned long long ulonglong; +#endif + +#endif + +longlong randomx(); + +/* + * Root of our class library. + */ + +struct OutBuffer; + +// Can't include arraytypes.h here, need to declare these directly. +template struct ArrayBase; +typedef ArrayBase Files; +typedef ArrayBase Strings; + + +struct Object +{ + Object() { } + virtual ~Object() { } + + virtual int equals(Object *o); + + /** + * Returns a hash code, useful for things like building hash tables of Objects. + */ + virtual hash_t hashCode(); + + /** + * Return <0, ==0, or >0 if this is less than, equal to, or greater than obj. + * Useful for sorting Objects. + */ + virtual int compare(Object *obj); + + /** + * Pretty-print an Object. Useful for debugging the old-fashioned way. + */ + virtual void print(); + + virtual char *toChars(); + virtual dchar *toDchars(); + virtual void toBuffer(OutBuffer *buf); + + /** + * Used as a replacement for dynamic_cast. Returns a unique number + * defined by the library user. For Object, the return value is 0. + */ + virtual int dyncast(); + + /** + * Marks pointers for garbage collector by calling mem.mark() for all pointers into heap. + */ + /*virtual*/ // not used, disable for now + void mark(); +}; + +struct String : Object +{ + int ref; // != 0 if this is a reference to someone else's string + char *str; // the string itself + + String(char *str, int ref = 1); + + ~String(); + + static hash_t calcHash(const char *str, size_t len); + static hash_t calcHash(const char *str); + hash_t hashCode(); + unsigned len(); + int equals(Object *obj); + int compare(Object *obj); + char *toChars(); + void print(); + void mark(); +}; + +struct FileName : String +{ + FileName(char *str, int ref); + FileName(char *path, char *name); + hash_t hashCode(); + int equals(Object *obj); + static int equals(const char *name1, const char *name2); + int compare(Object *obj); + static int compare(const char *name1, const char *name2); + static int absolute(const char *name); + static char *ext(const char *); + char *ext(); + static char *removeExt(const char *str); + static char *name(const char *); + char *name(); + static char *path(const char *); + static const char *replaceName(const char *path, const char *name); + + static char *combine(const char *path, const char *name); + static Strings *splitPath(const char *path); + static FileName *defaultExt(const char *name, const char *ext); + static FileName *forceExt(const char *name, const char *ext); + int equalsExt(const char *ext); + + void CopyTo(FileName *to); + static char *searchPath(Strings *path, const char *name, int cwd); + static char *safeSearchPath(Strings *path, const char *name); + static int exists(const char *name); + static void ensurePathExists(const char *path); + static char *canonicalName(const char *name); +}; + +struct File : Object +{ + int ref; // != 0 if this is a reference to someone else's buffer + unsigned char *buffer; // data for our file + unsigned len; // amount of data in buffer[] + void *touchtime; // system time to use for file + + FileName *name; // name of our file + + File(char *); + File(FileName *); + ~File(); + + void mark(); + + char *toChars(); + + /* Read file, return !=0 if error + */ + + int read(); + + /* Write file, either succeed or fail + * with error message & exit. + */ + + void readv(); + + /* Read file, return !=0 if error + */ + + int mmread(); + + /* Write file, either succeed or fail + * with error message & exit. + */ + + void mmreadv(); + + /* Write file, return !=0 if error + */ + + int write(); + + /* Write file, either succeed or fail + * with error message & exit. + */ + + void writev(); + + /* Return !=0 if file exists. + * 0: file doesn't exist + * 1: normal file + * 2: directory + */ + + /* Append to file, return !=0 if error + */ + + int append(); + + /* Append to file, either succeed or fail + * with error message & exit. + */ + + void appendv(); + + /* Return !=0 if file exists. + * 0: file doesn't exist + * 1: normal file + * 2: directory + */ + + int exists(); + + /* Given wildcard filespec, return an array of + * matching File's. + */ + + static Files *match(char *); + static Files *match(FileName *); + + // Compare file times. + // Return <0 this < f + // =0 this == f + // >0 this > f + int compareTime(File *f); + + // Read system file statistics + void stat(); + + /* Set buffer + */ + + void setbuffer(void *buffer, unsigned len) + { + this->buffer = (unsigned char *)buffer; + this->len = len; + } + + void checkoffset(size_t offset, size_t nbytes); + + void remove(); // delete file +}; + +struct OutBuffer : Object +{ + unsigned char *data; + unsigned offset; + unsigned size; + + OutBuffer(); + ~OutBuffer(); + char *extractData(); + void mark(); + + void reserve(unsigned nbytes); + void setsize(unsigned size); + void reset(); + void write(const void *data, unsigned nbytes); + void writebstring(unsigned char *string); + void writestring(const char *string); + void writedstring(const char *string); + void writedstring(const wchar_t *string); + void prependstring(const char *string); + void writenl(); // write newline + void writeByte(unsigned b); + void writebyte(unsigned b) { writeByte(b); } + void writeUTF8(unsigned b); + void writedchar(unsigned b); + void prependbyte(unsigned b); + void writeword(unsigned w); + void writeUTF16(unsigned w); + void write4(unsigned w); + void write(OutBuffer *buf); + void write(Object *obj); + void fill0(unsigned nbytes); + void align(unsigned size); + void vprintf(const char *format, va_list args); + void printf(const char *format, ...); +#if M_UNICODE + void vprintf(const unsigned short *format, va_list args); + void printf(const unsigned short *format, ...); +#endif + void bracket(char left, char right); + unsigned bracket(unsigned i, const char *left, unsigned j, const char *right); + void spread(unsigned offset, unsigned nbytes); + unsigned insert(unsigned offset, const void *data, unsigned nbytes); + void remove(unsigned offset, unsigned nbytes); + char *toChars(); + char *extractString(); +}; + +struct Array : Object +{ + unsigned dim; + void **data; + + private: + unsigned allocdim; + #define SMALLARRAYCAP 1 + void *smallarray[SMALLARRAYCAP]; // inline storage for small arrays + + public: + Array(); + ~Array(); + //Array(const Array&); + void mark(); + char *toChars(); + + void reserve(unsigned nentries); + void setDim(unsigned newdim); + void fixDim(); + void push(void *ptr); + void *pop(); + void shift(void *ptr); + void insert(unsigned index, void *ptr); + void insert(unsigned index, Array *a); + void append(Array *a); + void remove(unsigned i); + void zero(); + void *tos(); + void sort(); + Array *copy(); +}; + +template +struct ArrayBase : Array +{ + TYPE **tdata() + { + return (TYPE **)data; + } + + TYPE*& operator[] (size_t index) + { +#ifdef DEBUG + assert(index < dim); +#endif + return ((TYPE **)data)[index]; + } + + void insert(size_t index, TYPE *v) + { + Array::insert(index, (void *)v); + } + + void insert(size_t index, ArrayBase *a) + { + Array::insert(index, (Array *)a); + } + + void append(ArrayBase *a) + { + Array::append((Array *)a); + } + + void push(TYPE *a) + { + Array::push((void *)a); + } + + ArrayBase *copy() + { + return (ArrayBase *)Array::copy(); + } +}; + +struct Bits : Object +{ + unsigned bitdim; + unsigned allocdim; + unsigned *data; + + Bits(); + ~Bits(); + void mark(); + + void resize(unsigned bitdim); + + void set(unsigned bitnum); + void clear(unsigned bitnum); + int test(unsigned bitnum); + + void set(); + void clear(); + void copy(Bits *from); + Bits *clone(); + + void sub(Bits *b); +}; + +#endif diff --git a/root/speller.c b/root/speller.c new file mode 100644 index 00000000..d6437379 --- /dev/null +++ b/root/speller.c @@ -0,0 +1,257 @@ + +#include +#include +#include +#include + +#if __sun&&__SVR4 +#include +#endif + +#include "speller.h" + +const char idchars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; + +/************************************************** + * Looks for correct spelling. + * Currently only looks a 'distance' of one from the seed[]. + * This does an exhaustive search, so can potentially be very slow. + * Input: + * seed wrongly spelled word + * fp search function + * fparg argument to search function + * charset character set + * Returns: + * NULL no correct spellings found + * void* value returned by fp() for first possible correct spelling + */ + +void *spellerY(const char *seed, size_t seedlen, fp_speller_t fp, void *fparg, + const char *charset, size_t index) +{ + if (!seedlen) + return NULL; + assert(seed[seedlen] == 0); + + char tmp[30]; + char *buf; + if (seedlen <= sizeof(tmp) - 2) + buf = tmp; + else + { + buf = (char *)alloca(seedlen + 2); // leave space for extra char + if (!buf) + return NULL; // no matches + } + + memcpy(buf, seed, index); + + /* Delete at seed[index] */ + if (index < seedlen) + { + memcpy(buf + index, seed + index + 1, seedlen - index); + assert(buf[seedlen - 1] == 0); + void *p = (*fp)(fparg, buf); + if (p) + return p; + } + + if (charset && *charset) + { + /* Substitutions */ + if (index < seedlen) + { + memcpy(buf, seed, seedlen + 1); + for (const char *s = charset; *s; s++) + { + buf[index] = *s; + + //printf("sub buf = '%s'\n", buf); + void *p = (*fp)(fparg, buf); + if (p) + return p; + } + assert(buf[seedlen] == 0); + } + + /* Insertions */ + memcpy (buf + index + 1, seed + index, seedlen + 1 - index); + + for (const char *s = charset; *s; s++) + { + buf[index] = *s; + + //printf("ins buf = '%s'\n", buf); + void *p = (*fp)(fparg, buf); + if (p) + return p; + } + assert(buf[seedlen + 1] == 0); + } + + return NULL; // didn't find any corrections +} + +void *spellerX(const char *seed, size_t seedlen, fp_speller_t fp, void *fparg, + const char *charset, int flag) +{ + if (!seedlen) + return NULL; + + char tmp[30]; + char *buf; + if (seedlen <= sizeof(tmp) - 2) + buf = tmp; + else + { + buf = (char *)alloca(seedlen + 2); // leave space for extra char + if (!buf) + return NULL; // no matches + } + + /* Deletions */ + memcpy(buf, seed + 1, seedlen); + for (size_t i = 0; i < seedlen; i++) + { + //printf("del buf = '%s'\n", buf); + void *p; + if (flag) + p = spellerY(buf, seedlen - 1, fp, fparg, charset, i); + else + p = (*fp)(fparg, buf); + if (p) + return p; + + buf[i] = seed[i]; + } + + /* Transpositions */ + if (!flag) + { + memcpy(buf, seed, seedlen + 1); + for (size_t i = 0; i + 1 < seedlen; i++) + { + // swap [i] and [i + 1] + buf[i] = seed[i + 1]; + buf[i + 1] = seed[i]; + + //printf("tra buf = '%s'\n", buf); + void *p = (*fp)(fparg, buf); + if (p) + return p; + + buf[i] = seed[i]; + } + } + + if (charset && *charset) + { + /* Substitutions */ + memcpy(buf, seed, seedlen + 1); + for (size_t i = 0; i < seedlen; i++) + { + for (const char *s = charset; *s; s++) + { + buf[i] = *s; + + //printf("sub buf = '%s'\n", buf); + void *p; + if (flag) + p = spellerY(buf, seedlen, fp, fparg, charset, i + 1); + else + p = (*fp)(fparg, buf); + if (p) + return p; + } + buf[i] = seed[i]; + } + + /* Insertions */ + memcpy(buf + 1, seed, seedlen + 1); + for (size_t i = 0; i <= seedlen; i++) // yes, do seedlen+1 iterations + { + for (const char *s = charset; *s; s++) + { + buf[i] = *s; + + //printf("ins buf = '%s'\n", buf); + void *p; + if (flag) + p = spellerY(buf, seedlen + 1, fp, fparg, charset, i + 1); + else + p = (*fp)(fparg, buf); + if (p) + return p; + } + buf[i] = seed[i]; // going past end of seed[] is ok, as we hit the 0 + } + } + + return NULL; // didn't find any corrections +} + +void *speller(const char *seed, fp_speller_t fp, void *fparg, const char *charset) +{ + size_t seedlen = strlen(seed); + for (int distance = 0; distance < 2; distance++) + { void *p = spellerX(seed, seedlen, fp, fparg, charset, distance); + if (p) + return p; +// if (seedlen > 10) +// break; + } + return NULL; // didn't find it +} + + +#if UNITTEST + +#include +#include +#include + +void *speller_test(void *fparg, const char *s) +{ + //printf("speller_test(%s, %s)\n", fparg, s); + if (strcmp((char *)fparg, s) == 0) + return fparg; + return NULL; +} + +void unittest_speller() +{ + static const char *cases[][3] = + { + { "hello", "hell", "y" }, + { "hello", "hel", "y" }, + { "hello", "ello", "y" }, + { "hello", "llo", "y" }, + { "hello", "hellox", "y" }, + { "hello", "helloxy", "y" }, + { "hello", "xhello", "y" }, + { "hello", "xyhello", "y" }, + { "hello", "ehllo", "y" }, + { "hello", "helol", "y" }, + { "hello", "abcd", "n" }, + //{ "ehllo", "helol", "y" }, + { "hello", "helxxlo", "y" }, + { "hello", "ehlxxlo", "n" }, + { "hello", "heaao", "y" }, + { "_123456789_123456789_123456789_123456789", "_123456789_123456789_123456789_12345678", "y" }, + }; + //printf("unittest_speller()\n"); + const void *p = speller("hello", &speller_test, (void *)"hell", idchars); + assert(p != NULL); + for (int i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) + { + //printf("case [%d]\n", i); + void *p = speller(cases[i][0], &speller_test, (void *)cases[i][1], idchars); + if (p) + assert(cases[i][2][0] == 'y'); + else + assert(cases[i][2][0] == 'n'); + } + //printf("unittest_speller() success\n"); +} + +#endif diff --git a/root/speller.h b/root/speller.h new file mode 100644 index 00000000..bfffb739 --- /dev/null +++ b/root/speller.h @@ -0,0 +1,7 @@ + +typedef void *(fp_speller_t)(void *, const char *); + +extern const char idchars[]; + +void *speller(const char *seed, fp_speller_t fp, void *fparg, const char *charset); + diff --git a/root/stringtable.c b/root/stringtable.c new file mode 100644 index 00000000..f1c0044a --- /dev/null +++ b/root/stringtable.c @@ -0,0 +1,139 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#include +#include +#include + +#include "root.h" +#include "rmem.h" +#include "dchar.h" +#include "lstring.h" +#include "stringtable.h" + +void StringTable::init(unsigned size) +{ + table = (void **)mem.calloc(size, sizeof(void *)); + tabledim = size; + count = 0; +} + +StringTable::~StringTable() +{ + unsigned i; + + // Zero out dangling pointers to help garbage collector. + // Should zero out StringEntry's too. + for (i = 0; i < count; i++) + table[i] = NULL; + + mem.free(table); + table = NULL; +} + +struct StringEntry +{ + StringEntry *left; + StringEntry *right; + hash_t hash; + + StringValue value; + + static StringEntry *alloc(const dchar *s, unsigned len); +}; + +StringEntry *StringEntry::alloc(const dchar *s, unsigned len) +{ + StringEntry *se; + + se = (StringEntry *) mem.calloc(1,sizeof(StringEntry) - sizeof(Lstring) + Lstring::size(len)); + se->value.lstring.length = len; + se->hash = Dchar::calcHash(s,len); + memcpy(se->value.lstring.string, s, len * sizeof(dchar)); + return se; +} + +void **StringTable::search(const dchar *s, unsigned len) +{ + hash_t hash; + unsigned u; + int cmp; + StringEntry **se; + + //printf("StringTable::search(%p,%d)\n",s,len); + hash = Dchar::calcHash(s,len); + u = hash % tabledim; + se = (StringEntry **)&table[u]; + //printf("\thash = %d, u = %d\n",hash,u); + while (*se) + { + cmp = (*se)->hash - hash; + if (cmp == 0) + { + cmp = (*se)->value.lstring.len() - len; + if (cmp == 0) + { + cmp = Dchar::memcmp(s,(*se)->value.lstring.toDchars(),len); + if (cmp == 0) + break; + } + } + if (cmp < 0) + se = &(*se)->left; + else + se = &(*se)->right; + } + //printf("\treturn %p, %p\n",se, (*se)); + return (void **)se; +} + +StringValue *StringTable::lookup(const dchar *s, unsigned len) +{ StringEntry *se; + + se = *(StringEntry **)search(s,len); + if (se) + return &se->value; + else + return NULL; +} + +StringValue *StringTable::update(const dchar *s, unsigned len) +{ StringEntry **pse; + StringEntry *se; + + pse = (StringEntry **)search(s,len); + se = *pse; + if (!se) // not in table: so create new entry + { + se = StringEntry::alloc(s, len); + *pse = se; + } + return &se->value; +} + +StringValue *StringTable::insert(const dchar *s, unsigned len) +{ StringEntry **pse; + StringEntry *se; + + pse = (StringEntry **)search(s,len); + se = *pse; + if (se) + return NULL; // error: already in table + else + { + se = StringEntry::alloc(s, len); + *pse = se; + } + return &se->value; +} + + + + diff --git a/root/stringtable.h b/root/stringtable.h new file mode 100644 index 00000000..ce714587 --- /dev/null +++ b/root/stringtable.h @@ -0,0 +1,48 @@ +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#ifndef STRINGTABLE_H +#define STRINGTABLE_H + +#if __SC__ +#pragma once +#endif + +#include "root.h" +#include "dchar.h" +#include "lstring.h" + +struct StringValue +{ + union + { int intvalue; + void *ptrvalue; + dchar *string; + }; + Lstring lstring; +}; + +struct StringTable +{ + void **table; + unsigned count; + unsigned tabledim; + + void init(unsigned size = 37); + ~StringTable(); + + StringValue *lookup(const dchar *s, unsigned len); + StringValue *insert(const dchar *s, unsigned len); + StringValue *update(const dchar *s, unsigned len); + +private: + void **search(const dchar *s, unsigned len); +}; + +#endif diff --git a/root/thread.h b/root/thread.h new file mode 100644 index 00000000..58e53c24 --- /dev/null +++ b/root/thread.h @@ -0,0 +1,12 @@ + +#ifndef THREAD_H +#define THREAD_H 1 + +typedef long ThreadId; + +struct Thread +{ + static ThreadId getId(); +}; + +#endif diff --git a/s2ir.c b/s2ir.c new file mode 100644 index 00000000..172b0515 --- /dev/null +++ b/s2ir.c @@ -0,0 +1,1722 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 2000-2011 by Digital Mars +// All Rights Reserved +// Written by Walter Bright +// http://www.digitalmars.com + +#include +#include +#include + +#include "mars.h" +#include "lexer.h" +#include "statement.h" +#include "expression.h" +#include "mtype.h" +#include "dsymbol.h" +#include "declaration.h" +#include "irstate.h" +#include "init.h" +#include "module.h" +#include "enum.h" +#include "aggregate.h" +#include "template.h" +#include "id.h" + +// Back end +#include "cc.h" +#include "type.h" +#include "code.h" +#include "oper.h" +#include "global.h" +#include "dt.h" + +#include "rmem.h" + +static char __file__[] = __FILE__; // for tassert.h +#include "tassert.h" + +elem *callfunc(Loc loc, + IRState *irs, + int directcall, // 1: don't do virtual call + Type *tret, // return type + elem *ec, // evaluates to function address + Type *ectype, // original type of ec + FuncDeclaration *fd, // if !=NULL, this is the function being called + Type *t, // TypeDelegate or TypeFunction for this function + elem *ehidden, // if !=NULL, this is the 'hidden' argument + Expressions *arguments); + +elem *exp2_copytotemp(elem *e); +elem *incUsageElem(IRState *irs, Loc loc); +StructDeclaration *needsPostblit(Type *t); + +#define elem_setLoc(e,loc) ((e)->Esrcpos.Sfilename = (char *)(loc).filename, \ + (e)->Esrcpos.Slinnum = (loc).linnum) + +#define SEH (TARGET_WINDOS) + +/*********************************************** + * Generate code to set index into scope table. + */ + +#if SEH +void setScopeIndex(Blockx *blx, block *b, int scope_index) +{ + block_appendexp(b, nteh_setScopeTableIndex(blx, scope_index)); +} +#else +#define setScopeIndex(blx, b, scope_index) ; +#endif + +/**************************************** + * Allocate a new block, and set the tryblock. + */ + +block *block_calloc(Blockx *blx) +{ + block *b = block_calloc(); + b->Btry = blx->tryblock; + return b; +} + +/************************************** + * Convert label to block. + */ + +block *labelToBlock(Loc loc, Blockx *blx, LabelDsymbol *label, int flag = 0) +{ + if (!label->statement) + { + error(loc, "undefined label %s", label->toChars()); + return NULL; + } + LabelStatement *s = label->statement; + if (!s->lblock) + { s->lblock = block_calloc(blx); + s->lblock->Btry = NULL; // fill this in later + + if (flag) + { + // Keep track of the forward reference to this block, so we can check it later + if (!s->fwdrefs) + s->fwdrefs = new Blocks(); + s->fwdrefs->push(blx->curblock); + } + } + return s->lblock; +} + +/************************************** + * Add in code to increment usage count for linnum. + */ + +void incUsage(IRState *irs, Loc loc) +{ + + if (global.params.cov && loc.linnum) + { + block_appendexp(irs->blx->curblock, incUsageElem(irs, loc)); + } +} + +/**************************************** + * This should be overridden by each statement class. + */ + +void Statement::toIR(IRState *irs) +{ + print(); + assert(0); +} + +/************************************* + */ + +void OnScopeStatement::toIR(IRState *irs) +{ +} + +/**************************************** + */ + +void IfStatement::toIR(IRState *irs) +{ + elem *e; + Blockx *blx = irs->blx; + + //printf("IfStatement::toIR('%s')\n", condition->toChars()); + + IRState mystate(irs, this); + + // bexit is the block that gets control after this IfStatement is done + block *bexit = mystate.breakBlock ? mystate.breakBlock : block_calloc(); + + incUsage(irs, loc); +#if 0 + if (match) + { /* Generate: + * if (match = RTLSYM_IFMATCH(string, pattern)) ... + */ + assert(condition->op == TOKmatch); + e = matchexp_toelem((MatchExp *)condition, &mystate, RTLSYM_IFMATCH); + Symbol *s = match->toSymbol(); + symbol_add(s); + e = el_bin(OPeq, TYnptr, el_var(s), e); + } + else +#endif + e = condition->toElemDtor(&mystate); + block_appendexp(blx->curblock, e); + block *bcond = blx->curblock; + block_next(blx, BCiftrue, NULL); + + list_append(&bcond->Bsucc, blx->curblock); + if (ifbody) + ifbody->toIR(&mystate); + list_append(&blx->curblock->Bsucc, bexit); + + if (elsebody) + { + block_next(blx, BCgoto, NULL); + list_append(&bcond->Bsucc, blx->curblock); + elsebody->toIR(&mystate); + list_append(&blx->curblock->Bsucc, bexit); + } + else + list_append(&bcond->Bsucc, bexit); + + block_next(blx, BCgoto, bexit); + +} + +/************************************** + */ + +#if DMDV2 +void PragmaStatement::toIR(IRState *irs) +{ + //printf("PragmaStatement::toIR()\n"); + if (ident == Id::startaddress) + { + assert(args && args->dim == 1); + Expression *e = args->tdata()[0]; + Dsymbol *sa = getDsymbol(e); + FuncDeclaration *f = sa->isFuncDeclaration(); + assert(f); + Symbol *s = f->toSymbol(); + while (irs->prev) + irs = irs->prev; + irs->startaddress = s; + } +} +#endif + +/*********************** + */ + +void WhileStatement::toIR(IRState *irs) +{ + assert(0); // was "lowered" +#if 0 + Blockx *blx = irs->blx; + + /* Create a new state, because we need a new continue and break target + */ + IRState mystate(irs,this); + mystate.breakBlock = block_calloc(blx); + mystate.contBlock = block_calloc(blx); + + list_append(&blx->curblock->Bsucc, mystate.contBlock); + block_next(blx, BCgoto, mystate.contBlock); + incUsage(irs, loc); + block_appendexp(mystate.contBlock, condition->toElem(&mystate)); + + block_next(blx, BCiftrue, NULL); + + /* curblock is the start of the while loop body + */ + list_append(&mystate.contBlock->Bsucc, blx->curblock); + if (body) + body->toIR(&mystate); + list_append(&blx->curblock->Bsucc, mystate.contBlock); + block_next(blx, BCgoto, mystate.breakBlock); + + list_append(&mystate.contBlock->Bsucc, mystate.breakBlock); +#endif +} + +/****************************************** + */ + +void DoStatement::toIR(IRState *irs) +{ + Blockx *blx = irs->blx; + + IRState mystate(irs,this); + mystate.breakBlock = block_calloc(blx); + mystate.contBlock = block_calloc(blx); + + block *bpre = blx->curblock; + block_next(blx, BCgoto, NULL); + list_append(&bpre->Bsucc, blx->curblock); + + list_append(&mystate.contBlock->Bsucc, blx->curblock); + list_append(&mystate.contBlock->Bsucc, mystate.breakBlock); + + if (body) + body->toIR(&mystate); + list_append(&blx->curblock->Bsucc, mystate.contBlock); + + block_next(blx, BCgoto, mystate.contBlock); + incUsage(irs, condition->loc); + block_appendexp(mystate.contBlock, condition->toElemDtor(&mystate)); + block_next(blx, BCiftrue, mystate.breakBlock); + +} + +/***************************************** + */ + +void ForStatement::toIR(IRState *irs) +{ + Blockx *blx = irs->blx; + + IRState mystate(irs,this); + mystate.breakBlock = block_calloc(blx); + mystate.contBlock = block_calloc(blx); + + if (init) + init->toIR(&mystate); + block *bpre = blx->curblock; + block_next(blx,BCgoto,NULL); + block *bcond = blx->curblock; + list_append(&bpre->Bsucc, bcond); + list_append(&mystate.contBlock->Bsucc, bcond); + if (condition) + { + incUsage(irs, condition->loc); + block_appendexp(bcond, condition->toElemDtor(&mystate)); + block_next(blx,BCiftrue,NULL); + list_append(&bcond->Bsucc, blx->curblock); + list_append(&bcond->Bsucc, mystate.breakBlock); + } + else + { /* No conditional, it's a straight goto + */ + block_next(blx,BCgoto,NULL); + list_append(&bcond->Bsucc, blx->curblock); + } + + if (body) + body->toIR(&mystate); + /* End of the body goes to the continue block + */ + list_append(&blx->curblock->Bsucc, mystate.contBlock); + block_next(blx, BCgoto, mystate.contBlock); + + if (increment) + { + incUsage(irs, increment->loc); + block_appendexp(mystate.contBlock, increment->toElemDtor(&mystate)); + } + + /* The 'break' block follows the for statement. + */ + block_next(blx,BCgoto, mystate.breakBlock); +} + + +/************************************** + */ + +void ForeachStatement::toIR(IRState *irs) +{ + printf("ForeachStatement::toIR() %s\n", toChars()); + assert(0); // done by "lowering" in the front end +#if 0 + Type *tab; + elem *eaggr; + elem *e; + elem *elength; + tym_t keytym; + + //printf("ForeachStatement::toIR()\n"); + block *bpre; + block *bcond; + block *bbody; + block *bbodyx; + Blockx *blx = irs->blx; + + IRState mystate(irs,this); + mystate.breakBlock = block_calloc(blx); + mystate.contBlock = block_calloc(blx); + + tab = aggr->type->toBasetype(); + assert(tab->ty == Tarray || tab->ty == Tsarray); + + incUsage(irs, aggr->loc); + eaggr = aggr->toElem(irs); + + /* Create sp: pointer to start of array data + */ + + Symbol *sp = symbol_genauto(TYnptr); + + if (tab->ty == Tarray) + { + // stmp is copy of eaggr (the array), so eaggr is evaluated only once + Symbol *stmp; + + // Initialize stmp + stmp = symbol_genauto(eaggr); + e = el_bin(OPeq, eaggr->Ety, el_var(stmp), eaggr); + block_appendexp(blx->curblock, e); + + // Initialize sp + e = el_una(OPmsw, TYnptr, el_var(stmp)); + e = el_bin(OPeq, TYnptr, el_var(sp), e); + block_appendexp(blx->curblock, e); + + // Get array.length + elength = el_var(stmp); + elength->Ety = TYsize_t; + } + else // Tsarray + { + // Initialize sp + e = el_una(OPaddr, TYnptr, eaggr); + e = el_bin(OPeq, TYnptr, el_var(sp), e); + block_appendexp(blx->curblock, e); + + // Get array.length + elength = el_long(TYsize_t, ((TypeSArray *)tab)->dim->toInteger()); + } + + Symbol *spmax; + Symbol *skey; + + if (key) + { + /* Create skey, the index to the array. + * Initialize skey to 0 (foreach) or .length (foreach_reverse). + */ + skey = key->toSymbol(); + symbol_add(skey); + keytym = key->type->totym(); + elem *einit = (op == TOKforeach_reverse) ? elength : el_long(keytym, 0); + e = el_bin(OPeq, keytym, el_var(skey), einit); + } + else + { + /* Create spmax, pointer past end of data. + * Initialize spmax = sp + array.length * size + */ + spmax = symbol_genauto(TYnptr); + e = el_bin(OPmul, TYsize_t, elength, el_long(TYsize_t, tab->nextOf()->size())); + e = el_bin(OPadd, TYnptr, el_var(sp), e); + e = el_bin(OPeq, TYnptr, el_var(spmax), e); + + /* For foreach_reverse, swap sp and spmax + */ + if (op == TOKforeach_reverse) + { Symbol *s = sp; + sp = spmax; + spmax = s; + } + } + block_appendexp(blx->curblock, e); + + bpre = blx->curblock; + block_next(blx,BCgoto,NULL); + bcond = blx->curblock; + + if (key) + { + if (op == TOKforeach_reverse) + { + // Construct (key != 0) + e = el_bin(OPne, TYint, el_var(skey), el_long(keytym, 0)); + } + else + { + // Construct (key < elength) + e = el_bin(OPlt, TYint, el_var(skey), elength); + } + } + else + { + if (op == TOKforeach_reverse) + { + // Construct (sp > spmax) + e = el_bin(OPgt, TYint, el_var(sp), el_var(spmax)); + } + else + { + // Construct (sp < spmax) + e = el_bin(OPlt, TYint, el_var(sp), el_var(spmax)); + } + } + bcond->Belem = e; + block_next(blx, BCiftrue, NULL); + + if (op == TOKforeach_reverse) + { + if (key) + { // Construct (skey -= 1) + e = el_bin(OPminass, keytym, el_var(skey), el_long(keytym, 1)); + } + else + { // Construct (sp--) + e = el_bin(OPminass, TYnptr, el_var(sp), el_long(TYsize_t, tab->nextOf()->size())); + } + block_appendexp(blx->curblock, e); + } + + Symbol *s; + FuncDeclaration *fd = NULL; + if (value->toParent2()) + fd = value->toParent2()->isFuncDeclaration(); + int nrvo = 0; + if (fd && fd->nrvo_can && fd->nrvo_var == value) + { + s = fd->shidden; + nrvo = 1; + } + else + { s = value->toSymbol(); + symbol_add(s); + } + + // Construct (value = *sp) or (value = sp[skey * elemsize]) + tym_t tym = value->type->totym(); + if (key) + { // sp + skey * elemsize + e = el_bin(OPmul, keytym, el_var(skey), el_long(keytym, tab->nextOf()->size())); + e = el_bin(OPadd, TYnptr, el_var(sp), e); + } + else + e = el_var(sp); + + elem *evalue; +#if DMDV2 + if (value->offset) // if value is a member of a closure + { + assert(irs->sclosure); + evalue = el_var(irs->sclosure); + evalue = el_bin(OPadd, TYnptr, evalue, el_long(TYint, value->offset)); + evalue = el_una(OPind, value->type->totym(), evalue); + } + else +#endif + evalue = el_var(s); + + if (value->isOut() || value->isRef()) + { + assert(value->storage_class & (STCout | STCref)); + e = el_bin(OPeq, TYnptr, evalue, e); + } + else + { + if (nrvo) + evalue = el_una(OPind, tym, evalue); + StructDeclaration *sd = needsPostblit(value->type); + if (tybasic(tym) == TYstruct) + { + e = el_bin(OPeq, tym, evalue, el_una(OPind, tym, e)); + e->Eoper = OPstreq; + e->ET = value->type->toCtype(); +#if DMDV2 + // Call postblit on e + if (sd) + { FuncDeclaration *fd = sd->postblit; + elem *ec = el_copytree(evalue); + ec = el_una(OPaddr, TYnptr, ec); + ec = callfunc(loc, irs, 1, Type::tvoid, ec, sd->type->pointerTo(), fd, fd->type, NULL, NULL); + e = el_combine(e, ec); + } +#endif + } + else if (tybasic(tym) == TYarray) + { + if (sd) + { + /* Generate: + * _d_arrayctor(ti, efrom, eto) + */ + Expression *ti = value->type->toBasetype()->nextOf()->toBasetype()->getTypeInfo(NULL); + elem *esize = el_long(TYsize_t, ((TypeSArray *)value->type->toBasetype())->dim->toInteger()); + elem *eto = el_pair(TYdarray, esize, el_una(OPaddr, TYnptr, evalue)); + elem *efrom = el_pair(TYdarray, el_copytree(esize), e); + elem *ep = el_params(eto, efrom, ti->toElem(irs), NULL); + int rtl = RTLSYM_ARRAYCTOR; + e = el_bin(OPcall, TYvoid, el_var(rtlsym[rtl]), ep); + } + else + { + e = el_bin(OPeq, tym, evalue, el_una(OPind, tym, e)); + e->Eoper = OPstreq; + e->Ejty = e->Ety = TYstruct; + e->ET = value->type->toCtype(); + } + } + else + e = el_bin(OPeq, tym, evalue, el_una(OPind, tym, e)); + } + incUsage(irs, loc); + block_appendexp(blx->curblock, e); + + bbody = blx->curblock; + if (body) + body->toIR(&mystate); + bbodyx = blx->curblock; + block_next(blx,BCgoto,mystate.contBlock); + + if (op == TOKforeach) + { + if (key) + { // Construct (skey += 1) + e = el_bin(OPaddass, keytym, el_var(skey), el_long(keytym, 1)); + } + else + { // Construct (sp++) + e = el_bin(OPaddass, TYnptr, el_var(sp), el_long(TYsize_t, tab->nextOf()->size())); + } + mystate.contBlock->Belem = e; + } + block_next(blx,BCgoto,mystate.breakBlock); + + list_append(&bpre->Bsucc,bcond); + list_append(&bcond->Bsucc,bbody); + list_append(&bcond->Bsucc,mystate.breakBlock); + list_append(&bbodyx->Bsucc,mystate.contBlock); + list_append(&mystate.contBlock->Bsucc,bcond); +#endif +} + + +/************************************** + */ + +#if DMDV2 +void ForeachRangeStatement::toIR(IRState *irs) +{ + assert(0); +#if 0 + Type *tab; + elem *eaggr; + elem *elwr; + elem *eupr; + elem *e; + elem *elength; + tym_t keytym; + + //printf("ForeachStatement::toIR()\n"); + block *bpre; + block *bcond; + block *bbody; + block *bbodyx; + Blockx *blx = irs->blx; + + IRState mystate(irs,this); + mystate.breakBlock = block_calloc(blx); + mystate.contBlock = block_calloc(blx); + + incUsage(irs, lwr->loc); + elwr = lwr->toElem(irs); + + incUsage(irs, upr->loc); + eupr = upr->toElem(irs); + + /* Create skey, the index to the array. + * Initialize skey to elwr (foreach) or eupr (foreach_reverse). + */ + Symbol *skey = key->toSymbol(); + symbol_add(skey); + keytym = key->type->totym(); + + elem *ekey; + if (key->offset) // if key is member of a closure + { + assert(irs->sclosure); + ekey = el_var(irs->sclosure); + ekey = el_bin(OPadd, TYnptr, ekey, el_long(TYint, key->offset)); + ekey = el_una(OPind, keytym, ekey); + } + else + ekey = el_var(skey); + + elem *einit = (op == TOKforeach_reverse) ? eupr : elwr; + e = el_bin(OPeq, keytym, ekey, einit); // skey = einit; + block_appendexp(blx->curblock, e); + + /* Make a copy of the end condition, so it only + * gets evaluated once. + */ + elem *eend = (op == TOKforeach_reverse) ? elwr : eupr; + Symbol *send = symbol_genauto(eend); + e = el_bin(OPeq, eend->Ety, el_var(send), eend); + assert(tybasic(e->Ety) != TYstruct); + block_appendexp(blx->curblock, e); + + bpre = blx->curblock; + block_next(blx,BCgoto,NULL); + bcond = blx->curblock; + + if (op == TOKforeach_reverse) + { + // Construct (key > elwr) + e = el_bin(OPgt, TYint, el_copytree(ekey), el_var(send)); + } + else + { + // Construct (key < eupr) + e = el_bin(OPlt, TYint, el_copytree(ekey), el_var(send)); + } + + // The size of the increment + size_t sz = 1; + Type *tkeyb = key->type->toBasetype(); + if (tkeyb->ty == Tpointer) + sz = tkeyb->nextOf()->size(); + + bcond->Belem = e; + block_next(blx, BCiftrue, NULL); + + if (op == TOKforeach_reverse) + { + // Construct (skey -= 1) + e = el_bin(OPminass, keytym, el_copytree(ekey), el_long(keytym, sz)); + block_appendexp(blx->curblock, e); + } + + bbody = blx->curblock; + if (body) + body->toIR(&mystate); + bbodyx = blx->curblock; + block_next(blx,BCgoto,mystate.contBlock); + + if (op == TOKforeach) + { + // Construct (skey += 1) + e = el_bin(OPaddass, keytym, el_copytree(ekey), el_long(keytym, sz)); + mystate.contBlock->Belem = e; + } + block_next(blx,BCgoto,mystate.breakBlock); + + list_append(&bpre->Bsucc,bcond); + list_append(&bcond->Bsucc,bbody); + list_append(&bcond->Bsucc,mystate.breakBlock); + list_append(&bbodyx->Bsucc,mystate.contBlock); + list_append(&mystate.contBlock->Bsucc,bcond); +#endif +} +#endif + + +/**************************************** + */ + +void BreakStatement::toIR(IRState *irs) +{ + block *bbreak; + block *b; + Blockx *blx = irs->blx; + + bbreak = irs->getBreakBlock(ident); + assert(bbreak); + b = blx->curblock; + incUsage(irs, loc); + + // Adjust exception handler scope index if in different try blocks + if (b->Btry != bbreak->Btry) + { + //setScopeIndex(blx, b, bbreak->Btry ? bbreak->Btry->Bscope_index : -1); + } + + /* Nothing more than a 'goto' to the current break destination + */ + list_append(&b->Bsucc, bbreak); + block_next(blx, BCgoto, NULL); +} + +/************************************ + */ + +void ContinueStatement::toIR(IRState *irs) +{ + block *bcont; + block *b; + Blockx *blx = irs->blx; + + //printf("ContinueStatement::toIR() %p\n", this); + bcont = irs->getContBlock(ident); + assert(bcont); + b = blx->curblock; + incUsage(irs, loc); + + // Adjust exception handler scope index if in different try blocks + if (b->Btry != bcont->Btry) + { + //setScopeIndex(blx, b, bcont->Btry ? bcont->Btry->Bscope_index : -1); + } + + /* Nothing more than a 'goto' to the current continue destination + */ + list_append(&b->Bsucc, bcont); + block_next(blx, BCgoto, NULL); +} + +/************************************** + */ + +void el_setVolatile(elem *e) +{ + elem_debug(e); + while (1) + { + e->Ety |= mTYvolatile; + if (OTunary(e->Eoper)) + e = e->E1; + else if (OTbinary(e->Eoper)) + { el_setVolatile(e->E2); + e = e->E1; + } + else + break; + } +} + +void VolatileStatement::toIR(IRState *irs) +{ + block *b; + + if (statement) + { + Blockx *blx = irs->blx; + + block_goto(blx, BCgoto, NULL); + b = blx->curblock; + + statement->toIR(irs); + + block_goto(blx, BCgoto, NULL); + + // Mark the blocks generated as volatile + for (; b != blx->curblock; b = b->Bnext) + { b->Bflags |= BFLvolatile; + if (b->Belem) + el_setVolatile(b->Belem); + } + } +} + +/************************************** + */ + +void GotoStatement::toIR(IRState *irs) +{ + Blockx *blx = irs->blx; + + if (!label->statement) + { error("label %s is undefined", label->toChars()); + return; + } + if (tf != label->statement->tf) + error("cannot goto forward out of or into finally block"); + + block *bdest = labelToBlock(loc, blx, label, 1); + if (!bdest) + return; + block *b = blx->curblock; + incUsage(irs, loc); + + if (b->Btry != bdest->Btry) + { + // Check that bdest is in an enclosing try block + for (block *bt = b->Btry; bt != bdest->Btry; bt = bt->Btry) + { + if (!bt) + { + //printf("b->Btry = %p, bdest->Btry = %p\n", b->Btry, bdest->Btry); + error("cannot goto into try block"); + break; + } + } + } + + list_append(&b->Bsucc,bdest); + block_next(blx,BCgoto,NULL); +} + +void LabelStatement::toIR(IRState *irs) +{ + //printf("LabelStatement::toIR() %p, statement = %p\n", this, statement); + Blockx *blx = irs->blx; + block *bc = blx->curblock; + IRState mystate(irs,this); + mystate.ident = ident; + + if (lblock) + { + // At last, we know which try block this label is inside + lblock->Btry = blx->tryblock; + + /* Go through the forward references and check. + */ + if (fwdrefs) + { + for (size_t i = 0; i < fwdrefs->dim; i++) + { block *b = fwdrefs->tdata()[i]; + + if (b->Btry != lblock->Btry) + { + // Check that lblock is in an enclosing try block + for (block *bt = b->Btry; bt != lblock->Btry; bt = bt->Btry) + { + if (!bt) + { + //printf("b->Btry = %p, lblock->Btry = %p\n", b->Btry, lblock->Btry); + error("cannot goto into try block"); + break; + } + } + } + + } + delete fwdrefs; + fwdrefs = NULL; + } + } + else + lblock = block_calloc(blx); + block_next(blx,BCgoto,lblock); + list_append(&bc->Bsucc,blx->curblock); + if (statement) + statement->toIR(&mystate); +} + +/************************************** + */ + +void SwitchStatement::toIR(IRState *irs) +{ + int string; + Blockx *blx = irs->blx; + + //printf("SwitchStatement::toIR()\n"); + IRState mystate(irs,this); + + mystate.switchBlock = blx->curblock; + + /* Block for where "break" goes to + */ + mystate.breakBlock = block_calloc(blx); + + /* Block for where "default" goes to. + * If there is a default statement, then that is where default goes. + * If not, then do: + * default: break; + * by making the default block the same as the break block. + */ + mystate.defaultBlock = sdefault ? block_calloc(blx) : mystate.breakBlock; + + int numcases = 0; + if (cases) + numcases = cases->dim; + + incUsage(irs, loc); + elem *econd = condition->toElemDtor(&mystate); +#if DMDV2 + if (hasVars) + { /* Generate a sequence of if-then-else blocks for the cases. + */ + if (econd->Eoper != OPvar) + { + elem *e = exp2_copytotemp(econd); + block_appendexp(mystate.switchBlock, e); + econd = e->E2; + } + + for (int i = 0; i < numcases; i++) + { CaseStatement *cs = cases->tdata()[i]; + + elem *ecase = cs->exp->toElemDtor(&mystate); + elem *e = el_bin(OPeqeq, TYbool, el_copytree(econd), ecase); + block *b = blx->curblock; + block_appendexp(b, e); + block *bcase = block_calloc(blx); + cs->cblock = bcase; + block_next(blx, BCiftrue, NULL); + list_append(&b->Bsucc, bcase); + list_append(&b->Bsucc, blx->curblock); + } + + /* The final 'else' clause goes to the default + */ + block *b = blx->curblock; + block_next(blx, BCgoto, NULL); + list_append(&b->Bsucc, mystate.defaultBlock); + + body->toIR(&mystate); + + /* Have the end of the switch body fall through to the block + * following the switch statement. + */ + block_goto(blx, BCgoto, mystate.breakBlock); + return; + } +#endif + + if (condition->type->isString()) + { + // Number the cases so we can unscramble things after the sort() + for (int i = 0; i < numcases; i++) + { CaseStatement *cs = cases->tdata()[i]; + cs->index = i; + } + + cases->sort(); + + /* Create a sorted array of the case strings, and si + * will be the symbol for it. + */ + dt_t *dt = NULL; + Symbol *si = symbol_generate(SCstatic,type_fake(TYdarray)); +#if MACHOBJ + si->Sseg = DATA; +#endif + dtsize_t(&dt, numcases); + dtxoff(&dt, si, PTRSIZE * 2, TYnptr); + + for (int i = 0; i < numcases; i++) + { CaseStatement *cs = cases->tdata()[i]; + + if (cs->exp->op != TOKstring) + { error("case '%s' is not a string", cs->exp->toChars()); // BUG: this should be an assert + } + else + { + StringExp *se = (StringExp *)(cs->exp); + unsigned len = se->len; + dtsize_t(&dt, len); + dtabytes(&dt, TYnptr, 0, se->len * se->sz, (char *)se->string); + } + } + + si->Sdt = dt; + si->Sfl = FLdata; + outdata(si); + + /* Call: + * _d_switch_string(string[] si, string econd) + */ + elem *eparam = el_param(econd, el_var(si)); + switch (condition->type->nextOf()->ty) + { + case Tchar: + econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_STRING]), eparam); + break; + case Twchar: + econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_USTRING]), eparam); + break; + case Tdchar: // BUG: implement + econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_DSTRING]), eparam); + break; + default: + assert(0); + } + elem_setLoc(econd, loc); + string = 1; + } + else + string = 0; + block_appendexp(mystate.switchBlock, econd); + block_next(blx,BCswitch,NULL); + + // Corresponding free is in block_free + targ_llong *pu = (targ_llong *) ::malloc(sizeof(*pu) * (numcases + 1)); + mystate.switchBlock->BS.Bswitch = pu; + /* First pair is the number of cases, and the default block + */ + *pu++ = numcases; + list_append(&mystate.switchBlock->Bsucc, mystate.defaultBlock); + + /* Fill in the first entry in each pair, which is the case value. + * CaseStatement::toIR() will fill in + * the second entry for each pair with the block. + */ + for (int i = 0; i < numcases; i++) + { + CaseStatement *cs = cases->tdata()[i]; + if (string) + { + pu[cs->index] = i; + } + else + { + pu[i] = cs->exp->toInteger(); + } + } + + body->toIR(&mystate); + + /* Have the end of the switch body fall through to the block + * following the switch statement. + */ + block_goto(blx, BCgoto, mystate.breakBlock); +} + +void CaseStatement::toIR(IRState *irs) +{ + Blockx *blx = irs->blx; + block *bcase = blx->curblock; + if (!cblock) + cblock = block_calloc(blx); + block_next(blx,BCgoto,cblock); + block *bsw = irs->getSwitchBlock(); + if (bsw->BC == BCswitch) + list_append(&bsw->Bsucc,cblock); // second entry in pair + list_append(&bcase->Bsucc,cblock); + if (blx->tryblock != bsw->Btry) + error("case cannot be in different try block level from switch"); + incUsage(irs, loc); + if (statement) + statement->toIR(irs); +} + +void DefaultStatement::toIR(IRState *irs) +{ + Blockx *blx = irs->blx; + block *bcase = blx->curblock; + block *bdefault = irs->getDefaultBlock(); + block_next(blx,BCgoto,bdefault); + list_append(&bcase->Bsucc,blx->curblock); + if (blx->tryblock != irs->getSwitchBlock()->Btry) + error("default cannot be in different try block level from switch"); + incUsage(irs, loc); + if (statement) + statement->toIR(irs); +} + +void GotoDefaultStatement::toIR(IRState *irs) +{ + block *b; + Blockx *blx = irs->blx; + block *bdest = irs->getDefaultBlock(); + + b = blx->curblock; + + // The rest is equivalent to GotoStatement + + // Adjust exception handler scope index if in different try blocks + if (b->Btry != bdest->Btry) + { + // Check that bdest is in an enclosing try block + for (block *bt = b->Btry; bt != bdest->Btry; bt = bt->Btry) + { + if (!bt) + { + //printf("b->Btry = %p, bdest->Btry = %p\n", b->Btry, bdest->Btry); + error("cannot goto into try block"); + break; + } + } + + //setScopeIndex(blx, b, bdest->Btry ? bdest->Btry->Bscope_index : -1); + } + + list_append(&b->Bsucc,bdest); + incUsage(irs, loc); + block_next(blx,BCgoto,NULL); +} + +void GotoCaseStatement::toIR(IRState *irs) +{ + block *b; + Blockx *blx = irs->blx; + block *bdest = cs->cblock; + + if (!bdest) + { + bdest = block_calloc(blx); + cs->cblock = bdest; + } + + b = blx->curblock; + + // The rest is equivalent to GotoStatement + + // Adjust exception handler scope index if in different try blocks + if (b->Btry != bdest->Btry) + { + // Check that bdest is in an enclosing try block + for (block *bt = b->Btry; bt != bdest->Btry; bt = bt->Btry) + { + if (!bt) + { + //printf("b->Btry = %p, bdest->Btry = %p\n", b->Btry, bdest->Btry); + error("cannot goto into try block"); + break; + } + } + + //setScopeIndex(blx, b, bdest->Btry ? bdest->Btry->Bscope_index : -1); + } + + list_append(&b->Bsucc,bdest); + incUsage(irs, loc); + block_next(blx,BCgoto,NULL); +} + +void SwitchErrorStatement::toIR(IRState *irs) +{ + Blockx *blx = irs->blx; + + //printf("SwitchErrorStatement::toIR()\n"); + + elem *efilename = el_ptr(blx->module->toSymbol()); + elem *elinnum = el_long(TYint, loc.linnum); + elem *e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_DSWITCHERR]), el_param(elinnum, efilename)); + block_appendexp(blx->curblock, e); +} + +/************************************** + */ + +void ReturnStatement::toIR(IRState *irs) +{ + Blockx *blx = irs->blx; + + incUsage(irs, loc); + if (exp) + { elem *e; + + FuncDeclaration *func = irs->getFunc(); + assert(func); + assert(func->type->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)(func->type); + + enum RET retmethod = tf->retStyle(); + if (retmethod == RETstack) + { + elem *es; + + /* If returning struct literal, write result + * directly into return value + */ + if (exp->op == TOKstructliteral) + { StructLiteralExp *se = (StructLiteralExp *)exp; + char save[sizeof(StructLiteralExp)]; + memcpy(save, se, sizeof(StructLiteralExp)); + se->sym = irs->shidden; + se->soffset = 0; + se->fillHoles = 1; + e = exp->toElemDtor(irs); + memcpy(se, save, sizeof(StructLiteralExp)); + + } + else + e = exp->toElemDtor(irs); + assert(e); + + if (exp->op == TOKstructliteral || + (func->nrvo_can && func->nrvo_var)) + { + // Return value via hidden pointer passed as parameter + // Write exp; return shidden; + es = e; + } + else + { + // Return value via hidden pointer passed as parameter + // Write *shidden=exp; return shidden; + int op; + tym_t ety; + + ety = e->Ety; + es = el_una(OPind,ety,el_var(irs->shidden)); + op = (tybasic(ety) == TYstruct) ? OPstreq : OPeq; + es = el_bin(op, ety, es, e); + if (op == OPstreq) + es->ET = exp->type->toCtype(); +#if DMDV2 + /* Call postBlit() on *shidden + */ + Type *tb = exp->type->toBasetype(); + //if (tb->ty == Tstruct) exp->dump(0); + if ((exp->op == TOKvar || exp->op == TOKdotvar || exp->op == TOKstar || exp->op == TOKthis) && + tb->ty == Tstruct) + { StructDeclaration *sd = ((TypeStruct *)tb)->sym; + if (sd->postblit) + { FuncDeclaration *fd = sd->postblit; + if (fd->storage_class & STCdisable) + { + fd->toParent()->error(loc, "is not copyable because it is annotated with @disable"); + } + elem *ec = el_var(irs->shidden); + ec = callfunc(loc, irs, 1, Type::tvoid, ec, tb->pointerTo(), fd, fd->type, NULL, NULL); + es = el_bin(OPcomma, ec->Ety, es, ec); + } + +#if 0 + /* It has been moved, so disable destructor + */ + if (exp->op == TOKvar) + { VarExp *ve = (VarExp *)exp; + VarDeclaration *v = ve->var->isVarDeclaration(); + if (v && v->rundtor) + { + elem *er = el_var(v->rundtor->toSymbol()); + er = el_bin(OPeq, TYint, er, el_long(TYint, 0)); + es = el_bin(OPcomma, TYint, es, er); + } + } +#endif + } +#endif + } + e = el_var(irs->shidden); + e = el_bin(OPcomma, e->Ety, es, e); + } +#if DMDV2 + else if (tf->isref) + { // Reference return, so convert to a pointer + Expression *ae = exp->addressOf(NULL); + e = ae->toElemDtor(irs); + } +#endif + else + { + e = exp->toElemDtor(irs); + assert(e); + } + + elem_setLoc(e, loc); + block_appendexp(blx->curblock, e); + block_next(blx, BCretexp, NULL); + } + else + block_next(blx, BCret, NULL); +} + +/************************************** + */ + +void ExpStatement::toIR(IRState *irs) +{ + Blockx *blx = irs->blx; + + //printf("ExpStatement::toIR(), exp = %s\n", exp ? exp->toChars() : ""); + incUsage(irs, loc); + if (exp) + block_appendexp(blx->curblock,exp->toElemDtor(irs)); +} + +/************************************** + */ + +void DtorExpStatement::toIR(IRState *irs) +{ + //printf("DtorExpStatement::toIR(), exp = %s\n", exp ? exp->toChars() : ""); + + FuncDeclaration *fd = irs->getFunc(); + assert(fd); + if (fd->nrvo_can && fd->nrvo_var == var) + /* Do not call destructor, because var is returned as the nrvo variable. + * This is done at this stage because nrvo can be turned off at a + * very late stage in semantic analysis. + */ + ; + else + { + ExpStatement::toIR(irs); + } +} + +/************************************** + */ + +void CompoundStatement::toIR(IRState *irs) +{ + if (statements) + { + size_t dim = statements->dim; + for (size_t i = 0 ; i < dim ; i++) + { + Statement *s = statements->tdata()[i]; + if (s != NULL) + { + s->toIR(irs); + } + } + } +} + + +/************************************** + */ + +void UnrolledLoopStatement::toIR(IRState *irs) +{ + Blockx *blx = irs->blx; + + IRState mystate(irs, this); + mystate.breakBlock = block_calloc(blx); + + block *bpre = blx->curblock; + block_next(blx, BCgoto, NULL); + + block *bdo = blx->curblock; + list_append(&bpre->Bsucc, bdo); + + block *bdox; + + size_t dim = statements->dim; + for (size_t i = 0 ; i < dim ; i++) + { + Statement *s = statements->tdata()[i]; + if (s != NULL) + { + mystate.contBlock = block_calloc(blx); + + s->toIR(&mystate); + + bdox = blx->curblock; + block_next(blx, BCgoto, mystate.contBlock); + list_append(&bdox->Bsucc, mystate.contBlock); + } + } + + bdox = blx->curblock; + block_next(blx, BCgoto, mystate.breakBlock); + list_append(&bdox->Bsucc, mystate.breakBlock); +} + + +/************************************** + */ + +void ScopeStatement::toIR(IRState *irs) +{ + if (statement) + { + Blockx *blx = irs->blx; + IRState mystate(irs,this); + + if (mystate.prev->ident) + mystate.ident = mystate.prev->ident; + + statement->toIR(&mystate); + + if (mystate.breakBlock) + block_goto(blx,BCgoto,mystate.breakBlock); + } +} + +/*************************************** + */ + +void WithStatement::toIR(IRState *irs) +{ + Symbol *sp; + elem *e; + elem *ei; + ExpInitializer *ie; + Blockx *blx = irs->blx; + + //printf("WithStatement::toIR()\n"); + if (exp->op == TOKimport || exp->op == TOKtype) + { + } + else + { + // Declare with handle + sp = wthis->toSymbol(); + symbol_add(sp); + + // Perform initialization of with handle + ie = wthis->init->isExpInitializer(); + assert(ie); + ei = ie->exp->toElemDtor(irs); + e = el_var(sp); + e = el_bin(OPeq,e->Ety, e, ei); + elem_setLoc(e, loc); + incUsage(irs, loc); + block_appendexp(blx->curblock,e); + } + // Execute with block + if (body) + body->toIR(irs); +} + + +/*************************************** + */ + +void ThrowStatement::toIR(IRState *irs) +{ + // throw(exp) + + Blockx *blx = irs->blx; + + incUsage(irs, loc); + elem *e = exp->toElemDtor(irs); + e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_THROWC]),e); + block_appendexp(blx->curblock, e); +} + +/*************************************** + * Builds the following: + * _try + * block + * jcatch + * handler + * A try-catch statement. + */ + +void TryCatchStatement::toIR(IRState *irs) +{ + Blockx *blx = irs->blx; + +#if SEH + nteh_declarvars(blx); +#endif + + IRState mystate(irs, this); + + block *tryblock = block_goto(blx,BCgoto,NULL); + + int previndex = blx->scope_index; + tryblock->Blast_index = previndex; + blx->scope_index = tryblock->Bscope_index = blx->next_index++; + + // Set the current scope index + setScopeIndex(blx,tryblock,tryblock->Bscope_index); + + // This is the catch variable + tryblock->jcatchvar = symbol_genauto(type_fake(mTYvolatile | TYnptr)); + + blx->tryblock = tryblock; + block *breakblock = block_calloc(blx); + block_goto(blx,BC_try,NULL); + if (body) + { + body->toIR(&mystate); + } + blx->tryblock = tryblock->Btry; + + // break block goes here + block_goto(blx, BCgoto, breakblock); + + setScopeIndex(blx,blx->curblock, previndex); + blx->scope_index = previndex; + + // create new break block that follows all the catches + breakblock = block_calloc(blx); + + list_append(&blx->curblock->Bsucc, breakblock); + block_next(blx,BCgoto,NULL); + + assert(catches); + for (size_t i = 0 ; i < catches->dim; i++) + { + Catch *cs = catches->tdata()[i]; + if (cs->var) + cs->var->csym = tryblock->jcatchvar; + block *bcatch = blx->curblock; + if (cs->type) + bcatch->Bcatchtype = cs->type->toBasetype()->toSymbol(); + list_append(&tryblock->Bsucc,bcatch); + block_goto(blx,BCjcatch,NULL); + if (cs->handler != NULL) + { + IRState catchState(irs, this); + cs->handler->toIR(&catchState); + } + list_append(&blx->curblock->Bsucc, breakblock); + block_next(blx, BCgoto, NULL); + } + + block_next(blx,(enum BC)blx->curblock->BC, breakblock); +} + +/**************************************** + * A try-finally statement. + * Builds the following: + * _try + * block + * _finally + * finalbody + * _ret + */ + +void TryFinallyStatement::toIR(IRState *irs) +{ + //printf("TryFinallyStatement::toIR()\n"); + + Blockx *blx = irs->blx; + +#if SEH + nteh_declarvars(blx); +#endif + + block *tryblock = block_goto(blx, BCgoto, NULL); + + int previndex = blx->scope_index; + tryblock->Blast_index = previndex; + tryblock->Bscope_index = blx->next_index++; + blx->scope_index = tryblock->Bscope_index; + + // Current scope index + setScopeIndex(blx,tryblock,tryblock->Bscope_index); + + blx->tryblock = tryblock; + block_goto(blx,BC_try,NULL); + + IRState bodyirs(irs, this); + block *breakblock = block_calloc(blx); + block *contblock = block_calloc(blx); + + if (body) + body->toIR(&bodyirs); + blx->tryblock = tryblock->Btry; // back to previous tryblock + + setScopeIndex(blx,blx->curblock,previndex); + blx->scope_index = previndex; + + block_goto(blx,BCgoto, breakblock); + block *finallyblock = block_goto(blx,BCgoto,contblock); + + list_append(&tryblock->Bsucc,finallyblock); + + block_goto(blx,BC_finally,NULL); + + IRState finallyState(irs, this); + breakblock = block_calloc(blx); + contblock = block_calloc(blx); + + setScopeIndex(blx, blx->curblock, previndex); + if (finalbody) + finalbody->toIR(&finallyState); + block_goto(blx, BCgoto, contblock); + block_goto(blx, BCgoto, breakblock); + + block *retblock = blx->curblock; + block_next(blx,BC_ret,NULL); + + list_append(&finallyblock->Bsucc, blx->curblock); + list_append(&retblock->Bsucc, blx->curblock); +} + +/**************************************** + */ + +void SynchronizedStatement::toIR(IRState *irs) +{ + assert(0); +} + + +/**************************************** + */ + +void AsmStatement::toIR(IRState *irs) +{ + block *bpre; + block *basm; + Declaration *d; + Symbol *s; + Blockx *blx = irs->blx; + + //printf("AsmStatement::toIR(asmcode = %x)\n", asmcode); + bpre = blx->curblock; + block_next(blx,BCgoto,NULL); + basm = blx->curblock; + list_append(&bpre->Bsucc, basm); + basm->Bcode = asmcode; + basm->Balign = asmalign; +#if 0 + if (label) + { block *b; + + b = labelToBlock(loc, blx, label); + printf("AsmStatement::toIR() %p\n", b); + if (b) + list_append(&basm->Bsucc, b); + } +#endif + // Loop through each instruction, fixing Dsymbols into Symbol's + for (code *c = asmcode; c; c = c->next) + { LabelDsymbol *label; + block *b; + + switch (c->IFL1) + { + case FLblockoff: + case FLblock: + // FLblock and FLblockoff have LabelDsymbol's - convert to blocks + label = c->IEVlsym1; + b = labelToBlock(loc, blx, label); + list_append(&basm->Bsucc, b); + c->IEV1.Vblock = b; + break; + + case FLdsymbol: + case FLfunc: + s = c->IEVdsym1->toSymbol(); + if (s->Sclass == SCauto && s->Ssymnum == -1) + symbol_add(s); + c->IEVsym1 = s; + c->IFL1 = s->Sfl ? s->Sfl : FLauto; + break; + } + + // Repeat for second operand + switch (c->IFL2) + { + case FLblockoff: + case FLblock: + label = c->IEVlsym2; + b = labelToBlock(loc, blx, label); + list_append(&basm->Bsucc, b); + c->IEV2.Vblock = b; + break; + + case FLdsymbol: + case FLfunc: + d = c->IEVdsym2; + s = d->toSymbol(); + if (s->Sclass == SCauto && s->Ssymnum == -1) + symbol_add(s); + c->IEVsym2 = s; + c->IFL2 = s->Sfl ? s->Sfl : FLauto; + if (d->isDataseg()) + s->Sflags |= SFLlivexit; + break; + } + //c->print(); + } + + basm->bIasmrefparam = refparam; // are parameters reference? + basm->usIasmregs = regs; // registers modified + + block_next(blx,BCasm, NULL); + list_prepend(&basm->Bsucc, blx->curblock); + + if (naked) + { + blx->funcsym->Stype->Tty |= mTYnaked; + } +} + +/**************************************** + */ + +void ImportStatement::toIR(IRState *irs) +{ +} + + + diff --git a/scope.c b/scope.c new file mode 100644 index 00000000..44215108 --- /dev/null +++ b/scope.c @@ -0,0 +1,399 @@ + +// Copyright (c) 1999-2010 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "root.h" +#include "speller.h" + +#include "mars.h" +#include "init.h" +#include "identifier.h" +#include "scope.h" +#include "attrib.h" +#include "dsymbol.h" +#include "declaration.h" +#include "aggregate.h" +#include "module.h" +#include "id.h" +#include "lexer.h" + +Scope *Scope::freelist = NULL; + +void *Scope::operator new(size_t size) +{ + if (freelist) + { + Scope *s = freelist; + freelist = s->enclosing; + //printf("freelist %p\n", s); + assert(s->flags & SCOPEfree); + s->flags &= ~SCOPEfree; + return s; + } + + void *p = ::operator new(size); + //printf("new %p\n", p); + return p; +} + +Scope::Scope() +{ // Create root scope + + //printf("Scope::Scope() %p\n", this); + this->module = NULL; + this->scopesym = NULL; + this->sd = NULL; + this->enclosing = NULL; + this->parent = NULL; + this->sw = NULL; + this->tf = NULL; + this->tinst = NULL; + this->sbreak = NULL; + this->scontinue = NULL; + this->fes = NULL; + this->structalign = global.structalign; + this->func = NULL; + this->slabel = NULL; + this->linkage = LINKd; + this->protection = PROTpublic; + this->explicitProtection = 0; + this->stc = 0; + this->offset = 0; + this->inunion = 0; + this->incontract = 0; + this->nofree = 0; + this->noctor = 0; + this->noaccesscheck = 0; + this->mustsemantic = 0; + this->intypeof = 0; + this->parameterSpecialization = 0; + this->callSuper = 0; + this->flags = 0; + this->anonAgg = NULL; + this->lastdc = NULL; + this->lastoffset = 0; + this->docbuf = NULL; +} + +Scope::Scope(Scope *enclosing) +{ + //printf("Scope::Scope(enclosing = %p) %p\n", enclosing, this); + assert(!(enclosing->flags & SCOPEfree)); + this->module = enclosing->module; + this->func = enclosing->func; + this->parent = enclosing->parent; + this->scopesym = NULL; + this->sd = NULL; + this->sw = enclosing->sw; + this->tf = enclosing->tf; + this->tinst = enclosing->tinst; + this->sbreak = enclosing->sbreak; + this->scontinue = enclosing->scontinue; + this->fes = enclosing->fes; + this->structalign = enclosing->structalign; + this->enclosing = enclosing; +#ifdef DEBUG + if (enclosing->enclosing) + assert(!(enclosing->enclosing->flags & SCOPEfree)); + if (this == enclosing->enclosing) + { + printf("this = %p, enclosing = %p, enclosing->enclosing = %p\n", this, enclosing, enclosing->enclosing); + } + assert(this != enclosing->enclosing); +#endif + this->slabel = NULL; + this->linkage = enclosing->linkage; + this->protection = enclosing->protection; + this->explicitProtection = enclosing->explicitProtection; + this->stc = enclosing->stc; + this->offset = 0; + this->inunion = enclosing->inunion; + this->incontract = enclosing->incontract; + this->nofree = 0; + this->noctor = enclosing->noctor; + this->noaccesscheck = enclosing->noaccesscheck; + this->mustsemantic = enclosing->mustsemantic; + this->intypeof = enclosing->intypeof; + this->parameterSpecialization = enclosing->parameterSpecialization; + this->callSuper = enclosing->callSuper; + this->flags = 0; + this->anonAgg = NULL; + this->lastdc = NULL; + this->lastoffset = 0; + this->docbuf = enclosing->docbuf; + assert(this != enclosing); +} + +Scope *Scope::createGlobal(Module *module) +{ + Scope *sc; + + sc = new Scope(); + sc->module = module; + sc->scopesym = new ScopeDsymbol(); + sc->scopesym->symtab = new DsymbolTable(); + + // Add top level package as member of this global scope + Dsymbol *m = module; + while (m->parent) + m = m->parent; + m->addMember(NULL, sc->scopesym, 1); + m->parent = NULL; // got changed by addMember() + + // Create the module scope underneath the global scope + sc = sc->push(module); + sc->parent = module; + return sc; +} + +Scope *Scope::push() +{ + //printf("Scope::push()\n"); + Scope *s = new Scope(this); + assert(this != s); + return s; +} + +Scope *Scope::push(ScopeDsymbol *ss) +{ + //printf("Scope::push(%s)\n", ss->toChars()); + Scope *s = push(); + s->scopesym = ss; + return s; +} + +Scope *Scope::pop() +{ + //printf("Scope::pop() %p nofree = %d\n", this, nofree); + Scope *enc = enclosing; + + if (enclosing) + enclosing->callSuper |= callSuper; + + if (!nofree) + { enclosing = freelist; + freelist = this; + flags |= SCOPEfree; + } + + return enc; +} + +void Scope::mergeCallSuper(Loc loc, unsigned cs) +{ + // This does a primitive flow analysis to support the restrictions + // regarding when and how constructors can appear. + // It merges the results of two paths. + // The two paths are callSuper and cs; the result is merged into callSuper. + + if (cs != callSuper) + { int a; + int b; + + callSuper |= cs & (CSXany_ctor | CSXlabel); + if (cs & CSXreturn) + { + } + else if (callSuper & CSXreturn) + { + callSuper = cs | (callSuper & (CSXany_ctor | CSXlabel)); + } + else + { + a = (cs & (CSXthis_ctor | CSXsuper_ctor)) != 0; + b = (callSuper & (CSXthis_ctor | CSXsuper_ctor)) != 0; + if (a != b) + error(loc, "one path skips constructor"); + callSuper |= cs; + } + } +} + +Dsymbol *Scope::search(Loc loc, Identifier *ident, Dsymbol **pscopesym) +{ Dsymbol *s; + Scope *sc; + + //printf("Scope::search(%p, '%s')\n", this, ident->toChars()); + if (ident == Id::empty) + { + // Look for module scope + for (sc = this; sc; sc = sc->enclosing) + { + assert(sc != sc->enclosing); + if (sc->scopesym) + { + s = sc->scopesym->isModule(); + if (s) + { + //printf("\tfound %s.%s\n", s->parent ? s->parent->toChars() : "", s->toChars()); + if (pscopesym) + *pscopesym = sc->scopesym; + return s; + } + } + } + return NULL; + } + + for (sc = this; sc; sc = sc->enclosing) + { + assert(sc != sc->enclosing); + if (sc->scopesym) + { + //printf("\tlooking in scopesym '%s', kind = '%s'\n", sc->scopesym->toChars(), sc->scopesym->kind()); + s = sc->scopesym->search(loc, ident, 0); + if (s) + { + if ((global.params.warnings || + global.params.Dversion > 1) && + ident == Id::length && + sc->scopesym->isArrayScopeSymbol() && + sc->enclosing && + sc->enclosing->search(loc, ident, NULL)) + { + warning(s->loc, "array 'length' hides other 'length' name in outer scope"); + } + + //printf("\tfound %s.%s, kind = '%s'\n", s->parent ? s->parent->toChars() : "", s->toChars(), s->kind()); + if (pscopesym) + *pscopesym = sc->scopesym; + return s; + } + } + } + + return NULL; +} + +Dsymbol *Scope::insert(Dsymbol *s) +{ Scope *sc; + + for (sc = this; sc; sc = sc->enclosing) + { + //printf("\tsc = %p\n", sc); + if (sc->scopesym) + { + //printf("\t\tsc->scopesym = %p\n", sc->scopesym); + if (!sc->scopesym->symtab) + sc->scopesym->symtab = new DsymbolTable(); + return sc->scopesym->symtabInsert(s); + } + } + assert(0); + return NULL; +} + +/******************************************** + * Search enclosing scopes for ClassDeclaration. + */ + +ClassDeclaration *Scope::getClassScope() +{ Scope *sc; + + for (sc = this; sc; sc = sc->enclosing) + { + ClassDeclaration *cd; + + if (sc->scopesym) + { + cd = sc->scopesym->isClassDeclaration(); + if (cd) + return cd; + } + } + return NULL; +} + +/******************************************** + * Search enclosing scopes for ClassDeclaration. + */ + +AggregateDeclaration *Scope::getStructClassScope() +{ Scope *sc; + + for (sc = this; sc; sc = sc->enclosing) + { + AggregateDeclaration *ad; + + if (sc->scopesym) + { + ad = sc->scopesym->isClassDeclaration(); + if (ad) + return ad; + else + { ad = sc->scopesym->isStructDeclaration(); + if (ad) + return ad; + } + } + } + return NULL; +} + +/******************************************* + * For TemplateDeclarations, we need to remember the Scope + * where it was declared. So mark the Scope as not + * to be free'd. + */ + +void Scope::setNoFree() +{ Scope *sc; + //int i = 0; + + //printf("Scope::setNoFree(this = %p)\n", this); + for (sc = this; sc; sc = sc->enclosing) + { + //printf("\tsc = %p\n", sc); + sc->nofree = 1; + + assert(!(flags & SCOPEfree)); + //assert(sc != sc->enclosing); + //assert(!sc->enclosing || sc != sc->enclosing->enclosing); + //if (++i == 10) + //assert(0); + } +} + + +/************************************************ + * Given the failed search attempt, try to find + * one with a close spelling. + */ + +void *scope_search_fp(void *arg, const char *seed) +{ + //printf("scope_search_fp('%s')\n", seed); + + /* If not in the lexer's string table, it certainly isn't in the symbol table. + * Doing this first is a lot faster. + */ + size_t len = strlen(seed); + if (!len) + return NULL; + StringValue *sv = Lexer::stringtable.lookup(seed, len); + if (!sv) + return NULL; + Identifier *id = (Identifier *)sv->ptrvalue; + assert(id); + + Scope *sc = (Scope *)arg; + Module::clearCache(); + Dsymbol *s = sc->search(0, id, NULL); + return s; +} + +Dsymbol *Scope::search_correct(Identifier *ident) +{ + if (global.gag) + return NULL; // don't do it for speculative compiles; too time consuming + + return (Dsymbol *)speller(ident->toChars(), &scope_search_fp, this, idchars); +} diff --git a/scope.h b/scope.h new file mode 100644 index 00000000..d8c371f2 --- /dev/null +++ b/scope.h @@ -0,0 +1,122 @@ + +// Copyright (c) 1999-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_SCOPE_H +#define DMD_SCOPE_H + +#ifdef __DMC__ +#pragma once +#endif + +struct Dsymbol; +struct ScopeDsymbol; +struct Identifier; +struct Module; +struct Statement; +struct SwitchStatement; +struct TryFinallyStatement; +struct LabelStatement; +struct ForeachStatement; +struct ClassDeclaration; +struct AggregateDeclaration; +struct AnonymousAggregateDeclaration; +struct FuncDeclaration; +struct DocComment; +struct TemplateInstance; + +#if __GNUC__ +// Requires a full definition for PROT and LINK +#include "dsymbol.h" // PROT +#include "mars.h" // LINK +#else +enum LINK; +enum PROT; +#endif + +struct Scope +{ + Scope *enclosing; // enclosing Scope + + Module *module; // Root module + ScopeDsymbol *scopesym; // current symbol + ScopeDsymbol *sd; // if in static if, and declaring new symbols, + // sd gets the addMember() + FuncDeclaration *func; // function we are in + Dsymbol *parent; // parent to use + LabelStatement *slabel; // enclosing labelled statement + SwitchStatement *sw; // enclosing switch statement + TryFinallyStatement *tf; // enclosing try finally statement + TemplateInstance *tinst; // enclosing template instance + Statement *sbreak; // enclosing statement that supports "break" + Statement *scontinue; // enclosing statement that supports "continue" + ForeachStatement *fes; // if nested function for ForeachStatement, this is it + unsigned offset; // next offset to use in aggregate + int inunion; // we're processing members of a union + int incontract; // we're inside contract code + int nofree; // set if shouldn't free it + int noctor; // set if constructor calls aren't allowed + int intypeof; // in typeof(exp) + int parameterSpecialization; // if in template parameter specialization + int noaccesscheck; // don't do access checks + int mustsemantic; // cannot defer semantic() + + unsigned callSuper; // primitive flow analysis for constructors +#define CSXthis_ctor 1 // called this() +#define CSXsuper_ctor 2 // called super() +#define CSXthis 4 // referenced this +#define CSXsuper 8 // referenced super +#define CSXlabel 0x10 // seen a label +#define CSXreturn 0x20 // seen a return statement +#define CSXany_ctor 0x40 // either this() or super() was called + + unsigned structalign; // alignment for struct members + enum LINK linkage; // linkage for external functions + + enum PROT protection; // protection for class members + int explicitProtection; // set if in an explicit protection attribute + + StorageClass stc; // storage class + + unsigned flags; +#define SCOPEctor 1 // constructor type +#define SCOPEstaticif 2 // inside static if +#define SCOPEfree 4 // is on free list +#define SCOPEstaticassert 8 // inside static assert +#define SCOPEdebug 0x10 // inside debug conditional + + AnonymousAggregateDeclaration *anonAgg; // for temporary analysis + + DocComment *lastdc; // documentation comment for last symbol at this scope + unsigned lastoffset; // offset in docbuf of where to insert next dec + OutBuffer *docbuf; // buffer for documentation output + + static Scope *freelist; + static void *operator new(size_t sz); + static Scope *createGlobal(Module *module); + + Scope(); + Scope(Module *module); + Scope(Scope *enclosing); + + Scope *push(); + Scope *push(ScopeDsymbol *ss); + Scope *pop(); + + void mergeCallSuper(Loc loc, unsigned cs); + + Dsymbol *search(Loc loc, Identifier *ident, Dsymbol **pscopesym); + Dsymbol *search_correct(Identifier *ident); + Dsymbol *insert(Dsymbol *s); + + ClassDeclaration *getClassScope(); + AggregateDeclaration *getStructClassScope(); + void setNoFree(); +}; + +#endif /* DMD_SCOPE_H */ diff --git a/sideeffect.c b/sideeffect.c new file mode 100644 index 00000000..a80f874e --- /dev/null +++ b/sideeffect.c @@ -0,0 +1,250 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2012 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "mars.h" +#include "init.h" +#include "expression.h" +#include "template.h" +#include "statement.h" +#include "mtype.h" +#include "utf.h" +#include "declaration.h" +#include "aggregate.h" +#include "scope.h" +#include "attrib.h" + +int lambdaHasSideEffect(Expression *e, void *param); + +/******************************************** + * Determine if Expression has any side effects. + */ + +bool Expression::hasSideEffect() +{ + bool has = FALSE; + apply(&lambdaHasSideEffect, &has); + return has; +} + +int lambdaHasSideEffect(Expression *e, void *param) +{ + bool *phas = (bool *)param; + switch (e->op) + { + // Sort the cases by most frequently used first + case TOKassign: + case TOKplusplus: + case TOKminusminus: + case TOKdeclaration: + case TOKconstruct: + case TOKblit: + case TOKaddass: + case TOKminass: + case TOKcatass: + case TOKmulass: + case TOKdivass: + case TOKmodass: + case TOKshlass: + case TOKshrass: + case TOKushrass: + case TOKandass: + case TOKorass: + case TOKxorass: + case TOKpowass: + case TOKin: + case TOKremove: + case TOKassert: + case TOKhalt: + case TOKdelete: + case TOKnew: + case TOKnewanonclass: + *phas = TRUE; + break; + + case TOKcall: + { CallExp *ce = (CallExp *)e; + + /* Calling a function or delegate that is pure nothrow + * has no side effects. + */ + if (ce->e1->type) + { + Type *t = ce->e1->type->toBasetype(); + if ((t->ty == Tfunction && ((TypeFunction *)t)->purity > PUREweak && + ((TypeFunction *)t)->isnothrow) + || + (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->purity > PUREweak && + ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow) + ) + { + } + else + *phas = TRUE; + } + break; + } + + case TOKcast: + { CastExp *ce = (CastExp *)e; + + /* if: + * cast(classtype)func() // because it may throw + */ + if (ce->to->ty == Tclass && ce->e1->op == TOKcall && ce->e1->type->ty == Tclass) + *phas = TRUE; + break; + } + + default: + break; + } + return *phas; // stop walking if we determine this expression has side effects +} + + +/*********************************** + * The result of this expression will be discarded. + * Complain if the operation has no side effects (and hence is meaningless). + */ +void Expression::discardValue() +{ + bool has = FALSE; + lambdaHasSideEffect(this, &has); + if (!has) + { + switch (op) + { + case TOKcast: + { CastExp *ce = (CastExp *)this; + if (ce->to->equals(Type::tvoid)) + { /* + * Don't complain about an expression with no effect if it was cast to void + */ + ce->e1->useValue(); + break; + } + goto Ldefault; // complain + } + + case TOKerror: + break; + + case TOKcall: + /* Don't complain about calling functions with no effect, + * because purity and nothrow are inferred, and because some of the + * runtime library depends on it. Needs more investigation. + */ + break; + + case TOKimport: + error("%s has no effect", toChars()); + break; + + case TOKandand: + { AndAndExp *aae = (AndAndExp *)this; + aae->e1->useValue(); + aae->e2->discardValue(); + break; + } + + case TOKoror: + { OrOrExp *ooe = (OrOrExp *)this; + ooe->e1->useValue(); + ooe->e2->discardValue(); + break; + } + + case TOKquestion: + { CondExp *ce = (CondExp *)this; + ce->econd->useValue(); + ce->e1->discardValue(); + ce->e2->discardValue(); + break; + } + + case TOKcomma: + { CommaExp *ce = (CommaExp *)this; + + /* Check for compiler-generated code of the form auto __tmp, e, __tmp; + * In such cases, only check e for side effect (it's OK for __tmp to have + * no side effect). + * See Bugzilla 4231 for discussion + */ + CommaExp* firstComma = ce; + while (firstComma->e1->op == TOKcomma) + firstComma = (CommaExp *)firstComma->e1; + if (firstComma->e1->op == TOKdeclaration && + ce->e2->op == TOKvar && + ((DeclarationExp *)firstComma->e1)->declaration == ((VarExp*)ce->e2)->var) + { + ce->e1->useValue(); + break; + } + // Don't check e1 until we cast(void) the a,b code generation + //ce->e1->discardValue(); + ce->e2->discardValue(); + break; + } + + case TOKtuple: + /* Pass without complaint if any of the tuple elements have side effects. + * Ideally any tuple elements with no side effects should raise an error, + * this needs more investigation as to what is the right thing to do. + */ + if (!hasSideEffect()) + goto Ldefault; + break; + + default: + Ldefault: + error("%s has no effect in expression (%s)", + Token::toChars(op), toChars()); + break; + } + } + else + { + useValue(); + } +} + +/* This isn't used yet because the only way an expression has an unused sub-expression + * is with the CommaExp, and that currently generates messages from rewrites into comma + * expressions. Needs more investigation. + */ +void Expression::useValue() +{ +#if 0 + // Disabled because need to cast(void) the a,b code generation + void *p; + apply(&lambdaUseValue, &p); +#endif +} + +#if 0 +int lambdaUseValue(Expression *e, void *param) +{ + switch (e->op) + { + case TOKcomma: + { CommaExp *ce = (CommaExp *)e; + discardValue(ce->E1); + break; + } + + default: + break; + } + return 0; +} +#endif diff --git a/statement.c b/statement.c new file mode 100644 index 00000000..bc8a55c4 --- /dev/null +++ b/statement.c @@ -0,0 +1,5100 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include + +#include "rmem.h" + +#include "statement.h" +#include "expression.h" +#include "cond.h" +#include "init.h" +#include "staticassert.h" +#include "mtype.h" +#include "scope.h" +#include "declaration.h" +#include "aggregate.h" +#include "id.h" +#include "hdrgen.h" +#include "parse.h" +#include "template.h" +#include "attrib.h" + +extern int os_critsecsize32(); +extern int os_critsecsize64(); + +/******************************** Statement ***************************/ + +Statement::Statement(Loc loc) + : loc(loc) +{ + // If this is an in{} contract scope statement (skip for determining + // inlineStatus of a function body for header content) + incontract = 0; +} + +Statement *Statement::syntaxCopy() +{ + assert(0); + return NULL; +} + +void Statement::print() +{ + fprintf(stdmsg, "%s\n", toChars()); + fflush(stdmsg); +} + +char *Statement::toChars() +{ OutBuffer *buf; + HdrGenState hgs; + + buf = new OutBuffer(); + toCBuffer(buf, &hgs); + return buf->toChars(); +} + +void Statement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->printf("Statement::toCBuffer()"); + buf->writenl(); +} + +Statement *Statement::semantic(Scope *sc) +{ + return this; +} + +Statement *Statement::semanticNoScope(Scope *sc) +{ + //printf("Statement::semanticNoScope() %s\n", toChars()); + Statement *s = this; + if (!s->isCompoundStatement() && !s->isScopeStatement()) + { + s = new CompoundStatement(loc, this); // so scopeCode() gets called + } + s = s->semantic(sc); + return s; +} + +// Same as semanticNoScope(), but do create a new scope + +Statement *Statement::semanticScope(Scope *sc, Statement *sbreak, Statement *scontinue) +{ + Scope *scd = sc->push(); + if (sbreak) + scd->sbreak = sbreak; + if (scontinue) + scd->scontinue = scontinue; + Statement *s = semanticNoScope(scd); + scd->pop(); + return s; +} + +void Statement::error(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + ::verror(loc, format, ap); + va_end( ap ); +} + +void Statement::warning(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + ::vwarning(loc, format, ap); + va_end( ap ); +} + +int Statement::hasBreak() +{ + //printf("Statement::hasBreak()\n"); + return FALSE; +} + +int Statement::hasContinue() +{ + return FALSE; +} + +// TRUE if statement uses exception handling + +int Statement::usesEH() +{ + return FALSE; +} + +/* Only valid after semantic analysis + * If 'mustNotThrow' is true, generate an error if it throws + */ +int Statement::blockExit(bool mustNotThrow) +{ + printf("Statement::blockExit(%p)\n", this); + printf("%s\n", toChars()); + assert(0); + return BEany; +} + +// TRUE if statement 'comes from' somewhere else, like a goto + +int Statement::comeFrom() +{ + //printf("Statement::comeFrom()\n"); + return FALSE; +} + +// Return TRUE if statement has no code in it +int Statement::isEmpty() +{ + //printf("Statement::isEmpty()\n"); + return FALSE; +} + +Statement *Statement::last() +{ + return this; +} + +/**************************************** + * If this statement has code that needs to run in a finally clause + * at the end of the current scope, return that code in the form of + * a Statement. + * Output: + * *sentry code executed upon entry to the scope + * *sexception code executed upon exit from the scope via exception + * *sfinally code executed in finally block + */ + +Statement *Statement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally) +{ + //printf("Statement::scopeCode()\n"); + //print(); + *sentry = NULL; + *sexception = NULL; + *sfinally = NULL; + return this; +} + +/********************************* + * Flatten out the scope by presenting the statement + * as an array of statements. + * Returns NULL if no flattening necessary. + */ + +Statements *Statement::flatten(Scope *sc) +{ + return NULL; +} + + +/******************************** PeelStatement ***************************/ + +PeelStatement::PeelStatement(Statement *s) + : Statement(s->loc) +{ + this->s = s; +} + +Statement *PeelStatement::semantic(Scope *sc) +{ + /* "peel" off this wrapper, and don't run semantic() + * on the result. + */ + return s; +} + +/******************************** ExpStatement ***************************/ + +ExpStatement::ExpStatement(Loc loc, Expression *exp) + : Statement(loc) +{ + this->exp = exp; +} + +ExpStatement::ExpStatement(Loc loc, Dsymbol *declaration) + : Statement(loc) +{ + this->exp = new DeclarationExp(loc, declaration); +} + +Statement *ExpStatement::syntaxCopy() +{ + Expression *e = exp ? exp->syntaxCopy() : NULL; + ExpStatement *es = new ExpStatement(loc, e); + return es; +} + +void ExpStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (exp) + { exp->toCBuffer(buf, hgs); + if (exp->op != TOKdeclaration) + { buf->writeByte(';'); + if (!hgs->FLinit.init) + buf->writenl(); + } + } + else + { + buf->writeByte(';'); + if (!hgs->FLinit.init) + buf->writenl(); + } +} + +Statement *ExpStatement::semantic(Scope *sc) +{ + if (exp) + { + //printf("ExpStatement::semantic() %s\n", exp->toChars()); + +#if 0 // Doesn't work because of difficulty dealing with things like a.b.c!(args).Foo!(args) + // See if this should be rewritten as a TemplateMixin + if (exp->op == TOKdeclaration) + { DeclarationExp *de = (DeclarationExp *)exp; + Dsymbol *s = de->declaration; + + printf("s: %s %s\n", s->kind(), s->toChars()); + VarDeclaration *v = s->isVarDeclaration(); + if (v) + { + printf("%s, %d\n", v->type->toChars(), v->type->ty); + } + } +#endif + + exp = exp->semantic(sc); + exp = exp->addDtorHook(sc); + exp = resolveProperties(sc, exp); + exp->discardValue(); + exp = exp->optimize(0); + } + return this; +} + +int ExpStatement::blockExit(bool mustNotThrow) +{ int result = BEfallthru; + + if (exp) + { + if (exp->op == TOKhalt) + return BEhalt; + if (exp->op == TOKassert) + { AssertExp *a = (AssertExp *)exp; + + if (a->e1->isBool(FALSE)) // if it's an assert(0) + return BEhalt; + } + if (exp->canThrow(mustNotThrow)) + result |= BEthrow; + } + return result; +} + +int ExpStatement::isEmpty() +{ + return exp == NULL; +} + +Statement *ExpStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally) +{ + //printf("ExpStatement::scopeCode()\n"); + //print(); + + *sentry = NULL; + *sexception = NULL; + *sfinally = NULL; + + if (exp) + { + if (exp->op == TOKdeclaration) + { + DeclarationExp *de = (DeclarationExp *)(exp); + VarDeclaration *v = de->declaration->isVarDeclaration(); + if (v && !v->noscope && !v->isDataseg()) + { + Expression *e = v->edtor; + if (e) + { + //printf("dtor is: "); e->print(); +#if 0 + if (v->type->toBasetype()->ty == Tstruct) + { /* Need a 'gate' to turn on/off destruction, + * in case v gets moved elsewhere. + */ + Identifier *id = Lexer::uniqueId("__runDtor"); + ExpInitializer *ie = new ExpInitializer(loc, new IntegerExp(1)); + VarDeclaration *rd = new VarDeclaration(loc, Type::tint32, id, ie); + *sentry = new ExpStatement(loc, rd); + v->rundtor = rd; + + /* Rewrite e as: + * rundtor && e + */ + Expression *ve = new VarExp(loc, v->rundtor); + e = new AndAndExp(loc, ve, e); + e->type = Type::tbool; + } +#endif + *sfinally = new DtorExpStatement(loc, e, v); + } + v->noscope = 1; // don't add in dtor again + } + } + } + return this; +} + + +/******************************** DtorExpStatement ***************************/ + +DtorExpStatement::DtorExpStatement(Loc loc, Expression *exp, VarDeclaration *v) + : ExpStatement(loc, exp) +{ + this->var = v; +} + +Statement *DtorExpStatement::syntaxCopy() +{ + Expression *e = exp ? exp->syntaxCopy() : NULL; + DtorExpStatement *es = new DtorExpStatement(loc, e, var); + return es; +} + +/******************************** CompileStatement ***************************/ + +CompileStatement::CompileStatement(Loc loc, Expression *exp) + : Statement(loc) +{ + this->exp = exp; +} + +Statement *CompileStatement::syntaxCopy() +{ + Expression *e = exp->syntaxCopy(); + CompileStatement *es = new CompileStatement(loc, e); + return es; +} + +void CompileStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("mixin("); + exp->toCBuffer(buf, hgs); + buf->writestring(");"); + if (!hgs->FLinit.init) + buf->writenl(); +} + +Statements *CompileStatement::flatten(Scope *sc) +{ + //printf("CompileStatement::flatten() %s\n", exp->toChars()); + exp = exp->semantic(sc); + exp = resolveProperties(sc, exp); + exp = exp->optimize(WANTvalue | WANTinterpret); + if (exp->op == TOKerror) + return NULL; + StringExp *se = exp->toString(); + if (!se) + { error("argument to mixin must be a string, not (%s)", exp->toChars()); + return NULL; + } + se = se->toUTF8(sc); + Parser p(sc->module, (unsigned char *)se->string, se->len, 0); + p.loc = loc; + p.nextToken(); + + Statements *a = new Statements(); + while (p.token.value != TOKeof) + { + Statement *s = p.parseStatement(PSsemi | PScurlyscope); + if (s) // if no parsing errors + a->push(s); + } + return a; +} + +Statement *CompileStatement::semantic(Scope *sc) +{ + //printf("CompileStatement::semantic() %s\n", exp->toChars()); + Statements *a = flatten(sc); + if (!a) + return NULL; + Statement *s = new CompoundStatement(loc, a); + return s->semantic(sc); +} + +int CompileStatement::blockExit(bool mustNotThrow) +{ + assert(global.errors); + return BEfallthru; +} + + +/******************************** CompoundStatement ***************************/ + +CompoundStatement::CompoundStatement(Loc loc, Statements *s) + : Statement(loc) +{ + statements = s; +} + +CompoundStatement::CompoundStatement(Loc loc, Statement *s1, Statement *s2) + : Statement(loc) +{ + statements = new Statements(); + statements->reserve(2); + statements->push(s1); + statements->push(s2); +} + +CompoundStatement::CompoundStatement(Loc loc, Statement *s1) + : Statement(loc) +{ + statements = new Statements(); + statements->push(s1); +} + +Statement *CompoundStatement::syntaxCopy() +{ + Statements *a = new Statements(); + a->setDim(statements->dim); + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + s = s->syntaxCopy(); + (*a)[i] = s; + } + CompoundStatement *cs = new CompoundStatement(loc, a); + return cs; +} + + +Statement *CompoundStatement::semantic(Scope *sc) +{ Statement *s; + + //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", this, sc); + +#if 0 + for (size_t i = 0; i < statements->dim; i++) + { + s = (*statements)[i]; + if (s) + printf("[%d]: %s", i, s->toChars()); + } +#endif + + for (size_t i = 0; i < statements->dim; ) + { + s = (*statements)[i]; + if (s) + { Statements *a = s->flatten(sc); + + if (a) + { + statements->remove(i); + statements->insert(i, a); + continue; + } + s = s->semantic(sc); + (*statements)[i] = s; + if (s) + { + Statement *sentry; + Statement *sexception; + Statement *sfinally; + + (*statements)[i] = s->scopeCode(sc, &sentry, &sexception, &sfinally); + if (sentry) + { + sentry = sentry->semantic(sc); + statements->insert(i, sentry); + i++; + } + if (sexception) + sexception = sexception->semantic(sc); + if (sexception) + { + if (i + 1 == statements->dim && !sfinally) + { + } + else + { + /* Rewrite: + * s; s1; s2; + * As: + * s; + * try { s1; s2; } + * catch (Object __o) + * { sexception; throw __o; } + */ + Statements *a = new Statements(); + for (size_t j = i + 1; j < statements->dim; j++) + { + a->push((*statements)[j]); + } + Statement *body = new CompoundStatement(0, a); + body = new ScopeStatement(0, body); + + Identifier *id = Lexer::uniqueId("__o"); + + Statement *handler = sexception; + if (sexception->blockExit(FALSE) & BEfallthru) + { handler = new ThrowStatement(0, new IdentifierExp(0, id)); + handler = new CompoundStatement(0, sexception, handler); + } + + Catches *catches = new Catches(); + Catch *ctch = new Catch(0, NULL, id, handler); + catches->push(ctch); + s = new TryCatchStatement(0, body, catches); + + if (sfinally) + s = new TryFinallyStatement(0, s, sfinally); + s = s->semantic(sc); + statements->setDim(i + 1); + statements->push(s); + break; + } + } + else if (sfinally) + { + if (0 && i + 1 == statements->dim) + { + statements->push(sfinally); + } + else + { + /* Rewrite: + * s; s1; s2; + * As: + * s; try { s1; s2; } finally { sfinally; } + */ + Statements *a = new Statements(); + for (size_t j = i + 1; j < statements->dim; j++) + { + a->push((*statements)[j]); + } + Statement *body = new CompoundStatement(0, a); + s = new TryFinallyStatement(0, body, sfinally); + s = s->semantic(sc); + statements->setDim(i + 1); + statements->push(s); + break; + } + } + } + } + i++; + } + if (statements->dim == 1) + { + return (*statements)[0]; + } + return this; +} + +Statements *CompoundStatement::flatten(Scope *sc) +{ + return statements; +} + +ReturnStatement *CompoundStatement::isReturnStatement() +{ + ReturnStatement *rs = NULL; + + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + { + rs = s->isReturnStatement(); + if (rs) + break; + } + } + return rs; +} + +Statement *CompoundStatement::last() +{ + Statement *s = NULL; + + for (size_t i = statements->dim; i; --i) + { s = (*statements)[i - 1]; + if (s) + { + s = s->last(); + if (s) + break; + } + } + return s; +} + +void CompoundStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + s->toCBuffer(buf, hgs); + } +} + +int CompoundStatement::usesEH() +{ + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s && s->usesEH()) + return TRUE; + } + return FALSE; +} + +int CompoundStatement::blockExit(bool mustNotThrow) +{ + //printf("CompoundStatement::blockExit(%p) %d\n", this, statements->dim); + int result = BEfallthru; + Statement *slast = NULL; + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + { + //printf("result = x%x\n", result); + //printf("%s\n", s->toChars()); + if (global.params.warnings && result & BEfallthru && slast) + { + slast = slast->last(); + if (slast && (slast->isCaseStatement() || slast->isDefaultStatement()) && + (s->isCaseStatement() || s->isDefaultStatement())) + { + // Allow if last case/default was empty + CaseStatement *sc = slast->isCaseStatement(); + DefaultStatement *sd = slast->isDefaultStatement(); + if (sc && sc->statement->isEmpty()) + ; + else if (sd && sd->statement->isEmpty()) + ; + else + s->error("switch case fallthrough - use 'goto %s;' if intended", + s->isCaseStatement() ? "case" : "default"); + } + } + + if (!(result & BEfallthru) && !s->comeFrom()) + { + if (s->blockExit(mustNotThrow) != BEhalt && !s->isEmpty()) + s->warning("statement is not reachable"); + } + else + { + result &= ~BEfallthru; + result |= s->blockExit(mustNotThrow); + } + slast = s; + } + } + return result; +} + +int CompoundStatement::comeFrom() +{ int comefrom = FALSE; + + //printf("CompoundStatement::comeFrom()\n"); + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + + if (!s) + continue; + + comefrom |= s->comeFrom(); + } + return comefrom; +} + +int CompoundStatement::isEmpty() +{ + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s && !s->isEmpty()) + return FALSE; + } + return TRUE; +} + + +/******************************** CompoundDeclarationStatement ***************************/ + +CompoundDeclarationStatement::CompoundDeclarationStatement(Loc loc, Statements *s) + : CompoundStatement(loc, s) +{ + statements = s; +} + +Statement *CompoundDeclarationStatement::syntaxCopy() +{ + Statements *a = new Statements(); + a->setDim(statements->dim); + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + s = s->syntaxCopy(); + (*a)[i] = s; + } + CompoundDeclarationStatement *cs = new CompoundDeclarationStatement(loc, a); + return cs; +} + +void CompoundDeclarationStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + int nwritten = 0; + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + ExpStatement *ds; + if (s && + (ds = s->isExpStatement()) != NULL && + ds->exp->op == TOKdeclaration) + { + DeclarationExp *de = (DeclarationExp *)ds->exp; + Declaration *d = de->declaration->isDeclaration(); + assert(d); + VarDeclaration *v = d->isVarDeclaration(); + if (v) + { + /* This essentially copies the part of VarDeclaration::toCBuffer() + * that does not print the type. + * Should refactor this. + */ + if (nwritten) + { + buf->writeByte(','); + buf->writestring(v->ident->toChars()); + } + else + { + StorageClassDeclaration::stcToCBuffer(buf, v->storage_class); + if (v->type) + v->type->toCBuffer(buf, v->ident, hgs); + else + buf->writestring(v->ident->toChars()); + } + + if (v->init) + { buf->writestring(" = "); +#if DMDV2 + ExpInitializer *ie = v->init->isExpInitializer(); + if (ie && (ie->exp->op == TOKconstruct || ie->exp->op == TOKblit)) + ((AssignExp *)ie->exp)->e2->toCBuffer(buf, hgs); + else +#endif + v->init->toCBuffer(buf, hgs); + } + } + else + d->toCBuffer(buf, hgs); + nwritten++; + } + } + buf->writeByte(';'); + if (!hgs->FLinit.init) + buf->writenl(); +} + +/**************************** UnrolledLoopStatement ***************************/ + +UnrolledLoopStatement::UnrolledLoopStatement(Loc loc, Statements *s) + : Statement(loc) +{ + statements = s; +} + +Statement *UnrolledLoopStatement::syntaxCopy() +{ + Statements *a = new Statements(); + a->setDim(statements->dim); + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + s = s->syntaxCopy(); + (*a)[i] = s; + } + UnrolledLoopStatement *cs = new UnrolledLoopStatement(loc, a); + return cs; +} + + +Statement *UnrolledLoopStatement::semantic(Scope *sc) +{ + //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", this, sc); + + sc->noctor++; + Scope *scd = sc->push(); + scd->sbreak = this; + scd->scontinue = this; + + for (size_t i = 0; i < statements->dim; i++) + { + Statement *s = (*statements)[i]; + if (s) + { + //printf("[%d]: %s\n", i, s->toChars()); + s = s->semantic(scd); + (*statements)[i] = s; + } + } + + scd->pop(); + sc->noctor--; + return this; +} + +void UnrolledLoopStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("unrolled {"); + buf->writenl(); + + for (size_t i = 0; i < statements->dim; i++) + { Statement *s; + + s = (*statements)[i]; + if (s) + s->toCBuffer(buf, hgs); + } + + buf->writeByte('}'); + buf->writenl(); +} + +int UnrolledLoopStatement::hasBreak() +{ + return TRUE; +} + +int UnrolledLoopStatement::hasContinue() +{ + return TRUE; +} + +int UnrolledLoopStatement::usesEH() +{ + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s && s->usesEH()) + return TRUE; + } + return FALSE; +} + +int UnrolledLoopStatement::blockExit(bool mustNotThrow) +{ + int result = BEfallthru; + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + if (s) + { + int r = s->blockExit(mustNotThrow); + result |= r & ~(BEbreak | BEcontinue); + } + } + return result; +} + + +int UnrolledLoopStatement::comeFrom() +{ int comefrom = FALSE; + + //printf("UnrolledLoopStatement::comeFrom()\n"); + for (size_t i = 0; i < statements->dim; i++) + { Statement *s = (*statements)[i]; + + if (!s) + continue; + + comefrom |= s->comeFrom(); + } + return comefrom; +} + + +/******************************** ScopeStatement ***************************/ + +ScopeStatement::ScopeStatement(Loc loc, Statement *s) + : Statement(loc) +{ + this->statement = s; +} + +Statement *ScopeStatement::syntaxCopy() +{ + Statement *s; + + s = statement ? statement->syntaxCopy() : NULL; + s = new ScopeStatement(loc, s); + return s; +} + + +Statement *ScopeStatement::semantic(Scope *sc) +{ ScopeDsymbol *sym; + + //printf("ScopeStatement::semantic(sc = %p)\n", sc); + if (statement) + { Statements *a; + + sym = new ScopeDsymbol(); + sym->parent = sc->scopesym; + sc = sc->push(sym); + + a = statement->flatten(sc); + if (a) + { + statement = new CompoundStatement(loc, a); + } + + statement = statement->semantic(sc); + if (statement) + { + Statement *sentry; + Statement *sexception; + Statement *sfinally; + + statement = statement->scopeCode(sc, &sentry, &sexception, &sfinally); + assert(!sentry); + assert(!sexception); + if (sfinally) + { + //printf("adding sfinally\n"); + sfinally = sfinally->semantic(sc); + statement = new CompoundStatement(loc, statement, sfinally); + } + } + + sc->pop(); + } + return this; +} + +int ScopeStatement::hasBreak() +{ + //printf("ScopeStatement::hasBreak() %s\n", toChars()); + return statement ? statement->hasBreak() : FALSE; +} + +int ScopeStatement::hasContinue() +{ + return statement ? statement->hasContinue() : FALSE; +} + +int ScopeStatement::usesEH() +{ + return statement ? statement->usesEH() : FALSE; +} + +int ScopeStatement::blockExit(bool mustNotThrow) +{ + //printf("ScopeStatement::blockExit(%p)\n", statement); + return statement ? statement->blockExit(mustNotThrow) : BEfallthru; +} + + +int ScopeStatement::comeFrom() +{ + //printf("ScopeStatement::comeFrom()\n"); + return statement ? statement->comeFrom() : FALSE; +} + +int ScopeStatement::isEmpty() +{ + //printf("ScopeStatement::isEmpty() %d\n", statement ? statement->isEmpty() : TRUE); + return statement ? statement->isEmpty() : TRUE; +} + +void ScopeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writeByte('{'); + buf->writenl(); + + if (statement) + statement->toCBuffer(buf, hgs); + + buf->writeByte('}'); + buf->writenl(); +} + +/******************************** WhileStatement ***************************/ + +WhileStatement::WhileStatement(Loc loc, Expression *c, Statement *b) + : Statement(loc) +{ + condition = c; + body = b; +} + +Statement *WhileStatement::syntaxCopy() +{ + WhileStatement *s = new WhileStatement(loc, condition->syntaxCopy(), body ? body->syntaxCopy() : NULL); + return s; +} + + +Statement *WhileStatement::semantic(Scope *sc) +{ + /* Rewrite as a for(;condition;) loop + */ + + Statement *s = new ForStatement(loc, NULL, condition, NULL, body); + s = s->semantic(sc); + return s; +} + +int WhileStatement::hasBreak() +{ + return TRUE; +} + +int WhileStatement::hasContinue() +{ + return TRUE; +} + +int WhileStatement::usesEH() +{ + assert(0); + return body ? body->usesEH() : 0; +} + +int WhileStatement::blockExit(bool mustNotThrow) +{ + assert(0); + //printf("WhileStatement::blockExit(%p)\n", this); + + int result = BEnone; + if (condition->canThrow(mustNotThrow)) + result |= BEthrow; + if (condition->isBool(TRUE)) + { + if (body) + { result |= body->blockExit(mustNotThrow); + if (result & BEbreak) + result |= BEfallthru; + } + } + else if (condition->isBool(FALSE)) + { + result |= BEfallthru; + } + else + { + if (body) + result |= body->blockExit(mustNotThrow); + result |= BEfallthru; + } + result &= ~(BEbreak | BEcontinue); + return result; +} + + +int WhileStatement::comeFrom() +{ + assert(0); + if (body) + return body->comeFrom(); + return FALSE; +} + +void WhileStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("while ("); + condition->toCBuffer(buf, hgs); + buf->writebyte(')'); + buf->writenl(); + if (body) + body->toCBuffer(buf, hgs); +} + +/******************************** DoStatement ***************************/ + +DoStatement::DoStatement(Loc loc, Statement *b, Expression *c) + : Statement(loc) +{ + body = b; + condition = c; +} + +Statement *DoStatement::syntaxCopy() +{ + DoStatement *s = new DoStatement(loc, body ? body->syntaxCopy() : NULL, condition->syntaxCopy()); + return s; +} + + +Statement *DoStatement::semantic(Scope *sc) +{ + sc->noctor++; + if (body) + body = body->semanticScope(sc, this, this); + sc->noctor--; + condition = condition->semantic(sc); + condition = resolveProperties(sc, condition); + condition = condition->optimize(WANTvalue); + + condition = condition->checkToBoolean(sc); + + return this; +} + +int DoStatement::hasBreak() +{ + return TRUE; +} + +int DoStatement::hasContinue() +{ + return TRUE; +} + +int DoStatement::usesEH() +{ + return body ? body->usesEH() : 0; +} + +int DoStatement::blockExit(bool mustNotThrow) +{ int result; + + if (body) + { result = body->blockExit(mustNotThrow); + if (result == BEbreak) + return BEfallthru; + if (result & BEcontinue) + result |= BEfallthru; + } + else + result = BEfallthru; + if (result & BEfallthru) + { + if (condition->canThrow(mustNotThrow)) + result |= BEthrow; + if (!(result & BEbreak) && condition->isBool(TRUE)) + result &= ~BEfallthru; + } + result &= ~(BEbreak | BEcontinue); + return result; +} + + +int DoStatement::comeFrom() +{ + if (body) + return body->comeFrom(); + return FALSE; +} + +void DoStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("do"); + buf->writenl(); + if (body) + body->toCBuffer(buf, hgs); + buf->writestring("while ("); + condition->toCBuffer(buf, hgs); + buf->writestring(");"); +} + +/******************************** ForStatement ***************************/ + +ForStatement::ForStatement(Loc loc, Statement *init, Expression *condition, Expression *increment, Statement *body) + : Statement(loc) +{ + this->init = init; + this->condition = condition; + this->increment = increment; + this->body = body; +} + +Statement *ForStatement::syntaxCopy() +{ + Statement *i = NULL; + if (init) + i = init->syntaxCopy(); + Expression *c = NULL; + if (condition) + c = condition->syntaxCopy(); + Expression *inc = NULL; + if (increment) + inc = increment->syntaxCopy(); + ForStatement *s = new ForStatement(loc, i, c, inc, body->syntaxCopy()); + return s; +} + +Statement *ForStatement::semantic(Scope *sc) +{ + ScopeDsymbol *sym = new ScopeDsymbol(); + sym->parent = sc->scopesym; + sc = sc->push(sym); + if (init) + init = init->semantic(sc); + sc->noctor++; + if (condition) + { + condition = condition->semantic(sc); + condition = resolveProperties(sc, condition); + condition = condition->optimize(WANTvalue); + condition = condition->checkToBoolean(sc); + } + if (increment) + { increment = increment->semantic(sc); + increment = resolveProperties(sc, increment); + increment = increment->optimize(0); + } + + sc->sbreak = this; + sc->scontinue = this; + if (body) + body = body->semanticNoScope(sc); + sc->noctor--; + + sc->pop(); + return this; +} + +Statement *ForStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally) +{ + //printf("ForStatement::scopeCode()\n"); + //print(); + if (init) + init = init->scopeCode(sc, sentry, sexception, sfinally); + else + Statement::scopeCode(sc, sentry, sexception, sfinally); + return this; +} + +int ForStatement::hasBreak() +{ + //printf("ForStatement::hasBreak()\n"); + return TRUE; +} + +int ForStatement::hasContinue() +{ + return TRUE; +} + +int ForStatement::usesEH() +{ + return (init && init->usesEH()) || body->usesEH(); +} + +int ForStatement::blockExit(bool mustNotThrow) +{ int result = BEfallthru; + + if (init) + { result = init->blockExit(mustNotThrow); + if (!(result & BEfallthru)) + return result; + } + if (condition) + { if (condition->canThrow(mustNotThrow)) + result |= BEthrow; + if (condition->isBool(TRUE)) + result &= ~BEfallthru; + else if (condition->isBool(FALSE)) + return result; + } + else + result &= ~BEfallthru; // the body must do the exiting + if (body) + { int r = body->blockExit(mustNotThrow); + if (r & (BEbreak | BEgoto)) + result |= BEfallthru; + result |= r & ~(BEfallthru | BEbreak | BEcontinue); + } + if (increment && increment->canThrow(mustNotThrow)) + result |= BEthrow; + return result; +} + + +int ForStatement::comeFrom() +{ + //printf("ForStatement::comeFrom()\n"); + if (body) + { int result = body->comeFrom(); + //printf("result = %d\n", result); + return result; + } + return FALSE; +} + +void ForStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("for ("); + if (init) + { + hgs->FLinit.init++; + init->toCBuffer(buf, hgs); + hgs->FLinit.init--; + } + else + buf->writebyte(';'); + if (condition) + { buf->writebyte(' '); + condition->toCBuffer(buf, hgs); + } + buf->writebyte(';'); + if (increment) + { buf->writebyte(' '); + increment->toCBuffer(buf, hgs); + } + buf->writebyte(')'); + buf->writenl(); + buf->writebyte('{'); + buf->writenl(); + body->toCBuffer(buf, hgs); + buf->writebyte('}'); + buf->writenl(); +} + +/******************************** ForeachStatement ***************************/ + +ForeachStatement::ForeachStatement(Loc loc, enum TOK op, Parameters *arguments, + Expression *aggr, Statement *body) + : Statement(loc) +{ + this->op = op; + this->arguments = arguments; + this->aggr = aggr; + this->body = body; + + this->key = NULL; + this->value = NULL; + + this->func = NULL; + + this->cases = NULL; + this->gotos = NULL; +} + +Statement *ForeachStatement::syntaxCopy() +{ + Parameters *args = Parameter::arraySyntaxCopy(arguments); + Expression *exp = aggr->syntaxCopy(); + ForeachStatement *s = new ForeachStatement(loc, op, args, exp, + body ? body->syntaxCopy() : NULL); + return s; +} + +Statement *ForeachStatement::semantic(Scope *sc) +{ + //printf("ForeachStatement::semantic() %p\n", this); + ScopeDsymbol *sym; + Statement *s = this; + size_t dim = arguments->dim; + TypeAArray *taa = NULL; + Dsymbol *sapply = NULL; + + Type *tn = NULL; + Type *tnv = NULL; + + func = sc->func; + if (func->fes) + func = func->fes->func; + + if (!inferAggregate(sc, sapply)) + { + error("invalid foreach aggregate %s", aggr->toChars()); + return this; + } + + /* Check for inference errors + */ + if (!inferApplyArgTypes(sc, sapply)) + { + //printf("dim = %d, arguments->dim = %d\n", dim, arguments->dim); + error("cannot uniquely infer foreach argument types"); + return this; + } + + Type *tab = aggr->type->toBasetype(); + + if (tab->ty == Ttuple) // don't generate new scope for tuple loops + { + if (dim < 1 || dim > 2) + { + error("only one (value) or two (key,value) arguments for tuple foreach"); + return s; + } + + TypeTuple *tuple = (TypeTuple *)tab; + Statements *statements = new Statements(); + //printf("aggr: op = %d, %s\n", aggr->op, aggr->toChars()); + size_t n; + TupleExp *te = NULL; + Expression *prelude = NULL; + if (aggr->op == TOKtuple) // expression tuple + { te = (TupleExp *)aggr; + n = te->exps->dim; + + if (te->exps->dim > 0 && (*te->exps)[0]->op == TOKdotvar && + ((DotVarExp *)(*te->exps)[0])->e1->isTemp()) + { + CommaExp *ce = (CommaExp *)((DotVarExp *)(*te->exps)[0])->e1; + + prelude = ce->e1; + ((DotVarExp *)(*te->exps)[0])->e1 = ce->e2; + } + } + else if (aggr->op == TOKtype) // type tuple + { + n = Parameter::dim(tuple->arguments); + } + else + assert(0); + for (size_t j = 0; j < n; j++) + { size_t k = (op == TOKforeach) ? j : n - 1 - j; + Expression *e; + Type *t; + if (te) + e = (*te->exps)[k]; + else + t = Parameter::getNth(tuple->arguments, k)->type; + Parameter *arg = (*arguments)[0]; + Statements *st = new Statements(); + + if (dim == 2) + { // Declare key + if (arg->storageClass & (STCout | STCref | STClazy)) + error("no storage class for key %s", arg->ident->toChars()); + arg->type = arg->type->semantic(loc, sc); + TY keyty = arg->type->ty; + if (keyty != Tint32 && keyty != Tuns32) + { + if (global.params.is64bit) + { + if (keyty != Tint64 && keyty != Tuns64) + error("foreach: key type must be int or uint, long or ulong, not %s", arg->type->toChars()); + } + else + error("foreach: key type must be int or uint, not %s", arg->type->toChars()); + } + Initializer *ie = new ExpInitializer(0, new IntegerExp(k)); + VarDeclaration *var = new VarDeclaration(loc, arg->type, arg->ident, ie); + var->storage_class |= STCmanifest; + DeclarationExp *de = new DeclarationExp(loc, var); + st->push(new ExpStatement(loc, de)); + arg = (*arguments)[1]; // value + } + // Declare value + if (arg->storageClass & (STCout | STClazy) || + arg->storageClass & STCref && !te) + error("no storage class for value %s", arg->ident->toChars()); + Dsymbol *var; + if (te) + { Type *tb = e->type->toBasetype(); + Dsymbol *s = NULL; + if ((tb->ty == Tfunction || tb->ty == Tsarray) && e->op == TOKvar) + s = ((VarExp *)e)->var; + else if (e->op == TOKtemplate) + s =((TemplateExp *)e)->td; + else if (e->op == TOKimport) + s =((ScopeExp *)e)->sds; + + if (s) + { + var = new AliasDeclaration(loc, arg->ident, s); + if (arg->storageClass & STCref) + error("symbol %s cannot be ref", s->toChars()); + } + else + { + arg->type = e->type; + Initializer *ie = new ExpInitializer(0, e); + VarDeclaration *v = new VarDeclaration(loc, arg->type, arg->ident, ie); + if (arg->storageClass & STCref) + v->storage_class |= STCref | STCforeach; + if (e->isConst() || e->op == TOKstring) + { if (v->storage_class & STCref) + error("constant value %s cannot be ref", ie->toChars()); + else + v->storage_class |= STCmanifest; + } + var = v; + } + } + else + { + var = new AliasDeclaration(loc, arg->ident, t); + } + DeclarationExp *de = new DeclarationExp(loc, var); + st->push(new ExpStatement(loc, de)); + + st->push(body->syntaxCopy()); + s = new CompoundStatement(loc, st); + s = new ScopeStatement(loc, s); + statements->push(s); + } + + s = new UnrolledLoopStatement(loc, statements); + if (prelude) + s = new CompoundStatement(loc, + new ExpStatement(prelude->loc, prelude), s); + s = s->semantic(sc); + return s; + } + + sym = new ScopeDsymbol(); + sym->parent = sc->scopesym; + sc = sc->push(sym); + + sc->noctor++; + +Lagain: + switch (tab->ty) + { + case Tarray: + case Tsarray: + if (!checkForArgTypes()) + return this; + + if (dim < 1 || dim > 2) + { + error("only one or two arguments for array foreach"); + break; + } + + /* Look for special case of parsing char types out of char type + * array. + */ + tn = tab->nextOf()->toBasetype(); + if (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar) + { Parameter *arg; + + int i = (dim == 1) ? 0 : 1; // index of value + arg = (*arguments)[i]; + arg->type = arg->type->semantic(loc, sc); + tnv = arg->type->toBasetype(); + if (tnv->ty != tn->ty && + (tnv->ty == Tchar || tnv->ty == Twchar || tnv->ty == Tdchar)) + { + if (arg->storageClass & STCref) + error("foreach: value of UTF conversion cannot be ref"); + if (dim == 2) + { arg = (*arguments)[0]; + if (arg->storageClass & STCref) + error("foreach: key cannot be ref"); + } + goto Lapply; + } + } + + for (size_t i = 0; i < dim; i++) + { // Declare args + Parameter *arg = (*arguments)[i]; + Type *argtype = arg->type->semantic(loc, sc); + VarDeclaration *var; + + var = new VarDeclaration(loc, argtype, arg->ident, NULL); + var->storage_class |= STCforeach; + var->storage_class |= arg->storageClass & (STCin | STCout | STCref | STC_TYPECTOR); + if (var->storage_class & (STCref | STCout)) + var->storage_class |= STCnodtor; + if (dim == 2 && i == 0) + { key = var; + //var->storage_class |= STCfinal; + } + else + { + value = var; + /* Reference to immutable data should be marked as const + */ + if (var->storage_class & STCref && !tn->isMutable()) + { + var->storage_class |= STCconst; + } + } +#if 0 + DeclarationExp *de = new DeclarationExp(loc, var); + de->semantic(sc); +#endif + } + +#if 1 + { + /* Convert to a ForStatement + * foreach (key, value; a) body => + * for (T[] tmp = a[], size_t key; key < tmp.length; ++key) + * { T value = tmp[k]; body } + * + * foreach_reverse (key, value; a) body => + * for (T[] tmp = a[], size_t key = tmp.length; key--; ) + * { T value = tmp[k]; body } + */ + Identifier *id = Lexer::uniqueId("__aggr"); + ExpInitializer *ie = new ExpInitializer(loc, new SliceExp(loc, aggr, NULL, NULL)); + VarDeclaration *tmp = new VarDeclaration(loc, tab->nextOf()->arrayOf(), id, ie); + + Expression *tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id::length); + + if (!key) + { + Identifier *idkey = Lexer::uniqueId("__key"); + key = new VarDeclaration(loc, Type::tsize_t, idkey, NULL); + } + if (op == TOKforeach_reverse) + key->init = new ExpInitializer(loc, tmp_length); + else + key->init = new ExpInitializer(loc, new IntegerExp(0)); + + Statements *cs = new Statements(); + cs->push(new ExpStatement(loc, tmp)); + cs->push(new ExpStatement(loc, key)); + Statement *forinit = new CompoundDeclarationStatement(loc, cs); + + Expression *cond; + if (op == TOKforeach_reverse) + // key-- + cond = new PostExp(TOKminusminus, loc, new VarExp(loc, key)); + else + // key < tmp.length + cond = new CmpExp(TOKlt, loc, new VarExp(loc, key), tmp_length); + + Expression *increment = NULL; + if (op == TOKforeach) + // key += 1 + increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(1)); + + // T value = tmp[key]; + value->init = new ExpInitializer(loc, new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, key))); + Statement *ds = new ExpStatement(loc, value); + + body = new CompoundStatement(loc, ds, body); + + s = new ForStatement(loc, forinit, cond, increment, body); + s = s->semantic(sc); + break; + } +#else + if (tab->nextOf()->implicitConvTo(value->type) < MATCHconst) + { + if (aggr->op == TOKstring) + aggr = aggr->implicitCastTo(sc, value->type->arrayOf()); + else + error("foreach: %s is not an array of %s", + tab->toChars(), value->type->toChars()); + } + + if (key) + { + if (key->type->ty != Tint32 && key->type->ty != Tuns32) + { + if (global.params.is64bit) + { + if (key->type->ty != Tint64 && key->type->ty != Tuns64) + error("foreach: key type must be int or uint, long or ulong, not %s", key->type->toChars()); + } + else + error("foreach: key type must be int or uint, not %s", key->type->toChars()); + } + + if (key->storage_class & (STCout | STCref)) + error("foreach: key cannot be out or ref"); + } + + sc->sbreak = this; + sc->scontinue = this; + body = body->semantic(sc); + break; +#endif + + case Taarray: + if (!checkForArgTypes()) + return this; + + taa = (TypeAArray *)tab; + if (dim < 1 || dim > 2) + { + error("only one or two arguments for associative array foreach"); + break; + } +#if SARRAYVALUE + /* This only works if Key or Value is a static array. + */ + tab = taa->getImpl()->type; + goto Lagain; +#else + if (op == TOKforeach_reverse) + { + error("no reverse iteration on associative arrays"); + } + goto Lapply; +#endif + case Tclass: + case Tstruct: +#if DMDV2 + /* Prefer using opApply, if it exists + */ + if (sapply) + goto Lapply; + + { /* Look for range iteration, i.e. the properties + * .empty, .next, .retreat, .head and .rear + * foreach (e; aggr) { ... } + * translates to: + * for (auto __r = aggr[]; !__r.empty; __r.next) + * { auto e = __r.head; + * ... + * } + */ + AggregateDeclaration *ad = (tab->ty == Tclass) + ? (AggregateDeclaration *)((TypeClass *)tab)->sym + : (AggregateDeclaration *)((TypeStruct *)tab)->sym; + Identifier *idhead; + Identifier *idnext; + if (op == TOKforeach) + { idhead = Id::Ffront; + idnext = Id::FpopFront; + } + else + { idhead = Id::Fback; + idnext = Id::FpopBack; + } + Dsymbol *shead = search_function(ad, idhead); + if (!shead) + goto Lapply; + + /* Generate a temporary __r and initialize it with the aggregate. + */ + Identifier *id = Identifier::generateId("__r"); + VarDeclaration *r = new VarDeclaration(loc, NULL, id, new ExpInitializer(loc, aggr)); + Statement *init = new ExpStatement(loc, r); + + // !__r.empty + Expression *e = new VarExp(loc, r); + e = new DotIdExp(loc, e, Id::Fempty); + Expression *condition = new NotExp(loc, e); + + // __r.next + e = new VarExp(loc, r); + Expression *increment = new CallExp(loc, new DotIdExp(loc, e, idnext)); + + /* Declaration statement for e: + * auto e = __r.idhead; + */ + e = new VarExp(loc, r); + Expression *einit = new DotIdExp(loc, e, idhead); + Statement *makeargs, *forbody; + if (dim == 1) + { + Parameter *arg = (*arguments)[0]; + VarDeclaration *ve = new VarDeclaration(loc, arg->type, arg->ident, new ExpInitializer(loc, einit)); + ve->storage_class |= STCforeach; + ve->storage_class |= arg->storageClass & (STCin | STCout | STCref | STC_TYPECTOR); + + DeclarationExp *de = new DeclarationExp(loc, ve); + makeargs = new ExpStatement(loc, de); + } + else + { + Identifier *id = Lexer::uniqueId("__front"); + ExpInitializer *ei = new ExpInitializer(loc, einit); + VarDeclaration *vd = new VarDeclaration(loc, NULL, id, ei); + vd->storage_class |= STCctfe | STCref | STCforeach; + + Expression *de = new DeclarationExp(loc, vd); + makeargs = new ExpStatement(loc, de); + + Expression *ve = new VarExp(loc, vd); + ve->type = shead->isDeclaration()->type; + if (ve->type->toBasetype()->ty == Tfunction) + ve->type = ve->type->toBasetype()->nextOf(); + if (!ve->type || ve->type->ty == Terror) + goto Lrangeerr; + + // Resolve inout qualifier of front type + ve->type = ve->type->substWildTo(tab->mod); + + Expressions *exps = new Expressions(); + exps->push(ve); + int pos = 0; + while (exps->dim < dim) + { + pos = expandAliasThisTuples(exps, pos); + if (pos == -1) + break; + } + if (exps->dim != dim) + goto Lrangeerr; + + for (size_t i = 0; i < dim; i++) + { + Parameter *arg = (*arguments)[i]; + Expression *exp = (*exps)[i]; + #if 0 + printf("[%d] arg = %s %s, exp = %s %s\n", i, + arg->type ? arg->type->toChars() : "?", arg->ident->toChars(), + exp->type->toChars(), exp->toChars()); + #endif + if (arg->type && !exp->implicitConvTo(arg->type)) + goto Lrangeerr; + if (!arg->type) + arg->type = exp->type; + + VarDeclaration *var = new VarDeclaration(loc, arg->type, arg->ident, new ExpInitializer(loc, exp)); + var->storage_class |= STCctfe | STCref | STCforeach; + DeclarationExp *de = new DeclarationExp(loc, var); + makeargs = new CompoundStatement(loc, makeargs, new ExpStatement(loc, de)); + } + + } + + forbody = new CompoundStatement(loc, + makeargs, this->body); + + s = new ForStatement(loc, init, condition, increment, forbody); +#if 0 + printf("init: %s\n", init->toChars()); + printf("condition: %s\n", condition->toChars()); + printf("increment: %s\n", increment->toChars()); + printf("body: %s\n", forbody->toChars()); +#endif + s = s->semantic(sc); + break; + + Lrangeerr: + error("cannot infer argument types"); + break; + } +#endif + case Tdelegate: + Lapply: + { + Expression *ec; + Expression *e; + + if (!checkForArgTypes()) + { body = body->semanticNoScope(sc); + return this; + } + + Type *tret = func->type->nextOf(); + + // Need a variable to hold value from any return statements in body. + if (!sc->func->vresult && tret && tret != Type::tvoid) + { + VarDeclaration *v = new VarDeclaration(loc, tret, Id::result, NULL); + v->noscope = 1; + v->semantic(sc); + if (!sc->insert(v)) + assert(0); + v->parent = sc->func; + sc->func->vresult = v; + } + + TypeFunction *tfld = NULL; + if (sapply) + { FuncDeclaration *fdapply = sapply->isFuncDeclaration(); + if (fdapply) + { assert(fdapply->type && fdapply->type->ty == Tfunction); + tfld = (TypeFunction *)fdapply->type->semantic(loc, sc); + goto Lget; + } + else if (tab->ty == Tdelegate) + { + tfld = (TypeFunction *)tab->nextOf(); + Lget: + //printf("tfld = %s\n", tfld->toChars()); + if (tfld->parameters->dim == 1) + { + Parameter *p = Parameter::getNth(tfld->parameters, 0); + if (p->type && p->type->ty == Tdelegate) + { Type *t = p->type->semantic(loc, sc); + assert(t->ty == Tdelegate); + tfld = (TypeFunction *)t->nextOf(); + } + } + } + } + + /* Turn body into the function literal: + * int delegate(ref T arg) { body } + */ + Parameters *args = new Parameters(); + for (size_t i = 0; i < dim; i++) + { Parameter *arg = (*arguments)[i]; + StorageClass stc = STCref; + Identifier *id; + + arg->type = arg->type->semantic(loc, sc); + if (tfld) + { Parameter *prm = Parameter::getNth(tfld->parameters, i); + //printf("\tprm = %s%s\n", (prm->storageClass&STCref?"ref ":""), prm->ident->toChars()); + stc = prm->storageClass & STCref; + id = arg->ident; // argument copy is not need. + if ((arg->storageClass & STCref) != stc) + { if (!stc) + error("foreach: cannot make %s ref", arg->ident->toChars()); + goto LcopyArg; + } + } + else if (arg->storageClass & STCref) + { // default delegate parameters are marked as ref, then + // argument copy is not need. + id = arg->ident; + } + else + { // Make a copy of the ref argument so it isn't + // a reference. + LcopyArg: + id = Lexer::uniqueId("__applyArg", i); + + Initializer *ie = new ExpInitializer(0, new IdentifierExp(0, id)); + VarDeclaration *v = new VarDeclaration(0, arg->type, arg->ident, ie); + s = new ExpStatement(0, v); + body = new CompoundStatement(loc, s, body); + } + args->push(new Parameter(stc, arg->type, id, NULL)); + } + tfld = new TypeFunction(args, Type::tint32, 0, LINKd); + cases = new Statements(); + gotos = new CompoundStatements(); + FuncLiteralDeclaration *fld = new FuncLiteralDeclaration(loc, 0, tfld, TOKdelegate, this); + fld->fbody = body; + Expression *flde = new FuncExp(loc, fld); + flde = flde->semantic(sc); + fld->tookAddressOf = 0; + + // Resolve any forward referenced goto's + for (size_t i = 0; i < gotos->dim; i++) + { CompoundStatement *cs = (*gotos)[i]; + GotoStatement *gs = (GotoStatement *)(*cs->statements)[0]; + + if (!gs->label->statement) + { // 'Promote' it to this scope, and replace with a return + cases->push(gs); + s = new ReturnStatement(0, new IntegerExp(cases->dim + 1)); + (*cs->statements)[0] = s; + } + } + + if (taa) + { + // Check types + Parameter *arg = (*arguments)[0]; + if (dim == 2) + { + if (arg->storageClass & STCref) + error("foreach: index cannot be ref"); + if (!arg->type->equals(taa->index)) + error("foreach: index must be type %s, not %s", taa->index->toChars(), arg->type->toChars()); + arg = (*arguments)[1]; + } + if (!arg->type->equals(taa->nextOf())) + error("foreach: value must be type %s, not %s", taa->nextOf()->toChars(), arg->type->toChars()); + + /* Call: + * _aaApply(aggr, keysize, flde) + */ + FuncDeclaration *fdapply; + if (dim == 2) + fdapply = FuncDeclaration::genCfunc(Type::tindex, "_aaApply2"); + else + fdapply = FuncDeclaration::genCfunc(Type::tindex, "_aaApply"); + ec = new VarExp(0, fdapply); + Expressions *exps = new Expressions(); + exps->push(aggr); + size_t keysize = taa->index->size(); + keysize = (keysize + (PTRSIZE-1)) & ~(PTRSIZE-1); + exps->push(new IntegerExp(0, keysize, Type::tsize_t)); + exps->push(flde); + e = new CallExp(loc, ec, exps); + e->type = Type::tindex; // don't run semantic() on e + } + else if (tab->ty == Tarray || tab->ty == Tsarray) + { + /* Call: + * _aApply(aggr, flde) + */ + static char fntab[9][3] = + { "cc","cw","cd", + "wc","cc","wd", + "dc","dw","dd" + }; + char fdname[7+1+2+ sizeof(dim)*3 + 1]; + int flag; + + switch (tn->ty) + { + case Tchar: flag = 0; break; + case Twchar: flag = 3; break; + case Tdchar: flag = 6; break; + default: assert(0); + } + switch (tnv->ty) + { + case Tchar: flag += 0; break; + case Twchar: flag += 1; break; + case Tdchar: flag += 2; break; + default: assert(0); + } + const char *r = (op == TOKforeach_reverse) ? "R" : ""; + int j = sprintf(fdname, "_aApply%s%.*s%zd", r, 2, fntab[flag], dim); + assert(j < sizeof(fdname)); + FuncDeclaration *fdapply = FuncDeclaration::genCfunc(Type::tindex, fdname); + + ec = new VarExp(0, fdapply); + Expressions *exps = new Expressions(); + if (tab->ty == Tsarray) + aggr = aggr->castTo(sc, tn->arrayOf()); + exps->push(aggr); + exps->push(flde); + e = new CallExp(loc, ec, exps); + e->type = Type::tindex; // don't run semantic() on e + } + else if (tab->ty == Tdelegate) + { + /* Call: + * aggr(flde) + */ + Expressions *exps = new Expressions(); + exps->push(flde); + if (aggr->op == TOKdelegate && + ((DelegateExp *)aggr)->func->isNested()) + // See Bugzilla 3560 + e = new CallExp(loc, ((DelegateExp *)aggr)->e1, exps); + else + e = new CallExp(loc, aggr, exps); + e = e->semantic(sc); + if (e->type != Type::tint32) + error("opApply() function for %s must return an int", tab->toChars()); + } + else + { + assert(tab->ty == Tstruct || tab->ty == Tclass); + Expressions *exps = new Expressions(); + assert(sapply); +#if 0 + TemplateDeclaration *td; + if (sapply && + (td = sapply->isTemplateDeclaration()) != NULL) + { /* Call: + * aggr.apply!(fld)() + */ + Objects *tiargs = new Objects(); + tiargs->push(fld); + ec = new DotTemplateInstanceExp(loc, aggr, sapply->ident, tiargs); + } + else +#endif + { + /* Call: + * aggr.apply(flde) + */ + ec = new DotIdExp(loc, aggr, sapply->ident); + exps->push(flde); + } + e = new CallExp(loc, ec, exps); + e = e->semantic(sc); + if (e->type != Type::tint32) + error("opApply() function for %s must return an int", tab->toChars()); + } + + if (!cases->dim) + // Easy case, a clean exit from the loop + s = new ExpStatement(loc, e); + else + { // Construct a switch statement around the return value + // of the apply function. + Statements *a = new Statements(); + + // default: break; takes care of cases 0 and 1 + s = new BreakStatement(0, NULL); + s = new DefaultStatement(0, s); + a->push(s); + + // cases 2... + for (size_t i = 0; i < cases->dim; i++) + { + s = (*cases)[i]; + s = new CaseStatement(0, new IntegerExp(i + 2), s); + a->push(s); + } + + s = new CompoundStatement(loc, a); + s = new SwitchStatement(loc, e, s, FALSE); + } + s = s->semantic(sc); + break; + } + case Terror: + s = NULL; + break; + + default: + error("foreach: %s is not an aggregate type", aggr->type->toChars()); + s = NULL; // error recovery + break; + } + sc->noctor--; + sc->pop(); + return s; +} + +bool ForeachStatement::checkForArgTypes() +{ bool result = TRUE; + + for (size_t i = 0; i < arguments->dim; i++) + { Parameter *arg = (*arguments)[i]; + if (!arg->type) + { + error("cannot infer type for %s", arg->ident->toChars()); + arg->type = Type::terror; + result = FALSE; + } + } + return result; +} + +int ForeachStatement::hasBreak() +{ + return TRUE; +} + +int ForeachStatement::hasContinue() +{ + return TRUE; +} + +int ForeachStatement::usesEH() +{ + return body->usesEH(); +} + +int ForeachStatement::blockExit(bool mustNotThrow) +{ int result = BEfallthru; + + if (aggr->canThrow(mustNotThrow)) + result |= BEthrow; + + if (body) + { + result |= body->blockExit(mustNotThrow) & ~(BEbreak | BEcontinue); + } + return result; +} + + +int ForeachStatement::comeFrom() +{ + if (body) + return body->comeFrom(); + return FALSE; +} + +void ForeachStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(Token::toChars(op)); + buf->writestring(" ("); + for (size_t i = 0; i < arguments->dim; i++) + { + Parameter *a = (*arguments)[i]; + if (i) + buf->writestring(", "); + if (a->storageClass & STCref) + buf->writestring((global.params.Dversion == 1) + ? (char*)"inout " : (char*)"ref "); + if (a->type) + a->type->toCBuffer(buf, a->ident, hgs); + else + buf->writestring(a->ident->toChars()); + } + buf->writestring("; "); + aggr->toCBuffer(buf, hgs); + buf->writebyte(')'); + buf->writenl(); + buf->writebyte('{'); + buf->writenl(); + if (body) + body->toCBuffer(buf, hgs); + buf->writebyte('}'); + buf->writenl(); +} + +/**************************** ForeachRangeStatement ***************************/ + +#if DMDV2 + +ForeachRangeStatement::ForeachRangeStatement(Loc loc, enum TOK op, Parameter *arg, + Expression *lwr, Expression *upr, Statement *body) + : Statement(loc) +{ + this->op = op; + this->arg = arg; + this->lwr = lwr; + this->upr = upr; + this->body = body; + + this->key = NULL; +} + +Statement *ForeachRangeStatement::syntaxCopy() +{ + ForeachRangeStatement *s = new ForeachRangeStatement(loc, op, + arg->syntaxCopy(), + lwr->syntaxCopy(), + upr->syntaxCopy(), + body ? body->syntaxCopy() : NULL); + return s; +} + +Statement *ForeachRangeStatement::semantic(Scope *sc) +{ + //printf("ForeachRangeStatement::semantic() %p\n", this); + Statement *s = this; + + lwr = lwr->semantic(sc); + lwr = resolveProperties(sc, lwr); + lwr = lwr->optimize(WANTvalue); + if (!lwr->type) + { + error("invalid range lower bound %s", lwr->toChars()); + return this; + } + + upr = upr->semantic(sc); + upr = resolveProperties(sc, upr); + upr = upr->optimize(WANTvalue); + if (!upr->type) + { + error("invalid range upper bound %s", upr->toChars()); + return this; + } + + if (arg->type) + { + arg->type = arg->type->semantic(loc, sc); + lwr = lwr->implicitCastTo(sc, arg->type); + upr = upr->implicitCastTo(sc, arg->type); + } + else + { + /* Must infer types from lwr and upr + */ + Type *tlwr = lwr->type->toBasetype(); + if (tlwr->ty == Tstruct || tlwr->ty == Tclass) + { + /* Just picking the first really isn't good enough. + */ + arg->type = lwr->type->mutableOf(); + } + else + { + AddExp ea(loc, lwr, upr); + Expression *e = ea.typeCombine(sc); + arg->type = ea.type->mutableOf(); + lwr = ea.e1; + upr = ea.e2; + } + } +#if 1 + /* Convert to a for loop: + * foreach (key; lwr .. upr) => + * for (auto key = lwr, auto tmp = upr; key < tmp; ++key) + * + * foreach_reverse (key; lwr .. upr) => + * for (auto tmp = lwr, auto key = upr; key-- > tmp;) + */ + + ExpInitializer *ie = new ExpInitializer(loc, (op == TOKforeach) ? lwr : upr); + key = new VarDeclaration(loc, arg->type, arg->ident, ie); + + Identifier *id = Lexer::uniqueId("__limit"); + ie = new ExpInitializer(loc, (op == TOKforeach) ? upr : lwr); + VarDeclaration *tmp = new VarDeclaration(loc, arg->type, id, ie); + + Statements *cs = new Statements(); + // Keep order of evaluation as lwr, then upr + if (op == TOKforeach) + { + cs->push(new ExpStatement(loc, key)); + cs->push(new ExpStatement(loc, tmp)); + } + else + { + cs->push(new ExpStatement(loc, tmp)); + cs->push(new ExpStatement(loc, key)); + } + Statement *forinit = new CompoundDeclarationStatement(loc, cs); + + Expression *cond; + if (op == TOKforeach_reverse) + { + cond = new PostExp(TOKminusminus, loc, new VarExp(loc, key)); + if (arg->type->isscalar()) + // key-- > tmp + cond = new CmpExp(TOKgt, loc, cond, new VarExp(loc, tmp)); + else + // key-- != tmp + cond = new EqualExp(TOKnotequal, loc, cond, new VarExp(loc, tmp)); + } + else + { + if (arg->type->isscalar()) + // key < tmp + cond = new CmpExp(TOKlt, loc, new VarExp(loc, key), new VarExp(loc, tmp)); + else + // key != tmp + cond = new EqualExp(TOKnotequal, loc, new VarExp(loc, key), new VarExp(loc, tmp)); + } + + Expression *increment = NULL; + if (op == TOKforeach) + // key += 1 + //increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(1)); + increment = new PreExp(TOKpreplusplus, loc, new VarExp(loc, key)); + + ForStatement *fs = new ForStatement(loc, forinit, cond, increment, body); + s = fs->semantic(sc); + return s; +#else + if (!arg->type->isscalar()) + error("%s is not a scalar type", arg->type->toChars()); + + sym = new ScopeDsymbol(); + sym->parent = sc->scopesym; + sc = sc->push(sym); + + sc->noctor++; + + key = new VarDeclaration(loc, arg->type, arg->ident, NULL); + DeclarationExp *de = new DeclarationExp(loc, key); + de->semantic(sc); + + if (key->storage_class) + error("foreach range: key cannot have storage class"); + + sc->sbreak = this; + sc->scontinue = this; + body = body->semantic(sc); + + sc->noctor--; + sc->pop(); + return s; +#endif +} + +int ForeachRangeStatement::hasBreak() +{ + return TRUE; +} + +int ForeachRangeStatement::hasContinue() +{ + return TRUE; +} + +int ForeachRangeStatement::usesEH() +{ + assert(0); + return body->usesEH(); +} + +int ForeachRangeStatement::blockExit(bool mustNotThrow) +{ + assert(0); + int result = BEfallthru; + + if (lwr && lwr->canThrow(mustNotThrow)) + result |= BEthrow; + else if (upr && upr->canThrow(mustNotThrow)) + result |= BEthrow; + + if (body) + { + result |= body->blockExit(mustNotThrow) & ~(BEbreak | BEcontinue); + } + return result; +} + + +int ForeachRangeStatement::comeFrom() +{ + assert(0); + if (body) + return body->comeFrom(); + return FALSE; +} + +void ForeachRangeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(Token::toChars(op)); + buf->writestring(" ("); + + if (arg->type) + arg->type->toCBuffer(buf, arg->ident, hgs); + else + buf->writestring(arg->ident->toChars()); + + buf->writestring("; "); + lwr->toCBuffer(buf, hgs); + buf->writestring(" .. "); + upr->toCBuffer(buf, hgs); + buf->writebyte(')'); + buf->writenl(); + buf->writebyte('{'); + buf->writenl(); + if (body) + body->toCBuffer(buf, hgs); + buf->writebyte('}'); + buf->writenl(); +} + +#endif + +/******************************** IfStatement ***************************/ + +IfStatement::IfStatement(Loc loc, Parameter *arg, Expression *condition, Statement *ifbody, Statement *elsebody) + : Statement(loc) +{ + this->arg = arg; + this->condition = condition; + this->ifbody = ifbody; + this->elsebody = elsebody; + this->match = NULL; +} + +Statement *IfStatement::syntaxCopy() +{ + Statement *i = NULL; + if (ifbody) + i = ifbody->syntaxCopy(); + + Statement *e = NULL; + if (elsebody) + e = elsebody->syntaxCopy(); + + Parameter *a = arg ? arg->syntaxCopy() : NULL; + IfStatement *s = new IfStatement(loc, a, condition->syntaxCopy(), i, e); + return s; +} + +Statement *IfStatement::semantic(Scope *sc) +{ + // Evaluate at runtime + unsigned cs0 = sc->callSuper; + unsigned cs1; + + Scope *scd; + if (arg) + { /* Declare arg, which we will set to be the + * result of condition. + */ + ScopeDsymbol *sym = new ScopeDsymbol(); + sym->parent = sc->scopesym; + scd = sc->push(sym); + + match = new VarDeclaration(loc, arg->type, arg->ident, new ExpInitializer(loc, condition)); + match->parent = sc->func; + + DeclarationExp *de = new DeclarationExp(loc, match); + VarExp *ve = new VarExp(0, match); + condition = new CommaExp(loc, de, ve); + condition = condition->semantic(scd); + + if (match->edtor) + { + Statement *sdtor = new ExpStatement(loc, match->edtor); + sdtor = new OnScopeStatement(loc, TOKon_scope_exit, sdtor); + ifbody = new CompoundStatement(loc, sdtor, ifbody); + match->noscope = 1; + } + } + else + { + condition = condition->semantic(sc); + condition = condition->addDtorHook(sc); + condition = resolveProperties(sc, condition); + scd = sc->push(); + } + + // Convert to boolean after declaring arg so this works: + // if (S arg = S()) {} + // where S is a struct that defines opCast!bool. + condition = condition->checkToBoolean(sc); + + // If we can short-circuit evaluate the if statement, don't do the + // semantic analysis of the skipped code. + // This feature allows a limited form of conditional compilation. + condition = condition->optimize(WANTflags); + ifbody = ifbody->semanticNoScope(scd); + scd->pop(); + + cs1 = sc->callSuper; + sc->callSuper = cs0; + if (elsebody) + elsebody = elsebody->semanticScope(sc, NULL, NULL); + sc->mergeCallSuper(loc, cs1); + + return this; +} + +int IfStatement::usesEH() +{ + return (ifbody && ifbody->usesEH()) || (elsebody && elsebody->usesEH()); +} + +int IfStatement::blockExit(bool mustNotThrow) +{ + //printf("IfStatement::blockExit(%p)\n", this); + + int result = BEnone; + if (condition->canThrow(mustNotThrow)) + result |= BEthrow; + if (condition->isBool(TRUE)) + { + if (ifbody) + result |= ifbody->blockExit(mustNotThrow); + else + result |= BEfallthru; + } + else if (condition->isBool(FALSE)) + { + if (elsebody) + result |= elsebody->blockExit(mustNotThrow); + else + result |= BEfallthru; + } + else + { + if (ifbody) + result |= ifbody->blockExit(mustNotThrow); + else + result |= BEfallthru; + if (elsebody) + result |= elsebody->blockExit(mustNotThrow); + else + result |= BEfallthru; + } + //printf("IfStatement::blockExit(%p) = x%x\n", this, result); + return result; +} + + +void IfStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("if ("); + if (arg) + { + if (arg->type) + arg->type->toCBuffer(buf, arg->ident, hgs); + else + { buf->writestring("auto "); + buf->writestring(arg->ident->toChars()); + } + buf->writestring(" = "); + } + condition->toCBuffer(buf, hgs); + buf->writebyte(')'); + buf->writenl(); + ifbody->toCBuffer(buf, hgs); + if (elsebody) + { buf->writestring("else"); + buf->writenl(); + elsebody->toCBuffer(buf, hgs); + } +} + +/******************************** ConditionalStatement ***************************/ + +ConditionalStatement::ConditionalStatement(Loc loc, Condition *condition, Statement *ifbody, Statement *elsebody) + : Statement(loc) +{ + this->condition = condition; + this->ifbody = ifbody; + this->elsebody = elsebody; +} + +Statement *ConditionalStatement::syntaxCopy() +{ + Statement *e = NULL; + if (elsebody) + e = elsebody->syntaxCopy(); + ConditionalStatement *s = new ConditionalStatement(loc, + condition->syntaxCopy(), ifbody->syntaxCopy(), e); + return s; +} + +Statement *ConditionalStatement::semantic(Scope *sc) +{ + //printf("ConditionalStatement::semantic()\n"); + + // If we can short-circuit evaluate the if statement, don't do the + // semantic analysis of the skipped code. + // This feature allows a limited form of conditional compilation. + if (condition->include(sc, NULL)) + { + DebugCondition *dc = condition->isDebugCondition(); + if (dc) + { + sc = sc->push(); + sc->flags |= SCOPEdebug; + ifbody = ifbody->semantic(sc); + sc->pop(); + } + else + ifbody = ifbody->semantic(sc); + return ifbody; + } + else + { + if (elsebody) + elsebody = elsebody->semantic(sc); + return elsebody; + } +} + +Statements *ConditionalStatement::flatten(Scope *sc) +{ + Statement *s; + + //printf("ConditionalStatement::flatten()\n"); + if (condition->include(sc, NULL)) + { + DebugCondition *dc = condition->isDebugCondition(); + if (dc) + s = new DebugStatement(loc, ifbody); + else + s = ifbody; + } + else + s = elsebody; + + Statements *a = new Statements(); + a->push(s); + return a; +} + +int ConditionalStatement::usesEH() +{ + return (ifbody && ifbody->usesEH()) || (elsebody && elsebody->usesEH()); +} + +int ConditionalStatement::blockExit(bool mustNotThrow) +{ + int result = ifbody->blockExit(mustNotThrow); + if (elsebody) + result |= elsebody->blockExit(mustNotThrow); + return result; +} + +void ConditionalStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + condition->toCBuffer(buf, hgs); + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + if (ifbody) + ifbody->toCBuffer(buf, hgs); + buf->writeByte('}'); + buf->writenl(); + if (elsebody) + { + buf->writestring("else"); + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + elsebody->toCBuffer(buf, hgs); + buf->writeByte('}'); + buf->writenl(); + } + buf->writenl(); +} + + +/******************************** PragmaStatement ***************************/ + +PragmaStatement::PragmaStatement(Loc loc, Identifier *ident, Expressions *args, Statement *body) + : Statement(loc) +{ + this->ident = ident; + this->args = args; + this->body = body; +} + +Statement *PragmaStatement::syntaxCopy() +{ + Statement *b = NULL; + if (body) + b = body->syntaxCopy(); + PragmaStatement *s = new PragmaStatement(loc, + ident, Expression::arraySyntaxCopy(args), b); + return s; +} + +Statement *PragmaStatement::semantic(Scope *sc) +{ // Should be merged with PragmaDeclaration + //printf("PragmaStatement::semantic() %s\n", toChars()); + //printf("body = %p\n", body); + if (ident == Id::msg) + { + if (args) + { + for (size_t i = 0; i < args->dim; i++) + { + Expression *e = (*args)[i]; + + e = e->semantic(sc); + e = e->optimize(WANTvalue | WANTinterpret); + StringExp *se = e->toString(); + if (se) + { + fprintf(stdmsg, "%.*s", (int)se->len, (char *)se->string); + } + else + fprintf(stdmsg, "%s", e->toChars()); + } + fprintf(stdmsg, "\n"); + } + } + else if (ident == Id::lib) + { +#if 1 + /* Should this be allowed? + */ + error("pragma(lib) not allowed as statement"); +#else + if (!args || args->dim != 1) + error("string expected for library name"); + else + { + Expression *e = (*args)[0]; + + e = e->semantic(sc); + e = e->optimize(WANTvalue | WANTinterpret); + (*args)[0] = e; + StringExp *se = e->toString(); + if (!se) + error("string expected for library name, not '%s'", e->toChars()); + else if (global.params.verbose) + { + char *name = (char *)mem.malloc(se->len + 1); + memcpy(name, se->string, se->len); + name[se->len] = 0; + printf("library %s\n", name); + mem.free(name); + } + } +#endif + } +#if DMDV2 + else if (ident == Id::startaddress) + { + if (!args || args->dim != 1) + error("function name expected for start address"); + else + { + Expression *e = (*args)[0]; + e = e->semantic(sc); + e = e->optimize(WANTvalue | WANTinterpret); + (*args)[0] = e; + Dsymbol *sa = getDsymbol(e); + if (!sa || !sa->isFuncDeclaration()) + error("function name expected for start address, not '%s'", e->toChars()); + if (body) + { + body = body->semantic(sc); + } + return this; + } + } +#endif + else + error("unrecognized pragma(%s)", ident->toChars()); + + if (body) + { + body = body->semantic(sc); + } + return body; +} + +int PragmaStatement::usesEH() +{ + return body && body->usesEH(); +} + +int PragmaStatement::blockExit(bool mustNotThrow) +{ + int result = BEfallthru; +#if 0 // currently, no code is generated for Pragma's, so it's just fallthru + if (arrayExpressionCanThrow(args)) + result |= BEthrow; + if (body) + result |= body->blockExit(mustNotThrow); +#endif + return result; +} + + +void PragmaStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("pragma ("); + buf->writestring(ident->toChars()); + if (args && args->dim) + { + buf->writestring(", "); + argsToCBuffer(buf, args, hgs); + } + buf->writeByte(')'); + if (body) + { + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + + body->toCBuffer(buf, hgs); + + buf->writeByte('}'); + buf->writenl(); + } + else + { + buf->writeByte(';'); + buf->writenl(); + } +} + + +/******************************** StaticAssertStatement ***************************/ + +StaticAssertStatement::StaticAssertStatement(StaticAssert *sa) + : Statement(sa->loc) +{ + this->sa = sa; +} + +Statement *StaticAssertStatement::syntaxCopy() +{ + StaticAssertStatement *s = new StaticAssertStatement((StaticAssert *)sa->syntaxCopy(NULL)); + return s; +} + +Statement *StaticAssertStatement::semantic(Scope *sc) +{ + sa->semantic2(sc); + return NULL; +} + +int StaticAssertStatement::blockExit(bool mustNotThrow) +{ + return BEfallthru; +} + +void StaticAssertStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + sa->toCBuffer(buf, hgs); +} + + +/******************************** SwitchStatement ***************************/ + +SwitchStatement::SwitchStatement(Loc loc, Expression *c, Statement *b, bool isFinal) + : Statement(loc) +{ + this->condition = c; + this->body = b; + this->isFinal = isFinal; + sdefault = NULL; + tf = NULL; + cases = NULL; + hasNoDefault = 0; + hasVars = 0; +} + +Statement *SwitchStatement::syntaxCopy() +{ + SwitchStatement *s = new SwitchStatement(loc, + condition->syntaxCopy(), body->syntaxCopy(), isFinal); + return s; +} + +Statement *SwitchStatement::semantic(Scope *sc) +{ + //printf("SwitchStatement::semantic(%p)\n", this); + tf = sc->tf; + assert(!cases); // ensure semantic() is only run once + condition = condition->semantic(sc); + condition = resolveProperties(sc, condition); + if (condition->type->isString()) + { + // If it's not an array, cast it to one + if (condition->type->ty != Tarray) + { + condition = condition->implicitCastTo(sc, condition->type->nextOf()->arrayOf()); + } + condition->type = condition->type->constOf(); + } + else + { condition = condition->integralPromotions(sc); + condition->checkIntegral(); + } + condition = condition->optimize(WANTvalue); + + sc = sc->push(); + sc->sbreak = this; + sc->sw = this; + + cases = new CaseStatements(); + sc->noctor++; // BUG: should use Scope::mergeCallSuper() for each case instead + body = body->semantic(sc); + sc->noctor--; + + // Resolve any goto case's with exp + for (size_t i = 0; i < gotoCases.dim; i++) + { + GotoCaseStatement *gcs = gotoCases[i]; + + if (!gcs->exp) + { + gcs->error("no case statement following goto case;"); + break; + } + + for (Scope *scx = sc; scx; scx = scx->enclosing) + { + if (!scx->sw) + continue; + for (size_t j = 0; j < scx->sw->cases->dim; j++) + { + CaseStatement *cs = (*scx->sw->cases)[j]; + + if (cs->exp->equals(gcs->exp)) + { + gcs->cs = cs; + goto Lfoundcase; + } + } + } + gcs->error("case %s not found", gcs->exp->toChars()); + + Lfoundcase: + ; + } + + bool needswitcherror = FALSE; +#if DMDV2 + if (isFinal) + { Type *t = condition->type; + while (t->ty == Ttypedef) + { // Don't use toBasetype() because that will skip past enums + t = ((TypeTypedef *)t)->sym->basetype; + } + if (condition->type->ty == Tenum) + { TypeEnum *te = (TypeEnum *)condition->type; + EnumDeclaration *ed = te->toDsymbol(sc)->isEnumDeclaration(); + assert(ed); + size_t dim = ed->members->dim; + for (size_t i = 0; i < dim; i++) + { + EnumMember *em = (*ed->members)[i]->isEnumMember(); + if (em) + { + for (size_t j = 0; j < cases->dim; j++) + { CaseStatement *cs = (*cases)[j]; + if (cs->exp->equals(em->value)) + goto L1; + } + error("enum member %s not represented in final switch", em->toChars()); + } + L1: + ; + } + } + else + needswitcherror = TRUE; + } +#endif + + if (!sc->sw->sdefault && (!isFinal || needswitcherror)) + { hasNoDefault = 1; + + if (!global.params.useDeprecated && !isFinal) + error("non-final switch statement without a default is deprecated"); + + // Generate runtime error if the default is hit + Statements *a = new Statements(); + CompoundStatement *cs; + Statement *s; + + if (global.params.useSwitchError) + s = new SwitchErrorStatement(loc); + else + { Expression *e = new HaltExp(loc); + s = new ExpStatement(loc, e); + } + + a->reserve(2); + sc->sw->sdefault = new DefaultStatement(loc, s); + a->push(sc->sw->sdefault); + a->push(body); + cs = new CompoundStatement(loc, a); + body = cs; + } + + sc->pop(); + return this; +} + +int SwitchStatement::hasBreak() +{ + return TRUE; +} + +int SwitchStatement::usesEH() +{ + return body ? body->usesEH() : 0; +} + +int SwitchStatement::blockExit(bool mustNotThrow) +{ int result = BEnone; + if (condition->canThrow(mustNotThrow)) + result |= BEthrow; + + if (body) + { result |= body->blockExit(mustNotThrow); + if (result & BEbreak) + { result |= BEfallthru; + result &= ~BEbreak; + } + } + else + result |= BEfallthru; + + return result; +} + + +void SwitchStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(isFinal ? "final switch (" : "switch ("); + condition->toCBuffer(buf, hgs); + buf->writebyte(')'); + buf->writenl(); + if (body) + { + if (!body->isScopeStatement()) + { buf->writebyte('{'); + buf->writenl(); + body->toCBuffer(buf, hgs); + buf->writebyte('}'); + buf->writenl(); + } + else + { + body->toCBuffer(buf, hgs); + } + } +} + +/******************************** CaseStatement ***************************/ + +CaseStatement::CaseStatement(Loc loc, Expression *exp, Statement *s) + : Statement(loc) +{ + this->exp = exp; + this->statement = s; + index = 0; + cblock = NULL; +} + +Statement *CaseStatement::syntaxCopy() +{ + CaseStatement *s = new CaseStatement(loc, exp->syntaxCopy(), statement->syntaxCopy()); + return s; +} + +Statement *CaseStatement::semantic(Scope *sc) +{ SwitchStatement *sw = sc->sw; + + //printf("CaseStatement::semantic() %s\n", toChars()); + exp = exp->semantic(sc); + if (sw) + { + exp = exp->implicitCastTo(sc, sw->condition->type); + exp = exp->optimize(WANTvalue); + + /* This is where variables are allowed as case expressions. + */ + if (exp->op == TOKvar) + { VarExp *ve = (VarExp *)exp; + VarDeclaration *v = ve->var->isVarDeclaration(); + Type *t = exp->type->toBasetype(); + if (v && (t->isintegral() || t->ty == Tclass)) + { /* Flag that we need to do special code generation + * for this, i.e. generate a sequence of if-then-else + */ + sw->hasVars = 1; + if (sw->isFinal) + error("case variables not allowed in final switch statements"); + goto L1; + } + } + else + exp = exp->optimize(WANTvalue | WANTinterpret); + + if (exp->op != TOKstring && exp->op != TOKint64 && exp->op != TOKerror) + { + error("case must be a string or an integral constant, not %s", exp->toChars()); + exp = new IntegerExp(0); + } + + L1: + for (size_t i = 0; i < sw->cases->dim; i++) + { + CaseStatement *cs = (*sw->cases)[i]; + + //printf("comparing '%s' with '%s'\n", exp->toChars(), cs->exp->toChars()); + if (cs->exp->equals(exp)) + { error("duplicate case %s in switch statement", exp->toChars()); + break; + } + } + + sw->cases->push(this); + + // Resolve any goto case's with no exp to this case statement + for (size_t i = 0; i < sw->gotoCases.dim; i++) + { + GotoCaseStatement *gcs = sw->gotoCases[i]; + + if (!gcs->exp) + { + gcs->cs = this; + sw->gotoCases.remove(i); // remove from array + } + } + + if (sc->sw->tf != sc->tf) + error("switch and case are in different finally blocks"); + } + else + error("case not in switch statement"); + statement = statement->semantic(sc); + return this; +} + +int CaseStatement::compare(Object *obj) +{ + // Sort cases so we can do an efficient lookup + CaseStatement *cs2 = (CaseStatement *)(obj); + + return exp->compare(cs2->exp); +} + +int CaseStatement::usesEH() +{ + return statement->usesEH(); +} + +int CaseStatement::blockExit(bool mustNotThrow) +{ + return statement->blockExit(mustNotThrow); +} + + +int CaseStatement::comeFrom() +{ + return TRUE; +} + +void CaseStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("case "); + exp->toCBuffer(buf, hgs); + buf->writebyte(':'); + buf->writenl(); + statement->toCBuffer(buf, hgs); +} + +/******************************** CaseRangeStatement ***************************/ + +#if DMDV2 + +CaseRangeStatement::CaseRangeStatement(Loc loc, Expression *first, + Expression *last, Statement *s) + : Statement(loc) +{ + this->first = first; + this->last = last; + this->statement = s; +} + +Statement *CaseRangeStatement::syntaxCopy() +{ + CaseRangeStatement *s = new CaseRangeStatement(loc, + first->syntaxCopy(), last->syntaxCopy(), statement->syntaxCopy()); + return s; +} + +Statement *CaseRangeStatement::semantic(Scope *sc) +{ SwitchStatement *sw = sc->sw; + + //printf("CaseRangeStatement::semantic() %s\n", toChars()); + if (sw->isFinal) + error("case ranges not allowed in final switch"); + + first = first->semantic(sc); + first = first->implicitCastTo(sc, sw->condition->type); + first = first->optimize(WANTvalue | WANTinterpret); + + + last = last->semantic(sc); + last = last->implicitCastTo(sc, sw->condition->type); + last = last->optimize(WANTvalue | WANTinterpret); + + if (first->op == TOKerror || last->op == TOKerror) + return statement ? statement->semantic(sc) : NULL; + + uinteger_t fval = first->toInteger(); + uinteger_t lval = last->toInteger(); + + + if ( (first->type->isunsigned() && fval > lval) || + (!first->type->isunsigned() && (sinteger_t)fval > (sinteger_t)lval)) + { + error("first case %s is greater than last case %s", + first->toChars(), last->toChars()); + lval = fval; + } + + if (lval - fval > 256) + { error("had %llu cases which is more than 256 cases in case range", lval - fval); + lval = fval + 256; + } + + /* This works by replacing the CaseRange with an array of Case's. + * + * case a: .. case b: s; + * => + * case a: + * [...] + * case b: + * s; + */ + + Statements *statements = new Statements(); + for (uinteger_t i = fval; i != lval + 1; i++) + { + Statement *s = statement; + if (i != lval) // if not last case + s = new ExpStatement(loc, (Expression *)NULL); + Expression *e = new IntegerExp(loc, i, first->type); + Statement *cs = new CaseStatement(loc, e, s); + statements->push(cs); + } + Statement *s = new CompoundStatement(loc, statements); + s = s->semantic(sc); + return s; +} + +void CaseRangeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("case "); + first->toCBuffer(buf, hgs); + buf->writestring(": .. case "); + last->toCBuffer(buf, hgs); + buf->writebyte(':'); + buf->writenl(); + statement->toCBuffer(buf, hgs); +} + +#endif + +/******************************** DefaultStatement ***************************/ + +DefaultStatement::DefaultStatement(Loc loc, Statement *s) + : Statement(loc) +{ + this->statement = s; +#if IN_GCC ++ cblock = NULL; +#endif +} + +Statement *DefaultStatement::syntaxCopy() +{ + DefaultStatement *s = new DefaultStatement(loc, statement->syntaxCopy()); + return s; +} + +Statement *DefaultStatement::semantic(Scope *sc) +{ + //printf("DefaultStatement::semantic()\n"); + if (sc->sw) + { + if (sc->sw->sdefault) + { + error("switch statement already has a default"); + } + sc->sw->sdefault = this; + + if (sc->sw->tf != sc->tf) + error("switch and default are in different finally blocks"); + + if (sc->sw->isFinal) + error("default statement not allowed in final switch statement"); + } + else + error("default not in switch statement"); + statement = statement->semantic(sc); + return this; +} + +int DefaultStatement::usesEH() +{ + return statement->usesEH(); +} + +int DefaultStatement::blockExit(bool mustNotThrow) +{ + return statement->blockExit(mustNotThrow); +} + + +int DefaultStatement::comeFrom() +{ + return TRUE; +} + +void DefaultStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("default:\n"); + statement->toCBuffer(buf, hgs); +} + +/******************************** GotoDefaultStatement ***************************/ + +GotoDefaultStatement::GotoDefaultStatement(Loc loc) + : Statement(loc) +{ + sw = NULL; +} + +Statement *GotoDefaultStatement::syntaxCopy() +{ + GotoDefaultStatement *s = new GotoDefaultStatement(loc); + return s; +} + +Statement *GotoDefaultStatement::semantic(Scope *sc) +{ + sw = sc->sw; + if (!sw) + error("goto default not in switch statement"); + return this; +} + +int GotoDefaultStatement::blockExit(bool mustNotThrow) +{ + return BEgoto; +} + + +void GotoDefaultStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("goto default;\n"); +} + +/******************************** GotoCaseStatement ***************************/ + +GotoCaseStatement::GotoCaseStatement(Loc loc, Expression *exp) + : Statement(loc) +{ + cs = NULL; + this->exp = exp; +} + +Statement *GotoCaseStatement::syntaxCopy() +{ + Expression *e = exp ? exp->syntaxCopy() : NULL; + GotoCaseStatement *s = new GotoCaseStatement(loc, e); + return s; +} + +Statement *GotoCaseStatement::semantic(Scope *sc) +{ + if (exp) + exp = exp->semantic(sc); + + if (!sc->sw) + error("goto case not in switch statement"); + else + { + sc->sw->gotoCases.push(this); + if (exp) + { + exp = exp->implicitCastTo(sc, sc->sw->condition->type); + exp = exp->optimize(WANTvalue); + } + } + return this; +} + +int GotoCaseStatement::blockExit(bool mustNotThrow) +{ + return BEgoto; +} + + +void GotoCaseStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("goto case"); + if (exp) + { buf->writebyte(' '); + exp->toCBuffer(buf, hgs); + } + buf->writebyte(';'); + buf->writenl(); +} + +/******************************** SwitchErrorStatement ***************************/ + +SwitchErrorStatement::SwitchErrorStatement(Loc loc) + : Statement(loc) +{ +} + +int SwitchErrorStatement::blockExit(bool mustNotThrow) +{ + // Switch errors are non-recoverable + return BEhalt; +} + + +void SwitchErrorStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("SwitchErrorStatement::toCBuffer()"); + buf->writenl(); +} + +/******************************** ReturnStatement ***************************/ + +ReturnStatement::ReturnStatement(Loc loc, Expression *exp) + : Statement(loc) +{ + this->exp = exp; +} + +Statement *ReturnStatement::syntaxCopy() +{ + Expression *e = NULL; + if (exp) + e = exp->syntaxCopy(); + ReturnStatement *s = new ReturnStatement(loc, e); + return s; +} + +Statement *ReturnStatement::semantic(Scope *sc) +{ + //printf("ReturnStatement::semantic() %s\n", toChars()); + + FuncDeclaration *fd = sc->parent->isFuncDeclaration(); + Scope *scx = sc; + int implicit0 = 0; + Expression *eorg = NULL; + + if (fd->fes) + fd = fd->fes->func; // fd is now function enclosing foreach + + Type *tret = fd->type->nextOf(); + if (fd->tintro) + /* We'll be implicitly casting the return expression to tintro + */ + tret = fd->tintro->nextOf(); + Type *tbret = NULL; + + if (tret) + tbret = tret->toBasetype(); + + // main() returns 0, even if it returns void + if (!exp && (!tbret || tbret->ty == Tvoid) && fd->isMain()) + { implicit0 = 1; + exp = new IntegerExp(0); + } + + if (sc->incontract || scx->incontract) + error("return statements cannot be in contracts"); + if (sc->tf || scx->tf) + error("return statements cannot be in finally, scope(exit) or scope(success) bodies"); + + if (fd->isCtorDeclaration()) + { + // Constructors implicitly do: + // return this; + if (exp && exp->op != TOKthis) + error("cannot return expression from constructor"); + exp = new ThisExp(0); + exp->type = tret; + } + + if (!exp) + fd->nrvo_can = 0; + + if (exp) + { + fd->hasReturnExp |= 1; + + if (exp->op == TOKfunction && tbret) + ((FuncExp *)exp)->setType(tbret); + + exp = exp->semantic(sc); + exp = resolveProperties(sc, exp); + if (!((TypeFunction *)fd->type)->isref) + exp = exp->optimize(WANTvalue); + + if (fd->nrvo_can && exp->op == TOKvar) + { VarExp *ve = (VarExp *)exp; + VarDeclaration *v = ve->var->isVarDeclaration(); + + if (((TypeFunction *)fd->type)->isref) + // Function returns a reference + fd->nrvo_can = 0; + else if (!v || v->isOut() || v->isRef()) + fd->nrvo_can = 0; +// else if (tbret->ty == Tstruct && ((TypeStruct *)tbret)->sym->dtor) +// // Struct being returned has destructors +// fd->nrvo_can = 0; + else if (fd->nrvo_var == NULL) + { if (!v->isDataseg() && !v->isParameter() && v->toParent2() == fd) + { //printf("Setting nrvo to %s\n", v->toChars()); + fd->nrvo_var = v; + } + else + fd->nrvo_can = 0; + } + else if (fd->nrvo_var != v) + fd->nrvo_can = 0; + } + else + fd->nrvo_can = 0; + + if (fd->inferRetType) + { TypeFunction *tf = (TypeFunction *)fd->type; + assert(tf->ty == Tfunction); + Type *tfret = tf->nextOf(); + if (tfret) + { + if (tfret != Type::terror) + { + if (!exp->type->equals(tfret)) + { + int m1 = exp->type->implicitConvTo(tfret); + int m2 = tfret->implicitConvTo(exp->type); + //printf("exp->type = %s m2<-->m1 tret %s\n", exp->type->toChars(), tfret->toChars()); + //printf("m1 = %d, m2 = %d\n", m1, m2); + + if (m1 && m2) + ; + else if (!m1 && m2) + tf->next = exp->type; + else if (m1 && !m2) + ; + else + error("mismatched function return type inference of %s and %s", + exp->type->toChars(), tfret->toChars()); + } + } + + /* The "refness" is determined by the first return statement, + * not all of them. This means: + * return 3; return x; // ok, x can be a value + * return x; return 3; // error, 3 is not an lvalue + */ + } + else + { + if (tf->isref) + { /* Determine "refness" of function return: + * if it's an lvalue, return by ref, else return by value + */ + if (exp->isLvalue()) + { + /* Return by ref + * (but first ensure it doesn't fail the "check for + * escaping reference" test) + */ + unsigned errors = global.startGagging(); + exp->checkEscapeRef(); + if (global.endGagging(errors)) + { tf->isref = FALSE; // return by value + } + } + else + tf->isref = FALSE; // return by value + } + tf->next = exp->type; + //fd->type = tf->semantic(loc, sc); // Removed with 6902 + if (!fd->tintro) + { tret = fd->type->nextOf(); + tbret = tret->toBasetype(); + } + } + if (fd->returnLabel) + eorg = exp; + } + else if (tbret->ty != Tvoid) + { + if (fd->isPureBypassingInference() == PUREstrong && + !exp->type->implicitConvTo(tret) && + exp->type->invariantOf()->implicitConvTo(tret)) + { + exp = exp->castTo(sc, exp->type->invariantOf()); + } + if (fd->tintro) + exp = exp->implicitCastTo(sc, fd->type->nextOf()); + + // eorg isn't casted to tret (== fd->tintro->nextOf()) + if (fd->returnLabel) + eorg = exp->copy(); + exp = exp->implicitCastTo(sc, tret); + + if (!((TypeFunction *)fd->type)->isref) + exp = exp->optimize(WANTvalue); + } + } + else if (fd->inferRetType) + { + if (fd->type->nextOf()) + { + if (fd->type->nextOf()->ty != Tvoid) + error("mismatched function return type inference of void and %s", + fd->type->nextOf()->toChars()); + } + else + { + ((TypeFunction *)fd->type)->next = Type::tvoid; + //fd->type = fd->type->semantic(loc, sc); // Remove with7321, same as 6902 + if (!fd->tintro) + { tret = Type::tvoid; + tbret = tret; + } + } + } + else if (tbret->ty != Tvoid) // if non-void return + error("return expression expected"); + + if (sc->fes) + { + Statement *s; + + if (exp && !implicit0) + { + exp = exp->implicitCastTo(sc, tret); + } + if (!exp || exp->op == TOKint64 || exp->op == TOKfloat64 || + exp->op == TOKimaginary80 || exp->op == TOKcomplex80 || + exp->op == TOKthis || exp->op == TOKsuper || exp->op == TOKnull || + exp->op == TOKstring) + { + sc->fes->cases->push(this); + // Construct: return cases->dim+1; + s = new ReturnStatement(0, new IntegerExp(sc->fes->cases->dim + 1)); + } + else if (fd->type->nextOf()->toBasetype() == Type::tvoid) + { + s = new ReturnStatement(0, NULL); + sc->fes->cases->push(s); + + // Construct: { exp; return cases->dim + 1; } + Statement *s1 = new ExpStatement(loc, exp); + Statement *s2 = new ReturnStatement(0, new IntegerExp(sc->fes->cases->dim + 1)); + s = new CompoundStatement(loc, s1, s2); + } + else + { + // Construct: return vresult; + if (!fd->vresult) + { // Declare vresult + VarDeclaration *v = new VarDeclaration(loc, tret, Id::result, NULL); + v->noscope = 1; + v->storage_class |= STCresult; + v->semantic(scx); + if (!scx->insert(v)) + assert(0); + v->parent = fd; + fd->vresult = v; + } + + s = new ReturnStatement(0, new VarExp(0, fd->vresult)); + sc->fes->cases->push(s); + + // Construct: { vresult = exp; return cases->dim + 1; } + exp = new ConstructExp(loc, new VarExp(0, fd->vresult), exp); + exp = exp->semantic(sc); + Statement *s1 = new ExpStatement(loc, exp); + Statement *s2 = new ReturnStatement(0, new IntegerExp(sc->fes->cases->dim + 1)); + s = new CompoundStatement(loc, s1, s2); + } + return s; + } + + if (exp) + { + if (((TypeFunction *)fd->type)->isref && !fd->isCtorDeclaration()) + { // Function returns a reference + if (tret->isMutable()) + exp = exp->modifiableLvalue(sc, exp); + else + exp = exp->toLvalue(sc, exp); + + exp->checkEscapeRef(); + } + else + { + //exp->dump(0); + //exp->print(); + exp->checkEscape(); + } + + if (fd->returnLabel && tbret->ty != Tvoid) + { + assert(fd->vresult); + VarExp *v = new VarExp(0, fd->vresult); + + assert(eorg); + exp = new ConstructExp(loc, v, eorg); + exp = exp->semantic(sc); + } + } + + /* BUG: need to issue an error on: + * this + * { if (x) return; + * super(); + * } + */ + + if (sc->callSuper & CSXany_ctor && + !(sc->callSuper & (CSXthis_ctor | CSXsuper_ctor))) + error("return without calling constructor"); + + sc->callSuper |= CSXreturn; + + // See if all returns are instead to be replaced with a goto returnLabel; + if (fd->returnLabel) + { + GotoStatement *gs = new GotoStatement(loc, Id::returnLabel); + + gs->label = fd->returnLabel; + if (exp) + { /* Replace: return exp; + * with: exp; goto returnLabel; + */ + Statement *s = new ExpStatement(0, exp); + return new CompoundStatement(loc, s, gs); + } + return gs; + } + + if (exp && tbret->ty == Tvoid && !implicit0) + { + /* Replace: + * return exp; + * with: + * exp; return; + */ + Statement *s = new ExpStatement(loc, exp); + s = s->semantic(sc); + + if (exp->type->ty != Tvoid) + { + error("cannot return non-void from void function"); + } + + exp = NULL; + return new CompoundStatement(loc, s, this); + } + + if (exp) + { if (exp->op == TOKcall) + valueNoDtor(exp); + else + { + Expression *e = exp->isTemp(); + if (e) + exp = e; // don't need temporary + } + } + + return this; +} + +int ReturnStatement::blockExit(bool mustNotThrow) +{ int result = BEreturn; + + if (exp && exp->canThrow(mustNotThrow)) + result |= BEthrow; + return result; +} + + +void ReturnStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->printf("return "); + if (exp) + exp->toCBuffer(buf, hgs); + buf->writeByte(';'); + buf->writenl(); +} + +/******************************** BreakStatement ***************************/ + +BreakStatement::BreakStatement(Loc loc, Identifier *ident) + : Statement(loc) +{ + this->ident = ident; +} + +Statement *BreakStatement::syntaxCopy() +{ + BreakStatement *s = new BreakStatement(loc, ident); + return s; +} + +Statement *BreakStatement::semantic(Scope *sc) +{ + //printf("BreakStatement::semantic()\n"); + // If: + // break Identifier; + if (ident) + { + Scope *scx; + FuncDeclaration *thisfunc = sc->func; + + for (scx = sc; scx; scx = scx->enclosing) + { + LabelStatement *ls; + + if (scx->func != thisfunc) // if in enclosing function + { + if (sc->fes) // if this is the body of a foreach + { + /* Post this statement to the fes, and replace + * it with a return value that caller will put into + * a switch. Caller will figure out where the break + * label actually is. + * Case numbers start with 2, not 0, as 0 is continue + * and 1 is break. + */ + Statement *s; + sc->fes->cases->push(this); + s = new ReturnStatement(0, new IntegerExp(sc->fes->cases->dim + 1)); + return s; + } + break; // can't break to it + } + + ls = scx->slabel; + if (ls && ls->ident == ident) + { + Statement *s = ls->statement; + + if (!s->hasBreak()) + error("label '%s' has no break", ident->toChars()); + if (ls->tf != sc->tf) + error("cannot break out of finally block"); + return this; + } + } + error("enclosing label '%s' for break not found", ident->toChars()); + } + else if (!sc->sbreak) + { + if (sc->fes) + { Statement *s; + + // Replace break; with return 1; + s = new ReturnStatement(0, new IntegerExp(1)); + return s; + } + error("break is not inside a loop or switch"); + } + return this; +} + +int BreakStatement::blockExit(bool mustNotThrow) +{ + //printf("BreakStatement::blockExit(%p) = x%x\n", this, ident ? BEgoto : BEbreak); + return ident ? BEgoto : BEbreak; +} + + +void BreakStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("break"); + if (ident) + { buf->writebyte(' '); + buf->writestring(ident->toChars()); + } + buf->writebyte(';'); + buf->writenl(); +} + +/******************************** ContinueStatement ***************************/ + +ContinueStatement::ContinueStatement(Loc loc, Identifier *ident) + : Statement(loc) +{ + this->ident = ident; +} + +Statement *ContinueStatement::syntaxCopy() +{ + ContinueStatement *s = new ContinueStatement(loc, ident); + return s; +} + +Statement *ContinueStatement::semantic(Scope *sc) +{ + //printf("ContinueStatement::semantic() %p\n", this); + if (ident) + { + Scope *scx; + FuncDeclaration *thisfunc = sc->func; + + for (scx = sc; scx; scx = scx->enclosing) + { + LabelStatement *ls; + + if (scx->func != thisfunc) // if in enclosing function + { + if (sc->fes) // if this is the body of a foreach + { + for (; scx; scx = scx->enclosing) + { + ls = scx->slabel; + if (ls && ls->ident == ident && ls->statement == sc->fes) + { + // Replace continue ident; with return 0; + return new ReturnStatement(0, new IntegerExp(0)); + } + } + + /* Post this statement to the fes, and replace + * it with a return value that caller will put into + * a switch. Caller will figure out where the break + * label actually is. + * Case numbers start with 2, not 0, as 0 is continue + * and 1 is break. + */ + Statement *s; + sc->fes->cases->push(this); + s = new ReturnStatement(0, new IntegerExp(sc->fes->cases->dim + 1)); + return s; + } + break; // can't continue to it + } + + ls = scx->slabel; + if (ls && ls->ident == ident) + { + Statement *s = ls->statement; + + if (!s->hasContinue()) + error("label '%s' has no continue", ident->toChars()); + if (ls->tf != sc->tf) + error("cannot continue out of finally block"); + return this; + } + } + error("enclosing label '%s' for continue not found", ident->toChars()); + } + else if (!sc->scontinue) + { + if (sc->fes) + { Statement *s; + + // Replace continue; with return 0; + s = new ReturnStatement(0, new IntegerExp(0)); + return s; + } + error("continue is not inside a loop"); + } + return this; +} + +int ContinueStatement::blockExit(bool mustNotThrow) +{ + return ident ? BEgoto : BEcontinue; +} + + +void ContinueStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("continue"); + if (ident) + { buf->writebyte(' '); + buf->writestring(ident->toChars()); + } + buf->writebyte(';'); + buf->writenl(); +} + +/******************************** SynchronizedStatement ***************************/ + +SynchronizedStatement::SynchronizedStatement(Loc loc, Expression *exp, Statement *body) + : Statement(loc) +{ + this->exp = exp; + this->body = body; + this->esync = NULL; +} + +SynchronizedStatement::SynchronizedStatement(Loc loc, elem *esync, Statement *body) + : Statement(loc) +{ + this->exp = NULL; + this->body = body; + this->esync = esync; +} + +Statement *SynchronizedStatement::syntaxCopy() +{ + Expression *e = exp ? exp->syntaxCopy() : NULL; + SynchronizedStatement *s = new SynchronizedStatement(loc, e, body ? body->syntaxCopy() : NULL); + return s; +} + +Statement *SynchronizedStatement::semantic(Scope *sc) +{ + if (exp) + { + exp = exp->semantic(sc); + exp = resolveProperties(sc, exp); + ClassDeclaration *cd = exp->type->isClassHandle(); + if (!cd) + error("can only synchronize on class objects, not '%s'", exp->type->toChars()); + else if (cd->isInterfaceDeclaration()) + { /* Cast the interface to an object, as the object has the monitor, + * not the interface. + */ + Type *t = new TypeIdentifier(0, Id::Object); + + t = t->semantic(0, sc); + exp = new CastExp(loc, exp, t); + exp = exp->semantic(sc); + } + +#if 1 + /* Rewrite as: + * auto tmp = exp; + * _d_monitorenter(tmp); + * try { body } finally { _d_monitorexit(tmp); } + */ + Identifier *id = Lexer::uniqueId("__sync"); + ExpInitializer *ie = new ExpInitializer(loc, exp); + VarDeclaration *tmp = new VarDeclaration(loc, exp->type, id, ie); + + Statements *cs = new Statements(); + cs->push(new ExpStatement(loc, tmp)); + + FuncDeclaration *fdenter = FuncDeclaration::genCfunc(Type::tvoid, Id::monitorenter); + Expression *e = new CallExp(loc, new VarExp(loc, fdenter), new VarExp(loc, tmp)); + e->type = Type::tvoid; // do not run semantic on e + cs->push(new ExpStatement(loc, e)); + + FuncDeclaration *fdexit = FuncDeclaration::genCfunc(Type::tvoid, Id::monitorexit); + e = new CallExp(loc, new VarExp(loc, fdexit), new VarExp(loc, tmp)); + e->type = Type::tvoid; // do not run semantic on e + Statement *s = new ExpStatement(loc, e); + s = new TryFinallyStatement(loc, body, s); + cs->push(s); + + s = new CompoundStatement(loc, cs); + return s->semantic(sc); +#endif + } +#if 1 + else + { /* Generate our own critical section, then rewrite as: + * __gshared byte[CriticalSection.sizeof] critsec; + * _d_criticalenter(critsec.ptr); + * try { body } finally { _d_criticalexit(critsec.ptr); } + */ + Identifier *id = Lexer::uniqueId("__critsec"); + Type *t = new TypeSArray(Type::tint8, new IntegerExp(PTRSIZE + (global.params.is64bit ? os_critsecsize64() : os_critsecsize32()))); + VarDeclaration *tmp = new VarDeclaration(loc, t, id, NULL); + tmp->storage_class |= STCgshared | STCstatic; + + Statements *cs = new Statements(); + cs->push(new ExpStatement(loc, tmp)); + + FuncDeclaration *fdenter = FuncDeclaration::genCfunc(Type::tvoid, Id::criticalenter); + Expression *e = new DotIdExp(loc, new VarExp(loc, tmp), Id::ptr); + e = e->semantic(sc); + e = new CallExp(loc, new VarExp(loc, fdenter), e); + e->type = Type::tvoid; // do not run semantic on e + cs->push(new ExpStatement(loc, e)); + + FuncDeclaration *fdexit = FuncDeclaration::genCfunc(Type::tvoid, Id::criticalexit); + e = new DotIdExp(loc, new VarExp(loc, tmp), Id::ptr); + e = e->semantic(sc); + e = new CallExp(loc, new VarExp(loc, fdexit), e); + e->type = Type::tvoid; // do not run semantic on e + Statement *s = new ExpStatement(loc, e); + s = new TryFinallyStatement(loc, body, s); + cs->push(s); + + s = new CompoundStatement(loc, cs); + return s->semantic(sc); + } +#endif + if (body) + body = body->semantic(sc); + return this; +} + +int SynchronizedStatement::hasBreak() +{ + return FALSE; //TRUE; +} + +int SynchronizedStatement::hasContinue() +{ + return FALSE; //TRUE; +} + +int SynchronizedStatement::usesEH() +{ + return TRUE; +} + +int SynchronizedStatement::blockExit(bool mustNotThrow) +{ + return body ? body->blockExit(mustNotThrow) : BEfallthru; +} + + +void SynchronizedStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("synchronized"); + if (exp) + { buf->writebyte('('); + exp->toCBuffer(buf, hgs); + buf->writebyte(')'); + } + if (body) + { + buf->writebyte(' '); + body->toCBuffer(buf, hgs); + } +} + +/******************************** WithStatement ***************************/ + +WithStatement::WithStatement(Loc loc, Expression *exp, Statement *body) + : Statement(loc) +{ + this->exp = exp; + this->body = body; + wthis = NULL; +} + +Statement *WithStatement::syntaxCopy() +{ + WithStatement *s = new WithStatement(loc, exp->syntaxCopy(), body ? body->syntaxCopy() : NULL); + return s; +} + +Statement *WithStatement::semantic(Scope *sc) +{ ScopeDsymbol *sym; + Initializer *init; + + //printf("WithStatement::semantic()\n"); + exp = exp->semantic(sc); + exp = resolveProperties(sc, exp); + if (exp->op == TOKerror) + return NULL; + if (exp->op == TOKimport) + { ScopeExp *es = (ScopeExp *)exp; + + sym = es->sds; + } + else if (exp->op == TOKtype) + { TypeExp *es = (TypeExp *)exp; + + Dsymbol *s = es->type->toDsymbol(sc); + sym = s ? s->isScopeDsymbol() : NULL; + if (!sym) + { error("with type %s has no members", es->toChars()); + if (body) + body = body->semantic(sc); + return this; + } + } + else + { Type *t = exp->type; + + assert(t); + t = t->toBasetype(); + if (t->isClassHandle()) + { + init = new ExpInitializer(loc, exp); + wthis = new VarDeclaration(loc, exp->type, Id::withSym, init); + wthis->semantic(sc); + + sym = new WithScopeSymbol(this); + sym->parent = sc->scopesym; + } + else if (t->ty == Tstruct) + { + Expression *e = exp->addressOf(sc); + init = new ExpInitializer(loc, e); + wthis = new VarDeclaration(loc, e->type, Id::withSym, init); + wthis->semantic(sc); + sym = new WithScopeSymbol(this); + sym->parent = sc->scopesym; + } + else + { error("with expressions must be class objects, not '%s'", exp->type->toChars()); + return NULL; + } + } + sc = sc->push(sym); + + if (body) + body = body->semantic(sc); + + sc->pop(); + + return this; +} + +void WithStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("with ("); + exp->toCBuffer(buf, hgs); + buf->writestring(")\n"); + if (body) + body->toCBuffer(buf, hgs); +} + +int WithStatement::usesEH() +{ + return body ? body->usesEH() : 0; +} + +int WithStatement::blockExit(bool mustNotThrow) +{ + int result = BEnone; + if (exp->canThrow(mustNotThrow)) + result = BEthrow; + if (body) + result |= body->blockExit(mustNotThrow); + else + result |= BEfallthru; + return result; +} + + +/******************************** TryCatchStatement ***************************/ + +TryCatchStatement::TryCatchStatement(Loc loc, Statement *body, Catches *catches) + : Statement(loc) +{ + this->body = body; + this->catches = catches; +} + +Statement *TryCatchStatement::syntaxCopy() +{ + Catches *a = new Catches(); + a->setDim(catches->dim); + for (size_t i = 0; i < a->dim; i++) + { Catch *c; + + c = (*catches)[i]; + c = c->syntaxCopy(); + (*a)[i] = c; + } + TryCatchStatement *s = new TryCatchStatement(loc, body->syntaxCopy(), a); + return s; +} + +Statement *TryCatchStatement::semantic(Scope *sc) +{ + body = body->semanticScope(sc, NULL /*this*/, NULL); + + /* Even if body is NULL, still do semantic analysis on catches + */ + for (size_t i = 0; i < catches->dim; i++) + { Catch *c = (*catches)[i]; + c->semantic(sc); + + // Determine if current catch 'hides' any previous catches + for (size_t j = 0; j < i; j++) + { Catch *cj = (*catches)[j]; + char *si = c->loc.toChars(); + char *sj = cj->loc.toChars(); + + if (c->type->toBasetype()->implicitConvTo(cj->type->toBasetype())) + error("catch at %s hides catch at %s", sj, si); + } + } + + if (!body || body->isEmpty()) + { + return NULL; + } + return this; +} + +int TryCatchStatement::hasBreak() +{ + return FALSE; //TRUE; +} + +int TryCatchStatement::usesEH() +{ + return TRUE; +} + +int TryCatchStatement::blockExit(bool mustNotThrow) +{ + assert(body); + int result = body->blockExit(false); + + int catchresult = 0; + for (size_t i = 0; i < catches->dim; i++) + { + Catch *c = (*catches)[i]; + if (c->type == Type::terror) + continue; + + catchresult |= c->blockExit(mustNotThrow); + + /* If we're catching Object, then there is no throwing + */ + Identifier *id = c->type->toBasetype()->isClassHandle()->ident; + if (id == Id::Object || id == Id::Throwable || id == Id::Exception) + { + result &= ~BEthrow; + } + } + if (mustNotThrow && (result & BEthrow)) + { + body->blockExit(mustNotThrow); // now explain why this is nothrow + } + return result | catchresult; +} + + +void TryCatchStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("try"); + buf->writenl(); + if (body) + body->toCBuffer(buf, hgs); + for (size_t i = 0; i < catches->dim; i++) + { + Catch *c = (*catches)[i]; + c->toCBuffer(buf, hgs); + } +} + +/******************************** Catch ***************************/ + +Catch::Catch(Loc loc, Type *t, Identifier *id, Statement *handler) +{ + //printf("Catch(%s, loc = %s)\n", id->toChars(), loc.toChars()); + this->loc = loc; + this->type = t; + this->ident = id; + this->handler = handler; + var = NULL; + internalCatch = false; +} + +Catch *Catch::syntaxCopy() +{ + Catch *c = new Catch(loc, + (type ? type->syntaxCopy() : NULL), + ident, + (handler ? handler->syntaxCopy() : NULL)); + c->internalCatch = internalCatch; + return c; +} + +void Catch::semantic(Scope *sc) +{ ScopeDsymbol *sym; + + //printf("Catch::semantic(%s)\n", ident->toChars()); + +#ifndef IN_GCC + if (sc->tf) + { + /* This is because the _d_local_unwind() gets the stack munged + * up on this. The workaround is to place any try-catches into + * a separate function, and call that. + * To fix, have the compiler automatically convert the finally + * body into a nested function. + */ + error(loc, "cannot put catch statement inside finally block"); + } +#endif + + sym = new ScopeDsymbol(); + sym->parent = sc->scopesym; + sc = sc->push(sym); + + if (!type) + type = new TypeIdentifier(0, Id::Throwable); + type = type->semantic(loc, sc); + ClassDeclaration *cd = type->toBasetype()->isClassHandle(); + if (!cd || ((cd != ClassDeclaration::throwable) && !ClassDeclaration::throwable->isBaseOf(cd, NULL))) + { + if (type != Type::terror) + { error(loc, "can only catch class objects derived from Throwable, not '%s'", type->toChars()); + type = Type::terror; + } + } + else if (sc->func && + !sc->intypeof && + !internalCatch && + cd != ClassDeclaration::exception && + !ClassDeclaration::exception->isBaseOf(cd, NULL) && + sc->func->setUnsafe()) + { + error(loc, "can only catch class objects derived from Exception in @safe code, not '%s'", type->toChars()); + type = Type::terror; + } + else if (ident) + { + var = new VarDeclaration(loc, type, ident, NULL); + var->parent = sc->parent; + sc->insert(var); + } + handler = handler->semantic(sc); + + sc->pop(); +} + +int Catch::blockExit(bool mustNotThrow) +{ + return handler ? handler->blockExit(mustNotThrow) : BEfallthru; +} + +void Catch::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("catch"); + if (type) + { buf->writebyte('('); + type->toCBuffer(buf, ident, hgs); + buf->writebyte(')'); + } + buf->writenl(); + buf->writebyte('{'); + buf->writenl(); + if (handler) + handler->toCBuffer(buf, hgs); + buf->writebyte('}'); + buf->writenl(); +} + +/****************************** TryFinallyStatement ***************************/ + +TryFinallyStatement::TryFinallyStatement(Loc loc, Statement *body, Statement *finalbody) + : Statement(loc) +{ + this->body = body; + this->finalbody = finalbody; +} + +Statement *TryFinallyStatement::syntaxCopy() +{ + TryFinallyStatement *s = new TryFinallyStatement(loc, + body->syntaxCopy(), finalbody->syntaxCopy()); + return s; +} + +Statement *TryFinallyStatement::semantic(Scope *sc) +{ + //printf("TryFinallyStatement::semantic()\n"); + body = body->semantic(sc); + sc = sc->push(); + sc->tf = this; + sc->sbreak = NULL; + sc->scontinue = NULL; // no break or continue out of finally block + finalbody = finalbody->semanticNoScope(sc); + sc->pop(); + if (!body) + return finalbody; + if (!finalbody) + return body; + if (body->blockExit(false) == BEfallthru) + { Statement *s = new CompoundStatement(loc, body, finalbody); + return s; + } + return this; +} + +void TryFinallyStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->printf("try\n{\n"); + body->toCBuffer(buf, hgs); + buf->printf("}\nfinally\n{\n"); + finalbody->toCBuffer(buf, hgs); + buf->writeByte('}'); + buf->writenl(); +} + +int TryFinallyStatement::hasBreak() +{ + return FALSE; //TRUE; +} + +int TryFinallyStatement::hasContinue() +{ + return FALSE; //TRUE; +} + +int TryFinallyStatement::usesEH() +{ + return TRUE; +} + +int TryFinallyStatement::blockExit(bool mustNotThrow) +{ + if (body) + return body->blockExit(mustNotThrow); + return BEfallthru; +} + + +/****************************** OnScopeStatement ***************************/ + +OnScopeStatement::OnScopeStatement(Loc loc, TOK tok, Statement *statement) + : Statement(loc) +{ + this->tok = tok; + this->statement = statement; +} + +Statement *OnScopeStatement::syntaxCopy() +{ + OnScopeStatement *s = new OnScopeStatement(loc, + tok, statement->syntaxCopy()); + return s; +} + +Statement *OnScopeStatement::semantic(Scope *sc) +{ + /* semantic is called on results of scopeCode() */ + return this; +} + +int OnScopeStatement::blockExit(bool mustNotThrow) +{ // At this point, this statement is just an empty placeholder + return BEfallthru; +} + +void OnScopeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(Token::toChars(tok)); + buf->writebyte(' '); + statement->toCBuffer(buf, hgs); +} + +int OnScopeStatement::usesEH() +{ + return 1; +} + +Statement *OnScopeStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally) +{ + //printf("OnScopeStatement::scopeCode()\n"); + //print(); + *sentry = NULL; + *sexception = NULL; + *sfinally = NULL; + switch (tok) + { + case TOKon_scope_exit: + *sfinally = statement; + break; + + case TOKon_scope_failure: + *sexception = statement; + break; + + case TOKon_scope_success: + { + /* Create: + * sentry: bool x = false; + * sexception: x = true; + * sfinally: if (!x) statement; + */ + Identifier *id = Lexer::uniqueId("__os"); + + ExpInitializer *ie = new ExpInitializer(loc, new IntegerExp(0, 0, Type::tbool)); + VarDeclaration *v = new VarDeclaration(loc, Type::tbool, id, ie); + *sentry = new ExpStatement(loc, v); + + Expression *e = new IntegerExp(0, 1, Type::tbool); + e = new AssignExp(0, new VarExp(0, v), e); + *sexception = new ExpStatement(0, e); + + e = new VarExp(0, v); + e = new NotExp(0, e); + *sfinally = new IfStatement(0, NULL, e, statement, NULL); + + break; + } + + default: + assert(0); + } + return NULL; +} + +/******************************** ThrowStatement ***************************/ + +ThrowStatement::ThrowStatement(Loc loc, Expression *exp) + : Statement(loc) +{ + this->exp = exp; +} + +Statement *ThrowStatement::syntaxCopy() +{ + ThrowStatement *s = new ThrowStatement(loc, exp->syntaxCopy()); + return s; +} + +Statement *ThrowStatement::semantic(Scope *sc) +{ + //printf("ThrowStatement::semantic()\n"); + + FuncDeclaration *fd = sc->parent->isFuncDeclaration(); + fd->hasReturnExp |= 2; + +#if DMDV1 + // See bugzilla 3388. Should this be or not? + if (sc->incontract) + error("Throw statements cannot be in contracts"); +#endif + exp = exp->semantic(sc); + exp = resolveProperties(sc, exp); + if (exp->op == TOKerror) + return this; + ClassDeclaration *cd = exp->type->toBasetype()->isClassHandle(); + if (!cd || ((cd != ClassDeclaration::throwable) && !ClassDeclaration::throwable->isBaseOf(cd, NULL))) + error("can only throw class objects derived from Throwable, not type %s", exp->type->toChars()); + + return this; +} + +int ThrowStatement::blockExit(bool mustNotThrow) +{ + if (mustNotThrow) + error("%s is thrown but not caught", exp->type->toChars()); + return BEthrow; // obviously +} + + +void ThrowStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->printf("throw "); + exp->toCBuffer(buf, hgs); + buf->writeByte(';'); + buf->writenl(); +} + +/******************************** VolatileStatement **************************/ + +VolatileStatement::VolatileStatement(Loc loc, Statement *statement) + : Statement(loc) +{ + this->statement = statement; +} + +Statement *VolatileStatement::syntaxCopy() +{ + VolatileStatement *s = new VolatileStatement(loc, + statement ? statement->syntaxCopy() : NULL); + return s; +} + +Statement *VolatileStatement::semantic(Scope *sc) +{ + if (statement) + statement = statement->semantic(sc); + return this; +} + +Statements *VolatileStatement::flatten(Scope *sc) +{ + Statements *a; + + a = statement ? statement->flatten(sc) : NULL; + if (a) + { for (size_t i = 0; i < a->dim; i++) + { Statement *s = (*a)[i]; + + s = new VolatileStatement(loc, s); + (*a)[i] = s; + } + } + + return a; +} + +int VolatileStatement::blockExit(bool mustNotThrow) +{ + return statement ? statement->blockExit(mustNotThrow) : BEfallthru; +} + + +void VolatileStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("volatile"); + if (statement) + { if (statement->isScopeStatement()) + buf->writenl(); + else + buf->writebyte(' '); + statement->toCBuffer(buf, hgs); + } +} + + +/******************************** DebugStatement **************************/ + +DebugStatement::DebugStatement(Loc loc, Statement *statement) + : Statement(loc) +{ + this->statement = statement; +} + +Statement *DebugStatement::syntaxCopy() +{ + DebugStatement *s = new DebugStatement(loc, + statement ? statement->syntaxCopy() : NULL); + return s; +} + +Statement *DebugStatement::semantic(Scope *sc) +{ + if (statement) + { + sc = sc->push(); + sc->flags |= SCOPEdebug; + statement = statement->semantic(sc); + sc->pop(); + } + return statement; +} + +Statements *DebugStatement::flatten(Scope *sc) +{ + Statements *a = statement ? statement->flatten(sc) : NULL; + if (a) + { for (size_t i = 0; i < a->dim; i++) + { Statement *s = (*a)[i]; + + s = new DebugStatement(loc, s); + (*a)[i] = s; + } + } + + return a; +} + +void DebugStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + if (statement) + { + statement->toCBuffer(buf, hgs); + } +} + + +/******************************** GotoStatement ***************************/ + +GotoStatement::GotoStatement(Loc loc, Identifier *ident) + : Statement(loc) +{ + this->ident = ident; + this->label = NULL; + this->tf = NULL; +} + +Statement *GotoStatement::syntaxCopy() +{ + GotoStatement *s = new GotoStatement(loc, ident); + return s; +} + +Statement *GotoStatement::semantic(Scope *sc) +{ FuncDeclaration *fd = sc->parent->isFuncDeclaration(); + + //printf("GotoStatement::semantic()\n"); + tf = sc->tf; + label = fd->searchLabel(ident); + if (!label->statement && sc->fes) + { + /* Either the goto label is forward referenced or it + * is in the function that the enclosing foreach is in. + * Can't know yet, so wrap the goto in a compound statement + * so we can patch it later, and add it to a 'look at this later' + * list. + */ + Statements *a = new Statements(); + CompoundStatement *s; + + a->push(this); + s = new CompoundStatement(loc, a); + sc->fes->gotos->push(s); // 'look at this later' list + return s; + } + if (label->statement && label->statement->tf != sc->tf) + error("cannot goto in or out of finally block"); + return this; +} + +int GotoStatement::blockExit(bool mustNotThrow) +{ + //printf("GotoStatement::blockExit(%p)\n", this); + return BEgoto; +} + + +void GotoStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("goto "); + buf->writestring(ident->toChars()); + buf->writebyte(';'); + buf->writenl(); +} + +/******************************** LabelStatement ***************************/ + +LabelStatement::LabelStatement(Loc loc, Identifier *ident, Statement *statement) + : Statement(loc) +{ + this->ident = ident; + this->statement = statement; + this->tf = NULL; + this->lblock = NULL; + this->fwdrefs = NULL; +} + +Statement *LabelStatement::syntaxCopy() +{ + LabelStatement *s = new LabelStatement(loc, ident, statement->syntaxCopy()); + return s; +} + +Statement *LabelStatement::semantic(Scope *sc) +{ LabelDsymbol *ls; + FuncDeclaration *fd = sc->parent->isFuncDeclaration(); + + //printf("LabelStatement::semantic()\n"); + ls = fd->searchLabel(ident); + if (ls->statement) + error("Label '%s' already defined", ls->toChars()); + else + ls->statement = this; + tf = sc->tf; + sc = sc->push(); + sc->scopesym = sc->enclosing->scopesym; + sc->callSuper |= CSXlabel; + sc->slabel = this; + if (statement) + statement = statement->semanticNoScope(sc); + sc->pop(); + return this; +} + +Statements *LabelStatement::flatten(Scope *sc) +{ + Statements *a = NULL; + + if (statement) + { + a = statement->flatten(sc); + if (a) + { + if (!a->dim) + { + a->push(new ExpStatement(loc, (Expression *)NULL)); + } + Statement *s = (*a)[0]; + + s = new LabelStatement(loc, ident, s); + (*a)[0] = s; + } + } + + return a; +} + + +int LabelStatement::usesEH() +{ + return statement ? statement->usesEH() : FALSE; +} + +int LabelStatement::blockExit(bool mustNotThrow) +{ + //printf("LabelStatement::blockExit(%p)\n", this); + return statement ? statement->blockExit(mustNotThrow) : BEfallthru; +} + + +int LabelStatement::comeFrom() +{ + //printf("LabelStatement::comeFrom()\n"); + return TRUE; +} + +void LabelStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(ident->toChars()); + buf->writebyte(':'); + buf->writenl(); + if (statement) + statement->toCBuffer(buf, hgs); +} + + +/******************************** LabelDsymbol ***************************/ + +LabelDsymbol::LabelDsymbol(Identifier *ident) + : Dsymbol(ident) +{ + statement = NULL; +#if IN_GCC + asmLabelNum = 0; +#endif +} + +LabelDsymbol *LabelDsymbol::isLabel() // is this a LabelDsymbol()? +{ + return this; +} + + +/************************ AsmStatement ***************************************/ + +AsmStatement::AsmStatement(Loc loc, Token *tokens) + : Statement(loc) +{ + this->tokens = tokens; + asmcode = NULL; + asmalign = 0; + refparam = FALSE; + naked = FALSE; + regs = 0; +} + +Statement *AsmStatement::syntaxCopy() +{ + return new AsmStatement(loc, tokens); +} + + + +int AsmStatement::comeFrom() +{ + return TRUE; +} + +int AsmStatement::blockExit(bool mustNotThrow) +{ + if (mustNotThrow) + error("asm statements are assumed to throw", toChars()); + // Assume the worst + return BEfallthru | BEthrow | BEreturn | BEgoto | BEhalt; +} + +void AsmStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("asm { "); + Token *t = tokens; + while (t) + { + buf->writestring(t->toChars()); + if (t->next && + t->value != TOKmin && + t->value != TOKcomma && + t->next->value != TOKcomma && + t->value != TOKlbracket && + t->next->value != TOKlbracket && + t->next->value != TOKrbracket && + t->value != TOKlparen && + t->next->value != TOKlparen && + t->next->value != TOKrparen && + t->value != TOKdot && + t->next->value != TOKdot) + { + buf->writebyte(' '); + } + t = t->next; + } + buf->writestring("; }"); + buf->writenl(); +} + +/************************ ImportStatement ***************************************/ + +ImportStatement::ImportStatement(Loc loc, Dsymbols *imports) + : Statement(loc) +{ + this->imports = imports; +} + +Statement *ImportStatement::syntaxCopy() +{ + Dsymbols *m = new Dsymbols(); + m->setDim(imports->dim); + for (size_t i = 0; i < imports->dim; i++) + { Dsymbol *s = (*imports)[i]; + (*m)[i] = s->syntaxCopy(NULL); + } + return new ImportStatement(loc, m); +} + +Statement *ImportStatement::semantic(Scope *sc) +{ + for (size_t i = 0; i < imports->dim; i++) + { Dsymbol *s = (*imports)[i]; + s->semantic(sc); + sc->insert(s); + } + return this; +} + +int ImportStatement::blockExit(bool mustNotThrow) +{ + return BEfallthru; +} + +int ImportStatement::isEmpty() +{ + return TRUE; +} + +void ImportStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + for (size_t i = 0; i < imports->dim; i++) + { Dsymbol *s = (*imports)[i]; + s->toCBuffer(buf, hgs); + } +} diff --git a/statement.h b/statement.h new file mode 100644 index 00000000..6ce93d1d --- /dev/null +++ b/statement.h @@ -0,0 +1,904 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_STATEMENT_H +#define DMD_STATEMENT_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "root.h" + +#include "arraytypes.h" +#include "dsymbol.h" +#include "lexer.h" + +struct OutBuffer; +struct Scope; +struct Expression; +struct LabelDsymbol; +struct Identifier; +struct IfStatement; +struct ExpStatement; +struct DefaultStatement; +struct VarDeclaration; +struct Condition; +struct Module; +struct Token; +struct InlineCostState; +struct InlineDoState; +struct InlineScanState; +struct ReturnStatement; +struct CompoundStatement; +struct Parameter; +struct StaticAssert; +struct AsmStatement; +struct GotoStatement; +struct ScopeStatement; +struct TryCatchStatement; +struct TryFinallyStatement; +struct CaseStatement; +struct DefaultStatement; +struct LabelStatement; +struct HdrGenState; +struct InterState; + +enum TOK; + +// Back end +struct IRState; +struct Blockx; +#if IN_GCC +union tree_node; typedef union tree_node block; +union tree_node; typedef union tree_node elem; +#else +struct block; +struct elem; +#endif +struct code; + +/* How a statement exits; this is returned by blockExit() + */ +enum BE +{ + BEnone = 0, + BEfallthru = 1, + BEthrow = 2, + BEreturn = 4, + BEgoto = 8, + BEhalt = 0x10, + BEbreak = 0x20, + BEcontinue = 0x40, + BEany = (BEfallthru | BEthrow | BEreturn | BEgoto | BEhalt), +}; + +struct Statement : Object +{ + Loc loc; + + Statement(Loc loc); + virtual Statement *syntaxCopy(); + + void print(); + char *toChars(); + + void error(const char *format, ...); + void warning(const char *format, ...); + virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + int incontract; + virtual ScopeStatement *isScopeStatement() { return NULL; } + virtual Statement *semantic(Scope *sc); + Statement *semanticScope(Scope *sc, Statement *sbreak, Statement *scontinue); + Statement *semanticNoScope(Scope *sc); + virtual int hasBreak(); + virtual int hasContinue(); + virtual int usesEH(); + virtual int blockExit(bool mustNotThrow); + virtual int comeFrom(); + virtual int isEmpty(); + virtual Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally); + virtual Statements *flatten(Scope *sc); + virtual Expression *interpret(InterState *istate); + virtual Statement *last(); + + virtual int inlineCost(InlineCostState *ics); + virtual Expression *doInline(InlineDoState *ids); + virtual Statement *doInlineStatement(InlineDoState *ids); + virtual Statement *inlineScan(InlineScanState *iss); + + // Back end + virtual void toIR(IRState *irs); + + // Avoid dynamic_cast + virtual ExpStatement *isExpStatement() { return NULL; } + virtual CompoundStatement *isCompoundStatement() { return NULL; } + virtual ReturnStatement *isReturnStatement() { return NULL; } + virtual IfStatement *isIfStatement() { return NULL; } + virtual CaseStatement *isCaseStatement() { return NULL; } + virtual DefaultStatement *isDefaultStatement() { return NULL; } + virtual LabelStatement *isLabelStatement() { return NULL; } +}; + +struct PeelStatement : Statement +{ + Statement *s; + + PeelStatement(Statement *s); + Statement *semantic(Scope *sc); +}; + +struct ExpStatement : Statement +{ + Expression *exp; + + ExpStatement(Loc loc, Expression *exp); + ExpStatement(Loc loc, Dsymbol *s); + Statement *syntaxCopy(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Statement *semantic(Scope *sc); + Expression *interpret(InterState *istate); + int blockExit(bool mustNotThrow); + int isEmpty(); + Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally); + + int inlineCost(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + Statement *doInlineStatement(InlineDoState *ids); + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); + + ExpStatement *isExpStatement() { return this; } +}; + +struct DtorExpStatement : ExpStatement +{ + /* Wraps an expression that is the destruction of 'var' + */ + + VarDeclaration *var; + + DtorExpStatement(Loc loc, Expression *exp, VarDeclaration *v); + Statement *syntaxCopy(); + void toIR(IRState *irs); +}; + +struct CompileStatement : Statement +{ + Expression *exp; + + CompileStatement(Loc loc, Expression *exp); + Statement *syntaxCopy(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Statements *flatten(Scope *sc); + Statement *semantic(Scope *sc); + int blockExit(bool mustNotThrow); +}; + +struct CompoundStatement : Statement +{ + Statements *statements; + + CompoundStatement(Loc loc, Statements *s); + CompoundStatement(Loc loc, Statement *s1); + CompoundStatement(Loc loc, Statement *s1, Statement *s2); + Statement *syntaxCopy(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Statement *semantic(Scope *sc); + int usesEH(); + int blockExit(bool mustNotThrow); + int comeFrom(); + int isEmpty(); + Statements *flatten(Scope *sc); + ReturnStatement *isReturnStatement(); + Expression *interpret(InterState *istate); + Statement *last(); + + int inlineCost(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + Statement *doInlineStatement(InlineDoState *ids); + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); + + CompoundStatement *isCompoundStatement() { return this; } +}; + +struct CompoundDeclarationStatement : CompoundStatement +{ + CompoundDeclarationStatement(Loc loc, Statements *s); + Statement *syntaxCopy(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +/* The purpose of this is so that continue will go to the next + * of the statements, and break will go to the end of the statements. + */ +struct UnrolledLoopStatement : Statement +{ + Statements *statements; + + UnrolledLoopStatement(Loc loc, Statements *statements); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int hasBreak(); + int hasContinue(); + int usesEH(); + int blockExit(bool mustNotThrow); + int comeFrom(); + Expression *interpret(InterState *istate); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + int inlineCost(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + Statement *doInlineStatement(InlineDoState *ids); + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct ScopeStatement : Statement +{ + Statement *statement; + + ScopeStatement(Loc loc, Statement *s); + Statement *syntaxCopy(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + ScopeStatement *isScopeStatement() { return this; } + Statement *semantic(Scope *sc); + int hasBreak(); + int hasContinue(); + int usesEH(); + int blockExit(bool mustNotThrow); + int comeFrom(); + int isEmpty(); + Expression *interpret(InterState *istate); + + int inlineCost(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + Statement *doInlineStatement(InlineDoState *ids); + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct WhileStatement : Statement +{ + Expression *condition; + Statement *body; + + WhileStatement(Loc loc, Expression *c, Statement *b); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int hasBreak(); + int hasContinue(); + int usesEH(); + int blockExit(bool mustNotThrow); + int comeFrom(); + Expression *interpret(InterState *istate); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct DoStatement : Statement +{ + Statement *body; + Expression *condition; + + DoStatement(Loc loc, Statement *b, Expression *c); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int hasBreak(); + int hasContinue(); + int usesEH(); + int blockExit(bool mustNotThrow); + int comeFrom(); + Expression *interpret(InterState *istate); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct ForStatement : Statement +{ + Statement *init; + Expression *condition; + Expression *increment; + Statement *body; + + ForStatement(Loc loc, Statement *init, Expression *condition, Expression *increment, Statement *body); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally); + int hasBreak(); + int hasContinue(); + int usesEH(); + int blockExit(bool mustNotThrow); + int comeFrom(); + Expression *interpret(InterState *istate); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + int inlineCost(InlineCostState *ics); + Statement *inlineScan(InlineScanState *iss); + Statement *doInlineStatement(InlineDoState *ids); + + void toIR(IRState *irs); +}; + +struct ForeachStatement : Statement +{ + enum TOK op; // TOKforeach or TOKforeach_reverse + Parameters *arguments; // array of Parameter*'s + Expression *aggr; + Statement *body; + + VarDeclaration *key; + VarDeclaration *value; + + FuncDeclaration *func; // function we're lexically in + + Statements *cases; // put breaks, continues, gotos and returns here + CompoundStatements *gotos; // forward referenced goto's go here + + ForeachStatement(Loc loc, enum TOK op, Parameters *arguments, Expression *aggr, Statement *body); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + bool checkForArgTypes(); + int inferAggregate(Scope *sc, Dsymbol *&sapply); + int inferApplyArgTypes(Scope *sc, Dsymbol *&sapply); + int hasBreak(); + int hasContinue(); + int usesEH(); + int blockExit(bool mustNotThrow); + int comeFrom(); + Expression *interpret(InterState *istate); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +#if DMDV2 +struct ForeachRangeStatement : Statement +{ + enum TOK op; // TOKforeach or TOKforeach_reverse + Parameter *arg; // loop index variable + Expression *lwr; + Expression *upr; + Statement *body; + + VarDeclaration *key; + + ForeachRangeStatement(Loc loc, enum TOK op, Parameter *arg, + Expression *lwr, Expression *upr, Statement *body); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int hasBreak(); + int hasContinue(); + int usesEH(); + int blockExit(bool mustNotThrow); + int comeFrom(); + Expression *interpret(InterState *istate); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; +#endif + +struct IfStatement : Statement +{ + Parameter *arg; + Expression *condition; + Statement *ifbody; + Statement *elsebody; + + VarDeclaration *match; // for MatchExpression results + + IfStatement(Loc loc, Parameter *arg, Expression *condition, Statement *ifbody, Statement *elsebody); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + Expression *interpret(InterState *istate); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + int usesEH(); + int blockExit(bool mustNotThrow); + IfStatement *isIfStatement() { return this; } + + int inlineCost(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + Statement *doInlineStatement(InlineDoState *ids); + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct ConditionalStatement : Statement +{ + Condition *condition; + Statement *ifbody; + Statement *elsebody; + + ConditionalStatement(Loc loc, Condition *condition, Statement *ifbody, Statement *elsebody); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + Statements *flatten(Scope *sc); + int usesEH(); + int blockExit(bool mustNotThrow); + + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct PragmaStatement : Statement +{ + Identifier *ident; + Expressions *args; // array of Expression's + Statement *body; + + PragmaStatement(Loc loc, Identifier *ident, Expressions *args, Statement *body); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int usesEH(); + int blockExit(bool mustNotThrow); + + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + void toIR(IRState *irs); +}; + +struct StaticAssertStatement : Statement +{ + StaticAssert *sa; + + StaticAssertStatement(StaticAssert *sa); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int blockExit(bool mustNotThrow); + + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct SwitchStatement : Statement +{ + Expression *condition; + Statement *body; + bool isFinal; + + DefaultStatement *sdefault; + TryFinallyStatement *tf; + GotoCaseStatements gotoCases; // array of unresolved GotoCaseStatement's + CaseStatements *cases; // array of CaseStatement's + int hasNoDefault; // !=0 if no default statement + int hasVars; // !=0 if has variable case values + + SwitchStatement(Loc loc, Expression *c, Statement *b, bool isFinal); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int hasBreak(); + int usesEH(); + int blockExit(bool mustNotThrow); + Expression *interpret(InterState *istate); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct CaseStatement : Statement +{ + Expression *exp; + Statement *statement; + + int index; // which case it is (since we sort this) + block *cblock; // back end: label for the block + + CaseStatement(Loc loc, Expression *exp, Statement *s); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int compare(Object *obj); + int usesEH(); + int blockExit(bool mustNotThrow); + int comeFrom(); + Expression *interpret(InterState *istate); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + CaseStatement *isCaseStatement() { return this; } + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +#if DMDV2 + +struct CaseRangeStatement : Statement +{ + Expression *first; + Expression *last; + Statement *statement; + + CaseRangeStatement(Loc loc, Expression *first, Expression *last, Statement *s); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +#endif + +struct DefaultStatement : Statement +{ + Statement *statement; +#if IN_GCC + block *cblock; // back end: label for the block +#endif + + DefaultStatement(Loc loc, Statement *s); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int usesEH(); + int blockExit(bool mustNotThrow); + int comeFrom(); + Expression *interpret(InterState *istate); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + DefaultStatement *isDefaultStatement() { return this; } + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct GotoDefaultStatement : Statement +{ + SwitchStatement *sw; + + GotoDefaultStatement(Loc loc); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + Expression *interpret(InterState *istate); + int blockExit(bool mustNotThrow); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + void toIR(IRState *irs); +}; + +struct GotoCaseStatement : Statement +{ + Expression *exp; // NULL, or which case to goto + CaseStatement *cs; // case statement it resolves to + + GotoCaseStatement(Loc loc, Expression *exp); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + Expression *interpret(InterState *istate); + int blockExit(bool mustNotThrow); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + void toIR(IRState *irs); +}; + +struct SwitchErrorStatement : Statement +{ + SwitchErrorStatement(Loc loc); + int blockExit(bool mustNotThrow); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + void toIR(IRState *irs); +}; + +struct ReturnStatement : Statement +{ + Expression *exp; + + ReturnStatement(Loc loc, Expression *exp); + Statement *syntaxCopy(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Statement *semantic(Scope *sc); + int blockExit(bool mustNotThrow); + Expression *interpret(InterState *istate); + + int inlineCost(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + Statement *doInlineStatement(InlineDoState *ids); + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); + + ReturnStatement *isReturnStatement() { return this; } +}; + +struct BreakStatement : Statement +{ + Identifier *ident; + + BreakStatement(Loc loc, Identifier *ident); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + Expression *interpret(InterState *istate); + int blockExit(bool mustNotThrow); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + void toIR(IRState *irs); +}; + +struct ContinueStatement : Statement +{ + Identifier *ident; + + ContinueStatement(Loc loc, Identifier *ident); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + Expression *interpret(InterState *istate); + int blockExit(bool mustNotThrow); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + void toIR(IRState *irs); +}; + +struct SynchronizedStatement : Statement +{ + Expression *exp; + Statement *body; + + SynchronizedStatement(Loc loc, Expression *exp, Statement *body); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int hasBreak(); + int hasContinue(); + int usesEH(); + int blockExit(bool mustNotThrow); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + Statement *inlineScan(InlineScanState *iss); + +// Back end + elem *esync; + SynchronizedStatement(Loc loc, elem *esync, Statement *body); + void toIR(IRState *irs); +}; + +struct WithStatement : Statement +{ + Expression *exp; + Statement *body; + VarDeclaration *wthis; + + WithStatement(Loc loc, Expression *exp, Statement *body); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + int usesEH(); + int blockExit(bool mustNotThrow); + Expression *interpret(InterState *istate); + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct TryCatchStatement : Statement +{ + Statement *body; + Catches *catches; + + TryCatchStatement(Loc loc, Statement *body, Catches *catches); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int hasBreak(); + int usesEH(); + int blockExit(bool mustNotThrow); + Expression *interpret(InterState *istate); + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct Catch : Object +{ + Loc loc; + Type *type; + Identifier *ident; + VarDeclaration *var; + Statement *handler; + bool internalCatch; + + Catch(Loc loc, Type *t, Identifier *id, Statement *handler); + Catch *syntaxCopy(); + void semantic(Scope *sc); + int blockExit(bool mustNotThrow); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct TryFinallyStatement : Statement +{ + Statement *body; + Statement *finalbody; + + TryFinallyStatement(Loc loc, Statement *body, Statement *finalbody); + Statement *syntaxCopy(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Statement *semantic(Scope *sc); + int hasBreak(); + int hasContinue(); + int usesEH(); + int blockExit(bool mustNotThrow); + Expression *interpret(InterState *istate); + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct OnScopeStatement : Statement +{ + TOK tok; + Statement *statement; + + OnScopeStatement(Loc loc, TOK tok, Statement *statement); + Statement *syntaxCopy(); + int blockExit(bool mustNotThrow); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Statement *semantic(Scope *sc); + int usesEH(); + Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally); + Expression *interpret(InterState *istate); + + void toIR(IRState *irs); +}; + +struct ThrowStatement : Statement +{ + Expression *exp; + + ThrowStatement(Loc loc, Expression *exp); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + int blockExit(bool mustNotThrow); + Expression *interpret(InterState *istate); + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct VolatileStatement : Statement +{ + Statement *statement; + + VolatileStatement(Loc loc, Statement *statement); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + Statements *flatten(Scope *sc); + int blockExit(bool mustNotThrow); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct DebugStatement : Statement +{ + Statement *statement; + + DebugStatement(Loc loc, Statement *statement); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + Statements *flatten(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct GotoStatement : Statement +{ + Identifier *ident; + LabelDsymbol *label; + TryFinallyStatement *tf; + + GotoStatement(Loc loc, Identifier *ident); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int blockExit(bool mustNotThrow); + Expression *interpret(InterState *istate); + + void toIR(IRState *irs); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +struct LabelStatement : Statement +{ + Identifier *ident; + Statement *statement; + TryFinallyStatement *tf; + block *lblock; // back end + + Blocks *fwdrefs; // forward references to this LabelStatement + + LabelStatement(Loc loc, Identifier *ident, Statement *statement); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + Statements *flatten(Scope *sc); + int usesEH(); + int blockExit(bool mustNotThrow); + int comeFrom(); + Expression *interpret(InterState *istate); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + Statement *inlineScan(InlineScanState *iss); + LabelStatement *isLabelStatement() { return this; } + + void toIR(IRState *irs); +}; + +struct LabelDsymbol : Dsymbol +{ + LabelStatement *statement; +#if IN_GCC + unsigned asmLabelNum; // GCC-specific +#endif + + LabelDsymbol(Identifier *ident); + LabelDsymbol *isLabel(); +}; + +struct AsmStatement : Statement +{ + Token *tokens; + code *asmcode; + unsigned asmalign; // alignment of this statement + unsigned regs; // mask of registers modified (must match regm_t in back end) + unsigned char refparam; // !=0 if function parameter is referenced + unsigned char naked; // !=0 if function is to be naked + + AsmStatement(Loc loc, Token *tokens); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int blockExit(bool mustNotThrow); + int comeFrom(); + Expression *interpret(InterState *istate); + + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + //int inlineCost(InlineCostState *ics); + //Expression *doInline(InlineDoState *ids); + //Statement *inlineScan(InlineScanState *iss); + + void toIR(IRState *irs); +}; + +struct ImportStatement : Statement +{ + Dsymbols *imports; // Array of Import's + + ImportStatement(Loc loc, Dsymbols *imports); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int blockExit(bool mustNotThrow); + int isEmpty(); + Expression *interpret(InterState *istate); + + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + int inlineCost(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + Statement *doInlineStatement(InlineDoState *ids); + + void toIR(IRState *irs); +}; + +#endif /* DMD_STATEMENT_H */ diff --git a/staticassert.c b/staticassert.c new file mode 100644 index 00000000..4d8bfa42 --- /dev/null +++ b/staticassert.c @@ -0,0 +1,126 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// http://www.dsource.org/projects/dmd/browser/trunk/src/staticassert.c +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include + +#include "dsymbol.h" +#include "staticassert.h" +#include "expression.h" +#include "id.h" +#include "hdrgen.h" +#include "scope.h" +#include "template.h" +#include "declaration.h" + + +/********************************* AttribDeclaration ****************************/ + +StaticAssert::StaticAssert(Loc loc, Expression *exp, Expression *msg) + : Dsymbol(Id::empty) +{ + this->loc = loc; + this->exp = exp; + this->msg = msg; +} + +Dsymbol *StaticAssert::syntaxCopy(Dsymbol *s) +{ + StaticAssert *sa; + + assert(!s); + sa = new StaticAssert(loc, exp->syntaxCopy(), msg ? msg->syntaxCopy() : NULL); + return sa; +} + +int StaticAssert::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +{ + return 0; // we didn't add anything +} + +void StaticAssert::semantic(Scope *sc) +{ +} + +void StaticAssert::semantic2(Scope *sc) +{ + //printf("StaticAssert::semantic2() %s\n", toChars()); + ScopeDsymbol *sd = new ScopeDsymbol(); + sc = sc->push(sd); + sc->flags |= SCOPEstaticassert; + Expression *e = exp->semantic(sc); + sc = sc->pop(); + if (e->type == Type::terror) + return; + unsigned olderrs = global.errors; + e = e->optimize(WANTvalue | WANTinterpret); + if (global.errors != olderrs) + { + errorSupplemental(loc, "while evaluating: static assert(%s)", exp->toChars()); + } + else if (e->isBool(FALSE)) + { + if (msg) + { HdrGenState hgs; + OutBuffer buf; + + msg = msg->semantic(sc); + msg = msg->optimize(WANTvalue | WANTinterpret); + hgs.console = 1; + msg->toCBuffer(&buf, &hgs); + error("%s", buf.toChars()); + } + else + error("(%s) is false", exp->toChars()); + if (sc->tinst) + sc->tinst->printInstantiationTrace(); + if (!global.gag) + fatal(); + } + else if (!e->isBool(TRUE)) + { + error("(%s) is not evaluatable at compile time", exp->toChars()); + } +} + +int StaticAssert::oneMember(Dsymbol **ps, Identifier *ident) +{ + //printf("StaticAssert::oneMember())\n"); + *ps = NULL; + return TRUE; +} + +void StaticAssert::inlineScan() +{ +} + +void StaticAssert::toObjFile(int multiobj) +{ +} + +const char *StaticAssert::kind() +{ + return "static assert"; +} + +void StaticAssert::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(kind()); + buf->writeByte('('); + exp->toCBuffer(buf, hgs); + if (msg) + { + buf->writeByte(','); + msg->toCBuffer(buf, hgs); + } + buf->writestring(");"); + buf->writenl(); +} diff --git a/staticassert.h b/staticassert.h new file mode 100644 index 00000000..8d64416c --- /dev/null +++ b/staticassert.h @@ -0,0 +1,41 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_STATICASSERT_H +#define DMD_STATICASSERT_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "dsymbol.h" + +struct Expression; +struct HdrGenState; + +struct StaticAssert : Dsymbol +{ + Expression *exp; + Expression *msg; + + StaticAssert(Loc loc, Expression *exp, Expression *msg); + + Dsymbol *syntaxCopy(Dsymbol *s); + int addMember(Scope *sc, ScopeDsymbol *sd, int memnum); + void semantic(Scope *sc); + void semantic2(Scope *sc); + void inlineScan(); + int oneMember(Dsymbol **ps, Identifier *ident); + void toObjFile(int multiobj); + const char *kind(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + +#endif diff --git a/struct.c b/struct.c new file mode 100644 index 00000000..3a5bb4ea --- /dev/null +++ b/struct.c @@ -0,0 +1,717 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "root.h" +#include "aggregate.h" +#include "scope.h" +#include "mtype.h" +#include "declaration.h" +#include "module.h" +#include "id.h" +#include "statement.h" +#include "template.h" + +FuncDeclaration *StructDeclaration::xerreq; // object.xopEquals + +/********************************* AggregateDeclaration ****************************/ + +AggregateDeclaration::AggregateDeclaration(Loc loc, Identifier *id) + : ScopeDsymbol(id) +{ + this->loc = loc; + + storage_class = 0; + protection = PROTpublic; + type = NULL; + handle = NULL; + structsize = 0; // size of struct + alignsize = 0; // size of struct for alignment purposes + structalign = 0; // struct member alignment in effect + hasUnions = 0; + sizeok = 0; // size not determined yet + deferred = NULL; + isdeprecated = false; + inv = NULL; + aggNew = NULL; + aggDelete = NULL; + + stag = NULL; + sinit = NULL; + isnested = 0; + vthis = NULL; + +#if DMDV2 + ctor = NULL; + defaultCtor = NULL; + aliasthis = NULL; + noDefaultCtor = FALSE; +#endif + dtor = NULL; +} + +enum PROT AggregateDeclaration::prot() +{ + return protection; +} + +void AggregateDeclaration::semantic2(Scope *sc) +{ + //printf("AggregateDeclaration::semantic2(%s)\n", toChars()); + if (scope && members) + { error("has forward references"); + return; + } + if (members) + { + sc = sc->push(this); + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->semantic2(sc); + } + sc->pop(); + } +} + +void AggregateDeclaration::semantic3(Scope *sc) +{ + //printf("AggregateDeclaration::semantic3(%s)\n", toChars()); + if (members) + { + sc = sc->push(this); + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->semantic3(sc); + } + sc->pop(); + } +} + +void AggregateDeclaration::inlineScan() +{ + //printf("AggregateDeclaration::inlineScan(%s)\n", toChars()); + if (members) + { + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + //printf("inline scan aggregate symbol '%s'\n", s->toChars()); + s->inlineScan(); + } + } +} + +unsigned AggregateDeclaration::size(Loc loc) +{ + //printf("AggregateDeclaration::size() %s, scope = %p\n", toChars(), scope); + if (!members) + error(loc, "unknown size"); + if (sizeok != 1 && scope) + semantic(NULL); + if (sizeok != 1) + { error(loc, "no size yet for forward reference"); + //*(char*)0=0; + } + return structsize; +} + +Type *AggregateDeclaration::getType() +{ + return type; +} + +int AggregateDeclaration::isDeprecated() +{ + return isdeprecated; +} + +int AggregateDeclaration::isExport() +{ + return protection == PROTexport; +} + +/**************************** + * Do byte or word alignment as necessary. + * Align sizes of 0, as we may not know array sizes yet. + */ + +void AggregateDeclaration::alignmember( + unsigned salign, // struct alignment that is in effect + unsigned size, // alignment requirement of field + unsigned *poffset) +{ + //printf("salign = %d, size = %d, offset = %d\n",salign,size,offset); + if (salign > 1) + { + assert(size != 3); + unsigned sa = size; + if (sa == 0 || salign < sa) + sa = salign; + *poffset = (*poffset + sa - 1) & ~(sa - 1); + } + //printf("result = %d\n",offset); +} + + +void AggregateDeclaration::addField(Scope *sc, VarDeclaration *v) +{ + unsigned memsize; // size of member + unsigned memalignsize; // size of member for alignment purposes + unsigned xalign; // alignment boundaries + + //printf("AggregateDeclaration::addField('%s') %s\n", v->toChars(), toChars()); + assert(!(v->storage_class & (STCstatic | STCextern | STCparameter | STCtls))); + + // Check for forward referenced types which will fail the size() call + Type *t = v->type->toBasetype(); + if (v->storage_class & STCref) + { // References are the size of a pointer + t = Type::tvoidptr; + } + if (t->ty == Tstruct /*&& isStructDeclaration()*/) + { TypeStruct *ts = (TypeStruct *)t; +#if DMDV2 + if (ts->sym == this) + { + error("cannot have field %s with same struct type", v->toChars()); + } +#endif + + if (ts->sym->sizeok != 1 && ts->sym->scope) + ts->sym->semantic(NULL); + if (ts->sym->sizeok != 1) + { + sizeok = 2; // cannot finish; flag as forward referenced + return; + } + } + if (t->ty == Tident) + { + sizeok = 2; // cannot finish; flag as forward referenced + return; + } + + memsize = t->size(loc); + memalignsize = t->alignsize(); + xalign = t->memalign(sc->structalign); +#if 0 + alignmember(xalign, memalignsize, &sc->offset); + v->offset = sc->offset; + sc->offset += memsize; + if (sc->offset > structsize) + structsize = sc->offset; +#else + unsigned ofs = sc->offset; + alignmember(xalign, memalignsize, &ofs); + v->offset = ofs; + ofs += memsize; + if (ofs > structsize) + structsize = ofs; + if (!isUnionDeclaration()) + sc->offset = ofs; +#endif + if (global.params.is64bit && sc->structalign == 8 && memalignsize == 16) + /* Not sure how to handle this */ + ; + else if (sc->structalign < memalignsize) + memalignsize = sc->structalign; + if (alignsize < memalignsize) + alignsize = memalignsize; + //printf("\t%s: alignsize = %d\n", toChars(), alignsize); + + v->storage_class |= STCfield; + //printf(" addField '%s' to '%s' at offset %d, size = %d\n", v->toChars(), toChars(), v->offset, memsize); + fields.push(v); +} + + +/**************************************** + * Returns !=0 if there's an extra member which is the 'this' + * pointer to the enclosing context (enclosing aggregate or function) + */ + +int AggregateDeclaration::isNested() +{ + return isnested; +} + +/**************************************** + * If field[indx] is not part of a union, return indx. + * Otherwise, return the lowest field index of the union. + */ +int AggregateDeclaration::firstFieldInUnion(int indx) +{ + if (isUnionDeclaration()) + return 0; + VarDeclaration * vd = fields.tdata()[indx]; + int firstNonZero = indx; // first index in the union with non-zero size + for (; ;) + { + if (indx == 0) + return firstNonZero; + VarDeclaration * v = fields.tdata()[indx - 1]; + if (v->offset != vd->offset) + return firstNonZero; + --indx; + /* If it is a zero-length field, it's ambiguous: we don't know if it is + * in the union unless we find an earlier non-zero sized field with the + * same offset. + */ + if (v->size(loc) != 0) + firstNonZero = indx; + } +} + +/**************************************** + * Count the number of fields starting at firstIndex which are part of the + * same union as field[firstIndex]. If not a union, return 1. + */ +int AggregateDeclaration::numFieldsInUnion(int firstIndex) +{ + VarDeclaration * vd = fields.tdata()[firstIndex]; + /* If it is a zero-length field, AND we can't find an earlier non-zero + * sized field with the same offset, we assume it's not part of a union. + */ + if (vd->size(loc) == 0 && !isUnionDeclaration() && + firstFieldInUnion(firstIndex) == firstIndex) + return 1; + int count = 1; + for (size_t i = firstIndex+1; i < fields.dim; ++i) + { + VarDeclaration * v = fields.tdata()[i]; + // If offsets are different, they are not in the same union + if (v->offset != vd->offset) + break; + ++count; + } + return count; +} + +/********************************* StructDeclaration ****************************/ + +StructDeclaration::StructDeclaration(Loc loc, Identifier *id) + : AggregateDeclaration(loc, id) +{ + zeroInit = 0; // assume false until we do semantic processing +#if DMDV2 + hasIdentityAssign = 0; + hasIdentityEquals = 0; + cpctor = NULL; + postblit = NULL; + + xeq = NULL; +#endif + + // For forward references + type = new TypeStruct(this); +} + +Dsymbol *StructDeclaration::syntaxCopy(Dsymbol *s) +{ + StructDeclaration *sd; + + if (s) + sd = (StructDeclaration *)s; + else + sd = new StructDeclaration(loc, ident); + ScopeDsymbol::syntaxCopy(sd); + return sd; +} + +void StructDeclaration::semantic(Scope *sc) +{ + Scope *sc2; + + //printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", this, parent->toChars(), toChars(), sizeok); + + //static int count; if (++count == 20) halt(); + + assert(type); + if (!members) // if forward reference + return; + + if (symtab) + { if (sizeok == 1 || !scope) + { //printf("already completed\n"); + scope = NULL; + return; // semantic() already completed + } + } + else + symtab = new DsymbolTable(); + + Scope *scx = NULL; + if (scope) + { sc = scope; + scx = scope; // save so we don't make redundant copies + scope = NULL; + } + + int errors = global.gaggedErrors; + + unsigned dprogress_save = Module::dprogress; + + parent = sc->parent; + type = type->semantic(loc, sc); +#if STRUCTTHISREF + handle = type; +#else + handle = type->pointerTo(); +#endif + structalign = sc->structalign; + protection = sc->protection; + storage_class |= sc->stc; + if (sc->stc & STCdeprecated) + isdeprecated = true; + assert(!isAnonymous()); + if (sc->stc & STCabstract) + error("structs, unions cannot be abstract"); +#if DMDV2 + if (storage_class & STCimmutable) + type = type->addMod(MODimmutable); + if (storage_class & STCconst) + type = type->addMod(MODconst); + if (storage_class & STCshared) + type = type->addMod(MODshared); +#endif + + if (sizeok == 0) // if not already done the addMember step + { + int hasfunctions = 0; + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + //printf("adding member '%s' to '%s'\n", s->toChars(), this->toChars()); + s->addMember(sc, this, 1); + if (s->isFuncDeclaration()) + hasfunctions = 1; + } + + // If nested struct, add in hidden 'this' pointer to outer scope + if (hasfunctions && !(storage_class & STCstatic)) + { Dsymbol *s = toParent2(); + if (s) + { + AggregateDeclaration *ad = s->isAggregateDeclaration(); + FuncDeclaration *fd = s->isFuncDeclaration(); + + TemplateInstance *ti; + if (ad && (ti = ad->parent->isTemplateInstance()) != NULL && ti->isnested || fd) + { isnested = 1; + Type *t; + if (ad) + t = ad->handle; + else if (fd) + { AggregateDeclaration *ad = fd->isMember2(); + if (ad) + t = ad->handle; + else + t = Type::tvoidptr; + } + else + assert(0); + if (t->ty == Tstruct) + t = Type::tvoidptr; // t should not be a ref type + assert(!vthis); + vthis = new ThisDeclaration(loc, t); + //vthis->storage_class |= STCref; + members->push(vthis); + } + } + } + } + + sizeok = 0; + sc2 = sc->push(this); + sc2->stc &= STCsafe | STCtrusted | STCsystem; + sc2->parent = this; + if (isUnionDeclaration()) + sc2->inunion = 1; + sc2->protection = PROTpublic; + sc2->explicitProtection = 0; + + size_t members_dim = members->dim; + + /* Set scope so if there are forward references, we still might be able to + * resolve individual members like enums. + */ + for (size_t i = 0; i < members_dim; i++) + { Dsymbol *s = (*members)[i]; + /* There are problems doing this in the general case because + * Scope keeps track of things like 'offset' + */ + if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident)) + { + //printf("setScope %s %s\n", s->kind(), s->toChars()); + s->setScope(sc2); + } + } + + for (size_t i = 0; i < members_dim; i++) + { + Dsymbol *s = (*members)[i]; + + /* If this is the last member, see if we can finish setting the size. + * This could be much better - finish setting the size after the last + * field was processed. The problem is the chicken-and-egg determination + * of when that is. See Bugzilla 7426 for more info. + */ + if (i + 1 == members_dim) + { + if (sizeok == 0 && s->isAliasDeclaration()) + finalizeSize(); + } + s->semantic(sc2); + } + + if (sizeok == 2) + { // semantic() failed because of forward references. + // Unwind what we did, and defer it for later + fields.setDim(0); + structsize = 0; + alignsize = 0; + structalign = 0; + + scope = scx ? scx : new Scope(*sc); + scope->setNoFree(); + scope->module->addDeferredSemantic(this); + + Module::dprogress = dprogress_save; + //printf("\tdeferring %s\n", toChars()); + return; + } + + finalizeSize(); + Module::dprogress++; + + //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); + + // Determine if struct is all zeros or not + zeroInit = 1; + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol *s = fields.tdata()[i]; + VarDeclaration *vd = s->isVarDeclaration(); + if (vd && !vd->isDataseg()) + { + if (vd->init) + { + // Should examine init to see if it is really all 0's + zeroInit = 0; + break; + } + else + { + if (!vd->type->isZeroInit(loc)) + { + zeroInit = 0; + break; + } + } + } + } + +#if DMDV1 + /* This doesn't work for DMDV2 because (ref S) and (S) parameter + * lists will overload the same. + */ + /* The TypeInfo_Struct is expecting an opEquals and opCmp with + * a parameter that is a pointer to the struct. But if there + * isn't one, but is an opEquals or opCmp with a value, write + * another that is a shell around the value: + * int opCmp(struct *p) { return opCmp(*p); } + */ + + TypeFunction *tfeqptr; + { + Parameters *arguments = new Parameters; + Parameter *arg = new Parameter(STCin, handle, Id::p, NULL); + + arguments->push(arg); + tfeqptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); + tfeqptr = (TypeFunction *)tfeqptr->semantic(0, sc); + } + + TypeFunction *tfeq; + { + Parameters *arguments = new Parameters; + Parameter *arg = new Parameter(STCin, type, NULL, NULL); + + arguments->push(arg); + tfeq = new TypeFunction(arguments, Type::tint32, 0, LINKd); + tfeq = (TypeFunction *)tfeq->semantic(0, sc); + } + + Identifier *id = Id::eq; + for (int i = 0; i < 2; i++) + { + Dsymbol *s = search_function(this, id); + FuncDeclaration *fdx = s ? s->isFuncDeclaration() : NULL; + if (fdx) + { FuncDeclaration *fd = fdx->overloadExactMatch(tfeqptr); + if (!fd) + { fd = fdx->overloadExactMatch(tfeq); + if (fd) + { // Create the thunk, fdptr + FuncDeclaration *fdptr = new FuncDeclaration(loc, loc, fdx->ident, STCundefined, tfeqptr); + Expression *e = new IdentifierExp(loc, Id::p); + e = new PtrExp(loc, e); + Expressions *args = new Expressions(); + args->push(e); + e = new IdentifierExp(loc, id); + e = new CallExp(loc, e, args); + fdptr->fbody = new ReturnStatement(loc, e); + ScopeDsymbol *s = fdx->parent->isScopeDsymbol(); + assert(s); + s->members->push(fdptr); + fdptr->addMember(sc, s, 1); + fdptr->semantic(sc2); + } + } + } + + id = Id::cmp; + } +#endif +#if DMDV2 + dtor = buildDtor(sc2); + postblit = buildPostBlit(sc2); + cpctor = buildCpCtor(sc2); + + buildOpAssign(sc2); + hasIdentityEquals = (buildOpEquals(sc2) != NULL); + + xeq = buildXopEquals(sc2); +#endif + + sc2->pop(); + + /* Look for special member functions. + */ +#if DMDV2 + ctor = search(0, Id::ctor, 0); +#endif + inv = (InvariantDeclaration *)search(0, Id::classInvariant, 0); + aggNew = (NewDeclaration *)search(0, Id::classNew, 0); + aggDelete = (DeleteDeclaration *)search(0, Id::classDelete, 0); + + if (sc->func) + { + semantic2(sc); + semantic3(sc); + } + + if (global.gag && global.gaggedErrors != errors) + { // The type is no good, yet the error messages were gagged. + type = Type::terror; + } + + if (deferred && !global.gag) + { + deferred->semantic2(sc); + deferred->semantic3(sc); + } +} + +Dsymbol *StructDeclaration::search(Loc loc, Identifier *ident, int flags) +{ + //printf("%s.StructDeclaration::search('%s')\n", toChars(), ident->toChars()); + + if (scope && !symtab) + semantic(scope); + + if (!members || !symtab) + { + error("is forward referenced when looking for '%s'", ident->toChars()); + return NULL; + } + + return ScopeDsymbol::search(loc, ident, flags); +} + +void StructDeclaration::finalizeSize() +{ + // 0 sized struct's are set to 1 byte + if (structsize == 0) + { + structsize = 1; + alignsize = 1; + } + + // Round struct size up to next alignsize boundary. + // This will ensure that arrays of structs will get their internals + // aligned properly. + structsize = (structsize + alignsize - 1) & ~(alignsize - 1); + + sizeok = 1; +} + +void StructDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->printf("%s ", kind()); + if (!isAnonymous()) + buf->writestring(toChars()); + if (!members) + { + buf->writeByte(';'); + buf->writenl(); + return; + } + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + + buf->writestring(" "); + s->toCBuffer(buf, hgs); + } + buf->writeByte('}'); + buf->writenl(); +} + + +const char *StructDeclaration::kind() +{ + return "struct"; +} + +/********************************* UnionDeclaration ****************************/ + +UnionDeclaration::UnionDeclaration(Loc loc, Identifier *id) + : StructDeclaration(loc, id) +{ + hasUnions = 1; +} + +Dsymbol *UnionDeclaration::syntaxCopy(Dsymbol *s) +{ + UnionDeclaration *ud; + + if (s) + ud = (UnionDeclaration *)s; + else + ud = new UnionDeclaration(loc, ident); + StructDeclaration::syntaxCopy(ud); + return ud; +} + + +const char *UnionDeclaration::kind() +{ + return "union"; +} + + diff --git a/template.c b/template.c new file mode 100644 index 00000000..9b0d3115 --- /dev/null +++ b/template.c @@ -0,0 +1,6189 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2012 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +// Handle template implementation + +#include +#include + +#include "root.h" +#include "aav.h" +#include "rmem.h" +#include "stringtable.h" + +#include "mtype.h" +#include "template.h" +#include "init.h" +#include "expression.h" +#include "scope.h" +#include "module.h" +#include "aggregate.h" +#include "declaration.h" +#include "dsymbol.h" +#include "mars.h" +#include "dsymbol.h" +#include "identifier.h" +#include "hdrgen.h" +#include "id.h" + +#if WINDOWS_SEH +#include +long __cdecl __ehfilter(LPEXCEPTION_POINTERS ep); +#endif + +#define LOG 0 + +/******************************************** + * These functions substitute for dynamic_cast. dynamic_cast does not work + * on earlier versions of gcc. + */ + +Expression *isExpression(Object *o) +{ + //return dynamic_cast(o); + if (!o || o->dyncast() != DYNCAST_EXPRESSION) + return NULL; + return (Expression *)o; +} + +Dsymbol *isDsymbol(Object *o) +{ + //return dynamic_cast(o); + if (!o || o->dyncast() != DYNCAST_DSYMBOL) + return NULL; + return (Dsymbol *)o; +} + +Type *isType(Object *o) +{ + //return dynamic_cast(o); + if (!o || o->dyncast() != DYNCAST_TYPE) + return NULL; + return (Type *)o; +} + +Tuple *isTuple(Object *o) +{ + //return dynamic_cast(o); + if (!o || o->dyncast() != DYNCAST_TUPLE) + return NULL; + return (Tuple *)o; +} + +/************************************** + * Is this Object an error? + */ +int isError(Object *o) +{ + Type *t = isType(o); + if (t) + return (t->ty == Terror); + Expression *e = isExpression(o); + if (e) + return (e->op == TOKerror); + Tuple *v = isTuple(o); + if (v) + return arrayObjectIsError(&v->objects); + return 0; +} + +/************************************** + * Are any of the Objects an error? + */ +int arrayObjectIsError(Objects *args) +{ + for (size_t i = 0; i < args->dim; i++) + { + Object *o = args->tdata()[i]; + if (isError(o)) + return 1; + } + return 0; +} + +/*********************** + * Try to get arg as a type. + */ + +Type *getType(Object *o) +{ + Type *t = isType(o); + if (!t) + { Expression *e = isExpression(o); + if (e) + t = e->type; + } + return t; +} + +Dsymbol *getDsymbol(Object *oarg) +{ + Dsymbol *sa; + Expression *ea = isExpression(oarg); + if (ea) + { // Try to convert Expression to symbol + if (ea->op == TOKvar) + sa = ((VarExp *)ea)->var; + else if (ea->op == TOKfunction) + sa = ((FuncExp *)ea)->fd; + else + sa = NULL; + } + else + { // Try to convert Type to symbol + Type *ta = isType(oarg); + if (ta) + sa = ta->toDsymbol(NULL); + else + sa = isDsymbol(oarg); // if already a symbol + } + return sa; +} + +/****************************** + * If o1 matches o2, return 1. + * Else, return 0. + */ + +int match(Object *o1, Object *o2, TemplateDeclaration *tempdecl, Scope *sc) +{ + Type *t1 = isType(o1); + Type *t2 = isType(o2); + Expression *e1 = isExpression(o1); + Expression *e2 = isExpression(o2); + Dsymbol *s1 = isDsymbol(o1); + Dsymbol *s2 = isDsymbol(o2); + Tuple *u1 = isTuple(o1); + Tuple *u2 = isTuple(o2); + + //printf("\t match t1 %p t2 %p, e1 %p e2 %p, s1 %p s2 %p, u1 %p u2 %p\n", t1,t2,e1,e2,s1,s2,u1,u2); + + /* A proper implementation of the various equals() overrides + * should make it possible to just do o1->equals(o2), but + * we'll do that another day. + */ + + if (s1) + { + VarDeclaration *v1 = s1->isVarDeclaration(); + if (v1 && v1->storage_class & STCmanifest) + { ExpInitializer *ei1 = v1->init->isExpInitializer(); + if (ei1) + e1 = ei1->exp, s1 = NULL; + } + } + if (s2) + { + VarDeclaration *v2 = s2->isVarDeclaration(); + if (v2 && v2->storage_class & STCmanifest) + { ExpInitializer *ei2 = v2->init->isExpInitializer(); + if (ei2) + e2 = ei2->exp, s2 = NULL; + } + } + + if (t1) + { + /* if t1 is an instance of ti, then give error + * about recursive expansions. + */ + Dsymbol *s = t1->toDsymbol(sc); + if (s && s->parent) + { TemplateInstance *ti1 = s->parent->isTemplateInstance(); + if (ti1 && ti1->tempdecl == tempdecl) + { + for (Scope *sc1 = sc; sc1; sc1 = sc1->enclosing) + { + if (sc1->scopesym == ti1) + { + error("recursive template expansion for template argument %s", t1->toChars()); + return 1; // fake a match + } + } + } + } + + //printf("t1 = %s\n", t1->toChars()); + //printf("t2 = %s\n", t2->toChars()); + if (!t2 || !t1->equals(t2)) + goto Lnomatch; + } + else if (e1) + { +#if 0 + if (e1 && e2) + { + printf("match %d\n", e1->equals(e2)); + e1->print(); + e2->print(); + e1->type->print(); + e2->type->print(); + } +#endif + if (!e2) + goto Lnomatch; + if (!e1->equals(e2)) + goto Lnomatch; + } + else if (s1) + { + if (!s2 || !s1->equals(s2) || s1->parent != s2->parent) + goto Lnomatch; + } + else if (u1) + { + if (!u2) + goto Lnomatch; + if (u1->objects.dim != u2->objects.dim) + goto Lnomatch; + for (size_t i = 0; i < u1->objects.dim; i++) + { + if (!match(u1->objects.tdata()[i], + u2->objects.tdata()[i], + tempdecl, sc)) + goto Lnomatch; + } + } + //printf("match\n"); + return 1; // match + +Lnomatch: + //printf("nomatch\n"); + return 0; // nomatch; +} + + +/************************************ + * Match an array of them. + */ +int arrayObjectMatch(Objects *oa1, Objects *oa2, TemplateDeclaration *tempdecl, Scope *sc) +{ + if (oa1 == oa2) + return 1; + if (oa1->dim != oa2->dim) + return 0; + for (size_t j = 0; j < oa1->dim; j++) + { Object *o1 = oa1->tdata()[j]; + Object *o2 = oa2->tdata()[j]; + if (!match(o1, o2, tempdecl, sc)) + { + return 0; + } + } + return 1; +} + +/**************************************** + * This makes a 'pretty' version of the template arguments. + * It's analogous to genIdent() which makes a mangled version. + */ + +void ObjectToCBuffer(OutBuffer *buf, HdrGenState *hgs, Object *oarg) +{ + //printf("ObjectToCBuffer()\n"); + Type *t = isType(oarg); + Expression *e = isExpression(oarg); + Dsymbol *s = isDsymbol(oarg); + Tuple *v = isTuple(oarg); + /* The logic of this should match what genIdent() does. The _dynamic_cast() + * function relies on all the pretty strings to be unique for different classes + * (see Bugzilla 7375). + * Perhaps it would be better to demangle what genIdent() does. + */ + if (t) + { //printf("\tt: %s ty = %d\n", t->toChars(), t->ty); + t->toCBuffer(buf, NULL, hgs); + } + else if (e) + { + if (e->op == TOKvar) + e = e->optimize(WANTvalue); // added to fix Bugzilla 7375 + e->toCBuffer(buf, hgs); + } + else if (s) + { + char *p = s->ident ? s->ident->toChars() : s->toChars(); + buf->writestring(p); + } + else if (v) + { + Objects *args = &v->objects; + for (size_t i = 0; i < args->dim; i++) + { + if (i) + buf->writeByte(','); + Object *o = (*args)[i]; + ObjectToCBuffer(buf, hgs, o); + } + } + else if (!oarg) + { + buf->writestring("NULL"); + } + else + { +#ifdef DEBUG + printf("bad Object = %p\n", oarg); +#endif + assert(0); + } +} + +#if DMDV2 +Object *objectSyntaxCopy(Object *o) +{ + if (!o) + return NULL; + Type *t = isType(o); + if (t) + return t->syntaxCopy(); + Expression *e = isExpression(o); + if (e) + return e->syntaxCopy(); + return o; +} +#endif + + +/* ======================== TemplateDeclaration ============================= */ + +TemplateDeclaration::TemplateDeclaration(Loc loc, Identifier *id, + TemplateParameters *parameters, Expression *constraint, Dsymbols *decldefs, int ismixin) + : ScopeDsymbol(id) +{ +#if LOG + printf("TemplateDeclaration(this = %p, id = '%s')\n", this, id->toChars()); +#endif +#if 0 + if (parameters) + for (int i = 0; i < parameters->dim; i++) + { TemplateParameter *tp = parameters->tdata()[i]; + //printf("\tparameter[%d] = %p\n", i, tp); + TemplateTypeParameter *ttp = tp->isTemplateTypeParameter(); + + if (ttp) + { + printf("\tparameter[%d] = %s : %s\n", i, tp->ident->toChars(), ttp->specType ? ttp->specType->toChars() : ""); + } + } +#endif + this->loc = loc; + this->parameters = parameters; + this->origParameters = parameters; + this->constraint = constraint; + this->members = decldefs; + this->overnext = NULL; + this->overroot = NULL; + this->semanticRun = 0; + this->onemember = NULL; + this->literal = 0; + this->ismixin = ismixin; + this->previous = NULL; + + // Compute in advance for Ddoc's use + if (members) + { + Dsymbol *s; + if (Dsymbol::oneMembers(members, &s, ident) && s) + { + onemember = s; + s->parent = this; + } + } +} + +Dsymbol *TemplateDeclaration::syntaxCopy(Dsymbol *) +{ + //printf("TemplateDeclaration::syntaxCopy()\n"); + TemplateDeclaration *td; + TemplateParameters *p; + + p = NULL; + if (parameters) + { + p = new TemplateParameters(); + p->setDim(parameters->dim); + for (size_t i = 0; i < p->dim; i++) + { TemplateParameter *tp = (*parameters)[i]; + p->tdata()[i] = tp->syntaxCopy(); + } + } + Expression *e = NULL; + if (constraint) + e = constraint->syntaxCopy(); + Dsymbols *d = Dsymbol::arraySyntaxCopy(members); + td = new TemplateDeclaration(loc, ident, p, e, d, ismixin); + return td; +} + +void TemplateDeclaration::semantic(Scope *sc) +{ +#if LOG + printf("TemplateDeclaration::semantic(this = %p, id = '%s')\n", this, ident->toChars()); + printf("sc->stc = %llx\n", sc->stc); + printf("sc->module = %s\n", sc->module->toChars()); +#endif + if (semanticRun) + return; // semantic() already run + semanticRun = 1; + + if (sc->module && sc->module->ident == Id::object && ident == Id::AssociativeArray) + { Type::associativearray = this; + } + + if (sc->func) + { +#if DMDV1 + error("cannot declare template at function scope %s", sc->func->toChars()); +#endif + } + + if (/*global.params.useArrayBounds &&*/ sc->module) + { + // Generate this function as it may be used + // when template is instantiated in other modules + sc->module->toModuleArray(); + } + + if (/*global.params.useAssert &&*/ sc->module) + { + // Generate this function as it may be used + // when template is instantiated in other modules + sc->module->toModuleAssert(); + } + +#if DMDV2 + if (/*global.params.useUnitTests &&*/ sc->module) + { + // Generate this function as it may be used + // when template is instantiated in other modules + sc->module->toModuleUnittest(); + } +#endif + + /* Remember Scope for later instantiations, but make + * a copy since attributes can change. + */ + this->scope = new Scope(*sc); + this->scope->setNoFree(); + + // Set up scope for parameters + ScopeDsymbol *paramsym = new ScopeDsymbol(); + paramsym->parent = sc->parent; + Scope *paramscope = sc->push(paramsym); + paramscope->parameterSpecialization = 1; + paramscope->stc = 0; + + if (!parent) + parent = sc->parent; + + if (global.params.doDocComments) + { + origParameters = new TemplateParameters(); + origParameters->setDim(parameters->dim); + for (size_t i = 0; i < parameters->dim; i++) + { + TemplateParameter *tp = parameters->tdata()[i]; + origParameters->tdata()[i] = tp->syntaxCopy(); + } + } + + for (size_t i = 0; i < parameters->dim; i++) + { + TemplateParameter *tp = parameters->tdata()[i]; + + tp->declareParameter(paramscope); + } + + for (size_t i = 0; i < parameters->dim; i++) + { + TemplateParameter *tp = parameters->tdata()[i]; + + tp->semantic(paramscope); + if (i + 1 != parameters->dim && tp->isTemplateTupleParameter()) + error("template tuple parameter must be last one"); + } + + paramscope->pop(); + + // Compute again + onemember = NULL; + if (members) + { + Dsymbol *s; + if (Dsymbol::oneMembers(members, &s, ident) && s) + { + onemember = s; + s->parent = this; + } + } + + /* BUG: should check: + * o no virtual functions or non-static data members of classes + */ +} + +const char *TemplateDeclaration::kind() +{ + return (onemember && onemember->isAggregateDeclaration()) + ? onemember->kind() + : (char *)"template"; +} + +/********************************** + * Overload existing TemplateDeclaration 'this' with the new one 's'. + * Return !=0 if successful; i.e. no conflict. + */ + +int TemplateDeclaration::overloadInsert(Dsymbol *s) +{ + TemplateDeclaration **pf; + TemplateDeclaration *f; + +#if LOG + printf("TemplateDeclaration::overloadInsert('%s')\n", s->toChars()); +#endif + f = s->isTemplateDeclaration(); + if (!f) + return FALSE; + TemplateDeclaration *pthis = this; + for (pf = &pthis; *pf; pf = &(*pf)->overnext) + { +#if 0 + // Conflict if TemplateParameter's match + // Will get caught anyway later with TemplateInstance, but + // should check it now. + TemplateDeclaration *f2 = *pf; + + if (f->parameters->dim != f2->parameters->dim) + goto Lcontinue; + + for (size_t i = 0; i < f->parameters->dim; i++) + { TemplateParameter *p1 = f->parameters->tdata()[i]; + TemplateParameter *p2 = f2->parameters->tdata()[i]; + + if (!p1->overloadMatch(p2)) + goto Lcontinue; + } + +#if LOG + printf("\tfalse: conflict\n"); +#endif + return FALSE; + + Lcontinue: + ; +#endif + } + + f->overroot = this; + *pf = f; +#if LOG + printf("\ttrue: no conflict\n"); +#endif + return TRUE; +} + +/**************************** + * Declare all the function parameters as variables + * and add them to the scope + */ +void TemplateDeclaration::makeParamNamesVisibleInConstraint(Scope *paramscope, Expressions *fargs) +{ + /* We do this ONLY if there is only one function in the template. + */ + FuncDeclaration *fd = onemember && onemember->toAlias() ? + onemember->toAlias()->isFuncDeclaration() : NULL; + if (fd) + { + /* + Making parameters is similar to FuncDeclaration::semantic3 + */ + paramscope->parent = fd; + + TypeFunction *tf = (TypeFunction *)fd->type->syntaxCopy(); + + // Shouldn't run semantic on default arguments and return type. + for (int i = 0; iparameters->dim; i++) + tf->parameters->tdata()[i]->defaultArg = NULL; + tf->next = NULL; + + // Resolve parameter types and 'auto ref's. + tf->fargs = fargs; + tf = (TypeFunction *)tf->semantic(loc, paramscope); + + Parameters *fparameters = tf->parameters; + int fvarargs = tf->varargs; + + size_t nfparams = Parameter::dim(fparameters); // Num function parameters + for (size_t i = 0; i < nfparams; i++) + { + Parameter *fparam = Parameter::getNth(fparameters, i); + // Remove addMod same as func.d L1065 of FuncDeclaration::semantic3 + //Type *vtype = fparam->type; + //if (fd->type && fd->isPure()) + // vtype = vtype->addMod(MODconst); + fparam->storageClass &= (STCin | STCout | STCref | STClazy | STCfinal | STC_TYPECTOR | STCnodtor); + fparam->storageClass |= STCparameter; + if (fvarargs == 2 && i + 1 == nfparams) + fparam->storageClass |= STCvariadic; + } + for (size_t i = 0; i < fparameters->dim; i++) + { + Parameter *fparam = fparameters->tdata()[i]; + if (!fparam->ident) + continue; // don't add it, if it has no name + VarDeclaration *v = new VarDeclaration(loc, fparam->type, fparam->ident, NULL); + v->storage_class = fparam->storageClass; + v->semantic(paramscope); + if (!paramscope->insert(v)) + error("parameter %s.%s is already defined", toChars(), v->toChars()); + else + v->parent = this; + } + } +} + +/*************************************** + * Given that ti is an instance of this TemplateDeclaration, + * deduce the types of the parameters to this, and store + * those deduced types in dedtypes[]. + * Input: + * flag 1: don't do semantic() because of dummy types + * 2: don't change types in matchArg() + * Output: + * dedtypes deduced arguments + * Return match level. + */ + +MATCH TemplateDeclaration::matchWithInstance(TemplateInstance *ti, + Objects *dedtypes, Expressions *fargs, int flag) +{ MATCH m; + size_t dedtypes_dim = dedtypes->dim; + +#define LOGM 0 +#if LOGM + printf("\n+TemplateDeclaration::matchWithInstance(this = %s, ti = %s, flag = %d)\n", toChars(), ti->toChars(), flag); +#endif + +#if 0 + printf("dedtypes->dim = %d, parameters->dim = %d\n", dedtypes_dim, parameters->dim); + if (ti->tiargs->dim) + printf("ti->tiargs->dim = %d, [0] = %p\n", + ti->tiargs->dim, + ti->tiargs->tdata()[0]); +#endif + dedtypes->zero(); + + size_t parameters_dim = parameters->dim; + int variadic = isVariadic() != NULL; + + // If more arguments than parameters, no match + if (ti->tiargs->dim > parameters_dim && !variadic) + { +#if LOGM + printf(" no match: more arguments than parameters\n"); +#endif + return MATCHnomatch; + } + + assert(dedtypes_dim == parameters_dim); + assert(dedtypes_dim >= ti->tiargs->dim || variadic); + + // Set up scope for parameters + assert((size_t)scope > 0x10000); + ScopeDsymbol *paramsym = new ScopeDsymbol(); + paramsym->parent = scope->parent; + Scope *paramscope = scope->push(paramsym); + paramscope->stc = 0; + + // Attempt type deduction + m = MATCHexact; + for (size_t i = 0; i < dedtypes_dim; i++) + { MATCH m2; + TemplateParameter *tp = parameters->tdata()[i]; + Declaration *sparam; + + //printf("\targument [%d]\n", i); +#if LOGM + //printf("\targument [%d] is %s\n", i, oarg ? oarg->toChars() : "null"); + TemplateTypeParameter *ttp = tp->isTemplateTypeParameter(); + if (ttp) + printf("\tparameter[%d] is %s : %s\n", i, tp->ident->toChars(), ttp->specType ? ttp->specType->toChars() : ""); +#endif + + m2 = tp->matchArg(paramscope, ti->tiargs, i, parameters, dedtypes, &sparam); + //printf("\tm2 = %d\n", m2); + + if (m2 == MATCHnomatch) + { +#if 0 + printf("\tmatchArg() for parameter %i failed\n", i); +#endif + goto Lnomatch; + } + + if (m2 < m) + m = m2; + + if (!flag) + sparam->semantic(paramscope); + if (!paramscope->insert(sparam)) + goto Lnomatch; + } + + if (!flag) + { + /* Any parameter left without a type gets the type of + * its corresponding arg + */ + for (size_t i = 0; i < dedtypes_dim; i++) + { + if (!dedtypes->tdata()[i]) + { assert(i < ti->tiargs->dim); + dedtypes->tdata()[i] = (Type *)ti->tiargs->tdata()[i]; + } + } + } + +#if DMDV2 + if (m && constraint && !flag) + { /* Check to see if constraint is satisfied. + */ + makeParamNamesVisibleInConstraint(paramscope, fargs); + Expression *e = constraint->syntaxCopy(); + Scope *sc = paramscope->push(); + + /* There's a chicken-and-egg problem here. We don't know yet if this template + * instantiation will be a local one (isnested is set), and we won't know until + * after selecting the correct template. Thus, function we're nesting inside + * is not on the sc scope chain, and this can cause errors in FuncDeclaration::getLevel(). + * Workaround the problem by setting a flag to relax the checking on frame errors. + */ + sc->flags |= SCOPEstaticif; + + FuncDeclaration *fd = onemember && onemember->toAlias() ? + onemember->toAlias()->isFuncDeclaration() : NULL; + Dsymbol *s = parent; + while (s->isTemplateInstance() || s->isTemplateMixin()) + s = s->parent; + AggregateDeclaration *ad = s->isAggregateDeclaration(); + VarDeclaration *vthissave; + if (fd && ad) + { + vthissave = fd->vthis; + fd->vthis = fd->declareThis(paramscope, ad); + } + + e = e->semantic(sc); + + if (fd && fd->vthis) + fd->vthis = vthissave; + + sc->pop(); + e = e->optimize(WANTvalue | WANTinterpret); + if (e->isBool(TRUE)) + ; + else if (e->isBool(FALSE)) + goto Lnomatch; + else + { + e->error("constraint %s is not constant or does not evaluate to a bool", e->toChars()); + } + } +#endif + +#if LOGM + // Print out the results + printf("--------------------------\n"); + printf("template %s\n", toChars()); + printf("instance %s\n", ti->toChars()); + if (m) + { + for (size_t i = 0; i < dedtypes_dim; i++) + { + TemplateParameter *tp = parameters->tdata()[i]; + Object *oarg; + + printf(" [%d]", i); + + if (i < ti->tiargs->dim) + oarg = ti->tiargs->tdata()[i]; + else + oarg = NULL; + tp->print(oarg, dedtypes->tdata()[i]); + } + } + else + goto Lnomatch; +#endif + +#if LOGM + printf(" match = %d\n", m); +#endif + goto Lret; + +Lnomatch: +#if LOGM + printf(" no match\n"); +#endif + m = MATCHnomatch; + +Lret: + paramscope->pop(); +#if LOGM + printf("-TemplateDeclaration::matchWithInstance(this = %p, ti = %p) = %d\n", this, ti, m); +#endif + return m; +} + +/******************************************** + * Determine partial specialization order of 'this' vs td2. + * Returns: + * match this is at least as specialized as td2 + * 0 td2 is more specialized than this + */ + +MATCH TemplateDeclaration::leastAsSpecialized(TemplateDeclaration *td2, Expressions *fargs) +{ + /* This works by taking the template parameters to this template + * declaration and feeding them to td2 as if it were a template + * instance. + * If it works, then this template is at least as specialized + * as td2. + */ + + TemplateInstance ti(0, ident); // create dummy template instance + Objects dedtypes; + +#define LOG_LEASTAS 0 + +#if LOG_LEASTAS + printf("%s.leastAsSpecialized(%s)\n", toChars(), td2->toChars()); +#endif + + // Set type arguments to dummy template instance to be types + // generated from the parameters to this template declaration + ti.tiargs = new Objects(); + ti.tiargs->setDim(parameters->dim); + for (size_t i = 0; i < ti.tiargs->dim; i++) + { + TemplateParameter *tp = parameters->tdata()[i]; + + Object *p = (Object *)tp->dummyArg(); + if (p) + ti.tiargs->tdata()[i] = p; + else + ti.tiargs->setDim(i); + } + + // Temporary Array to hold deduced types + //dedtypes.setDim(parameters->dim); + dedtypes.setDim(td2->parameters->dim); + + // Attempt a type deduction + MATCH m = td2->matchWithInstance(&ti, &dedtypes, fargs, 1); + if (m) + { + /* A non-variadic template is more specialized than a + * variadic one. + */ + if (isVariadic() && !td2->isVariadic()) + goto L1; + +#if LOG_LEASTAS + printf(" matches %d, so is least as specialized\n", m); +#endif + return m; + } + L1: +#if LOG_LEASTAS + printf(" doesn't match, so is not as specialized\n"); +#endif + return MATCHnomatch; +} + + +/************************************************* + * Match function arguments against a specific template function. + * Input: + * loc instantiation location + * targsi Expression/Type initial list of template arguments + * ethis 'this' argument if !NULL + * fargs arguments to function + * Output: + * dedargs Expression/Type deduced template arguments + * Returns: + * match level + */ + +MATCH TemplateDeclaration::deduceFunctionTemplateMatch(Scope *sc, Loc loc, Objects *targsi, + Expression *ethis, Expressions *fargs, + Objects *dedargs) +{ + size_t nfparams; + size_t nfargs; + size_t nargsi; // array size of targsi + int fptupindex = -1; + int tuple_dim = 0; + MATCH match = MATCHexact; + FuncDeclaration *fd = onemember->toAlias()->isFuncDeclaration(); + Parameters *fparameters; // function parameter list + int fvarargs; // function varargs + Objects dedtypes; // for T:T*, the dedargs is the T*, dedtypes is the T + unsigned wildmatch = 0; + + TypeFunction *tf = (TypeFunction *)fd->type; + +#if 0 + printf("\nTemplateDeclaration::deduceFunctionTemplateMatch() %s\n", toChars()); + for (size_t i = 0; i < fargs->dim; i++) + { Expression *e = fargs->tdata()[i]; + printf("\tfarg[%d] is %s, type is %s\n", i, e->toChars(), e->type->toChars()); + } + printf("fd = %s\n", fd->toChars()); + printf("fd->type = %s\n", fd->type->toChars()); + if (ethis) + printf("ethis->type = %s\n", ethis->type->toChars()); +#endif + + assert((size_t)scope > 0x10000); + + dedargs->setDim(parameters->dim); + dedargs->zero(); + + dedtypes.setDim(parameters->dim); + dedtypes.zero(); + + // Set up scope for parameters + ScopeDsymbol *paramsym = new ScopeDsymbol(); + paramsym->parent = scope->parent; + Scope *paramscope = scope->push(paramsym); + paramscope->stc = 0; + + TemplateTupleParameter *tp = isVariadic(); + int tp_is_declared = 0; + +#if 0 + for (size_t i = 0; i < dedargs->dim; i++) + { + printf("\tdedarg[%d] = ", i); + Object *oarg = dedargs->tdata()[i]; + if (oarg) printf("%s", oarg->toChars()); + printf("\n"); + } +#endif + + + nargsi = 0; + if (targsi) + { // Set initial template arguments + + nargsi = targsi->dim; + size_t n = parameters->dim; + if (tp) + n--; + if (nargsi > n) + { if (!tp) + goto Lnomatch; + + /* The extra initial template arguments + * now form the tuple argument. + */ + Tuple *t = new Tuple(); + assert(parameters->dim); + dedargs->tdata()[parameters->dim - 1] = t; + + tuple_dim = nargsi - n; + t->objects.setDim(tuple_dim); + for (size_t i = 0; i < tuple_dim; i++) + { + t->objects.tdata()[i] = targsi->tdata()[n + i]; + } + declareParameter(paramscope, tp, t); + tp_is_declared = 1; + } + else + n = nargsi; + + memcpy(dedargs->tdata(), targsi->tdata(), n * sizeof(*dedargs->tdata())); + + for (size_t i = 0; i < n; i++) + { assert(i < parameters->dim); + TemplateParameter *tp = parameters->tdata()[i]; + MATCH m; + Declaration *sparam = NULL; + + m = tp->matchArg(paramscope, dedargs, i, parameters, &dedtypes, &sparam); + //printf("\tdeduceType m = %d\n", m); + if (m == MATCHnomatch) + goto Lnomatch; + if (m < match) + match = m; + + sparam->semantic(paramscope); + if (!paramscope->insert(sparam)) + goto Lnomatch; + } + } +#if 0 + for (size_t i = 0; i < dedargs->dim; i++) + { + printf("\tdedarg[%d] = ", i); + Object *oarg = dedargs->tdata()[i]; + if (oarg) printf("%s", oarg->toChars()); + printf("\n"); + } +#endif + + fparameters = fd->getParameters(&fvarargs); + nfparams = Parameter::dim(fparameters); // number of function parameters + nfargs = fargs ? fargs->dim : 0; // number of function arguments + + /* Check for match of function arguments with variadic template + * parameter, such as: + * + * template Foo(T, A...) { void Foo(T t, A a); } + * void main() { Foo(1,2,3); } + */ + if (tp) // if variadic + { + if (nfparams == 0 && nfargs != 0) // if no function parameters + { + if (tp_is_declared) + goto L2; + Tuple *t = new Tuple(); + //printf("t = %p\n", t); + dedargs->tdata()[parameters->dim - 1] = t; + declareParameter(paramscope, tp, t); + goto L2; + } + else if (nfargs < nfparams - 1) + goto L1; + else + { + /* Figure out which of the function parameters matches + * the tuple template parameter. Do this by matching + * type identifiers. + * Set the index of this function parameter to fptupindex. + */ + for (fptupindex = 0; fptupindex < nfparams; fptupindex++) + { + Parameter *fparam = fparameters->tdata()[fptupindex]; + if (fparam->type->ty != Tident) + continue; + TypeIdentifier *tid = (TypeIdentifier *)fparam->type; + if (!tp->ident->equals(tid->ident) || tid->idents.dim) + continue; + + if (fvarargs) // variadic function doesn't + goto Lnomatch; // go with variadic template + + if (tp_is_declared) + goto L2; + + // Apply function parameter storage classes to parameter type + tid = (TypeIdentifier *)tid->addStorageClass(fparam->storageClass); + + /* The types of the function arguments + * now form the tuple argument. + */ + Tuple *t = new Tuple(); + dedargs->tdata()[parameters->dim - 1] = t; + + tuple_dim = nfargs - (nfparams - 1); + t->objects.setDim(tuple_dim); + for (size_t i = 0; i < tuple_dim; i++) + { Expression *farg = fargs->tdata()[fptupindex + i]; + unsigned mod = farg->type->mod; + Type *tt; + MATCH m; + + #define X(U,T) ((U) << 4) | (T) + if (tid->mod & MODwild) + { + switch (X(tid->mod, mod)) + { + case X(MODwild, MODwild): + case X(MODwild | MODshared, MODwild | MODshared): + case X(MODwild, 0): + case X(MODwild, MODconst): + case X(MODwild, MODimmutable): + case X(MODwild | MODshared, MODshared): + case X(MODwild | MODshared, MODconst | MODshared): + + if (mod & MODwild) + wildmatch |= MODwild; + else if (mod == 0) + wildmatch |= MODmutable; + else + wildmatch |= (mod & ~MODshared); + tt = farg->type->mutableOf(); + m = MATCHconst; + goto Lx; + + default: + break; + } + } + + switch (X(tid->mod, mod)) + { + case X(0, 0): + case X(0, MODconst): + case X(0, MODimmutable): + case X(0, MODshared): + case X(0, MODconst | MODshared): + case X(0, MODwild): + case X(0, MODwild | MODshared): + // foo(U:U) T => T + // foo(U:U) const(T) => const(T) + // foo(U:U) immutable(T) => immutable(T) + // foo(U:U) shared(T) => shared(T) + // foo(U:U) const(shared(T)) => const(shared(T)) + // foo(U:U) wild(T) => wild(T) + // foo(U:U) wild(shared(T)) => wild(shared(T)) + + tt = farg->type; + m = MATCHexact; + break; + + case X(MODconst, MODconst): + case X(MODimmutable, MODimmutable): + case X(MODshared, MODshared): + case X(MODconst | MODshared, MODconst | MODshared): + case X(MODwild, MODwild): + case X(MODwild | MODshared, MODwild | MODshared): + // foo(U:const(U)) const(T) => T + // foo(U:immutable(U)) immutable(T) => T + // foo(U:shared(U)) shared(T) => T + // foo(U:const(shared(U)) const(shared(T)) => T + // foo(U:wild(U)) wild(T) => T + // foo(U:wild(shared(U)) wild(shared(T)) => T + + tt = farg->type->mutableOf()->unSharedOf(); + m = MATCHexact; + break; + + case X(MODconst, 0): + case X(MODconst, MODimmutable): + case X(MODconst, MODconst | MODshared): + case X(MODconst | MODshared, MODimmutable): + case X(MODconst, MODwild): + case X(MODconst, MODwild | MODshared): + // foo(U:const(U)) T => T + // foo(U:const(U)) immutable(T) => T + // foo(U:const(U)) const(shared(T)) => shared(T) + // foo(U:const(shared(U)) immutable(T) => T + // foo(U:const(U)) wild(shared(T)) => shared(T) + + tt = farg->type->mutableOf(); + m = MATCHconst; + break; + + case X(MODshared, MODconst | MODshared): + case X(MODconst | MODshared, MODshared): + case X(MODshared, MODwild | MODshared): + // foo(U:shared(U)) const(shared(T)) => const(T) + // foo(U:const(shared(U)) shared(T) => T + // foo(U:shared(U)) wild(shared(T)) => wild(T) + tt = farg->type->unSharedOf(); + m = MATCHconst; + break; + + case X(MODimmutable, 0): + case X(MODimmutable, MODconst): + case X(MODimmutable, MODshared): + case X(MODimmutable, MODconst | MODshared): + case X(MODconst, MODshared): + case X(MODshared, 0): + case X(MODshared, MODconst): + case X(MODshared, MODimmutable): + case X(MODconst | MODshared, 0): + case X(MODconst | MODshared, MODconst): + case X(MODimmutable, MODwild): + case X(MODshared, MODwild): + case X(MODconst | MODshared, MODwild): + case X(MODwild, 0): + case X(MODwild, MODconst): + case X(MODwild, MODimmutable): + case X(MODwild, MODshared): + case X(MODwild, MODconst | MODshared): + case X(MODwild | MODshared, 0): + case X(MODwild | MODshared, MODconst): + case X(MODwild | MODshared, MODimmutable): + case X(MODwild | MODshared, MODshared): + case X(MODwild | MODshared, MODconst | MODshared): + case X(MODwild | MODshared, MODwild): + case X(MODimmutable, MODwild | MODshared): + case X(MODconst | MODshared, MODwild | MODshared): + case X(MODwild, MODwild | MODshared): + + // foo(U:immutable(U)) T => nomatch + // foo(U:immutable(U)) const(T) => nomatch + // foo(U:immutable(U)) shared(T) => nomatch + // foo(U:immutable(U)) const(shared(T)) => nomatch + // foo(U:const(U)) shared(T) => nomatch + // foo(U:shared(U)) T => nomatch + // foo(U:shared(U)) const(T) => nomatch + // foo(U:shared(U)) immutable(T) => nomatch + // foo(U:const(shared(U)) T => nomatch + // foo(U:const(shared(U)) const(T) => nomatch + // foo(U:immutable(U)) wild(T) => nomatch + // foo(U:shared(U)) wild(T) => nomatch + // foo(U:const(shared(U)) wild(T) => nomatch + // foo(U:wild(U)) T => nomatch + // foo(U:wild(U)) const(T) => nomatch + // foo(U:wild(U)) immutable(T) => nomatch + // foo(U:wild(U)) shared(T) => nomatch + // foo(U:wild(U)) const(shared(T)) => nomatch + // foo(U:wild(shared(U)) T => nomatch + // foo(U:wild(shared(U)) const(T) => nomatch + // foo(U:wild(shared(U)) immutable(T) => nomatch + // foo(U:wild(shared(U)) shared(T) => nomatch + // foo(U:wild(shared(U)) const(shared(T)) => nomatch + // foo(U:wild(shared(U)) wild(T) => nomatch + // foo(U:immutable(U)) wild(shared(T)) => nomatch + // foo(U:const(shared(U))) wild(shared(T)) => nomatch + // foo(U:wild(U)) wild(shared(T)) => nomatch + m = MATCHnomatch; + break; + + default: + assert(0); + } + #undef X + + Lx: + if (m == MATCHnomatch) + goto Lnomatch; + if (m < match) + match = m; + + t->objects.tdata()[i] = tt; + } + declareParameter(paramscope, tp, t); + goto L2; + } + fptupindex = -1; + } + } + +L1: + if (nfparams == nfargs) + ; + else if (nfargs > nfparams) + { + if (fvarargs == 0) + goto Lnomatch; // too many args, no match + match = MATCHconvert; // match ... with a conversion + } + +L2: +#if DMDV2 + if (ethis) + { + // Match 'ethis' to any TemplateThisParameter's + for (size_t i = 0; i < parameters->dim; i++) + { TemplateParameter *tp = parameters->tdata()[i]; + TemplateThisParameter *ttp = tp->isTemplateThisParameter(); + if (ttp) + { MATCH m; + + Type *t = new TypeIdentifier(0, ttp->ident); + m = ethis->type->deduceType(paramscope, t, parameters, &dedtypes); + if (!m) + goto Lnomatch; + if (m < match) + match = m; // pick worst match + } + } + + // Match attributes of ethis against attributes of fd + if (fd->type) + { + Type *tthis = ethis->type; + unsigned mod = fd->type->mod; + StorageClass stc = scope->stc | fd->storage_class2; + // Propagate parent storage class (see bug 5504) + Dsymbol *p = parent; + while (p->isTemplateDeclaration() || p->isTemplateInstance()) + p = p->parent; + AggregateDeclaration *ad = p->isAggregateDeclaration(); + if (ad) + stc |= ad->storage_class; + + if (stc & (STCshared | STCsynchronized)) + mod |= MODshared; + if (stc & STCimmutable) + mod |= MODimmutable; + if (stc & STCconst) + mod |= MODconst; + if (stc & STCwild) + mod |= MODwild; + // Fix mod + if (mod & MODimmutable) + mod = MODimmutable; + if (mod & MODconst) + mod &= ~STCwild; + if (tthis->mod != mod) + { + if (!MODmethodConv(tthis->mod, mod)) + goto Lnomatch; + if (MATCHconst < match) + match = MATCHconst; + } + } + } +#endif + + // Loop through the function parameters + for (size_t parami = 0; parami < nfparams; parami++) + { + /* Skip over function parameters which wound up + * as part of a template tuple parameter. + */ + if (parami == fptupindex) + continue; + /* Set i = index into function arguments + * Function parameters correspond to function arguments as follows. + * Note that tuple_dim may be zero, and there may be default or + * variadic arguments at the end. + * arg [0..fptupindex] == param[0..fptupindex] + * arg [fptupindex..fptupindex+tuple_dim] == param[fptupindex] + * arg[fputupindex+dim.. ] == param[fptupindex+1.. ] + */ + size_t i = parami; + if (fptupindex >= 0 && parami > fptupindex) + i += tuple_dim - 1; + + Parameter *fparam = Parameter::getNth(fparameters, parami); + + if (i >= nfargs) // if not enough arguments + { + if (fparam->defaultArg) + { /* Default arguments do not participate in template argument + * deduction. + */ + goto Lmatch; + } + } + else + { + Expression *farg = fargs->tdata()[i]; +Lretry: +#if 0 + printf("\tfarg->type = %s\n", farg->type->toChars()); + printf("\tfparam->type = %s\n", fparam->type->toChars()); +#endif + Type *argtype = farg->type; + + // Apply function parameter storage classes to parameter types + fparam->type = fparam->type->addStorageClass(fparam->storageClass); + +#if DMDV2 + /* Allow string literals which are type [] to match with [dim] + */ + if (farg->op == TOKstring) + { StringExp *se = (StringExp *)farg; + if (!se->committed && argtype->ty == Tarray && + fparam->type->toBasetype()->ty == Tsarray) + { + argtype = new TypeSArray(argtype->nextOf(), new IntegerExp(se->loc, se->len, Type::tindex)); + argtype = argtype->semantic(se->loc, NULL); + argtype = argtype->invariantOf(); + } + } + + /* Allow implicit function literals to delegate conversion + */ + if (farg->op == TOKfunction) + { FuncExp *fe = (FuncExp *)farg; + Type *tp = fparam->type; + if (tp->ty == Tdelegate && + fe->type->ty == Tpointer && fe->type->nextOf()->ty == Tfunction && + fe->tok == TOKreserved) + { Type *tdg = new TypeDelegate(fe->type->nextOf()); + tdg = tdg->semantic(loc, sc); + farg = fe->inferType(sc, tdg); + } + else if (fe->type == Type::tvoid) + { + farg = fe->inferType(sc, tp); + if (!farg) + goto Lvarargs; + } + argtype = farg->type; + } + + /* Remove top const for dynamic array types and pointer types + */ + if ((argtype->ty == Tarray || argtype->ty == Tpointer) && + !argtype->isMutable() && + (!(fparam->storageClass & STCref) || + (fparam->storageClass & STCauto) && !farg->isLvalue())) + { + argtype = argtype->mutableOf(); + } +#endif + + if (fvarargs == 2 && i + 1 == nfparams && i + 1 < nfargs) + goto Lvarargs; + + MATCH m; + m = argtype->deduceType(paramscope, fparam->type, parameters, &dedtypes, + tf->hasWild() ? &wildmatch : NULL); + //printf("\tdeduceType m = %d\n", m); + //if (tf->hasWild()) + // printf("\twildmatch = x%x m = %d\n", wildmatch, m); + + /* If no match, see if there's a conversion to a delegate + */ + if (!m) + { Type *tbp = fparam->type->toBasetype(); + Type *tba = farg->type->toBasetype(); + AggregateDeclaration *ad; + if (tbp->ty == Tdelegate) + { + TypeDelegate *td = (TypeDelegate *)fparam->type->toBasetype(); + TypeFunction *tf = (TypeFunction *)td->next; + + if (!tf->varargs && Parameter::dim(tf->parameters) == 0) + { + m = farg->type->deduceType(paramscope, tf->next, parameters, &dedtypes); + if (!m && tf->next->toBasetype()->ty == Tvoid) + m = MATCHconvert; + } + //printf("\tm2 = %d\n", m); + } + else if (tba->ty == Tclass) + { + ad = ((TypeClass *)tba)->sym; + goto Lad; + } + else if (tba->ty == Tstruct) + { + ad = ((TypeStruct *)tba)->sym; + Lad: + if (ad->aliasthis) + { /* If a semantic error occurs while doing alias this, + * eg purity(bug 7295), just regard it as not a match. + */ + unsigned olderrors = global.startGagging(); + Expression *e = new DotIdExp(farg->loc, farg, ad->aliasthis->ident); + e = e->semantic(sc); + e = resolveProperties(sc, e); + if (!global.endGagging(olderrors)) + { farg = e; + goto Lretry; + } + } + } + } + + if (m && (fparam->storageClass & (STCref | STCauto)) == STCref) + { if (!farg->isLvalue()) + goto Lnomatch; + } + if (m && (fparam->storageClass & STCout)) + { if (!farg->isLvalue()) + goto Lnomatch; + } + if (!m && (fparam->storageClass & STClazy) && fparam->type->ty == Tvoid && + farg->type->ty != Tvoid) + m = MATCHconvert; + + if (m) + { if (m < match) + match = m; // pick worst match + continue; + } + } + + Lvarargs: + /* The following code for variadic arguments closely + * matches TypeFunction::callMatch() + */ + if (!(fvarargs == 2 && i + 1 == nfparams)) + goto Lnomatch; + + /* Check for match with function parameter T... + */ + Type *tb = fparam->type->toBasetype(); + switch (tb->ty) + { + // Perhaps we can do better with this, see TypeFunction::callMatch() + case Tsarray: + { TypeSArray *tsa = (TypeSArray *)tb; + dinteger_t sz = tsa->dim->toInteger(); + if (sz != nfargs - i) + goto Lnomatch; + } + case Tarray: + { TypeArray *ta = (TypeArray *)tb; + for (; i < nfargs; i++) + { + Expression *arg = fargs->tdata()[i]; + assert(arg); + + if (arg->op == TOKfunction) + { FuncExp *fe = (FuncExp *)arg; + Type *tp = tb->nextOf(); + if (tp->ty == Tdelegate && + fe->type->ty == Tpointer && fe->type->nextOf()->ty == Tfunction && + fe->tok == TOKreserved) + { tp = new TypeDelegate(fe->type->nextOf()); + tp = tp->semantic(loc, sc); + arg = fe->inferType(sc, tp); + } + else if (arg->type == Type::tvoid) + { + arg = fe->inferType(sc, tp); + if (!arg) + goto Lnomatch; + } + } + + MATCH m; + /* If lazy array of delegates, + * convert arg(s) to delegate(s) + */ + Type *tret = fparam->isLazyArray(); + if (tret) + { + if (ta->next->equals(arg->type)) + { m = MATCHexact; + } + else + { + m = arg->implicitConvTo(tret); + if (m == MATCHnomatch) + { + if (tret->toBasetype()->ty == Tvoid) + m = MATCHconvert; + } + } + } + else + { + m = arg->type->deduceType(paramscope, ta->next, parameters, &dedtypes); + //m = arg->implicitConvTo(ta->next); + } + if (m == MATCHnomatch) + goto Lnomatch; + if (m < match) + match = m; + } + goto Lmatch; + } + case Tclass: + case Tident: + goto Lmatch; + + default: + goto Lnomatch; + } + } + +Lmatch: + + for (size_t i = nargsi; i < dedargs->dim; i++) + { + TemplateParameter *tparam = parameters->tdata()[i]; + //printf("tparam[%d] = %s\n", i, tparam->ident->toChars()); + /* For T:T*, the dedargs is the T*, dedtypes is the T + * But for function templates, we really need them to match + */ + Object *oarg = dedargs->tdata()[i]; + Object *oded = dedtypes.tdata()[i]; + //printf("1dedargs[%d] = %p, dedtypes[%d] = %p\n", i, oarg, i, oded); + //if (oarg) printf("oarg: %s\n", oarg->toChars()); + //if (oded) printf("oded: %s\n", oded->toChars()); + if (!oarg) + { + if (oded) + { + if (tparam->specialization()) + { /* The specialization can work as long as afterwards + * the oded == oarg + */ + Declaration *sparam; + dedargs->tdata()[i] = oded; + MATCH m2 = tparam->matchArg(paramscope, dedargs, i, parameters, &dedtypes, &sparam); + //printf("m2 = %d\n", m2); + if (!m2) + goto Lnomatch; + if (m2 < match) + match = m2; // pick worst match + if (dedtypes.tdata()[i] != oded) + error("specialization not allowed for deduced parameter %s", tparam->ident->toChars()); + } + } + else + { oded = tparam->defaultArg(loc, paramscope); + if (!oded) + { + if (tp && // if tuple parameter and + fptupindex < 0 && // tuple parameter was not in function parameter list and + nargsi == dedargs->dim - 1) // we're one argument short (i.e. no tuple argument) + { // make tuple argument an empty tuple + oded = (Object *)new Tuple(); + } + else + goto Lnomatch; + } + } + declareParameter(paramscope, tparam, oded); + dedargs->tdata()[i] = oded; + } + } + +#if DMDV2 + if (constraint) + { /* Check to see if constraint is satisfied. + * Most of this code appears twice; this is a good candidate for refactoring. + */ + makeParamNamesVisibleInConstraint(paramscope, fargs); + Expression *e = constraint->syntaxCopy(); + paramscope->flags |= SCOPEstaticif; + + /* Detect recursive attempts to instantiate this template declaration, + * Bugzilla 4072 + * void foo(T)(T x) if (is(typeof(foo(x)))) { } + * static assert(!is(typeof(foo(7)))); + * Recursive attempts are regarded as a constraint failure. + */ + int nmatches = 0; + for (Previous *p = previous; p; p = p->prev) + { + if (arrayObjectMatch(p->dedargs, dedargs, this, sc)) + { + //printf("recursive, no match p->sc=%p %p %s\n", p->sc, this, this->toChars()); + /* It must be a subscope of p->sc, other scope chains are not recursive + * instantiations. + */ + for (Scope *scx = sc; scx; scx = scx->enclosing) + { + if (scx == p->sc) + goto Lnomatch; + } + } + /* BUG: should also check for ref param differences + */ + } + + Previous pr; + pr.prev = previous; + pr.sc = paramscope; + pr.dedargs = dedargs; + previous = ≺ // add this to threaded list + + int nerrors = global.errors; + + FuncDeclaration *fd = onemember && onemember->toAlias() ? + onemember->toAlias()->isFuncDeclaration() : NULL; + Dsymbol *s = parent; + while (s->isTemplateInstance() || s->isTemplateMixin()) + s = s->parent; + AggregateDeclaration *ad = s->isAggregateDeclaration(); + VarDeclaration *vthissave; + if (fd && ad) + { + vthissave = fd->vthis; + fd->vthis = fd->declareThis(paramscope, ad); + } + + e = e->semantic(paramscope); + + if (fd && fd->vthis) + fd->vthis = vthissave; + + previous = pr.prev; // unlink from threaded list + + if (nerrors != global.errors) // if any errors from evaluating the constraint, no match + goto Lnomatch; + + e = e->optimize(WANTvalue | WANTinterpret); + if (e->isBool(TRUE)) + ; + else if (e->isBool(FALSE)) + goto Lnomatch; + else + { + e->error("constraint %s is not constant or does not evaluate to a bool", e->toChars()); + } + } +#endif + +#if 0 + for (i = 0; i < dedargs->dim; i++) + { Type *t = dedargs->tdata()[i]; + printf("\tdedargs[%d] = %d, %s\n", i, t->dyncast(), t->toChars()); + } +#endif + + paramscope->pop(); + //printf("\tmatch %d\n", match); + return match; + +Lnomatch: + paramscope->pop(); + //printf("\tnomatch\n"); + return MATCHnomatch; +} + +/************************************************** + * Declare template parameter tp with value o, and install it in the scope sc. + */ + +void TemplateDeclaration::declareParameter(Scope *sc, TemplateParameter *tp, Object *o) +{ + //printf("TemplateDeclaration::declareParameter('%s', o = %p)\n", tp->ident->toChars(), o); + + Type *targ = isType(o); + Expression *ea = isExpression(o); + Dsymbol *sa = isDsymbol(o); + Tuple *va = isTuple(o); + + Dsymbol *s; + + // See if tp->ident already exists with a matching definition + Dsymbol *scopesym; + s = sc->search(loc, tp->ident, &scopesym); + if (s && scopesym == sc->scopesym) + { + TupleDeclaration *td = s->isTupleDeclaration(); + if (va && td) + { Tuple tup; + tup.objects = *td->objects; + if (match(va, &tup, this, sc)) + { + return; + } + } + } + + if (targ) + { + //printf("type %s\n", targ->toChars()); + s = new AliasDeclaration(0, tp->ident, targ); + } + else if (sa) + { + //printf("Alias %s %s;\n", sa->ident->toChars(), tp->ident->toChars()); + s = new AliasDeclaration(0, tp->ident, sa); + } + else if (ea) + { + // tdtypes.data[i] always matches ea here + Initializer *init = new ExpInitializer(loc, ea); + TemplateValueParameter *tvp = tp->isTemplateValueParameter(); + + Type *t = tvp ? tvp->valType : NULL; + + VarDeclaration *v = new VarDeclaration(loc, t, tp->ident, init); + v->storage_class = STCmanifest; + s = v; + } + else if (va) + { + //printf("\ttuple\n"); + s = new TupleDeclaration(loc, tp->ident, &va->objects); + } + else + { +#ifdef DEBUG + o->print(); +#endif + assert(0); + } + if (!sc->insert(s)) + error("declaration %s is already defined", tp->ident->toChars()); + s->semantic(sc); +} + +/************************************** + * Determine if TemplateDeclaration is variadic. + */ + +TemplateTupleParameter *isVariadic(TemplateParameters *parameters) +{ size_t dim = parameters->dim; + TemplateTupleParameter *tp = NULL; + + if (dim) + tp = (parameters->tdata()[dim - 1])->isTemplateTupleParameter(); + return tp; +} + +TemplateTupleParameter *TemplateDeclaration::isVariadic() +{ + return ::isVariadic(parameters); +} + +/*********************************** + * We can overload templates. + */ + +int TemplateDeclaration::isOverloadable() +{ + return 1; +} + +/************************************************* + * Given function arguments, figure out which template function + * to expand, and return that function. + * If no match, give error message and return NULL. + * Input: + * sc instantiation scope + * loc instantiation location + * targsi initial list of template arguments + * ethis if !NULL, the 'this' pointer argument + * fargs arguments to function + * flags 1: do not issue error message on no match, just return NULL + */ + +FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc, + Objects *targsi, Expression *ethis, Expressions *fargs, int flags) +{ + MATCH m_best = MATCHnomatch; + TemplateDeclaration *td_ambig = NULL; + TemplateDeclaration *td_best = NULL; + Objects *tdargs = new Objects(); + TemplateInstance *ti; + FuncDeclaration *fd_best; + +#if 0 + printf("TemplateDeclaration::deduceFunctionTemplate() %s\n", toChars()); + printf(" targsi:\n"); + if (targsi) + { for (size_t i = 0; i < targsi->dim; i++) + { Object *arg = targsi->tdata()[i]; + printf("\t%s\n", arg->toChars()); + } + } + printf(" fargs:\n"); + for (size_t i = 0; i < fargs->dim; i++) + { Expression *arg = fargs->tdata()[i]; + printf("\t%s %s\n", arg->type->toChars(), arg->toChars()); + //printf("\tty = %d\n", arg->type->ty); + } + printf("stc = %llx\n", scope->stc); +#endif + + for (TemplateDeclaration *td = this; td; td = td->overnext) + { + if (!td->semanticRun) + { + error("forward reference to template %s", td->toChars()); + goto Lerror; + } + if (!td->onemember || !td->onemember->toAlias()->isFuncDeclaration()) + { + error("is not a function template"); + goto Lerror; + } + + MATCH m; + Objects dedargs; + FuncDeclaration *fd = NULL; + + m = td->deduceFunctionTemplateMatch(sc, loc, targsi, ethis, fargs, &dedargs); + //printf("deduceFunctionTemplateMatch = %d\n", m); + if (!m) // if no match + continue; + + if (m < m_best) + goto Ltd_best; + if (m > m_best) + goto Ltd; + + { + // Disambiguate by picking the most specialized TemplateDeclaration + MATCH c1 = td->leastAsSpecialized(td_best, fargs); + MATCH c2 = td_best->leastAsSpecialized(td, fargs); + //printf("1: c1 = %d, c2 = %d\n", c1, c2); + + if (c1 > c2) + goto Ltd; + else if (c1 < c2) + goto Ltd_best; + } + + if (!fd_best) + { + fd_best = td_best->doHeaderInstantiation(sc, tdargs, fargs); + if (!fd_best) + goto Lerror; + } + { + tdargs->setDim(dedargs.dim); + memcpy(tdargs->data, dedargs.data, tdargs->dim * sizeof(void *)); + fd = td->doHeaderInstantiation(sc, tdargs, fargs); + if (!fd) + goto Lerror; + } + assert(fd && fd_best); + + { + // Disambiguate by tf->callMatch + TypeFunction *tf1 = (TypeFunction *)fd->type; + TypeFunction *tf2 = (TypeFunction *)fd_best->type; + MATCH c1 = (MATCH) tf1->callMatch(fd->needThis() && !fd->isCtorDeclaration() ? ethis : NULL, fargs); + MATCH c2 = (MATCH) tf2->callMatch(fd_best->needThis() && !fd->isCtorDeclaration() ? ethis : NULL, fargs); + //printf("2: c1 = %d, c2 = %d\n", c1, c2); + + if (c1 > c2) + goto Ltd; + if (c1 < c2) + goto Ltd_best; + } + + { + // Disambiguate by picking the most specialized FunctionDeclaration + MATCH c1 = fd->leastAsSpecialized(fd_best); + MATCH c2 = fd_best->leastAsSpecialized(fd); + //printf("3: c1 = %d, c2 = %d\n", c1, c2); + + if (c1 > c2) + goto Ltd; + if (c1 < c2) + goto Ltd_best; + } + + Lambig: // td_best and td are ambiguous + td_ambig = td; + continue; + + Ltd_best: // td_best is the best match so far + td_ambig = NULL; + continue; + + Ltd: // td is the new best match + td_ambig = NULL; + assert((size_t)td->scope > 0x10000); + td_best = td; + fd_best = fd; + m_best = m; + tdargs->setDim(dedargs.dim); + memcpy(tdargs->tdata(), dedargs.tdata(), tdargs->dim * sizeof(void *)); + continue; + } + if (!td_best) + { + if (!(flags & 1)) + error(loc, "does not match any function template declaration"); + goto Lerror; + } + if (td_ambig) + { + error(loc, "%s matches more than one template declaration, %s(%d):%s and %s(%d):%s", + toChars(), + td_best->loc.filename, td_best->loc.linnum, td_best->toChars(), + td_ambig->loc.filename, td_ambig->loc.linnum, td_ambig->toChars()); + } + + /* The best match is td_best with arguments tdargs. + * Now instantiate the template. + */ + assert((size_t)td_best->scope > 0x10000); + ti = new TemplateInstance(loc, td_best, tdargs); + ti->semantic(sc, fargs); + fd_best = ti->toAlias()->isFuncDeclaration(); + if (!fd_best || !((TypeFunction*)fd_best->type)->callMatch(ethis, fargs, flags)) + goto Lerror; + return fd_best; + + Lerror: +#if DMDV2 + if (!(flags & 1)) +#endif + { + HdrGenState hgs; + + OutBuffer bufa; + Objects *args = targsi; + if (args) + { for (size_t i = 0; i < args->dim; i++) + { + if (i) + bufa.writeByte(','); + Object *oarg = args->tdata()[i]; + ObjectToCBuffer(&bufa, &hgs, oarg); + } + } + + OutBuffer buf; + argExpTypesToCBuffer(&buf, fargs, &hgs); + error(loc, "cannot deduce template function from argument types !(%s)(%s)", + bufa.toChars(), buf.toChars()); + } + return NULL; +} + +/************************************************* + * Limited function template instantiation for using fd->leastAsSpecialized() + */ +FuncDeclaration *TemplateDeclaration::doHeaderInstantiation(Scope *sc, + Objects *tdargs, Expressions *fargs) +{ + FuncDeclaration *fd = onemember->toAlias()->isFuncDeclaration(); + if (!fd) + return NULL; + +#if 0 + printf("doHeaderInstantiation this = %s\n", toChars()); + for (size_t i = 0; i < tdargs->dim; ++i) + printf("\ttdargs[%d] = %s\n", i, ((Object *)tdargs->data[i])->toChars()); +#endif + + assert((size_t)scope > 0x10000); + TemplateInstance *ti = new TemplateInstance(loc, this, tdargs); + ti->tinst = sc->tinst; + { + ti->tdtypes.setDim(ti->tempdecl->parameters->dim); + if (!ti->tempdecl->matchWithInstance(ti, &ti->tdtypes, fargs, 2)) + return NULL; + } + + ti->parent = parent; + + // function body and contracts are not need + //fd = fd->syntaxCopy(NULL)->isFuncDeclaration(); + fd = new FuncDeclaration(fd->loc, fd->endloc, fd->ident, fd->storage_class, fd->type->syntaxCopy()); + fd->parent = ti; + + Scope *scope = this->scope; + + ti->argsym = new ScopeDsymbol(); + ti->argsym->parent = scope->parent; + scope = scope->push(ti->argsym); + + Scope *paramscope = scope->push(); + paramscope->stc = 0; + ti->declareParameters(paramscope); + paramscope->pop(); + + { + TypeFunction *tf = (TypeFunction *)fd->type; + if (tf && tf->ty == Tfunction) + tf->fargs = fargs; + } + + Scope *sc2; + sc2 = scope->push(ti); + sc2->parent = /*isnested ? sc->parent :*/ ti; + sc2->tinst = ti; + + { + Scope *sc = sc2; + sc = sc->push(); + + if (fd->isCtorDeclaration()) + sc->flags |= SCOPEctor; + fd->type = fd->type->semantic(fd->loc, sc); + sc = sc->pop(); + } + //printf("\t[%s] fd->type = %s, mod = %x, ", loc.toChars(), fd->type->toChars(), fd->type->mod); + //printf("fd->needThis() = %d\n", fd->needThis()); + + sc2->pop(); + scope->pop(); + + return fd; +} + +bool TemplateDeclaration::hasStaticCtorOrDtor() +{ + return FALSE; // don't scan uninstantiated templates +} + +void TemplateDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ +#if 0 // Should handle template functions for doc generation + if (onemember && onemember->isFuncDeclaration()) + buf->writestring("foo "); +#endif + if (hgs->ddoc) + buf->writestring(kind()); + else + buf->writestring("template"); + buf->writeByte(' '); + buf->writestring(ident->toChars()); + buf->writeByte('('); + for (size_t i = 0; i < parameters->dim; i++) + { + TemplateParameter *tp = parameters->tdata()[i]; + if (hgs->ddoc) + tp = origParameters->tdata()[i]; + if (i) + buf->writeByte(','); + tp->toCBuffer(buf, hgs); + } + buf->writeByte(')'); +#if DMDV2 + if (constraint) + { buf->writestring(" if ("); + constraint->toCBuffer(buf, hgs); + buf->writeByte(')'); + } +#endif + + if (hgs->hdrgen) + { + hgs->tpltMember++; + buf->writenl(); + buf->writebyte('{'); + buf->writenl(); + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->toCBuffer(buf, hgs); + } + buf->writebyte('}'); + buf->writenl(); + hgs->tpltMember--; + } +} + + +char *TemplateDeclaration::toChars() +{ OutBuffer buf; + HdrGenState hgs; + + memset(&hgs, 0, sizeof(hgs)); + buf.writestring(ident->toChars()); + buf.writeByte('('); + for (size_t i = 0; i < parameters->dim; i++) + { + TemplateParameter *tp = parameters->tdata()[i]; + if (i) + buf.writeByte(','); + tp->toCBuffer(&buf, &hgs); + } + buf.writeByte(')'); +#if DMDV2 + if (constraint) + { buf.writestring(" if ("); + constraint->toCBuffer(&buf, &hgs); + buf.writeByte(')'); + } +#endif + buf.writeByte(0); + return (char *)buf.extractData(); +} + +/* ======================== Type ============================================ */ + +/**** + * Given an identifier, figure out which TemplateParameter it is. + * Return -1 if not found. + */ + +int templateIdentifierLookup(Identifier *id, TemplateParameters *parameters) +{ + for (size_t i = 0; i < parameters->dim; i++) + { TemplateParameter *tp = parameters->tdata()[i]; + + if (tp->ident->equals(id)) + return i; + } + return -1; +} + +int templateParameterLookup(Type *tparam, TemplateParameters *parameters) +{ + assert(tparam->ty == Tident); + TypeIdentifier *tident = (TypeIdentifier *)tparam; + //printf("\ttident = '%s'\n", tident->toChars()); + if (tident->idents.dim == 0) + { + return templateIdentifierLookup(tident->ident, parameters); + } + return -1; +} + +/* These form the heart of template argument deduction. + * Given 'this' being the type argument to the template instance, + * it is matched against the template declaration parameter specialization + * 'tparam' to determine the type to be used for the parameter. + * Example: + * template Foo(T:T*) // template declaration + * Foo!(int*) // template instantiation + * Input: + * this = int* + * tparam = T + * parameters = [ T:T* ] // Array of TemplateParameter's + * Output: + * dedtypes = [ int ] // Array of Expression/Type's + */ + +MATCH Type::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, + Objects *dedtypes, unsigned *wildmatch) +{ +#if 0 + printf("Type::deduceType()\n"); + printf("\tthis = %d, ", ty); print(); + printf("\ttparam = %d, ", tparam->ty); tparam->print(); +#endif + if (!tparam) + goto Lnomatch; + + if (this == tparam) + goto Lexact; + + if (tparam->ty == Tident) + { + // Determine which parameter tparam is + int i = templateParameterLookup(tparam, parameters); + if (i == -1) + { + if (!sc) + goto Lnomatch; + + /* Need a loc to go with the semantic routine. + */ + Loc loc; + if (parameters->dim) + { + TemplateParameter *tp = parameters->tdata()[0]; + loc = tp->loc; + } + + /* BUG: what if tparam is a template instance, that + * has as an argument another Tident? + */ + tparam = tparam->semantic(loc, sc); + assert(tparam->ty != Tident); + return deduceType(sc, tparam, parameters, dedtypes, wildmatch); + } + + TemplateParameter *tp = parameters->tdata()[i]; + + // Found the corresponding parameter tp + if (!tp->isTemplateTypeParameter()) + goto Lnomatch; + Type *tt = this; + Type *at = (Type *)dedtypes->tdata()[i]; + + // 7*7 == 49 cases + + #define X(U,T) ((U) << 4) | (T) + + if (wildmatch && (tparam->mod & MODwild)) + { + switch (X(tparam->mod, mod)) + { + case X(MODwild, MODwild): + case X(MODwild | MODshared, MODwild | MODshared): + case X(MODwild, 0): + case X(MODwild, MODconst): + case X(MODwild, MODimmutable): + case X(MODwild | MODshared, MODshared): + case X(MODwild | MODshared, MODconst | MODshared): + + if (!at) + { + if (mod & MODwild) + *wildmatch |= MODwild; + else if (mod == 0) + *wildmatch |= MODmutable; + else + *wildmatch |= (mod & ~MODshared); + tt = mutableOf(); + dedtypes->tdata()[i] = tt; + goto Lconst; + } + + //printf("\t> tt = %s, at = %s\n", tt->toChars(), at->toChars()); + //printf("\t> tt->implicitConvTo(at->constOf()) = %d\n", tt->implicitConvTo(at->constOf())); + //printf("\t> at->implicitConvTo(tt->constOf()) = %d\n", at->implicitConvTo(tt->constOf())); + + if (tt->equals(at)) + { + goto Lconst; + } + else if (tt->implicitConvTo(at->constOf())) + { + dedtypes->tdata()[i] = at->constOf()->mutableOf(); + *wildmatch |= MODconst; + goto Lconst; + } + else if (at->implicitConvTo(tt->constOf())) + { + dedtypes->tdata()[i] = tt->constOf()->mutableOf(); + *wildmatch |= MODconst; + goto Lconst; + } + goto Lnomatch; + + default: + break; + } + } + + switch (X(tparam->mod, mod)) + { + case X(0, 0): + case X(0, MODconst): + case X(0, MODimmutable): + case X(0, MODshared): + case X(0, MODconst | MODshared): + case X(0, MODwild): + case X(0, MODwild | MODshared): + // foo(U:U) T => T + // foo(U:U) const(T) => const(T) + // foo(U:U) immutable(T) => immutable(T) + // foo(U:U) shared(T) => shared(T) + // foo(U:U) const(shared(T)) => const(shared(T)) + // foo(U:U) wild(T) => wild(T) + // foo(U:U) wild(shared(T)) => wild(shared(T)) + if (!at) + { dedtypes->tdata()[i] = tt; + goto Lexact; + } + break; + + case X(MODconst, MODconst): + case X(MODimmutable, MODimmutable): + case X(MODshared, MODshared): + case X(MODconst | MODshared, MODconst | MODshared): + case X(MODwild, MODwild): + case X(MODwild | MODshared, MODwild | MODshared): + // foo(U:const(U)) const(T) => T + // foo(U:immutable(U)) immutable(T) => T + // foo(U:shared(U)) shared(T) => T + // foo(U:const(shared(U)) const(shared(T)) => T + // foo(U:wild(U)) wild(T) => T + // foo(U:wild(shared(U)) wild(shared(T)) => T + tt = mutableOf()->unSharedOf(); + if (!at) + { dedtypes->tdata()[i] = tt; + goto Lexact; + } + break; + + case X(MODconst, 0): + case X(MODconst, MODimmutable): + case X(MODconst, MODconst | MODshared): + case X(MODconst | MODshared, MODimmutable): + case X(MODconst, MODwild): + case X(MODconst, MODwild | MODshared): + // foo(U:const(U)) T => T + // foo(U:const(U)) immutable(T) => T + // foo(U:const(U)) const(shared(T)) => shared(T) + // foo(U:const(shared(U)) immutable(T) => T + // foo(U:const(U)) wild(shared(T)) => shared(T) + tt = mutableOf(); + if (!at) + { dedtypes->tdata()[i] = tt; + goto Lconst; + } + break; + + case X(MODshared, MODconst | MODshared): + case X(MODconst | MODshared, MODshared): + case X(MODshared, MODwild | MODshared): + // foo(U:shared(U)) const(shared(T)) => const(T) + // foo(U:const(shared(U)) shared(T) => T + // foo(U:shared(U)) wild(shared(T)) => wild(T) + tt = unSharedOf(); + if (!at) + { dedtypes->tdata()[i] = tt; + goto Lconst; + } + break; + + case X(MODimmutable, 0): + case X(MODimmutable, MODconst): + case X(MODimmutable, MODshared): + case X(MODimmutable, MODconst | MODshared): + case X(MODconst, MODshared): + case X(MODshared, 0): + case X(MODshared, MODconst): + case X(MODshared, MODimmutable): + case X(MODconst | MODshared, 0): + case X(MODconst | MODshared, MODconst): + case X(MODimmutable, MODwild): + case X(MODshared, MODwild): + case X(MODconst | MODshared, MODwild): + case X(MODwild, 0): + case X(MODwild, MODconst): + case X(MODwild, MODimmutable): + case X(MODwild, MODshared): + case X(MODwild, MODconst | MODshared): + case X(MODwild | MODshared, 0): + case X(MODwild | MODshared, MODconst): + case X(MODwild | MODshared, MODimmutable): + case X(MODwild | MODshared, MODshared): + case X(MODwild | MODshared, MODconst | MODshared): + case X(MODwild | MODshared, MODwild): + case X(MODimmutable, MODwild | MODshared): + case X(MODconst | MODshared, MODwild | MODshared): + case X(MODwild, MODwild | MODshared): + + // foo(U:immutable(U)) T => nomatch + // foo(U:immutable(U)) const(T) => nomatch + // foo(U:immutable(U)) shared(T) => nomatch + // foo(U:immutable(U)) const(shared(T)) => nomatch + // foo(U:const(U)) shared(T) => nomatch + // foo(U:shared(U)) T => nomatch + // foo(U:shared(U)) const(T) => nomatch + // foo(U:shared(U)) immutable(T) => nomatch + // foo(U:const(shared(U)) T => nomatch + // foo(U:const(shared(U)) const(T) => nomatch + // foo(U:immutable(U)) wild(T) => nomatch + // foo(U:shared(U)) wild(T) => nomatch + // foo(U:const(shared(U)) wild(T) => nomatch + // foo(U:wild(U)) T => nomatch + // foo(U:wild(U)) const(T) => nomatch + // foo(U:wild(U)) immutable(T) => nomatch + // foo(U:wild(U)) shared(T) => nomatch + // foo(U:wild(U)) const(shared(T)) => nomatch + // foo(U:wild(shared(U)) T => nomatch + // foo(U:wild(shared(U)) const(T) => nomatch + // foo(U:wild(shared(U)) immutable(T) => nomatch + // foo(U:wild(shared(U)) shared(T) => nomatch + // foo(U:wild(shared(U)) const(shared(T)) => nomatch + // foo(U:wild(shared(U)) wild(T) => nomatch + // foo(U:immutable(U)) wild(shared(T)) => nomatch + // foo(U:const(shared(U))) wild(shared(T)) => nomatch + // foo(U:wild(U)) wild(shared(T)) => nomatch + //if (!at) + goto Lnomatch; + break; + + default: + assert(0); + } + #undef X + + if (tt->equals(at)) + goto Lexact; + else if (tt->ty == Tclass && at->ty == Tclass) + { + return tt->implicitConvTo(at); + } + else if (tt->ty == Tsarray && at->ty == Tarray && + tt->nextOf()->implicitConvTo(at->nextOf()) >= MATCHconst) + { + goto Lexact; + } + else + goto Lnomatch; + } + else if (tparam->ty == Ttypeof) + { + /* Need a loc to go with the semantic routine. + */ + Loc loc; + if (parameters->dim) + { + TemplateParameter *tp = parameters->tdata()[0]; + loc = tp->loc; + } + + tparam = tparam->semantic(loc, sc); + } + + if (ty != tparam->ty) + { +#if DMDV2 + // Can't instantiate AssociativeArray!() without a scope + if (tparam->ty == Taarray && !((TypeAArray*)tparam)->sc) + ((TypeAArray*)tparam)->sc = sc; + + MATCH m = implicitConvTo(tparam); + if (m == MATCHnomatch) + { + Type *at = aliasthisOf(); + if (at) + m = at->deduceType(sc, tparam, parameters, dedtypes, wildmatch); + } + return m; +#else + return implicitConvTo(tparam); +#endif + } + + if (nextOf()) + return nextOf()->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wildmatch); + +Lexact: + return MATCHexact; + +Lnomatch: + return MATCHnomatch; + +#if DMDV2 +Lconst: + return MATCHconst; +#endif +} + +#if DMDV2 +MATCH TypeDArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, + Objects *dedtypes, unsigned *wildmatch) +{ +#if 0 + printf("TypeDArray::deduceType()\n"); + printf("\tthis = %d, ", ty); print(); + printf("\ttparam = %d, ", tparam->ty); tparam->print(); +#endif + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); +} +#endif + +MATCH TypeSArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, + Objects *dedtypes, unsigned *wildmatch) +{ +#if 0 + printf("TypeSArray::deduceType()\n"); + printf("\tthis = %d, ", ty); print(); + printf("\ttparam = %d, ", tparam->ty); tparam->print(); +#endif + + // Extra check that array dimensions must match + if (tparam) + { + if (tparam->ty == Tarray) + { MATCH m; + + m = next->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wildmatch); + if (m == MATCHexact) + m = MATCHconvert; + return m; + } + + Identifier *id = NULL; + if (tparam->ty == Tsarray) + { + TypeSArray *tp = (TypeSArray *)tparam; + if (tp->dim->op == TOKvar && + ((VarExp *)tp->dim)->var->storage_class & STCtemplateparameter) + { + id = ((VarExp *)tp->dim)->var->ident; + } + else if (dim->toInteger() != tp->dim->toInteger()) + return MATCHnomatch; + } + else if (tparam->ty == Taarray) + { + TypeAArray *tp = (TypeAArray *)tparam; + if (tp->index->ty == Tident && + ((TypeIdentifier *)tp->index)->idents.dim == 0) + { + id = ((TypeIdentifier *)tp->index)->ident; + } + } + if (id) + { + // This code matches code in TypeInstance::deduceType() + int i = templateIdentifierLookup(id, parameters); + if (i == -1) + goto Lnomatch; + TemplateParameter *tprm = parameters->tdata()[i]; + TemplateValueParameter *tvp = tprm->isTemplateValueParameter(); + if (!tvp) + goto Lnomatch; + Expression *e = (Expression *)dedtypes->tdata()[i]; + if (e) + { + if (!dim->equals(e)) + goto Lnomatch; + } + else + { + Type *vt = tvp->valType->semantic(0, sc); + MATCH m = (MATCH)dim->implicitConvTo(vt); + if (!m) + goto Lnomatch; + dedtypes->tdata()[i] = dim; + } + return next->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wildmatch); + } + } + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); + + Lnomatch: + return MATCHnomatch; +} + +MATCH TypeAArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) +{ +#if 0 + printf("TypeAArray::deduceType()\n"); + printf("\tthis = %d, ", ty); print(); + printf("\ttparam = %d, ", tparam->ty); tparam->print(); +#endif + + // Extra check that index type must match + if (tparam && tparam->ty == Taarray) + { + TypeAArray *tp = (TypeAArray *)tparam; + if (!index->deduceType(sc, tp->index, parameters, dedtypes, wildmatch)) + { + return MATCHnomatch; + } + } + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); +} + +MATCH TypeFunction::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) +{ + //printf("TypeFunction::deduceType()\n"); + //printf("\tthis = %d, ", ty); print(); + //printf("\ttparam = %d, ", tparam->ty); tparam->print(); + + // Extra check that function characteristics must match + if (tparam && tparam->ty == Tfunction) + { + TypeFunction *tp = (TypeFunction *)tparam; + if (varargs != tp->varargs || + linkage != tp->linkage) + return MATCHnomatch; + + size_t nfargs = Parameter::dim(this->parameters); + size_t nfparams = Parameter::dim(tp->parameters); + + // bug 2579 fix: Apply function parameter storage classes to parameter types + for (size_t i = 0; i < nfparams; i++) + { + Parameter *fparam = Parameter::getNth(tp->parameters, i); + fparam->type = fparam->type->addStorageClass(fparam->storageClass); + fparam->storageClass &= ~(STC_TYPECTOR | STCin); + } + //printf("\t-> this = %d, ", ty); print(); + //printf("\t-> tparam = %d, ", tparam->ty); tparam->print(); + + /* See if tuple match + */ + if (nfparams > 0 && nfargs >= nfparams - 1) + { + /* See if 'A' of the template parameter matches 'A' + * of the type of the last function parameter. + */ + Parameter *fparam = Parameter::getNth(tp->parameters, nfparams - 1); + assert(fparam); + assert(fparam->type); + if (fparam->type->ty != Tident) + goto L1; + TypeIdentifier *tid = (TypeIdentifier *)fparam->type; + if (tid->idents.dim) + goto L1; + + /* Look through parameters to find tuple matching tid->ident + */ + size_t tupi = 0; + for (; 1; tupi++) + { if (tupi == parameters->dim) + goto L1; + TemplateParameter *t = parameters->tdata()[tupi]; + TemplateTupleParameter *tup = t->isTemplateTupleParameter(); + if (tup && tup->ident->equals(tid->ident)) + break; + } + + /* The types of the function arguments [nfparams - 1 .. nfargs] + * now form the tuple argument. + */ + size_t tuple_dim = nfargs - (nfparams - 1); + + /* See if existing tuple, and whether it matches or not + */ + Object *o = dedtypes->tdata()[tupi]; + if (o) + { // Existing deduced argument must be a tuple, and must match + Tuple *t = isTuple(o); + if (!t || t->objects.dim != tuple_dim) + return MATCHnomatch; + for (size_t i = 0; i < tuple_dim; i++) + { Parameter *arg = Parameter::getNth(this->parameters, nfparams - 1 + i); + if (!arg->type->equals(t->objects.tdata()[i])) + return MATCHnomatch; + } + } + else + { // Create new tuple + Tuple *t = new Tuple(); + t->objects.setDim(tuple_dim); + for (size_t i = 0; i < tuple_dim; i++) + { Parameter *arg = Parameter::getNth(this->parameters, nfparams - 1 + i); + t->objects.tdata()[i] = arg->type; + } + dedtypes->tdata()[tupi] = t; + } + nfparams--; // don't consider the last parameter for type deduction + goto L2; + } + + L1: + if (nfargs != nfparams) + return MATCHnomatch; + L2: + for (size_t i = 0; i < nfparams; i++) + { + Parameter *a = Parameter::getNth(this->parameters, i); + Parameter *ap = Parameter::getNth(tp->parameters, i); + if (a->storageClass != ap->storageClass || + !a->type->deduceType(sc, ap->type, parameters, dedtypes, wildmatch)) + return MATCHnomatch; + } + } + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); +} + +MATCH TypeIdentifier::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) +{ + // Extra check + if (tparam && tparam->ty == Tident) + { + TypeIdentifier *tp = (TypeIdentifier *)tparam; + + for (size_t i = 0; i < idents.dim; i++) + { + Identifier *id1 = idents.tdata()[i]; + Identifier *id2 = tp->idents.tdata()[i]; + + if (!id1->equals(id2)) + return MATCHnomatch; + } + } + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); +} + +MATCH TypeInstance::deduceType(Scope *sc, + Type *tparam, TemplateParameters *parameters, + Objects *dedtypes, unsigned *wildmatch) +{ +#if 0 + printf("TypeInstance::deduceType()\n"); + printf("\tthis = %d, ", ty); print(); + printf("\ttparam = %d, ", tparam->ty); tparam->print(); +#endif + + // Extra check + if (tparam && tparam->ty == Tinstance) + { + TypeInstance *tp = (TypeInstance *)tparam; + + //printf("tempinst->tempdecl = %p\n", tempinst->tempdecl); + //printf("tp->tempinst->tempdecl = %p\n", tp->tempinst->tempdecl); + if (!tp->tempinst->tempdecl) + { //printf("tp->tempinst->name = '%s'\n", tp->tempinst->name->toChars()); + if (!tp->tempinst->name->equals(tempinst->name)) + { + /* Handle case of: + * template Foo(T : sa!(T), alias sa) + */ + int i = templateIdentifierLookup(tp->tempinst->name, parameters); + if (i == -1) + { /* Didn't find it as a parameter identifier. Try looking + * it up and seeing if is an alias. See Bugzilla 1454 + */ + Dsymbol *s = tempinst->tempdecl->scope->search(0, tp->tempinst->name, NULL); + if (s) + { + s = s->toAlias(); + TemplateDeclaration *td = s->isTemplateDeclaration(); + if (td && td == tempinst->tempdecl) + goto L2; + } + goto Lnomatch; + } + TemplateParameter *tpx = parameters->tdata()[i]; + // This logic duplicates tpx->matchArg() + TemplateAliasParameter *ta = tpx->isTemplateAliasParameter(); + if (!ta) + goto Lnomatch; + Object *sa = tempinst->tempdecl; + if (!sa) + goto Lnomatch; + if (ta->specAlias && sa != ta->specAlias) + goto Lnomatch; + if (dedtypes->tdata()[i]) + { // Must match already deduced symbol + Object *s = dedtypes->tdata()[i]; + + if (s != sa) + goto Lnomatch; + } + dedtypes->tdata()[i] = sa; + } + } + else if (tempinst->tempdecl != tp->tempinst->tempdecl) + goto Lnomatch; + + L2: + + for (size_t i = 0; 1; i++) + { + //printf("\ttest: tempinst->tiargs[%d]\n", i); + Object *o1; + if (i < tempinst->tiargs->dim) + o1 = tempinst->tiargs->tdata()[i]; + else if (i < tempinst->tdtypes.dim && i < tp->tempinst->tiargs->dim) + // Pick up default arg + o1 = tempinst->tdtypes.tdata()[i]; + else + break; + + if (i >= tp->tempinst->tiargs->dim) + goto Lnomatch; + + Object *o2 = tp->tempinst->tiargs->tdata()[i]; + + Type *t1 = isType(o1); + Type *t2 = isType(o2); + + Expression *e1 = isExpression(o1); + Expression *e2 = isExpression(o2); + + Dsymbol *s1 = isDsymbol(o1); + Dsymbol *s2 = isDsymbol(o2); + + Tuple *v1 = isTuple(o1); + Tuple *v2 = isTuple(o2); +#if 0 + if (t1) printf("t1 = %s\n", t1->toChars()); + if (t2) printf("t2 = %s\n", t2->toChars()); + if (e1) printf("e1 = %s\n", e1->toChars()); + if (e2) printf("e2 = %s\n", e2->toChars()); + if (s1) printf("s1 = %s\n", s1->toChars()); + if (s2) printf("s2 = %s\n", s2->toChars()); + if (v1) printf("v1 = %s\n", v1->toChars()); + if (v2) printf("v2 = %s\n", v2->toChars()); +#endif + + TemplateTupleParameter *ttp; + int j; + if (t2 && + t2->ty == Tident && + i == tp->tempinst->tiargs->dim - 1 && + i == tempinst->tempdecl->parameters->dim - 1 && + (ttp = tempinst->tempdecl->isVariadic()) != NULL) + { + /* Given: + * struct A(B...) {} + * alias A!(int, float) X; + * static if (!is(X Y == A!(Z), Z)) + * deduce that Z is a tuple(int, float) + */ + + j = templateParameterLookup(t2, parameters); + if (j == -1) + goto Lnomatch; + + /* Create tuple from remaining args + */ + Tuple *vt = new Tuple(); + size_t vtdim = tempinst->tiargs->dim - i; + vt->objects.setDim(vtdim); + for (size_t k = 0; k < vtdim; k++) + vt->objects.tdata()[k] = tempinst->tiargs->tdata()[i + k]; + + Tuple *v = (Tuple *)dedtypes->tdata()[j]; + if (v) + { + if (!match(v, vt, tempinst->tempdecl, sc)) + goto Lnomatch; + } + else + dedtypes->tdata()[j] = vt; + break; //return MATCHexact; + } + + if (t1 && t2) + { + if (!t1->deduceType(sc, t2, parameters, dedtypes, wildmatch)) + goto Lnomatch; + } + else if (e1 && e2) + { + if (!e1->equals(e2)) + { if (e2->op == TOKvar) + { + /* + * (T:Number!(e2), int e2) + */ + j = templateIdentifierLookup(((VarExp *)e2)->var->ident, parameters); + goto L1; + } + goto Lnomatch; + } + } + else if (e1 && t2 && t2->ty == Tident) + { + j = templateParameterLookup(t2, parameters); + L1: + if (j == -1) + goto Lnomatch; + TemplateParameter *tp = parameters->tdata()[j]; + // BUG: use tp->matchArg() instead of the following + TemplateValueParameter *tv = tp->isTemplateValueParameter(); + if (!tv) + goto Lnomatch; + Expression *e = (Expression *)dedtypes->tdata()[j]; + if (e) + { + if (!e1->equals(e)) + goto Lnomatch; + } + else + { Type *vt = tv->valType->semantic(0, sc); + MATCH m = (MATCH)e1->implicitConvTo(vt); + if (!m) + goto Lnomatch; + dedtypes->tdata()[j] = e1; + } + } + else if (s1 && t2 && t2->ty == Tident) + { + j = templateParameterLookup(t2, parameters); + if (j == -1) + goto Lnomatch; + TemplateParameter *tp = parameters->tdata()[j]; + // BUG: use tp->matchArg() instead of the following + TemplateAliasParameter *ta = tp->isTemplateAliasParameter(); + if (!ta) + goto Lnomatch; + Dsymbol *s = (Dsymbol *)dedtypes->tdata()[j]; + if (s) + { + if (!s1->equals(s)) + goto Lnomatch; + } + else + { + dedtypes->tdata()[j] = s1; + } + } + else if (s1 && s2) + { + if (!s1->equals(s2)) + goto Lnomatch; + } + // BUG: Need to handle tuple parameters + else + goto Lnomatch; + } + } + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); + +Lnomatch: + //printf("no match\n"); + return MATCHnomatch; +} + +MATCH TypeStruct::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) +{ + //printf("TypeStruct::deduceType()\n"); + //printf("\tthis->parent = %s, ", sym->parent->toChars()); print(); + //printf("\ttparam = %d, ", tparam->ty); tparam->print(); + + /* If this struct is a template struct, and we're matching + * it against a template instance, convert the struct type + * to a template instance, too, and try again. + */ + TemplateInstance *ti = sym->parent->isTemplateInstance(); + + if (tparam && tparam->ty == Tinstance) + { + if (ti && ti->toAlias() == sym) + { + TypeInstance *t = new TypeInstance(0, ti); + return t->deduceType(sc, tparam, parameters, dedtypes, wildmatch); + } + + /* Match things like: + * S!(T).foo + */ + TypeInstance *tpi = (TypeInstance *)tparam; + if (tpi->idents.dim) + { Identifier *id = tpi->idents.tdata()[tpi->idents.dim - 1]; + if (id->dyncast() == DYNCAST_IDENTIFIER && sym->ident->equals(id)) + { + Type *tparent = sym->parent->getType(); + if (tparent) + { + /* Slice off the .foo in S!(T).foo + */ + tpi->idents.dim--; + MATCH m = tparent->deduceType(sc, tpi, parameters, dedtypes, wildmatch); + tpi->idents.dim++; + return m; + } + } + } + } + + // Extra check + if (tparam && tparam->ty == Tstruct) + { + TypeStruct *tp = (TypeStruct *)tparam; + + //printf("\t%d\n", (MATCH) implicitConvTo(tp)); + return implicitConvTo(tp); + } + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); +} + +MATCH TypeEnum::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) +{ + // Extra check + if (tparam && tparam->ty == Tenum) + { + TypeEnum *tp = (TypeEnum *)tparam; + + if (sym != tp->sym) + return MATCHnomatch; + } + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); +} + +MATCH TypeTypedef::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) +{ + // Extra check + if (tparam && tparam->ty == Ttypedef) + { + TypeTypedef *tp = (TypeTypedef *)tparam; + + if (sym != tp->sym) + return MATCHnomatch; + } + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); +} + +/* Helper for TypeClass::deduceType(). + * Classes can match with implicit conversion to a base class or interface. + * This is complicated, because there may be more than one base class which + * matches. In such cases, one or more parameters remain ambiguous. + * For example, + * + * interface I(X, Y) {} + * class C : I(uint, double), I(char, double) {} + * C x; + * foo(T, U)( I!(T, U) x) + * + * deduces that U is double, but T remains ambiguous (could be char or uint). + * + * Given a baseclass b, and initial deduced types 'dedtypes', this function + * tries to match tparam with b, and also tries all base interfaces of b. + * If a match occurs, numBaseClassMatches is incremented, and the new deduced + * types are ANDed with the current 'best' estimate for dedtypes. + */ +void deduceBaseClassParameters(BaseClass *b, + Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, + Objects *best, int &numBaseClassMatches) +{ + TemplateInstance *parti = b->base ? b->base->parent->isTemplateInstance() : NULL; + if (parti) + { + // Make a temporary copy of dedtypes so we don't destroy it + Objects *tmpdedtypes = new Objects(); + tmpdedtypes->setDim(dedtypes->dim); + memcpy(tmpdedtypes->tdata(), dedtypes->tdata(), dedtypes->dim * sizeof(void *)); + + TypeInstance *t = new TypeInstance(0, parti); + MATCH m = t->deduceType(sc, tparam, parameters, tmpdedtypes); + if (m != MATCHnomatch) + { + // If this is the first ever match, it becomes our best estimate + if (numBaseClassMatches==0) + memcpy(best->tdata(), tmpdedtypes->tdata(), tmpdedtypes->dim * sizeof(void *)); + else for (size_t k = 0; k < tmpdedtypes->dim; ++k) + { + // If we've found more than one possible type for a parameter, + // mark it as unknown. + if (tmpdedtypes->tdata()[k] != best->tdata()[k]) + best->tdata()[k] = dedtypes->tdata()[k]; + } + ++numBaseClassMatches; + } + } + // Now recursively test the inherited interfaces + for (size_t j = 0; j < b->baseInterfaces_dim; ++j) + { + deduceBaseClassParameters( &(b->baseInterfaces)[j], + sc, tparam, parameters, dedtypes, + best, numBaseClassMatches); + } + +} + +MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) +{ + //printf("TypeClass::deduceType(this = %s)\n", toChars()); + + /* If this class is a template class, and we're matching + * it against a template instance, convert the class type + * to a template instance, too, and try again. + */ + TemplateInstance *ti = sym->parent->isTemplateInstance(); + + if (tparam && tparam->ty == Tinstance) + { + if (ti && ti->toAlias() == sym) + { + TypeInstance *t = new TypeInstance(0, ti); + MATCH m = t->deduceType(sc, tparam, parameters, dedtypes, wildmatch); + // Even if the match fails, there is still a chance it could match + // a base class. + if (m != MATCHnomatch) + return m; + } + + /* Match things like: + * S!(T).foo + */ + TypeInstance *tpi = (TypeInstance *)tparam; + if (tpi->idents.dim) + { Identifier *id = tpi->idents.tdata()[tpi->idents.dim - 1]; + if (id->dyncast() == DYNCAST_IDENTIFIER && sym->ident->equals(id)) + { + Type *tparent = sym->parent->getType(); + if (tparent) + { + /* Slice off the .foo in S!(T).foo + */ + tpi->idents.dim--; + MATCH m = tparent->deduceType(sc, tpi, parameters, dedtypes, wildmatch); + tpi->idents.dim++; + return m; + } + } + } + + // If it matches exactly or via implicit conversion, we're done + MATCH m = Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); + if (m != MATCHnomatch) + return m; + + /* There is still a chance to match via implicit conversion to + * a base class or interface. Because there could be more than one such + * match, we need to check them all. + */ + + int numBaseClassMatches = 0; // Have we found an interface match? + + // Our best guess at dedtypes + Objects *best = new Objects(); + best->setDim(dedtypes->dim); + + ClassDeclaration *s = sym; + while(s && s->baseclasses->dim > 0) + { + // Test the base class + deduceBaseClassParameters((s->baseclasses->tdata()[0]), + sc, tparam, parameters, dedtypes, + best, numBaseClassMatches); + + // Test the interfaces inherited by the base class + for (size_t i = 0; i < s->interfaces_dim; ++i) + { + BaseClass *b = s->interfaces[i]; + deduceBaseClassParameters(b, sc, tparam, parameters, dedtypes, + best, numBaseClassMatches); + } + s = ((s->baseclasses->tdata()[0]))->base; + } + + if (numBaseClassMatches == 0) + return MATCHnomatch; + + // If we got at least one match, copy the known types into dedtypes + memcpy(dedtypes->tdata(), best->tdata(), best->dim * sizeof(void *)); + return MATCHconvert; + } + + // Extra check + if (tparam && tparam->ty == Tclass) + { + TypeClass *tp = (TypeClass *)tparam; + + //printf("\t%d\n", (MATCH) implicitConvTo(tp)); + return implicitConvTo(tp); + } + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); +} + +/* ======================== TemplateParameter =============================== */ + +TemplateParameter::TemplateParameter(Loc loc, Identifier *ident) +{ + this->loc = loc; + this->ident = ident; + this->sparam = NULL; +} + +TemplateTypeParameter *TemplateParameter::isTemplateTypeParameter() +{ + return NULL; +} + +TemplateValueParameter *TemplateParameter::isTemplateValueParameter() +{ + return NULL; +} + +TemplateAliasParameter *TemplateParameter::isTemplateAliasParameter() +{ + return NULL; +} + +TemplateTupleParameter *TemplateParameter::isTemplateTupleParameter() +{ + return NULL; +} + +#if DMDV2 +TemplateThisParameter *TemplateParameter::isTemplateThisParameter() +{ + return NULL; +} +#endif + +/* ======================== TemplateTypeParameter =========================== */ + +// type-parameter + +Type *TemplateTypeParameter::tdummy = NULL; + +TemplateTypeParameter::TemplateTypeParameter(Loc loc, Identifier *ident, Type *specType, + Type *defaultType) + : TemplateParameter(loc, ident) +{ + this->ident = ident; + this->specType = specType; + this->defaultType = defaultType; +} + +TemplateTypeParameter *TemplateTypeParameter::isTemplateTypeParameter() +{ + return this; +} + +TemplateParameter *TemplateTypeParameter::syntaxCopy() +{ + TemplateTypeParameter *tp = new TemplateTypeParameter(loc, ident, specType, defaultType); + if (tp->specType) + tp->specType = specType->syntaxCopy(); + if (defaultType) + tp->defaultType = defaultType->syntaxCopy(); + return tp; +} + +void TemplateTypeParameter::declareParameter(Scope *sc) +{ + //printf("TemplateTypeParameter::declareParameter('%s')\n", ident->toChars()); + TypeIdentifier *ti = new TypeIdentifier(loc, ident); + sparam = new AliasDeclaration(loc, ident, ti); + if (!sc->insert(sparam)) + error(loc, "parameter '%s' multiply defined", ident->toChars()); +} + +void TemplateTypeParameter::semantic(Scope *sc) +{ + //printf("TemplateTypeParameter::semantic('%s')\n", ident->toChars()); + if (specType) + { + specType = specType->semantic(loc, sc); + } +#if 0 // Don't do semantic() until instantiation + if (defaultType) + { + defaultType = defaultType->semantic(loc, sc); + } +#endif +} + +/**************************************** + * Determine if two TemplateParameters are the same + * as far as TemplateDeclaration overloading goes. + * Returns: + * 1 match + * 0 no match + */ + +int TemplateTypeParameter::overloadMatch(TemplateParameter *tp) +{ + TemplateTypeParameter *ttp = tp->isTemplateTypeParameter(); + + if (ttp) + { + if (specType != ttp->specType) + goto Lnomatch; + + if (specType && !specType->equals(ttp->specType)) + goto Lnomatch; + + return 1; // match + } + +Lnomatch: + return 0; +} + +/******************************************* + * Match to a particular TemplateParameter. + * Input: + * i i'th argument + * tiargs[] actual arguments to template instance + * parameters[] template parameters + * dedtypes[] deduced arguments to template instance + * *psparam set to symbol declared and initialized to dedtypes[i] + */ + +MATCH TemplateTypeParameter::matchArg(Scope *sc, Objects *tiargs, + size_t i, TemplateParameters *parameters, Objects *dedtypes, + Declaration **psparam) +{ + //printf("TemplateTypeParameter::matchArg()\n"); + Object *oarg; + MATCH m = MATCHexact; + Type *ta; + + if (i < tiargs->dim) + oarg = tiargs->tdata()[i]; + else + { // Get default argument instead + oarg = defaultArg(loc, sc); + if (!oarg) + { assert(i < dedtypes->dim); + // It might have already been deduced + oarg = dedtypes->tdata()[i]; + if (!oarg) + { + goto Lnomatch; + } + } + } + + ta = isType(oarg); + if (!ta) + { + //printf("%s %p %p %p\n", oarg->toChars(), isExpression(oarg), isDsymbol(oarg), isTuple(oarg)); + goto Lnomatch; + } + //printf("ta is %s\n", ta->toChars()); + + if (specType) + { + if (!ta || ta == tdummy) + goto Lnomatch; + + //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta->toChars(), specType->toChars()); + MATCH m2 = ta->deduceType(sc, specType, parameters, dedtypes); + if (m2 == MATCHnomatch) + { //printf("\tfailed deduceType\n"); + goto Lnomatch; + } + + if (m2 < m) + m = m2; + if (dedtypes->tdata()[i]) + ta = (Type *)dedtypes->tdata()[i]; + } + else + { + if (dedtypes->tdata()[i]) + { // Must match already deduced type + Type *t = (Type *)dedtypes->tdata()[i]; + + if (!t->equals(ta)) + { //printf("t = %s ta = %s\n", t->toChars(), ta->toChars()); + goto Lnomatch; + } + } + else + { + // So that matches with specializations are better + m = MATCHconvert; + } + } + dedtypes->tdata()[i] = ta; + + *psparam = new AliasDeclaration(loc, ident, ta); + //printf("\tm = %d\n", m); + return m; + +Lnomatch: + *psparam = NULL; + //printf("\tm = %d\n", MATCHnomatch); + return MATCHnomatch; +} + + +void TemplateTypeParameter::print(Object *oarg, Object *oded) +{ + printf(" %s\n", ident->toChars()); + + Type *t = isType(oarg); + Type *ta = isType(oded); + + assert(ta); + + if (specType) + printf("\tSpecialization: %s\n", specType->toChars()); + if (defaultType) + printf("\tDefault: %s\n", defaultType->toChars()); + printf("\tParameter: %s\n", t ? t->toChars() : "NULL"); + printf("\tDeduced Type: %s\n", ta->toChars()); +} + + +void TemplateTypeParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(ident->toChars()); + if (specType) + { + buf->writestring(" : "); + specType->toCBuffer(buf, NULL, hgs); + } + if (defaultType) + { + buf->writestring(" = "); + defaultType->toCBuffer(buf, NULL, hgs); + } +} + + +void *TemplateTypeParameter::dummyArg() +{ Type *t; + + if (specType) + t = specType; + else + { // Use this for alias-parameter's too (?) + if (!tdummy) + tdummy = new TypeIdentifier(loc, ident); + t = tdummy; + } + return (void *)t; +} + + +Object *TemplateTypeParameter::specialization() +{ + return specType; +} + + +Object *TemplateTypeParameter::defaultArg(Loc loc, Scope *sc) +{ + Type *t; + + t = defaultType; + if (t) + { + t = t->syntaxCopy(); + t = t->semantic(loc, sc); + } + return t; +} + +/* ======================== TemplateThisParameter =========================== */ + +#if DMDV2 +// this-parameter + +TemplateThisParameter::TemplateThisParameter(Loc loc, Identifier *ident, + Type *specType, + Type *defaultType) + : TemplateTypeParameter(loc, ident, specType, defaultType) +{ +} + +TemplateThisParameter *TemplateThisParameter::isTemplateThisParameter() +{ + return this; +} + +TemplateParameter *TemplateThisParameter::syntaxCopy() +{ + TemplateThisParameter *tp = new TemplateThisParameter(loc, ident, specType, defaultType); + if (tp->specType) + tp->specType = specType->syntaxCopy(); + if (defaultType) + tp->defaultType = defaultType->syntaxCopy(); + return tp; +} + +void TemplateThisParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("this "); + TemplateTypeParameter::toCBuffer(buf, hgs); +} +#endif + +/* ======================== TemplateAliasParameter ========================== */ + +// alias-parameter + +Dsymbol *TemplateAliasParameter::sdummy = NULL; + +TemplateAliasParameter::TemplateAliasParameter(Loc loc, Identifier *ident, + Type *specType, Object *specAlias, Object *defaultAlias) + : TemplateParameter(loc, ident) +{ + this->ident = ident; + this->specType = specType; + this->specAlias = specAlias; + this->defaultAlias = defaultAlias; +} + +TemplateAliasParameter *TemplateAliasParameter::isTemplateAliasParameter() +{ + return this; +} + +TemplateParameter *TemplateAliasParameter::syntaxCopy() +{ + TemplateAliasParameter *tp = new TemplateAliasParameter(loc, ident, specType, specAlias, defaultAlias); + if (tp->specType) + tp->specType = specType->syntaxCopy(); + tp->specAlias = objectSyntaxCopy(specAlias); + tp->defaultAlias = objectSyntaxCopy(defaultAlias); + return tp; +} + +void TemplateAliasParameter::declareParameter(Scope *sc) +{ + TypeIdentifier *ti = new TypeIdentifier(loc, ident); + sparam = new AliasDeclaration(loc, ident, ti); + if (!sc->insert(sparam)) + error(loc, "parameter '%s' multiply defined", ident->toChars()); +} + +Object *aliasParameterSemantic(Loc loc, Scope *sc, Object *o) +{ + if (o) + { + Expression *ea = isExpression(o); + Type *ta = isType(o); + if (ta) + { Dsymbol *s = ta->toDsymbol(sc); + if (s) + o = s; + else + o = ta->semantic(loc, sc); + } + else if (ea) + { + ea = ea->semantic(sc); + o = ea->optimize(WANTvalue | WANTinterpret); + } + } + return o; +} + +void TemplateAliasParameter::semantic(Scope *sc) +{ + if (specType) + { + specType = specType->semantic(loc, sc); + } + specAlias = aliasParameterSemantic(loc, sc, specAlias); +#if 0 // Don't do semantic() until instantiation + if (defaultAlias) + defaultAlias = defaultAlias->semantic(loc, sc); +#endif +} + +int TemplateAliasParameter::overloadMatch(TemplateParameter *tp) +{ + TemplateAliasParameter *tap = tp->isTemplateAliasParameter(); + + if (tap) + { + if (specAlias != tap->specAlias) + goto Lnomatch; + + return 1; // match + } + +Lnomatch: + return 0; +} + +MATCH TemplateAliasParameter::matchArg(Scope *sc, Objects *tiargs, + size_t i, TemplateParameters *parameters, Objects *dedtypes, + Declaration **psparam) +{ + Object *sa; + Object *oarg; + Expression *ea; + Dsymbol *s; + + //printf("TemplateAliasParameter::matchArg()\n"); + + if (i < tiargs->dim) + oarg = tiargs->tdata()[i]; + else + { // Get default argument instead + oarg = defaultArg(loc, sc); + if (!oarg) + { assert(i < dedtypes->dim); + // It might have already been deduced + oarg = dedtypes->tdata()[i]; + if (!oarg) + goto Lnomatch; + } + } + + sa = getDsymbol(oarg); + if (sa) + { + /* specType means the alias must be a declaration with a type + * that matches specType. + */ + if (specType) + { Declaration *d = ((Dsymbol *)sa)->isDeclaration(); + if (!d) + goto Lnomatch; + if (!d->type->equals(specType)) + goto Lnomatch; + } + } + else + { + sa = oarg; + ea = isExpression(oarg); + if (ea) + { if (specType) + { + if (!ea->type->equals(specType)) + goto Lnomatch; + } + } + else + goto Lnomatch; + } + + if (specAlias) + { + if (sa == sdummy) + goto Lnomatch; + if (sa != specAlias) + goto Lnomatch; + } + else if (dedtypes->tdata()[i]) + { // Must match already deduced symbol + Object *si = dedtypes->tdata()[i]; + + if (!sa || si != sa) + goto Lnomatch; + } + dedtypes->tdata()[i] = sa; + + s = isDsymbol(sa); + if (s) + *psparam = new AliasDeclaration(loc, ident, s); + else + { + assert(ea); + + // Declare manifest constant + Initializer *init = new ExpInitializer(loc, ea); + VarDeclaration *v = new VarDeclaration(loc, NULL, ident, init); + v->storage_class = STCmanifest; + v->semantic(sc); + *psparam = v; + } + return MATCHexact; + +Lnomatch: + *psparam = NULL; + //printf("\tm = %d\n", MATCHnomatch); + return MATCHnomatch; +} + + +void TemplateAliasParameter::print(Object *oarg, Object *oded) +{ + printf(" %s\n", ident->toChars()); + + Dsymbol *sa = isDsymbol(oded); + assert(sa); + + printf("\tParameter alias: %s\n", sa->toChars()); +} + +void TemplateAliasParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("alias "); + if (specType) + { HdrGenState hgs1; + specType->toCBuffer(buf, ident, &hgs1); + } + else + buf->writestring(ident->toChars()); + if (specAlias) + { + buf->writestring(" : "); + ObjectToCBuffer(buf, hgs, specAlias); + } + if (defaultAlias) + { + buf->writestring(" = "); + ObjectToCBuffer(buf, hgs, defaultAlias); + } +} + + +void *TemplateAliasParameter::dummyArg() +{ Object *s; + + s = specAlias; + if (!s) + { + if (!sdummy) + sdummy = new Dsymbol(); + s = sdummy; + } + return (void*)s; +} + + +Object *TemplateAliasParameter::specialization() +{ + return specAlias; +} + + +Object *TemplateAliasParameter::defaultArg(Loc loc, Scope *sc) +{ + Object *da = defaultAlias; + Type *ta = isType(defaultAlias); + if (ta) + { + if (ta->ty == Tinstance) + { + // If the default arg is a template, instantiate for each type + da = ta->syntaxCopy(); + } + } + + Object *o = aliasParameterSemantic(loc, sc, da); + return o; +} + +/* ======================== TemplateValueParameter ========================== */ + +// value-parameter + +AA *TemplateValueParameter::edummies = NULL; + +TemplateValueParameter::TemplateValueParameter(Loc loc, Identifier *ident, Type *valType, + Expression *specValue, Expression *defaultValue) + : TemplateParameter(loc, ident) +{ + this->ident = ident; + this->valType = valType; + this->specValue = specValue; + this->defaultValue = defaultValue; +} + +TemplateValueParameter *TemplateValueParameter::isTemplateValueParameter() +{ + return this; +} + +TemplateParameter *TemplateValueParameter::syntaxCopy() +{ + TemplateValueParameter *tp = + new TemplateValueParameter(loc, ident, valType, specValue, defaultValue); + tp->valType = valType->syntaxCopy(); + if (specValue) + tp->specValue = specValue->syntaxCopy(); + if (defaultValue) + tp->defaultValue = defaultValue->syntaxCopy(); + return tp; +} + +void TemplateValueParameter::declareParameter(Scope *sc) +{ + VarDeclaration *v = new VarDeclaration(loc, valType, ident, NULL); + v->storage_class = STCtemplateparameter; + if (!sc->insert(v)) + error(loc, "parameter '%s' multiply defined", ident->toChars()); + sparam = v; +} + +void TemplateValueParameter::semantic(Scope *sc) +{ + sparam->semantic(sc); + valType = valType->semantic(loc, sc); + if (!(valType->isintegral() || valType->isfloating() || valType->isString()) && + valType->ty != Tident) + { + if (valType != Type::terror) + error(loc, "arithmetic/string type expected for value-parameter, not %s", valType->toChars()); + } + + if (specValue) + { Expression *e = specValue; + + e = e->semantic(sc); + e = e->implicitCastTo(sc, valType); + e = e->optimize(WANTvalue | WANTinterpret); + if (e->op == TOKint64 || e->op == TOKfloat64 || + e->op == TOKcomplex80 || e->op == TOKnull || e->op == TOKstring) + specValue = e; + //e->toInteger(); + } + +#if 0 // defer semantic analysis to arg match + if (defaultValue) + { Expression *e = defaultValue; + + e = e->semantic(sc); + e = e->implicitCastTo(sc, valType); + e = e->optimize(WANTvalue | WANTinterpret); + if (e->op == TOKint64) + defaultValue = e; + //e->toInteger(); + } +#endif +} + +int TemplateValueParameter::overloadMatch(TemplateParameter *tp) +{ + TemplateValueParameter *tvp = tp->isTemplateValueParameter(); + + if (tvp) + { + if (valType != tvp->valType) + goto Lnomatch; + + if (valType && !valType->equals(tvp->valType)) + goto Lnomatch; + + if (specValue != tvp->specValue) + goto Lnomatch; + + return 1; // match + } + +Lnomatch: + return 0; +} + + +MATCH TemplateValueParameter::matchArg(Scope *sc, Objects *tiargs, + size_t i, TemplateParameters *parameters, Objects *dedtypes, + Declaration **psparam) +{ + //printf("TemplateValueParameter::matchArg()\n"); + + Initializer *init; + Declaration *sparam; + MATCH m = MATCHexact; + Expression *ei; + Object *oarg; + + if (i < tiargs->dim) + oarg = tiargs->tdata()[i]; + else + { // Get default argument instead + oarg = defaultArg(loc, sc); + if (!oarg) + { assert(i < dedtypes->dim); + // It might have already been deduced + oarg = dedtypes->tdata()[i]; + if (!oarg) + goto Lnomatch; + } + } + + ei = isExpression(oarg); + Type *vt; + + if (!ei && oarg) + goto Lnomatch; + + if (ei && ei->op == TOKvar) + { // Resolve const variables that we had skipped earlier + ei = ei->optimize(WANTvalue | WANTinterpret); + } + + //printf("\tvalType: %s, ty = %d\n", valType->toChars(), valType->ty); + vt = valType->semantic(0, sc); + //printf("ei: %s, ei->type: %s\n", ei->toChars(), ei->type->toChars()); + //printf("vt = %s\n", vt->toChars()); + + if (ei->type) + { + m = (MATCH)ei->implicitConvTo(vt); + //printf("m: %d\n", m); + if (!m) + goto Lnomatch; + } + + if (specValue) + { + if (!ei || _aaGetRvalue(edummies, ei->type) == ei) + goto Lnomatch; + + Expression *e = specValue; + + e = e->semantic(sc); + e = e->implicitCastTo(sc, valType); + e = e->optimize(WANTvalue | WANTinterpret); + + ei = ei->syntaxCopy(); + ei = ei->semantic(sc); + ei = ei->implicitCastTo(sc, vt); + ei = ei->optimize(WANTvalue | WANTinterpret); + //printf("\tei: %s, %s\n", ei->toChars(), ei->type->toChars()); + //printf("\te : %s, %s\n", e->toChars(), e->type->toChars()); + if (!ei->equals(e)) + goto Lnomatch; + } + else + { + if (dedtypes->tdata()[i]) + { // Must match already deduced value + Expression *e = (Expression *)dedtypes->tdata()[i]; + + if (!ei || !ei->equals(e)) + goto Lnomatch; + } + else if (m != MATCHexact) + { + ei = ei->implicitCastTo(sc, vt); + ei = ei->optimize(WANTvalue | WANTinterpret); + } + } + dedtypes->tdata()[i] = ei; + + init = new ExpInitializer(loc, ei); + sparam = new VarDeclaration(loc, vt, ident, init); + sparam->storage_class = STCmanifest; + *psparam = sparam; + return m; + +Lnomatch: + //printf("\tno match\n"); + *psparam = NULL; + return MATCHnomatch; +} + + +void TemplateValueParameter::print(Object *oarg, Object *oded) +{ + printf(" %s\n", ident->toChars()); + + Expression *ea = isExpression(oded); + + if (specValue) + printf("\tSpecialization: %s\n", specValue->toChars()); + printf("\tParameter Value: %s\n", ea ? ea->toChars() : "NULL"); +} + + +void TemplateValueParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + valType->toCBuffer(buf, ident, hgs); + if (specValue) + { + buf->writestring(" : "); + specValue->toCBuffer(buf, hgs); + } + if (defaultValue) + { + buf->writestring(" = "); + defaultValue->toCBuffer(buf, hgs); + } +} + + +void *TemplateValueParameter::dummyArg() +{ Expression *e; + + e = specValue; + if (!e) + { + // Create a dummy value + Expression **pe = (Expression **)_aaGet(&edummies, valType); + if (!*pe) + *pe = valType->defaultInit(); + e = *pe; + } + return (void *)e; +} + + +Object *TemplateValueParameter::specialization() +{ + return specValue; +} + + +Object *TemplateValueParameter::defaultArg(Loc loc, Scope *sc) +{ + Expression *e = defaultValue; + if (e) + { + e = e->syntaxCopy(); + e = e->semantic(sc); +#if DMDV2 + e = e->resolveLoc(loc, sc); +#endif + } + return e; +} + +/* ======================== TemplateTupleParameter ========================== */ + +// variadic-parameter + +TemplateTupleParameter::TemplateTupleParameter(Loc loc, Identifier *ident) + : TemplateParameter(loc, ident) +{ + this->ident = ident; +} + +TemplateTupleParameter *TemplateTupleParameter::isTemplateTupleParameter() +{ + return this; +} + +TemplateParameter *TemplateTupleParameter::syntaxCopy() +{ + TemplateTupleParameter *tp = new TemplateTupleParameter(loc, ident); + return tp; +} + +void TemplateTupleParameter::declareParameter(Scope *sc) +{ + TypeIdentifier *ti = new TypeIdentifier(loc, ident); + sparam = new AliasDeclaration(loc, ident, ti); + if (!sc->insert(sparam)) + error(loc, "parameter '%s' multiply defined", ident->toChars()); +} + +void TemplateTupleParameter::semantic(Scope *sc) +{ +} + +int TemplateTupleParameter::overloadMatch(TemplateParameter *tp) +{ + TemplateTupleParameter *tvp = tp->isTemplateTupleParameter(); + + if (tvp) + { + return 1; // match + } + + return 0; +} + +MATCH TemplateTupleParameter::matchArg(Scope *sc, Objects *tiargs, + size_t i, TemplateParameters *parameters, Objects *dedtypes, + Declaration **psparam) +{ + //printf("TemplateTupleParameter::matchArg()\n"); + + /* The rest of the actual arguments (tiargs[]) form the match + * for the variadic parameter. + */ + assert(i + 1 == dedtypes->dim); // must be the last one + Tuple *ovar; + + if (dedtypes->tdata()[i] && isTuple(dedtypes->tdata()[i])) + // It was already been deduced + ovar = isTuple(dedtypes->tdata()[i]); + else if (i + 1 == tiargs->dim && isTuple(tiargs->tdata()[i])) + ovar = isTuple(tiargs->tdata()[i]); + else + { + ovar = new Tuple(); + //printf("ovar = %p\n", ovar); + if (i < tiargs->dim) + { + //printf("i = %d, tiargs->dim = %d\n", i, tiargs->dim); + ovar->objects.setDim(tiargs->dim - i); + for (size_t j = 0; j < ovar->objects.dim; j++) + ovar->objects.tdata()[j] = tiargs->tdata()[i + j]; + } + } + *psparam = new TupleDeclaration(loc, ident, &ovar->objects); + dedtypes->tdata()[i] = ovar; + return MATCHexact; +} + + +void TemplateTupleParameter::print(Object *oarg, Object *oded) +{ + printf(" %s... [", ident->toChars()); + Tuple *v = isTuple(oded); + assert(v); + + //printf("|%d| ", v->objects.dim); + for (size_t i = 0; i < v->objects.dim; i++) + { + if (i) + printf(", "); + + Object *o = v->objects.tdata()[i]; + + Dsymbol *sa = isDsymbol(o); + if (sa) + printf("alias: %s", sa->toChars()); + + Type *ta = isType(o); + if (ta) + printf("type: %s", ta->toChars()); + + Expression *ea = isExpression(o); + if (ea) + printf("exp: %s", ea->toChars()); + + assert(!isTuple(o)); // no nested Tuple arguments + } + + printf("]\n"); +} + +void TemplateTupleParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring(ident->toChars()); + buf->writestring("..."); +} + + +void *TemplateTupleParameter::dummyArg() +{ + return NULL; +} + + +Object *TemplateTupleParameter::specialization() +{ + return NULL; +} + + +Object *TemplateTupleParameter::defaultArg(Loc loc, Scope *sc) +{ + return NULL; +} + +/* ======================== TemplateInstance ================================ */ + +TemplateInstance::TemplateInstance(Loc loc, Identifier *ident) + : ScopeDsymbol(NULL) +{ +#if LOG + printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident->toChars() : "null"); +#endif + this->loc = loc; + this->name = ident; + this->tiargs = NULL; + this->tempdecl = NULL; + this->inst = NULL; + this->tinst = NULL; + this->argsym = NULL; + this->aliasdecl = NULL; + this->semanticRun = 0; + this->semantictiargsdone = 0; + this->withsym = NULL; + this->nest = 0; + this->havetempdecl = 0; + this->isnested = NULL; + this->errors = 0; + this->speculative = 0; +} + +/***************** + * This constructor is only called when we figured out which function + * template to instantiate. + */ + +TemplateInstance::TemplateInstance(Loc loc, TemplateDeclaration *td, Objects *tiargs) + : ScopeDsymbol(NULL) +{ +#if LOG + printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td->toChars()); +#endif + this->loc = loc; + this->name = td->ident; + this->tiargs = tiargs; + this->tempdecl = td; + this->inst = NULL; + this->tinst = NULL; + this->argsym = NULL; + this->aliasdecl = NULL; + this->semanticRun = 0; + this->semantictiargsdone = 1; + this->withsym = NULL; + this->nest = 0; + this->havetempdecl = 1; + this->isnested = NULL; + this->errors = 0; + this->speculative = 0; + + assert((size_t)tempdecl->scope > 0x10000); +} + + +Objects *TemplateInstance::arraySyntaxCopy(Objects *objs) +{ + Objects *a = NULL; + if (objs) + { a = new Objects(); + a->setDim(objs->dim); + for (size_t i = 0; i < objs->dim; i++) + { + a->tdata()[i] = objectSyntaxCopy(objs->tdata()[i]); + } + } + return a; +} + +Dsymbol *TemplateInstance::syntaxCopy(Dsymbol *s) +{ + TemplateInstance *ti; + + if (s) + ti = (TemplateInstance *)s; + else + ti = new TemplateInstance(loc, name); + + ti->tiargs = arraySyntaxCopy(tiargs); + + ScopeDsymbol::syntaxCopy(ti); + return ti; +} + + +void TemplateInstance::semantic(Scope *sc) +{ + semantic(sc, NULL); +} + +void TemplateInstance::semantic(Scope *sc, Expressions *fargs) +{ + //printf("TemplateInstance::semantic('%s', this=%p, gag = %d, sc = %p)\n", toChars(), this, global.gag, sc); + if (global.errors && name != Id::AssociativeArray) + { + //printf("not instantiating %s due to %d errors\n", toChars(), global.errors); + if (!global.gag) + { + /* Trying to soldier on rarely generates useful messages + * at this point. + */ + fatal(); + } +// return; + } +#if LOG + printf("\n+TemplateInstance::semantic('%s', this=%p)\n", toChars(), this); +#endif + if (inst) // if semantic() was already run + { +#if LOG + printf("-TemplateInstance::semantic('%s', this=%p) already run\n", inst->toChars(), inst); +#endif + return; + } + + // get the enclosing template instance from the scope tinst + tinst = sc->tinst; + + if (semanticRun != 0) + { +#if LOG + printf("Recursive template expansion\n"); +#endif + error(loc, "recursive template expansion"); +// inst = this; + return; + } + semanticRun = 1; + +#if LOG + printf("\tdo semantic\n"); +#endif + if (havetempdecl) + { + assert((size_t)tempdecl->scope > 0x10000); + // Deduce tdtypes + tdtypes.setDim(tempdecl->parameters->dim); + if (!tempdecl->matchWithInstance(this, &tdtypes, fargs, 2)) + { + error("incompatible arguments for template instantiation"); + inst = this; + return; + } + } + else + { + /* Run semantic on each argument, place results in tiargs[] + * (if we havetempdecl, then tiargs is already evaluated) + */ + semanticTiargs(sc); + if (arrayObjectIsError(tiargs)) + { inst = this; + //printf("error return %p, %d\n", tempdecl, global.errors); + return; // error recovery + } + + tempdecl = findTemplateDeclaration(sc); + if (tempdecl) + tempdecl = findBestMatch(sc, fargs); + if (!tempdecl || global.errors) + { inst = this; + //printf("error return %p, %d\n", tempdecl, global.errors); + return; // error recovery + } + } + + // If tempdecl is a mixin, disallow it + if (tempdecl->ismixin) + error("mixin templates are not regular templates"); + + hasNestedArgs(tiargs); + + /* See if there is an existing TemplateInstantiation that already + * implements the typeargs. If so, just refer to that one instead. + */ + + for (size_t i = 0; i < tempdecl->instances.dim; i++) + { + TemplateInstance *ti = tempdecl->instances.tdata()[i]; +#if LOG + printf("\t%s: checking for match with instance %d (%p): '%s'\n", toChars(), i, ti, ti->toChars()); +#endif + assert(tdtypes.dim == ti->tdtypes.dim); + + // Nesting must match + if (isnested != ti->isnested) + { + //printf("test2 isnested %s ti->isnested %s\n", isnested ? isnested->toChars() : "", ti->isnested ? ti->isnested->toChars() : ""); + continue; + } +#if 0 + if (isnested && sc->parent != ti->parent) + continue; +#endif + if (!arrayObjectMatch(&tdtypes, &ti->tdtypes, tempdecl, sc)) + goto L1; + + /* Template functions may have different instantiations based on + * "auto ref" parameters. + */ + if (fargs) + { + FuncDeclaration *fd = ti->toAlias()->isFuncDeclaration(); + if (fd) + { + Parameters *fparameters = fd->getParameters(NULL); + size_t nfparams = Parameter::dim(fparameters); // Num function parameters + for (size_t j = 0; j < nfparams && j < fargs->dim; j++) + { Parameter *fparam = Parameter::getNth(fparameters, j); + Expression *farg = fargs->tdata()[j]; + if (fparam->storageClass & STCauto) // if "auto ref" + { + if (farg->isLvalue()) + { if (!(fparam->storageClass & STCref)) + goto L1; // auto ref's don't match + } + else + { if (fparam->storageClass & STCref) + goto L1; // auto ref's don't match + } + } + } + } + } + + // It's a match + inst = ti; + parent = ti->parent; + + // If both this and the previous instantiation were speculative, + // use the number of errors that happened last time. + if (inst->speculative && global.gag) + { + global.errors += inst->errors; + global.gaggedErrors += inst->errors; + } + + // If the first instantiation was speculative, but this is not: + if (inst->speculative && !global.gag) + { + // If the first instantiation had failed, re-run semantic, + // so that error messages are shown. + if (inst->errors) + goto L1; + // It had succeeded, mark it is a non-speculative instantiation, + // and reuse it. + inst->speculative = 0; + } + +#if LOG + printf("\tit's a match with instance %p\n", inst); +#endif + return; + + L1: + ; + } + + /* So, we need to implement 'this' instance. + */ +#if LOG + printf("\timplement template instance %s '%s'\n", tempdecl->parent->toChars(), toChars()); + printf("\ttempdecl %s\n", tempdecl->toChars()); +#endif + unsigned errorsave = global.errors; + inst = this; + // Mark as speculative if we are instantiated from inside is(typeof()) + if (global.gag && sc->intypeof) + speculative = 1; + + int tempdecl_instance_idx = tempdecl->instances.dim; + tempdecl->instances.push(this); + parent = tempdecl->parent; + //printf("parent = '%s'\n", parent->kind()); + + ident = genIdent(tiargs); // need an identifier for name mangling purposes. + +#if 1 + if (isnested) + parent = isnested; +#endif + //printf("parent = '%s'\n", parent->kind()); + + // Add 'this' to the enclosing scope's members[] so the semantic routines + // will get called on the instance members. Store the place we added it to + // in target_symbol_list(_idx) so we can remove it later if we encounter + // an error. +#if 1 + int dosemantic3 = 0; + Dsymbols *target_symbol_list = NULL; + int target_symbol_list_idx; + + if (!sc->parameterSpecialization) + { Dsymbols *a; + + Scope *scx = sc; +#if 0 + for (scx = sc; scx; scx = scx->enclosing) + if (scx->scopesym) + break; +#endif + + //if (scx && scx->scopesym) printf("3: scx is %s %s\n", scx->scopesym->kind(), scx->scopesym->toChars()); + if (scx && scx->scopesym && + scx->scopesym->members && !scx->scopesym->isTemplateMixin() +#if 0 // removed because it bloated compile times + /* The problem is if A imports B, and B imports A, and both A + * and B instantiate the same template, does the compilation of A + * or the compilation of B do the actual instantiation? + * + * see bugzilla 2500. + */ + && !scx->module->selfImports() +#endif + ) + { + //printf("\t1: adding to %s %s\n", scx->scopesym->kind(), scx->scopesym->toChars()); + a = scx->scopesym->members; + } + else + { Module *m = sc->module->importedFrom; + //printf("\t2: adding to module %s instead of module %s\n", m->toChars(), sc->module->toChars()); + a = m->members; + if (m->semanticRun >= 3) + { + dosemantic3 = 1; + } + } + for (int i = 0; 1; i++) + { + if (i == a->dim) + { + target_symbol_list = a; + target_symbol_list_idx = i; + a->push(this); + break; + } + if (this == a->tdata()[i]) // if already in Array + break; + } + } +#endif + + // Copy the syntax trees from the TemplateDeclaration + members = Dsymbol::arraySyntaxCopy(tempdecl->members); + + // Create our own scope for the template parameters + Scope *scope = tempdecl->scope; + if (!tempdecl->semanticRun) + { + error("template instantiation %s forward references template declaration %s\n", toChars(), tempdecl->toChars()); + return; + } + +#if LOG + printf("\tcreate scope for template parameters '%s'\n", toChars()); +#endif + argsym = new ScopeDsymbol(); + argsym->parent = scope->parent; + scope = scope->push(argsym); +// scope->stc = 0; + + // Declare each template parameter as an alias for the argument type + Scope *paramscope = scope->push(); + paramscope->stc = 0; + declareParameters(paramscope); + paramscope->pop(); + + // Add members of template instance to template instance symbol table +// parent = scope->scopesym; + symtab = new DsymbolTable(); + int memnum = 0; + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; +#if LOG + printf("\t[%d] adding member '%s' %p kind %s to '%s', memnum = %d\n", i, s->toChars(), s, s->kind(), this->toChars(), memnum); +#endif + memnum |= s->addMember(scope, this, memnum); + } +#if LOG + printf("adding members done\n"); +#endif + + /* See if there is only one member of template instance, and that + * member has the same name as the template instance. + * If so, this template instance becomes an alias for that member. + */ + //printf("members->dim = %d\n", members->dim); + if (members->dim) + { + Dsymbol *s; + if (Dsymbol::oneMembers(members, &s, tempdecl->ident) && s) + { + //printf("s->kind = '%s'\n", s->kind()); + //s->print(); + //printf("'%s', '%s'\n", s->ident->toChars(), tempdecl->ident->toChars()); + //printf("setting aliasdecl\n"); + aliasdecl = new AliasDeclaration(loc, s->ident, s); + } + } + + /* If function template declaration + */ + if (fargs && aliasdecl) + { + FuncDeclaration *fd = aliasdecl->toAlias()->isFuncDeclaration(); + if (fd) + { + /* Transmit fargs to type so that TypeFunction::semantic() can + * resolve any "auto ref" storage classes. + */ + TypeFunction *tf = (TypeFunction *)fd->type; + if (tf && tf->ty == Tfunction) + tf->fargs = fargs; + } + } + + // Do semantic() analysis on template instance members +#if LOG + printf("\tdo semantic() on template instance members '%s'\n", toChars()); +#endif + Scope *sc2; + sc2 = scope->push(this); + //printf("isnested = %d, sc->parent = %s\n", isnested, sc->parent->toChars()); + sc2->parent = /*isnested ? sc->parent :*/ this; + sc2->tinst = this; + +#if WINDOWS_SEH + __try + { +#endif + static int nest; + //printf("%d\n", nest); + if (++nest > 500) + { + global.gag = 0; // ensure error message gets printed + error("recursive expansion"); + fatal(); + } + + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = (*members)[i]; + s->setScope(sc2); + } + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + //printf("\t[%d] semantic on '%s' %p kind %s in '%s'\n", i, s->toChars(), s, s->kind(), this->toChars()); + //printf("test: isnested = %d, sc2->parent = %s\n", isnested, sc2->parent->toChars()); +// if (isnested) +// s->parent = sc->parent; + //printf("test3: isnested = %d, s->parent = %s\n", isnested, s->parent->toChars()); + s->semantic(sc2); + //printf("test4: isnested = %d, s->parent = %s\n", isnested, s->parent->toChars()); + sc2->module->runDeferredSemantic(); + } + --nest; +#if WINDOWS_SEH + } + __except (__ehfilter(GetExceptionInformation())) + { + global.gag = 0; // ensure error message gets printed + error("recursive expansion"); + fatal(); + } +#endif + + /* If any of the instantiation members didn't get semantic() run + * on them due to forward references, we cannot run semantic2() + * or semantic3() yet. + */ + for (size_t i = 0; i < Module::deferred.dim; i++) + { Dsymbol *sd = Module::deferred.tdata()[i]; + + if (sd->parent == this) + { + //printf("deferred %s %s\n", sd->parent->toChars(), sd->toChars()); + AggregateDeclaration *ad = sd->isAggregateDeclaration(); + if (ad) + ad->deferred = this; + goto Laftersemantic; + } + } + + /* ConditionalDeclaration may introduce eponymous declaration, + * so we should find it once again after semantic. + */ + if (members->dim) + { + Dsymbol *s; + if (Dsymbol::oneMembers(members, &s, tempdecl->ident) && s) + { + if (!aliasdecl || aliasdecl->toAlias() != s) + { + //printf("s->kind = '%s'\n", s->kind()); + //s->print(); + //printf("'%s', '%s'\n", s->ident->toChars(), tempdecl->ident->toChars()); + //printf("setting aliasdecl 2\n"); + aliasdecl = new AliasDeclaration(loc, s->ident, s); + } + } + else if (aliasdecl) + aliasdecl = NULL; + } + + /* The problem is when to parse the initializer for a variable. + * Perhaps VarDeclaration::semantic() should do it like it does + * for initializers inside a function. + */ +// if (sc->parent->isFuncDeclaration()) + + /* BUG 782: this has problems if the classes this depends on + * are forward referenced. Find a way to defer semantic() + * on this template. + */ + semantic2(sc2); + + if (sc->func || dosemantic3) + { +#if WINDOWS_SEH + __try + { +#endif + static int nest; + if (++nest > 300) + { + global.gag = 0; // ensure error message gets printed + error("recursive expansion"); + fatal(); + } + semantic3(sc2); + --nest; +#if WINDOWS_SEH + } + __except (__ehfilter(GetExceptionInformation())) + { + global.gag = 0; // ensure error message gets printed + error("recursive expansion"); + fatal(); + } +#endif + } + + Laftersemantic: + sc2->pop(); + + scope->pop(); + + // Give additional context info if error occurred during instantiation + if (global.errors != errorsave) + { + error(loc, "error instantiating"); + if (tinst) + { tinst->printInstantiationTrace(); + } + errors = 1; + if (global.gag) + { + // Errors are gagged, so remove the template instance from the + // instance/symbol lists we added it to and reset our state to + // finish clean and so we can try to instantiate it again later + // (see bugzilla 4302 and 6602). + tempdecl->instances.remove(tempdecl_instance_idx); + if (target_symbol_list) + { + // Because we added 'this' in the last position above, we + // should be able to remove it without messing other indices up. + assert(target_symbol_list->tdata()[target_symbol_list_idx] == this); + target_symbol_list->remove(target_symbol_list_idx); + } + semanticRun = 0; + inst = NULL; + } + } + +#if LOG + printf("-TemplateInstance::semantic('%s', this=%p)\n", toChars(), this); +#endif +} + + +void TemplateInstance::semanticTiargs(Scope *sc) +{ + //printf("+TemplateInstance::semanticTiargs() %s\n", toChars()); + if (semantictiargsdone) + return; + semantictiargsdone = 1; + semanticTiargs(loc, sc, tiargs, 0); +} + +/********************************** + * Input: + * flags 1: replace const variables with their initializers + */ + +void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags) +{ + // Run semantic on each argument, place results in tiargs[] + //printf("+TemplateInstance::semanticTiargs()\n"); + if (!tiargs) + return; + for (size_t j = 0; j < tiargs->dim; j++) + { + Object *o = tiargs->tdata()[j]; + Type *ta = isType(o); + Expression *ea = isExpression(o); + Dsymbol *sa = isDsymbol(o); + + //printf("1: tiargs->tdata()[%d] = %p, %p, %p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta); + if (ta) + { + //printf("type %s\n", ta->toChars()); + // It might really be an Expression or an Alias + ta->resolve(loc, sc, &ea, &ta, &sa); + if (ea) + { + ea = ea->semantic(sc); + /* This test is to skip substituting a const var with + * its initializer. The problem is the initializer won't + * match with an 'alias' parameter. Instead, do the + * const substitution in TemplateValueParameter::matchArg(). + */ + if (flags & 1) // only used by __traits, must not interpret the args + ea = ea->optimize(WANTvalue); + else if (ea->op != TOKvar) + ea = ea->optimize(WANTvalue | WANTinterpret); + tiargs->tdata()[j] = ea; + } + else if (sa) + { + Ldsym: + tiargs->tdata()[j] = sa; + TupleDeclaration *d = sa->toAlias()->isTupleDeclaration(); + if (d) + { + size_t dim = d->objects->dim; + tiargs->remove(j); + tiargs->insert(j, d->objects); + j--; + } + } + else if (ta) + { + Ltype: + if (ta->ty == Ttuple) + { // Expand tuple + TypeTuple *tt = (TypeTuple *)ta; + size_t dim = tt->arguments->dim; + tiargs->remove(j); + if (dim) + { tiargs->reserve(dim); + for (size_t i = 0; i < dim; i++) + { Parameter *arg = tt->arguments->tdata()[i]; + tiargs->insert(j + i, arg->type); + } + } + j--; + } + else + tiargs->tdata()[j] = ta; + } + else + { + assert(global.errors); + tiargs->tdata()[j] = Type::terror; + } + } + else if (ea) + { + if (!ea) + { assert(global.errors); + ea = new ErrorExp(); + } + assert(ea); + ea = ea->semantic(sc); + if (flags & 1) // only used by __traits, must not interpret the args + ea = ea->optimize(WANTvalue); + else if (ea->op != TOKvar && ea->op != TOKtuple) + ea = ea->optimize(WANTvalue | WANTinterpret); + tiargs->tdata()[j] = ea; + if (ea->op == TOKtype) + { ta = ea->type; + goto Ltype; + } + if (ea->op == TOKimport) + { sa = ((ScopeExp *)ea)->sds; + goto Ldsym; + } + if (ea->op == TOKtuple) + { // Expand tuple + TupleExp *te = (TupleExp *)ea; + size_t dim = te->exps->dim; + tiargs->remove(j); + if (dim) + { tiargs->reserve(dim); + for (size_t i = 0; i < dim; i++) + tiargs->insert(j + i, te->exps->tdata()[i]); + } + j--; + } + } + else if (sa) + { + TemplateDeclaration *td = sa->isTemplateDeclaration(); + if (td && !td->semanticRun && td->literal) + td->semantic(sc); + } + else + { + assert(0); + } + //printf("1: tiargs->tdata()[%d] = %p\n", j, tiargs->tdata()[j]); + } +#if 0 + printf("-TemplateInstance::semanticTiargs()\n"); + for (size_t j = 0; j < tiargs->dim; j++) + { + Object *o = tiargs->tdata()[j]; + Type *ta = isType(o); + Expression *ea = isExpression(o); + Dsymbol *sa = isDsymbol(o); + Tuple *va = isTuple(o); + + printf("\ttiargs[%d] = ta %p, ea %p, sa %p, va %p\n", j, ta, ea, sa, va); + } +#endif +} + +/********************************************** + * Find template declaration corresponding to template instance. + */ + +TemplateDeclaration *TemplateInstance::findTemplateDeclaration(Scope *sc) +{ + //printf("TemplateInstance::findTemplateDeclaration() %s\n", toChars()); + if (!tempdecl) + { + /* Given: + * foo!( ... ) + * figure out which TemplateDeclaration foo refers to. + */ + Dsymbol *s; + Dsymbol *scopesym; + Identifier *id; + + id = name; + s = sc->search(loc, id, &scopesym); + if (!s) + { + s = sc->search_correct(id); + if (s) + error("template '%s' is not defined, did you mean %s?", id->toChars(), s->toChars()); + else + error("template '%s' is not defined", id->toChars()); + return NULL; + } + + /* If an OverloadSet, look for a unique member that is a template declaration + */ + OverloadSet *os = s->isOverloadSet(); + if (os) + { s = NULL; + for (size_t i = 0; i < os->a.dim; i++) + { Dsymbol *s2 = os->a.tdata()[i]; + if (s2->isTemplateDeclaration()) + { + if (s) + error("ambiguous template declaration %s and %s", s->toPrettyChars(), s2->toPrettyChars()); + s = s2; + } + } + if (!s) + { error("template '%s' is not defined", id->toChars()); + return NULL; + } + } + +#if LOG + printf("It's an instance of '%s' kind '%s'\n", s->toChars(), s->kind()); + if (s->parent) + printf("s->parent = '%s'\n", s->parent->toChars()); +#endif + withsym = scopesym->isWithScopeSymbol(); + + /* We might have found an alias within a template when + * we really want the template. + */ + TemplateInstance *ti; + if (s->parent && + (ti = s->parent->isTemplateInstance()) != NULL) + { + if (ti->tempdecl && ti->tempdecl->ident == id) + { + /* This is so that one can refer to the enclosing + * template, even if it has the same name as a member + * of the template, if it has a !(arguments) + */ + tempdecl = ti->tempdecl; + if (tempdecl->overroot) // if not start of overloaded list of TemplateDeclaration's + tempdecl = tempdecl->overroot; // then get the start + s = tempdecl; + } + } + + s = s->toAlias(); + + /* It should be a TemplateDeclaration, not some other symbol + */ + tempdecl = s->isTemplateDeclaration(); + if (!tempdecl) + { + if (!s->parent && global.errors) + return NULL; + if (!s->parent && s->getType()) + { Dsymbol *s2 = s->getType()->toDsymbol(sc); + if (!s2) + { + error("%s is not a template declaration, it is a %s", id->toChars(), s->kind()); + return NULL; + } + s = s2; + } +#ifdef DEBUG + //if (!s->parent) printf("s = %s %s\n", s->kind(), s->toChars()); +#endif + //assert(s->parent); + TemplateInstance *ti = s->parent ? s->parent->isTemplateInstance() : NULL; + if (ti && + (ti->name == id || + ti->toAlias()->ident == id) + && + ti->tempdecl) + { + /* This is so that one can refer to the enclosing + * template, even if it has the same name as a member + * of the template, if it has a !(arguments) + */ + tempdecl = ti->tempdecl; + if (tempdecl->overroot) // if not start of overloaded list of TemplateDeclaration's + tempdecl = tempdecl->overroot; // then get the start + } + else + { + error("%s is not a template declaration, it is a %s", id->toChars(), s->kind()); + return NULL; + } + } + } + else + assert(tempdecl->isTemplateDeclaration()); + return tempdecl; +} + +TemplateDeclaration *TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) +{ + /* Since there can be multiple TemplateDeclaration's with the same + * name, look for the best match. + */ + TemplateDeclaration *td_ambig = NULL; + TemplateDeclaration *td_best = NULL; + MATCH m_best = MATCHnomatch; + Objects dedtypes; + +#if LOG + printf("TemplateInstance::findBestMatch()\n"); +#endif + // First look for forward references + for (TemplateDeclaration *td = tempdecl; td; td = td->overnext) + { + if (!td->semanticRun) + { + if (td->scope) + { // Try to fix forward reference + td->semantic(td->scope); + } + if (!td->semanticRun) + { + error("%s forward references template declaration %s\n", toChars(), td->toChars()); + return NULL; + } + } + } + + for (TemplateDeclaration *td = tempdecl; td; td = td->overnext) + { + MATCH m; + +//if (tiargs->dim) printf("2: tiargs->dim = %d, data[0] = %p\n", tiargs->dim, tiargs->tdata()[0]); + + // If more arguments than parameters, + // then this is no match. + if (td->parameters->dim < tiargs->dim) + { + if (!td->isVariadic()) + continue; + } + + dedtypes.setDim(td->parameters->dim); + dedtypes.zero(); + assert(td->semanticRun); + m = td->matchWithInstance(this, &dedtypes, fargs, 0); + //printf("matchWithInstance = %d\n", m); + if (!m) // no match at all + continue; + + if (m < m_best) + goto Ltd_best; + if (m > m_best) + goto Ltd; + + { + // Disambiguate by picking the most specialized TemplateDeclaration + MATCH c1 = td->leastAsSpecialized(td_best, fargs); + MATCH c2 = td_best->leastAsSpecialized(td, fargs); + //printf("c1 = %d, c2 = %d\n", c1, c2); + + if (c1 > c2) + goto Ltd; + else if (c1 < c2) + goto Ltd_best; + else + goto Lambig; + } + + Lambig: // td_best and td are ambiguous + td_ambig = td; + continue; + + Ltd_best: // td_best is the best match so far + td_ambig = NULL; + continue; + + Ltd: // td is the new best match + td_ambig = NULL; + td_best = td; + m_best = m; + tdtypes.setDim(dedtypes.dim); + memcpy(tdtypes.tdata(), dedtypes.tdata(), tdtypes.dim * sizeof(void *)); + continue; + } + + if (!td_best) + { + if (tempdecl && !tempdecl->overnext) + // Only one template, so we can give better error message + error("%s does not match template declaration %s", toChars(), tempdecl->toChars()); + else + error("%s does not match any template declaration", toChars()); + return NULL; + } + if (td_ambig) + { + error("%s matches more than one template declaration, %s(%d):%s and %s(%d):%s", + toChars(), + td_best->loc.filename, td_best->loc.linnum, td_best->toChars(), + td_ambig->loc.filename, td_ambig->loc.linnum, td_ambig->toChars()); + } + + /* The best match is td_best + */ + tempdecl = td_best; + +#if 0 + /* Cast any value arguments to be same type as value parameter + */ + for (size_t i = 0; i < tiargs->dim; i++) + { Object *o = tiargs->tdata()[i]; + Expression *ea = isExpression(o); // value argument + TemplateParameter *tp = tempdecl->parameters->tdata()[i]; + assert(tp); + TemplateValueParameter *tvp = tp->isTemplateValueParameter(); + if (tvp) + { + assert(ea); + ea = ea->castTo(tvp->valType); + ea = ea->optimize(WANTvalue | WANTinterpret); + tiargs->tdata()[i] = (Object *)ea; + } + } +#endif + +#if LOG + printf("\tIt's a match with template declaration '%s'\n", tempdecl->toChars()); +#endif + return tempdecl; +} + + +/***************************************** + * Determines if a TemplateInstance will need a nested + * generation of the TemplateDeclaration. + */ + +int TemplateInstance::hasNestedArgs(Objects *args) +{ int nested = 0; + //printf("TemplateInstance::hasNestedArgs('%s')\n", tempdecl->ident->toChars()); + + /* A nested instance happens when an argument references a local + * symbol that is on the stack. + */ + for (size_t i = 0; i < args->dim; i++) + { Object *o = args->tdata()[i]; + Expression *ea = isExpression(o); + Dsymbol *sa = isDsymbol(o); + Tuple *va = isTuple(o); + if (ea) + { + if (ea->op == TOKvar) + { + sa = ((VarExp *)ea)->var; + goto Lsa; + } + if (ea->op == TOKfunction) + { + sa = ((FuncExp *)ea)->fd; + goto Lsa; + } + } + else if (sa) + { + Lsa: + TemplateDeclaration *td = sa->isTemplateDeclaration(); + Declaration *d = sa->isDeclaration(); + if ((td && td->literal) || + (d && !d->isDataseg() && +#if DMDV2 + !(d->storage_class & STCmanifest) && +#endif + (!d->isFuncDeclaration() || d->isFuncDeclaration()->isNested()) && + !isTemplateMixin() + )) + { + // if module level template + if (tempdecl->toParent()->isModule()) + { Dsymbol *dparent = sa->toParent(); + if (!isnested) + isnested = dparent; + else if (isnested != dparent) + { + /* Select the more deeply nested of the two. + * Error if one is not nested inside the other. + */ + for (Dsymbol *p = isnested; p; p = p->parent) + { + if (p == dparent) + goto L1; // isnested is most nested + } + for (Dsymbol *p = dparent; p; p = p->parent) + { + if (p == isnested) + { isnested = dparent; + goto L1; // dparent is most nested + } + } + error("%s is nested in both %s and %s", + toChars(), isnested->toChars(), dparent->toChars()); + } + L1: + //printf("\tnested inside %s\n", isnested->toChars()); + nested |= 1; + } + else + error("cannot use local '%s' as parameter to non-global template %s", sa->toChars(), tempdecl->toChars()); + } + } + else if (va) + { + nested |= hasNestedArgs(&va->objects); + } + } + return nested; +} + +/**************************************** + * This instance needs an identifier for name mangling purposes. + * Create one by taking the template declaration name and adding + * the type signature for it. + */ + +Identifier *TemplateInstance::genIdent(Objects *args) +{ OutBuffer buf; + + //printf("TemplateInstance::genIdent('%s')\n", tempdecl->ident->toChars()); + char *id = tempdecl->ident->toChars(); + buf.printf("__T%zu%s", strlen(id), id); + for (size_t i = 0; i < args->dim; i++) + { Object *o = args->tdata()[i]; + Type *ta = isType(o); + Expression *ea = isExpression(o); + Dsymbol *sa = isDsymbol(o); + Tuple *va = isTuple(o); + //printf("\to [%d] %p ta %p ea %p sa %p va %p\n", i, o, ta, ea, sa, va); + if (ta) + { + buf.writeByte('T'); + if (ta->deco) + buf.writestring(ta->deco); + else + { +#ifdef DEBUG + printf("ta = %d, %s\n", ta->ty, ta->toChars()); +#endif + assert(global.errors); + } + } + else if (ea) + { + // Don't interpret it yet, it might actually be an alias + ea = ea->optimize(WANTvalue); + if (ea->op == TOKvar) + { + sa = ((VarExp *)ea)->var; + ea = NULL; + goto Lsa; + } + if (ea->op == TOKthis) + { + sa = ((ThisExp *)ea)->var; + ea = NULL; + goto Lsa; + } + if (ea->op == TOKfunction) + { + sa = ((FuncExp *)ea)->fd; + ea = NULL; + goto Lsa; + } + buf.writeByte('V'); + if (ea->op == TOKtuple) + { ea->error("tuple is not a valid template value argument"); + continue; + } + // Now that we know it is not an alias, we MUST obtain a value + ea = ea->optimize(WANTvalue | WANTinterpret); + if (ea->op == TOKerror) + continue; +#if 1 + /* Use deco that matches what it would be for a function parameter + */ + buf.writestring(ea->type->deco); +#else + // Use type of parameter, not type of argument + TemplateParameter *tp = tempdecl->parameters->tdata()[i]; + assert(tp); + TemplateValueParameter *tvp = tp->isTemplateValueParameter(); + assert(tvp); + buf.writestring(tvp->valType->deco); +#endif + ea->toMangleBuffer(&buf); + } + else if (sa) + { + Lsa: + buf.writeByte('S'); + Declaration *d = sa->isDeclaration(); + if (d && (!d->type || !d->type->deco)) + { error("forward reference of %s", d->toChars()); + continue; + } +#if 0 + VarDeclaration *v = sa->isVarDeclaration(); + if (v && v->storage_class & STCmanifest) + { ExpInitializer *ei = v->init->isExpInitializer(); + if (ei) + { + ea = ei->exp; + goto Lea; + } + } +#endif + const char *p = sa->mangle(); + + /* Bugzilla 3043: if the first character of p is a digit this + * causes ambiguity issues because the digits of the two numbers are adjacent. + * Current demanglers resolve this by trying various places to separate the + * numbers until one gets a successful demangle. + * Unfortunately, fixing this ambiguity will break existing binary + * compatibility and the demanglers, so we'll leave it as is. + */ + buf.printf("%zu%s", strlen(p), p); + } + else if (va) + { + assert(i + 1 == args->dim); // must be last one + args = &va->objects; + i = -1; + } + else + assert(0); + } + buf.writeByte('Z'); + id = buf.toChars(); + //buf.data = NULL; // we can free the string after call to idPool() + //printf("\tgenIdent = %s\n", id); + return Lexer::idPool(id); +} + + +/**************************************************** + * Declare parameters of template instance, initialize them with the + * template instance arguments. + */ + +void TemplateInstance::declareParameters(Scope *sc) +{ + //printf("TemplateInstance::declareParameters()\n"); + for (size_t i = 0; i < tdtypes.dim; i++) + { + TemplateParameter *tp = tempdecl->parameters->tdata()[i]; + //Object *o = tiargs->tdata()[i]; + Object *o = tdtypes.tdata()[i]; // initializer for tp + + //printf("\ttdtypes[%d] = %p\n", i, o); + tempdecl->declareParameter(sc, tp, o); + } +} + +/***************************************************** + * Determine if template instance is really a template function, + * and that template function needs to infer types from the function + * arguments. + */ + +int TemplateInstance::needsTypeInference(Scope *sc) +{ + //printf("TemplateInstance::needsTypeInference() %s\n", toChars()); + if (!tempdecl) + tempdecl = findTemplateDeclaration(sc); + int multipleMatches = FALSE; + for (TemplateDeclaration *td = tempdecl; td; td = td->overnext) + { + /* If any of the overloaded template declarations need inference, + * then return TRUE + */ + FuncDeclaration *fd; + if (!td->onemember || + (fd = td->onemember->toAlias()->isFuncDeclaration()) == NULL || + fd->type->ty != Tfunction) + { + /* Not a template function, therefore type inference is not possible. + */ + //printf("false\n"); + return FALSE; + } + + for (size_t i = 0; i < td->parameters->dim; i++) + if (td->parameters->tdata()[i]->isTemplateThisParameter()) + return TRUE; + + /* Determine if the instance arguments, tiargs, are all that is necessary + * to instantiate the template. + */ + TemplateTupleParameter *tp = td->isVariadic(); + //printf("tp = %p, td->parameters->dim = %d, tiargs->dim = %d\n", tp, td->parameters->dim, tiargs->dim); + TypeFunction *fdtype = (TypeFunction *)fd->type; + if (Parameter::dim(fdtype->parameters) && + ((tp && td->parameters->dim > 1) || tiargs->dim < td->parameters->dim)) + return TRUE; + /* If there is more than one function template which matches, we may + * need type inference (see Bugzilla 4430) + */ + if (td != tempdecl) + multipleMatches = TRUE; + } + //printf("false\n"); + return multipleMatches; +} + +void TemplateInstance::semantic2(Scope *sc) +{ int i; + + if (semanticRun >= 2) + return; + semanticRun = 2; +#if LOG + printf("+TemplateInstance::semantic2('%s')\n", toChars()); +#endif + if (!errors && members) + { + sc = tempdecl->scope; + assert(sc); + sc = sc->push(argsym); + sc = sc->push(this); + sc->tinst = this; + for (i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; +#if LOG +printf("\tmember '%s', kind = '%s'\n", s->toChars(), s->kind()); +#endif + s->semantic2(sc); + } + sc = sc->pop(); + sc->pop(); + } +#if LOG + printf("-TemplateInstance::semantic2('%s')\n", toChars()); +#endif +} + +void TemplateInstance::semantic3(Scope *sc) +{ +#if LOG + printf("TemplateInstance::semantic3('%s'), semanticRun = %d\n", toChars(), semanticRun); +#endif +//if (toChars()[0] == 'D') *(char*)0=0; + if (semanticRun >= 3) + return; + semanticRun = 3; + if (!errors && members) + { + sc = tempdecl->scope; + sc = sc->push(argsym); + sc = sc->push(this); + sc->tinst = this; + int oldgag = global.gag; + int olderrors = global.errors; + /* If this is a speculative instantiation, gag errors. + * Future optimisation: If the results are actually needed, errors + * would already be gagged, so we don't really need to run semantic + * on the members. + */ + if (speculative && !oldgag) + olderrors = global.startGagging(); + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->semantic3(sc); + if (speculative && global.errors != olderrors) + break; + } + if (speculative && !oldgag) + { // If errors occurred, this instantiation failed + errors += global.errors - olderrors; + global.endGagging(olderrors); + } + sc = sc->pop(); + sc->pop(); + } +} + +/************************************** + * Given an error instantiating the TemplateInstance, + * give the nested TemplateInstance instantiations that got + * us here. Those are a list threaded into the nested scopes. + */ +void TemplateInstance::printInstantiationTrace() +{ + if (global.gag) + return; + + const unsigned max_shown = 6; + const char format[] = "instantiated from here: %s"; + + // determine instantiation depth and number of recursive instantiations + int n_instantiations = 1; + int n_totalrecursions = 0; + for (TemplateInstance *cur = this; cur; cur = cur->tinst) + { + ++n_instantiations; + // If two instantiations use the same declaration, they are recursive. + // (this works even if they are instantiated from different places in the + // same template). + // In principle, we could also check for multiple-template recursion, but it's + // probably not worthwhile. + if (cur->tinst && cur->tempdecl && cur->tinst->tempdecl + && cur->tempdecl->loc.equals(cur->tinst->tempdecl->loc)) + ++n_totalrecursions; + } + + // show full trace only if it's short or verbose is on + if (n_instantiations <= max_shown || global.params.verbose) + { + for (TemplateInstance *cur = this; cur; cur = cur->tinst) + { + errorSupplemental(cur->loc, format, cur->toChars()); + } + } + else if (n_instantiations - n_totalrecursions <= max_shown) + { + // By collapsing recursive instantiations into a single line, + // we can stay under the limit. + int recursionDepth=0; + for (TemplateInstance *cur = this; cur; cur = cur->tinst) + { + if (cur->tinst && cur->tempdecl && cur->tinst->tempdecl + && cur->tempdecl->loc.equals(cur->tinst->tempdecl->loc)) + { + ++recursionDepth; + } + else + { + if (recursionDepth) + errorSupplemental(cur->loc, "%d recursive instantiations from here: %s", recursionDepth+2, cur->toChars()); + else + errorSupplemental(cur->loc, format, cur->toChars()); + recursionDepth = 0; + } + } + } + else + { + // Even after collapsing the recursions, the depth is too deep. + // Just display the first few and last few instantiations. + unsigned i = 0; + for (TemplateInstance *cur = this; cur; cur = cur->tinst) + { + if (i == max_shown / 2) + errorSupplemental(cur->loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown); + + if (i < max_shown / 2 || + i >= n_instantiations - max_shown + max_shown / 2) + errorSupplemental(cur->loc, format, cur->toChars()); + ++i; + } + } +} + +void TemplateInstance::toObjFile(int multiobj) +{ +#if LOG + printf("TemplateInstance::toObjFile('%s', this = %p)\n", toChars(), this); +#endif + if (!errors && members) + { + if (multiobj) + // Append to list of object files to be written later + obj_append(this); + else + { + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->toObjFile(multiobj); + } + } + } +} + +void TemplateInstance::inlineScan() +{ +#if LOG + printf("TemplateInstance::inlineScan('%s')\n", toChars()); +#endif + if (!errors && members) + { + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->inlineScan(); + } + } +} + +void TemplateInstance::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + int i; + + Identifier *id = name; + buf->writestring(id->toChars()); + buf->writestring("!("); + if (nest) + buf->writestring("..."); + else + { + nest++; + Objects *args = tiargs; + for (i = 0; i < args->dim; i++) + { + if (i) + buf->writeByte(','); + Object *oarg = args->tdata()[i]; + ObjectToCBuffer(buf, hgs, oarg); + } + nest--; + } + buf->writeByte(')'); +} + + +Dsymbol *TemplateInstance::toAlias() +{ +#if LOG + printf("TemplateInstance::toAlias()\n"); +#endif + if (!inst) + { error("cannot resolve forward reference"); + errors = 1; + return this; + } + + if (inst != this) + return inst->toAlias(); + + if (aliasdecl) + { + return aliasdecl->toAlias(); + } + + return inst; +} + +AliasDeclaration *TemplateInstance::isAliasDeclaration() +{ + return aliasdecl; +} + +const char *TemplateInstance::kind() +{ + return "template instance"; +} + +int TemplateInstance::oneMember(Dsymbol **ps, Identifier *ident) +{ + *ps = NULL; + return TRUE; +} + +char *TemplateInstance::toChars() +{ + OutBuffer buf; + HdrGenState hgs; + char *s; + + toCBuffer(&buf, &hgs); + s = buf.toChars(); + buf.data = NULL; + return s; +} + +/* ======================== TemplateMixin ================================ */ + +TemplateMixin::TemplateMixin(Loc loc, Identifier *ident, Type *tqual, + Identifiers *idents, Objects *tiargs) + : TemplateInstance(loc, idents->tdata()[idents->dim - 1]) +{ + //printf("TemplateMixin(ident = '%s')\n", ident ? ident->toChars() : ""); + this->ident = ident; + this->tqual = tqual; + this->idents = idents; + this->tiargs = tiargs ? tiargs : new Objects(); +} + +Dsymbol *TemplateMixin::syntaxCopy(Dsymbol *s) +{ TemplateMixin *tm; + + Identifiers *ids = new Identifiers(); + ids->setDim(idents->dim); + for (size_t i = 0; i < idents->dim; i++) + { // Matches TypeQualified::syntaxCopyHelper() + Identifier *id = idents->tdata()[i]; + if (id->dyncast() == DYNCAST_DSYMBOL) + { + TemplateInstance *ti = (TemplateInstance *)id; + + ti = (TemplateInstance *)ti->syntaxCopy(NULL); + id = (Identifier *)ti; + } + ids->tdata()[i] = id; + } + + tm = new TemplateMixin(loc, ident, + (Type *)(tqual ? tqual->syntaxCopy() : NULL), + ids, tiargs); + TemplateInstance::syntaxCopy(tm); + return tm; +} + +void TemplateMixin::semantic(Scope *sc) +{ +#if LOG + printf("+TemplateMixin::semantic('%s', this=%p)\n", toChars(), this); + fflush(stdout); +#endif + if (semanticRun) + { + // This for when a class/struct contains mixin members, and + // is done over because of forward references + if (parent && toParent()->isAggregateDeclaration()) + semanticRun = 1; // do over + else + { +#if LOG + printf("\tsemantic done\n"); +#endif + return; + } + } + if (!semanticRun) + semanticRun = 1; +#if LOG + printf("\tdo semantic\n"); +#endif + util_progress(); + + Scope *scx = NULL; + if (scope) + { sc = scope; + scx = scope; // save so we don't make redundant copies + scope = NULL; + } + + // Follow qualifications to find the TemplateDeclaration + if (!tempdecl) + { Dsymbol *s; + size_t i; + Identifier *id; + + if (tqual) + { s = tqual->toDsymbol(sc); + i = 0; + } + else + { + i = 1; + id = idents->tdata()[0]; + switch (id->dyncast()) + { + case DYNCAST_IDENTIFIER: + s = sc->search(loc, id, NULL); + break; + + case DYNCAST_DSYMBOL: + { + TemplateInstance *ti = (TemplateInstance *)id; + ti->semantic(sc); + s = ti; + break; + } + default: + assert(0); + } + } + + for (; i < idents->dim; i++) + { + if (!s) + break; + id = idents->tdata()[i]; + s = s->searchX(loc, sc, id); + } + if (!s) + { + error("is not defined"); + inst = this; + return; + } + tempdecl = s->toAlias()->isTemplateDeclaration(); + if (!tempdecl) + { + error("%s isn't a template", s->toChars()); + inst = this; + return; + } + } + + // Look for forward reference + assert(tempdecl); + for (TemplateDeclaration *td = tempdecl; td; td = td->overnext) + { + if (!td->semanticRun) + { + /* Cannot handle forward references if mixin is a struct member, + * because addField must happen during struct's semantic, not + * during the mixin semantic. + * runDeferred will re-run mixin's semantic outside of the struct's + * semantic. + */ + semanticRun = 0; + AggregateDeclaration *ad = toParent()->isAggregateDeclaration(); + if (ad) + ad->sizeok = 2; + else + { + // Forward reference + //printf("forward reference - deferring\n"); + scope = scx ? scx : new Scope(*sc); + scope->setNoFree(); + scope->module->addDeferredSemantic(this); + } + return; + } + } + + // Run semantic on each argument, place results in tiargs[] + semanticTiargs(sc); + if (errors || arrayObjectIsError(tiargs)) + return; + + tempdecl = findBestMatch(sc, NULL); + if (!tempdecl) + { inst = this; + return; // error recovery + } + + if (!ident) + ident = genIdent(tiargs); + + inst = this; + parent = sc->parent; + + /* Detect recursive mixin instantiations. + */ + for (Dsymbol *s = parent; s; s = s->parent) + { + //printf("\ts = '%s'\n", s->toChars()); + TemplateMixin *tm = s->isTemplateMixin(); + if (!tm || tempdecl != tm->tempdecl) + continue; + + /* Different argument list lengths happen with variadic args + */ + if (tiargs->dim != tm->tiargs->dim) + continue; + + for (size_t i = 0; i < tiargs->dim; i++) + { Object *o = tiargs->tdata()[i]; + Type *ta = isType(o); + Expression *ea = isExpression(o); + Dsymbol *sa = isDsymbol(o); + Object *tmo = tm->tiargs->tdata()[i]; + if (ta) + { + Type *tmta = isType(tmo); + if (!tmta) + goto Lcontinue; + if (!ta->equals(tmta)) + goto Lcontinue; + } + else if (ea) + { Expression *tme = isExpression(tmo); + if (!tme || !ea->equals(tme)) + goto Lcontinue; + } + else if (sa) + { + Dsymbol *tmsa = isDsymbol(tmo); + if (sa != tmsa) + goto Lcontinue; + } + else + assert(0); + } + error("recursive mixin instantiation"); + return; + + Lcontinue: + continue; + } + + // Copy the syntax trees from the TemplateDeclaration + members = Dsymbol::arraySyntaxCopy(tempdecl->members); + if (!members) + return; + + symtab = new DsymbolTable(); + + for (Scope *sce = sc; 1; sce = sce->enclosing) + { + ScopeDsymbol *sds = (ScopeDsymbol *)sce->scopesym; + if (sds) + { + sds->importScope(this, PROTpublic); + break; + } + } + +#if LOG + printf("\tcreate scope for template parameters '%s'\n", toChars()); +#endif + Scope *scy = sc; + scy = sc->push(this); + scy->parent = this; + + argsym = new ScopeDsymbol(); + argsym->parent = scy->parent; + Scope *argscope = scy->push(argsym); + + unsigned errorsave = global.errors; + + // Declare each template parameter as an alias for the argument type + declareParameters(argscope); + + // Add members to enclosing scope, as well as this scope + for (unsigned i = 0; i < members->dim; i++) + { Dsymbol *s; + + s = members->tdata()[i]; + s->addMember(argscope, this, i); + //sc->insert(s); + //printf("sc->parent = %p, sc->scopesym = %p\n", sc->parent, sc->scopesym); + //printf("s->parent = %s\n", s->parent->toChars()); + } + + // Do semantic() analysis on template instance members +#if LOG + printf("\tdo semantic() on template instance members '%s'\n", toChars()); +#endif + Scope *sc2; + sc2 = argscope->push(this); + sc2->offset = sc->offset; + + static int nest; + //printf("%d\n", nest); + if (++nest > 500) + { + global.gag = 0; // ensure error message gets printed + error("recursive expansion"); + fatal(); + } + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->semantic(sc2); + } + + nest--; + + sc->offset = sc2->offset; + + /* The problem is when to parse the initializer for a variable. + * Perhaps VarDeclaration::semantic() should do it like it does + * for initializers inside a function. + */ +// if (sc->parent->isFuncDeclaration()) + + semantic2(sc2); + + if (sc->func) + { + semantic3(sc2); + } + + // Give additional context info if error occurred during instantiation + if (global.errors != errorsave) + { + error("error instantiating"); + } + + sc2->pop(); + + argscope->pop(); + +// if (!isAnonymous()) + { + scy->pop(); + } +#if LOG + printf("-TemplateMixin::semantic('%s', this=%p)\n", toChars(), this); +#endif +} + +void TemplateMixin::semantic2(Scope *sc) +{ + if (semanticRun >= 2) + return; + semanticRun = 2; +#if LOG + printf("+TemplateMixin::semantic2('%s')\n", toChars()); +#endif + if (members) + { + assert(sc); + sc = sc->push(argsym); + sc = sc->push(this); + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; +#if LOG + printf("\tmember '%s', kind = '%s'\n", s->toChars(), s->kind()); +#endif + s->semantic2(sc); + } + sc = sc->pop(); + sc->pop(); + } +#if LOG + printf("-TemplateMixin::semantic2('%s')\n", toChars()); +#endif +} + +void TemplateMixin::semantic3(Scope *sc) +{ + if (semanticRun >= 3) + return; + semanticRun = 3; +#if LOG + printf("TemplateMixin::semantic3('%s')\n", toChars()); +#endif + if (members) + { + sc = sc->push(argsym); + sc = sc->push(this); + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = members->tdata()[i]; + s->semantic3(sc); + } + sc = sc->pop(); + sc->pop(); + } +} + +void TemplateMixin::inlineScan() +{ + TemplateInstance::inlineScan(); +} + +const char *TemplateMixin::kind() +{ + return "mixin"; +} + +int TemplateMixin::oneMember(Dsymbol **ps, Identifier *ident) +{ + return Dsymbol::oneMember(ps, ident); +} + +int TemplateMixin::hasPointers() +{ + //printf("TemplateMixin::hasPointers() %s\n", toChars()); + + if (members) + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + //printf(" s = %s %s\n", s->kind(), s->toChars()); + if (s->hasPointers()) + { + return 1; + } + } + return 0; +} + +char *TemplateMixin::toChars() +{ + OutBuffer buf; + HdrGenState hgs; + char *s; + + TemplateInstance::toCBuffer(&buf, &hgs); + s = buf.toChars(); + buf.data = NULL; + return s; +} + +void TemplateMixin::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("mixin "); + + for (size_t i = 0; i < idents->dim; i++) + { Identifier *id = idents->tdata()[i]; + + if (i) + buf->writeByte('.'); + buf->writestring(id->toChars()); + } + buf->writestring("!("); + if (tiargs) + { + for (size_t i = 0; i < tiargs->dim; i++) + { if (i) + buf->writebyte(','); + Object *oarg = tiargs->tdata()[i]; + Type *t = isType(oarg); + Expression *e = isExpression(oarg); + Dsymbol *s = isDsymbol(oarg); + if (t) + t->toCBuffer(buf, NULL, hgs); + else if (e) + e->toCBuffer(buf, hgs); + else if (s) + { + char *p = s->ident ? s->ident->toChars() : s->toChars(); + buf->writestring(p); + } + else if (!oarg) + { + buf->writestring("NULL"); + } + else + { + assert(0); + } + } + } + buf->writebyte(')'); + if (ident) + { + buf->writebyte(' '); + buf->writestring(ident->toChars()); + } + buf->writebyte(';'); + buf->writenl(); +} + + +void TemplateMixin::toObjFile(int multiobj) +{ + //printf("TemplateMixin::toObjFile('%s')\n", toChars()); + TemplateInstance::toObjFile(multiobj); +} + diff --git a/template.h b/template.h new file mode 100644 index 00000000..60bcd1b1 --- /dev/null +++ b/template.h @@ -0,0 +1,375 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_TEMPLATE_H +#define DMD_TEMPLATE_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "root.h" +#include "arraytypes.h" +#include "dsymbol.h" + + +struct OutBuffer; +struct Identifier; +struct TemplateInstance; +struct TemplateParameter; +struct TemplateTypeParameter; +struct TemplateThisParameter; +struct TemplateValueParameter; +struct TemplateAliasParameter; +struct TemplateTupleParameter; +struct Type; +struct TypeTypeof; +struct Scope; +struct Expression; +struct AliasDeclaration; +struct FuncDeclaration; +struct HdrGenState; +enum MATCH; + +struct Tuple : Object +{ + Objects objects; + + int dyncast() { return DYNCAST_TUPLE; } // kludge for template.isType() +}; + + +struct TemplateDeclaration : ScopeDsymbol +{ + TemplateParameters *parameters; // array of TemplateParameter's + + TemplateParameters *origParameters; // originals for Ddoc + Expression *constraint; + TemplateInstances instances; // array of TemplateInstance's + + TemplateDeclaration *overnext; // next overloaded TemplateDeclaration + TemplateDeclaration *overroot; // first in overnext list + + int semanticRun; // 1 semantic() run + + Dsymbol *onemember; // if !=NULL then one member of this template + + int literal; // this template declaration is a literal + int ismixin; // template declaration is only to be used as a mixin + + struct Previous + { Previous *prev; + Scope *sc; + Objects *dedargs; + }; + Previous *previous; // threaded list of previous instantiation attempts on stack + + TemplateDeclaration(Loc loc, Identifier *id, TemplateParameters *parameters, + Expression *constraint, Dsymbols *decldefs, int ismixin); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc); + int overloadInsert(Dsymbol *s); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + bool hasStaticCtorOrDtor(); + const char *kind(); + char *toChars(); + + void emitComment(Scope *sc); + void toJsonBuffer(OutBuffer *buf); +// void toDocBuffer(OutBuffer *buf); + + MATCH matchWithInstance(TemplateInstance *ti, Objects *atypes, Expressions *fargs, int flag); + MATCH leastAsSpecialized(TemplateDeclaration *td2, Expressions *fargs); + + MATCH deduceFunctionTemplateMatch(Scope *sc, Loc loc, Objects *targsi, Expression *ethis, Expressions *fargs, Objects *dedargs); + FuncDeclaration *deduceFunctionTemplate(Scope *sc, Loc loc, Objects *targsi, Expression *ethis, Expressions *fargs, int flags = 0); + void declareParameter(Scope *sc, TemplateParameter *tp, Object *o); + FuncDeclaration *doHeaderInstantiation(Scope *sc, Objects *tdargs, Expressions *fargs); + + TemplateDeclaration *isTemplateDeclaration() { return this; } + + TemplateTupleParameter *isVariadic(); + int isOverloadable(); + + void makeParamNamesVisibleInConstraint(Scope *paramscope, Expressions *fargs); +}; + +struct TemplateParameter +{ + /* For type-parameter: + * template Foo(ident) // specType is set to NULL + * template Foo(ident : specType) + * For value-parameter: + * template Foo(valType ident) // specValue is set to NULL + * template Foo(valType ident : specValue) + * For alias-parameter: + * template Foo(alias ident) + * For this-parameter: + * template Foo(this ident) + */ + + Loc loc; + Identifier *ident; + + Declaration *sparam; + + TemplateParameter(Loc loc, Identifier *ident); + + virtual TemplateTypeParameter *isTemplateTypeParameter(); + virtual TemplateValueParameter *isTemplateValueParameter(); + virtual TemplateAliasParameter *isTemplateAliasParameter(); +#if DMDV2 + virtual TemplateThisParameter *isTemplateThisParameter(); +#endif + virtual TemplateTupleParameter *isTemplateTupleParameter(); + + virtual TemplateParameter *syntaxCopy() = 0; + virtual void declareParameter(Scope *sc) = 0; + virtual void semantic(Scope *) = 0; + virtual void print(Object *oarg, Object *oded) = 0; + virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs) = 0; + virtual Object *specialization() = 0; + virtual Object *defaultArg(Loc loc, Scope *sc) = 0; + + /* If TemplateParameter's match as far as overloading goes. + */ + virtual int overloadMatch(TemplateParameter *) = 0; + + /* Match actual argument against parameter. + */ + virtual MATCH matchArg(Scope *sc, Objects *tiargs, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam) = 0; + + /* Create dummy argument based on parameter. + */ + virtual void *dummyArg() = 0; +}; + +struct TemplateTypeParameter : TemplateParameter +{ + /* Syntax: + * ident : specType = defaultType + */ + Type *specType; // type parameter: if !=NULL, this is the type specialization + Type *defaultType; + + static Type *tdummy; + + TemplateTypeParameter(Loc loc, Identifier *ident, Type *specType, Type *defaultType); + + TemplateTypeParameter *isTemplateTypeParameter(); + TemplateParameter *syntaxCopy(); + void declareParameter(Scope *sc); + void semantic(Scope *); + void print(Object *oarg, Object *oded); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Object *specialization(); + Object *defaultArg(Loc loc, Scope *sc); + int overloadMatch(TemplateParameter *); + MATCH matchArg(Scope *sc, Objects *tiargs, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam); + void *dummyArg(); +}; + +#if DMDV2 +struct TemplateThisParameter : TemplateTypeParameter +{ + /* Syntax: + * this ident : specType = defaultType + */ + Type *specType; // type parameter: if !=NULL, this is the type specialization + Type *defaultType; + + TemplateThisParameter(Loc loc, Identifier *ident, Type *specType, Type *defaultType); + + TemplateThisParameter *isTemplateThisParameter(); + TemplateParameter *syntaxCopy(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; +#endif + +struct TemplateValueParameter : TemplateParameter +{ + /* Syntax: + * valType ident : specValue = defaultValue + */ + + Type *valType; + Expression *specValue; + Expression *defaultValue; + + static AA *edummies; + + TemplateValueParameter(Loc loc, Identifier *ident, Type *valType, Expression *specValue, Expression *defaultValue); + + TemplateValueParameter *isTemplateValueParameter(); + TemplateParameter *syntaxCopy(); + void declareParameter(Scope *sc); + void semantic(Scope *); + void print(Object *oarg, Object *oded); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Object *specialization(); + Object *defaultArg(Loc loc, Scope *sc); + int overloadMatch(TemplateParameter *); + MATCH matchArg(Scope *sc, Objects *tiargs, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam); + void *dummyArg(); +}; + +struct TemplateAliasParameter : TemplateParameter +{ + /* Syntax: + * specType ident : specAlias = defaultAlias + */ + + Type *specType; + Object *specAlias; + Object *defaultAlias; + + static Dsymbol *sdummy; + + TemplateAliasParameter(Loc loc, Identifier *ident, Type *specType, Object *specAlias, Object *defaultAlias); + + TemplateAliasParameter *isTemplateAliasParameter(); + TemplateParameter *syntaxCopy(); + void declareParameter(Scope *sc); + void semantic(Scope *); + void print(Object *oarg, Object *oded); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Object *specialization(); + Object *defaultArg(Loc loc, Scope *sc); + int overloadMatch(TemplateParameter *); + MATCH matchArg(Scope *sc, Objects *tiargs, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam); + void *dummyArg(); +}; + +struct TemplateTupleParameter : TemplateParameter +{ + /* Syntax: + * ident ... + */ + + TemplateTupleParameter(Loc loc, Identifier *ident); + + TemplateTupleParameter *isTemplateTupleParameter(); + TemplateParameter *syntaxCopy(); + void declareParameter(Scope *sc); + void semantic(Scope *); + void print(Object *oarg, Object *oded); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Object *specialization(); + Object *defaultArg(Loc loc, Scope *sc); + int overloadMatch(TemplateParameter *); + MATCH matchArg(Scope *sc, Objects *tiargs, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam); + void *dummyArg(); +}; + +struct TemplateInstance : ScopeDsymbol +{ + /* Given: + * foo!(args) => + * name = foo + * tiargs = args + */ + Identifier *name; + //Identifiers idents; + Objects *tiargs; // Array of Types/Expressions of template + // instance arguments [int*, char, 10*10] + + Objects tdtypes; // Array of Types/Expressions corresponding + // to TemplateDeclaration.parameters + // [int, char, 100] + + TemplateDeclaration *tempdecl; // referenced by foo.bar.abc + TemplateInstance *inst; // refer to existing instance + TemplateInstance *tinst; // enclosing template instance + ScopeDsymbol *argsym; // argument symbol table + AliasDeclaration *aliasdecl; // !=NULL if instance is an alias for its + // sole member + WithScopeSymbol *withsym; // if a member of a with statement + int semanticRun; // has semantic() been done? + int semantictiargsdone; // has semanticTiargs() been done? + int nest; // for recursion detection + int havetempdecl; // 1 if used second constructor + Dsymbol *isnested; // if referencing local symbols, this is the context + int errors; // 1 if compiled with errors + int speculative; // 1 if only instantiated with errors gagged +#ifdef IN_GCC + /* On some targets, it is necessary to know whether a symbol + will be emitted in the output or not before the symbol + is used. This can be different from getModule(). */ + Module * objFileModule; +#endif + + TemplateInstance(Loc loc, Identifier *temp_id); + TemplateInstance(Loc loc, TemplateDeclaration *tempdecl, Objects *tiargs); + static Objects *arraySyntaxCopy(Objects *objs); + Dsymbol *syntaxCopy(Dsymbol *); + void semantic(Scope *sc, Expressions *fargs); + void semantic(Scope *sc); + void semantic2(Scope *sc); + void semantic3(Scope *sc); + void inlineScan(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Dsymbol *toAlias(); // resolve real symbol + const char *kind(); + int oneMember(Dsymbol **ps, Identifier *ident); + int needsTypeInference(Scope *sc); + char *toChars(); + char *mangle(); + void printInstantiationTrace(); + + void toObjFile(int multiobj); // compile to .obj file + + // Internal + static void semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags); + void semanticTiargs(Scope *sc); + TemplateDeclaration *findTemplateDeclaration(Scope *sc); + TemplateDeclaration *findBestMatch(Scope *sc, Expressions *fargs); + void declareParameters(Scope *sc); + int hasNestedArgs(Objects *tiargs); + Identifier *genIdent(Objects *args); + + TemplateInstance *isTemplateInstance() { return this; } + AliasDeclaration *isAliasDeclaration(); +}; + +struct TemplateMixin : TemplateInstance +{ + Identifiers *idents; + Type *tqual; + + TemplateMixin(Loc loc, Identifier *ident, Type *tqual, Identifiers *idents, Objects *tiargs); + Dsymbol *syntaxCopy(Dsymbol *s); + void semantic(Scope *sc); + void semantic2(Scope *sc); + void semantic3(Scope *sc); + void inlineScan(); + const char *kind(); + int oneMember(Dsymbol **ps, Identifier *ident); + int hasPointers(); + char *toChars(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + void toObjFile(int multiobj); // compile to .obj file + + TemplateMixin *isTemplateMixin() { return this; } +}; + +Expression *isExpression(Object *o); +Dsymbol *isDsymbol(Object *o); +Type *isType(Object *o); +Tuple *isTuple(Object *o); +int arrayObjectIsError(Objects *args); +int isError(Object *o); +Type *getType(Object *o); +Dsymbol *getDsymbol(Object *o); + +void ObjectToCBuffer(OutBuffer *buf, HdrGenState *hgs, Object *oarg); +Object *objectSyntaxCopy(Object *o); + +#endif /* DMD_TEMPLATE_H */ diff --git a/tk.c b/tk.c new file mode 100644 index 00000000..8989b90b --- /dev/null +++ b/tk.c @@ -0,0 +1,30 @@ + +// Copyright (c) 1999-2002 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com + +#include +#include +#include + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +#include "mem.h" +#include "filespec.c" + +#if 0 +#define malloc ph_malloc +#define calloc(x,y) ph_calloc((x) * (y)) +#define realloc ph_realloc +#define free ph_free +#endif + +#if !MEM_DEBUG +#define MEM_NOMEMCOUNT 1 +#define MEM_NONEW 1 +#endif +#include "mem.c" +#include "list.c" +#include "vec.c" diff --git a/tk/filespec.c b/tk/filespec.c new file mode 100644 index 00000000..7fc4d454 --- /dev/null +++ b/tk/filespec.c @@ -0,0 +1,424 @@ +/*_ filespec.c Mon Jul 3 1989 Modified by: Walter Bright */ +/* Copyright (C) 1986-1987 by Northwest Software */ +/* All Rights Reserved */ +/* Written by Walter Bright */ + +/* Package with which to manipulate filespecs */ + +#include +#include "filespec.h" + +#ifndef MEM_H +#include "mem.h" +#endif + +#ifndef VAX11C +#include +#endif + +#if BSDUNIX +#include +#endif + +#if MSDOS || __OS2__ || __NT__ || _WIN32 +#include +#include +#endif + +#if M_UNIX || M_XENIX || linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#include +#include +#endif + +#ifndef assert +#include +#endif + +/* Macro to determine if char is a path or drive delimiter */ +#if MSDOS || __OS2__ || __NT__ || _WIN32 +#define ispathdelim(c) ((c) == '\\' || (c) == ':' || (c) == '/') +#else +#ifdef VMS +#define ispathdelim(c) ((c)==':' || (c)=='[' || (c)==']' ) +#else +#ifdef MPW +#define ispathdelim(c) ((c) == ':') +#else +#define ispathdelim(c) ((c) == '/') +#endif /* MPW */ +#endif /* VMS */ +#endif + +/**********************/ + +char * filespecaddpath(const char *path,const char *filename) +{ register char *filespec; + register unsigned pathlen; + + if (!path || (pathlen = strlen(path)) == 0) + filespec = mem_strdup(filename); + else + { + filespec = (char *) mem_malloc(pathlen + 1 + strlen(filename) + 1); + if (filespec) + { strcpy(filespec,path); +#if MSDOS || __OS2__ || __NT__ || _WIN32 + if (!ispathdelim(filespec[pathlen - 1])) + strcat(filespec,"\\"); +#else +#if VMS +#else +#if MPW + if (!ispathdelim(filespec[pathlen - 1])) + strcat(filespec,":"); +#else + if (!ispathdelim(filespec[pathlen - 1])) + strcat(filespec,"/"); +#endif /* MPW */ +#endif /* VMS */ +#endif + strcat(filespec,filename); + } + } + return filespec; +} + +#ifndef MPW +/**********************/ +char * filespecrootpath(char *filespec) +{ +#if SUN || M_UNIX || M_XENIX || linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#define DIRCHAR '/' +#endif +#if MSDOS || __OS2__ || __NT__ || _WIN32 +#define DIRCHAR '\\' +#endif +#ifdef MPW +#define DIRCHAR ':' +#endif + + char *cwd, *cwd_t; + char *p, *p2; + + if (!filespec) + return filespec; +#if MSDOS || __OS2__ || __NT__ || _WIN32 + /* if already absolute (with \ or drive:) ... */ + if (*filespec == DIRCHAR || (isalpha((unsigned char)*filespec) && *(filespec+1) == ':')) + return filespec; /* ... return input string */ +#else + if (*filespec == DIRCHAR) /* already absolute ... */ + return filespec; /* ... return input string */ +#endif + + /* get current working directory path */ +#if SUN || M_UNIX || M_XENIX || linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + cwd_t = (char *)getcwd(NULL, 256); +#endif +#if MSDOS || __OS2__ || __NT__ || _WIN32 + char cwd_d[132]; + if (getcwd(cwd_d, sizeof(cwd_d))) + cwd_t = cwd_d; + else + cwd_t = NULL; +#endif + + if (cwd_t == NULL) + { + mem_free(filespec); + return NULL; /* error - path too long (more than 256 chars !)*/ + } + cwd = mem_strdup(cwd_t); /* convert cwd to mem package */ +#if MSDOS + assert(strlen(cwd) > 0); + if (cwd[strlen(cwd) - 1] == DIRCHAR) + cwd[strlen(cwd) - 1] = '\0'; +#endif +#if SUN || M_UNIX || M_XENIX || linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + free(cwd_t); +#endif + p = filespec; + while (p != NULL) + { + p2 = (char *)strchr(p, DIRCHAR); + if (p2 != NULL) + { + *p2 = '\0'; + if (strcmp(p, "..") == 0) /* move up cwd */ + /* remove last directory from cwd */ + *((char *)strrchr(cwd, DIRCHAR)) = '\0'; + else if (strcmp(p, ".") != 0) /* not current directory */ + { + cwd_t = cwd; + cwd = (char *)mem_calloc(strlen(cwd_t) + 1 + strlen(p) + 1); +#if SUN || M_UNIX || M_XENIX || linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + sprintf(cwd, "%s/%s", cwd_t, p); /* add relative directory */ +#endif +#if MSDOS || __OS2__ || __NT__ || _WIN32 + sprintf(cwd, "%s\\%s", cwd_t, p); /* add relative directory */ +#endif + mem_free(cwd_t); + } + /* else if ".", then ignore - it means current directory */ + *p2 = DIRCHAR; + p2++; + } + else if (strcmp(p,"..") == 0) /* move up cwd */ + { + /* remove last directory from cwd */ + *((char *)strrchr(cwd, DIRCHAR)) = '\0'; + } + else if (strcmp(p,".") != 0) /* no more subdirectories ... */ + { /* ... save remaining string */ + cwd_t = cwd; + cwd = (char *)mem_calloc(strlen(cwd_t) + 1 + strlen(p) + 1); +#if SUN || M_UNIX || M_XENIX || linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + sprintf(cwd, "%s/%s", cwd_t, p); /* add relative directory */ +#endif +#if MSDOS || __OS2__ || __NT__ || _WIN32 + sprintf(cwd, "%s\\%s", cwd_t, p); /* add relative directory */ +#endif + mem_free(cwd_t); + } + p = p2; + } + mem_free(filespec); + + return cwd; +#ifdef VMS + assert(0); +#endif +} +#endif + +/**********************/ + +char * filespecdotext(const char *filespec) +{ register const char *p; + register int len; + + if ((p = filespec) != NULL) + { p += (len = strlen(p)); + while (1) + { if (*p == '.') + break; + if (p <= filespec || ispathdelim(*p)) + { p = filespec + len; + break; + } + p--; + } + } + return (char *)p; +} + +/**********************/ + +char * filespecname(const char *filespec) +{ register const char *p; + + /* Start at end of string and back up till we find the beginning */ + /* of the filename or a path. */ + for (p = filespec + strlen(filespec); + p != filespec && !ispathdelim(*(p - 1)); + p-- + ) + ; + return (char *)p; +} + +/***********************/ + +char * filespecgetroot(const char *name) +{ char *root,*p,c; + + p = filespecdotext(name); + c = *p; + *p = 0; + root = mem_strdup(name); + *p = c; + return root; +} + +/*****************************/ + +char * filespecforceext(const char *filespec,const char *ext) +{ register char *p; + register const char *pext; + + if (*ext == '.') + ext++; + if ((p = (char *)filespec) != NULL) + { pext = filespecdotext(filespec); + if (ext) + { int n = pext - filespec; + p = (char *) mem_malloc(n + 1 + strlen(ext) + 1); + if (p) + { memcpy(p,filespec,n); + p[n] = '.'; + strcpy(&p[n + 1],ext); + } + } + else + p = mem_strdup(filespec); + } + return p; +} + +/*****************************/ + +char * filespecdefaultext(const char *filespec,const char *ext) +{ register char *p; + register const char *pext; + + pext = filespecdotext(filespec); + if (*pext == '.') /* if already got an extension */ + { + p = mem_strdup(filespec); + } + else + { int n = pext - filespec; + p = (char *) mem_malloc(n + 1 + strlen(ext) + 1); + if (p) + { + memcpy(p,filespec,n); + p[n] = '.'; + strcpy(&p[n + 1],ext); + } + } + return p; +} + +/************************************/ + +#if !(MSDOS || __OS2__ || __NT__ || _WIN32) + +char *filespectilde(char *filespec) +{ +#if BSDUNIX + struct passwd *passwdPtr; + register char *f; + + if (filespec && *filespec == '~') + { + passwdPtr = NULL; + if (filespec[1] == '/' || filespec[1] == 0) /* if ~/... or ~ */ + { f = filespec + 1; + passwdPtr = getpwuid(getuid()); + } + else /* else ~name */ + { + char c; + + f = strpbrk(filespec," /"); + if (!f) + f = filespec + strlen(filespec); /* point at trailing 0 */ + c = *f; + *f = 0; + passwdPtr = getpwnam(filespec + 1); + *f = c; + } + if (passwdPtr) + { char *p; + + p = (char *) mem_malloc(strlen(passwdPtr->pw_dir) + strlen(f) + 1); + if (p) + { + strcpy(p,passwdPtr->pw_dir); + strcat(p,f); + mem_free(filespec); + filespec = p; + } + } + } +#endif +#if MSDOS || __OS2__ || __NT__ || _WIN32 +#endif +#if VMS + assert(0); +#endif + return filespec; +} + +/************************************/ + +char *filespecmultitilde(char *string) +{ + register char *p, *p2, *p3, *p4; + + string = filespectilde(string); /* expand if first character is a '~' */ + + if (string) + { + for (p = string; *p != '\0'; p++) + { + if (*p == '~') + { + *p = '\0'; /* terminate sub string */ + p2 = mem_strdup(string); /* get new sub string from old string */ + *p = '~'; /* reset ~ character */ + for (p3 = p + 1; *p3 != ' ' && *p3 != '\0'; p3++) + ; /* scan to next name, or end of the string */ + p4 = NULL; + if (*p3 == ' ') + { + p4 = mem_strdup(p3); /* save reminder of the string */ + *p3 = '\0'; + } + p = mem_strdup(p); /* get tilde string */ + mem_free(string); + p = filespectilde(p); + + /* reconstruct the string from pieces */ + if (p4) + { + string = (char *) + mem_calloc(strlen(p2) + strlen(p) + strlen(p4) + 1); + sprintf(string, "%s%s%s", p2, p, p4); + } + else + { + string = (char *) + mem_calloc(strlen(p2) + strlen(p) + 1); + sprintf(string, "%s%s", p2, p); + } + mem_free(p); + p = string + strlen(p2) + 2; + mem_free(p2); + if (p4) + mem_free(p4); + } + } + } + return string; +} + +#endif + +#ifndef MPW +/************************************/ + +char * filespecbackup(const char *filespec) +{ +#if MSDOS || __OS2__ || __NT__ || _WIN32 + return filespecforceext(filespec,"BAK"); +#endif +#if BSDUNIX || linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + char *p,*f; + + /* Prepend .B to file name, if it isn't already there */ + if (!filespec) + return (char *)filespec; + p = filespecname(filespec); + if (p[0] == '.' && p[1] == 'B') + return mem_strdup(filespec); + f = (char *) mem_malloc(strlen(filespec) + 2 + 1); + if (f) + { strcpy(f,filespec); + strcpy(&f[p - filespec],".B"); + strcat(f,p); + } + return f; +#endif +} +#endif diff --git a/tk/filespec.h b/tk/filespec.h new file mode 100644 index 00000000..97012be3 --- /dev/null +++ b/tk/filespec.h @@ -0,0 +1,136 @@ +/*_ filespec.h Fri Jul 8 1988 Modified by: bright */ +/* Copyright (C) 1986-1987 by Northwest Software */ +/* All Rights Reserved */ +/* Written by Walter Bright */ + +#ifndef FILESPEC_H +#define FILESPEC_H 1 + +/********************************* + * String compare of filenames. + */ + +#if MSDOS || __OS2__ || _WIN32 +#define filespeccmp(f1,f2) stricmp((f1),(f2)) +#define filespecmemcmp(f1,f2,n) memicmp((f1),(f2),(n)) +#endif +#if BSDUNIX || M_UNIX || M_XENIX +#define filespeccmp(f1,f2) strcmp((f1),(f2)) +#define filespecmemcmp(f1,f2,n) memcmp((f1),(f2),(n)) +#endif + +/**************************** + * Combine path and filename to form a filespec. + * Input: + * path Path, with or without trailing / + * (can be NULL) + * filename Cannot be NULL + * Returns: + * filespec mem_malloc'd file specification + * NULL Out of memory + */ + +char *filespecaddpath(const char *,const char *); + +/******************************* filespecrootpath ************************** + * Purpose: To expand a relative path into an absolute path. + * + * Side Effects: mem_frees input string. + * + * Returns: mem_malloced string with absolute path. + * NULL if some failure. + */ + +char *filespecrootpath(char *); + +/***************************** + * Add extension onto filespec, if one isn't already there. + * Input: + * filespec Cannot be NULL + * ext Extension (without the .) + * Returns: + * mem_malloc'ed string (NULL if error) + */ + +char *filespecdefaultext(const char *,const char *); + +/********************** + * Return string that is the dot and extension. + * The string returned is NOT mem_malloc'ed. + * Return pointer to the 0 at the end of filespec if dot isn't found. + * Return NULL if filespec is NULL. + */ + +char *filespecdotext(const char *); + +/***************************** + * Force extension onto filespec. + * Input: + * filespec String that may or may not contain an extension + * ext Extension that doesn't contain a . + * Returns: + * mem_malloc'ed string (NULL if error) + * NULL if filespec is NULL + * If ext is NULL, return mem_strdup(filespec) + */ + +char *filespecforceext(const char *,const char *); + +/*********************** + * Get root name of file name. + * That is, return a mem_strdup()'d version of the filename without + * the .ext. + */ + +char *filespecgetroot(const char *); + +/********************** + * Return string that is the filename plus dot and extension. + * The string returned is NOT mem_malloc'ed. + */ + +char *filespecname(const char *); + +/************************************ + * If first character of filespec is a ~, perform tilde-expansion. + * Output: + * Input filespec is mem_free'd. + * Returns: + * mem_malloc'd string + */ + +#if MSDOS || __OS2__ || __NT__ +#define filespectilde(f) (f) +#else +char *filespectilde(char *); +#endif + +/************************************ + * Expand all ~ in the given string. + * + * Output: + * Input filespec is mem_free'd. + * Returns: + * mem_malloc'd string + */ + +#if MSDOS || __OS2__ || __NT__ +#define filespecmultitilde(f) (f) +#else +char *filespecmultitilde(char *); +#endif + +/***************************** + * Convert filespec into a backup filename appropriate for the + * operating system. For instance, under MS-DOS path\filename.ext will + * be converted to path\filename.bak. + * Input: + * filespec String that may or may not contain an extension + * Returns: + * mem_malloc'ed string (NULL if error) + * NULL if filespec is NULL + */ + +char *filespecbackup(const char *); + +#endif /* FILESPEC_H */ diff --git a/tk/list.c b/tk/list.c new file mode 100644 index 00000000..3f45364a --- /dev/null +++ b/tk/list.c @@ -0,0 +1,462 @@ +/*_ list.c Mon Oct 31 1994 */ +/* Copyright (C) 1986-1994 by Symantec */ +/* All Rights Reserved */ +/* Written by Walter Bright */ + +#ifndef __STDIO_H +#include +#endif +#ifndef __STRING_H +#include +#endif +#ifndef __STDARG_H +#include +#endif +#ifndef assert +#include +#endif +#ifndef LIST_H +#include "list.h" +#endif +#ifndef MEM_H +#include "mem.h" +#endif + +#if MEM_DEBUG +#define fileline __FILE__,__LINE__ +#else +#define fileline +#endif + +#ifndef list_freelist +list_t list_freelist = NULL; /* list of free list entries */ +#endif +static int nlist; /* number of list items created */ +int list_inited = 0; /* 1 if initialized */ + +/* Free storage allocation */ +#ifndef list_new + +#if (__ZTC__ || __SC__) && !MEM_DEBUG +#define list_new() ((list_t) mem_fmalloc(sizeof(struct LIST))) +#define list_delete(list) mem_ffree(list) +#else +#if MEM_DEBUG +#define list_new() ((list_t) mem_calloc_debug(sizeof(struct LIST),file,line)) +#else +#define list_new() ((list_t) mem_malloc(sizeof(struct LIST))) +#endif +#define list_delete(list) mem_free(list) +#endif + +#endif + +/**********************/ + +void list_init() +{ +#ifdef DEBUG + assert(mem_inited); +#endif + if (list_inited == 0) + { nlist = 0; + list_inited++; + } +} + +/*******************/ + +void list_term() +{ + if (list_inited) + { +#ifdef DEBUG + printf("Max # of lists = %d\n",nlist); +#endif + while (list_freelist) + { list_t list; + + list = list_next(list_freelist); + list_delete(list_freelist); + list_freelist = list; + nlist--; + } +#ifdef DEBUG + if (nlist) + printf("nlist = %d\n",nlist); +#endif +#if !MEM_DEBUG + assert(nlist == 0); +#endif + list_inited = 0; + } +} + +/**************************** + * Allocate list item. + */ + +static list_t list_alloc +#if MEM_DEBUG + (char *file,int line) +#else + () +#endif +{ list_t list; + + if (list_freelist) + { list = list_freelist; + list_freelist = list_next(list); +#if MEM_DEBUG + mem_setnewfileline(list,file,line); +#endif + } + else + { nlist++; + list = list_new(); + } + return list; +} + +/******************************/ + +void list_free(list_t *plist,list_free_fp freeptr) +{ list_t list,listnext; + + list = *plist; + *plist = 0; /* block any circular reference bugs */ + while (list && --list->count == 0) + { listnext = list_next(list); + if (freeptr) + (*freeptr)(list_ptr(list)); +#if DEBUG + memset(list,0,sizeof(*list)); +#endif +#if 1 + list->next = list_freelist; + list_freelist = list; +#else + list_delete(list); + nlist--; +#endif + list = listnext; + } +} + +/***************************/ + +void *list_subtract(list_t *plist,void *ptr) +{ list_t list; + + while ((list = *plist) != 0) + { + if (list_ptr(list) == ptr) + { + if (--list->count == 0) + { *plist = list_next(list); + list->next = list_freelist; + list_freelist = list; + } + return ptr; + } + else + plist = &(list_next(list)); + } + return NULL; /* it wasn't in the list */ +} + +/*************************/ + +#if MEM_DEBUG +#undef list_append + +list_t list_append(list_t *plist,void *ptr) +{ + return list_append_debug(plist,ptr,__FILE__,__LINE__); +} + +list_t list_append_debug(list_t *plist,void *ptr,char *file,int line) +#else +list_t list_append(list_t *plist,void *ptr) +#endif +{ register list_t list; + + while (*plist) + plist = &(list_next(*plist)); /* find end of list */ + +#if MEM_DEBUG + list = list_alloc(file,line); +#else + list = list_alloc(); +#endif + if (list) + { *plist = list; + list_next(list) = 0; + list_ptr(list) = ptr; + list->count = 1; + } + return list; +} + +/*************************/ + +list_t list_prepend(list_t *plist,void *ptr) +{ register list_t list; + + list = list_alloc(fileline); + if (list) + { list_next(list) = *plist; + list_ptr(list) = ptr; + list->count = 1; + *plist = list; + } + return list; +} + +/*************************/ + +#if __SC__ && __INTSIZE == 4 && _M_I86 && !_DEBUG_TRACE + +__declspec(naked) int __pascal list_nitems(list_t list) +{ + _asm + { + mov ECX,list-4[ESP] + xor EAX,EAX + test ECX,ECX + jz L1 + L2: + mov ECX,[ECX]LIST.next + inc EAX + test ECX,ECX + jnz L2 + L1: + ret 4 + } +} + +#else + +#if __DMC__ +int __pascal list_nitems(list_t list) +#else +int list_nitems(list_t list) +#endif +{ register int n; + + n = 0; + while (list) + { n++; + list = list_next(list); + } + return n; +} + +#endif + +/*************************/ + +list_t list_nth(list_t list,int n) +{ register int i; + + for (i = 0; i < n; i++) + { assert(list); + list = list_next(list); + } + return list; +} + +/*************************/ + +list_t list_last(list_t list) +{ + if (list) + while (list_next(list)) + list = list_next(list); + return list; +} + +/**************************/ + +list_t list_prev(list_t start,list_t list) +{ + if (start) + { if (start == list) + start = NULL; + else + while (list_next(start) != list) + { start = list_next(start); + assert(start); + } + } + return start; +} + +/****************************/ + +list_t list_copy(list_t list) +{ list_t c; + + c = NULL; + for (; list; list = list_next(list)) + list_append(&c,list_ptr(list)); + return c; +} + +/****************************/ + +int list_equal(list_t list1,list_t list2) +{ + while (list1 && list2) + { + if (list_ptr(list1) != list_ptr(list2)) + break; + list1 = list_next(list1); + list2 = list_next(list2); + } + return list1 == list2; +} + +/****************************/ + +int list_cmp(list_t list1,list_t list2,int (*func)(void *,void *)) +{ int result = 0; + + while (1) + { + if (!list1) + { if (list2) + result = -1; /* list1 < list2 */ + break; + } + if (!list2) + { result = 1; /* list1 > list2 */ + break; + } + result = (*func)(list_ptr(list1),list_ptr(list2)); + if (result) + break; + list1 = list_next(list1); + list2 = list_next(list2); + } + return result; +} + +/*****************************/ + +list_t list_inlist(list_t list,void *ptr) +{ + for (; list; list = list_next(list)) + if (list_ptr(list) == ptr) + break; + return list; +} + +/******************************/ + +list_t list_cat(list_t *pl1,list_t l2) +{ list_t *pl; + + for (pl = pl1; *pl; pl = &list_next(*pl)) + ; + *pl = l2; + return *pl1; +} + +/******************************/ + +list_t list_build(void *p,...) +{ list_t alist; + list_t *pe; + va_list ap; + + alist = NULL; + pe = &alist; + for (va_start(ap,p); p; p = va_arg(ap,void *)) + { list_t list; + + list = list_alloc(fileline); + if (list) + { list_next(list) = NULL; + list_ptr(list) = p; + list->count = 1; + *pe = list; + pe = &list_next(list); + } + } + va_end(ap); + return alist; +} + +/*************************************** + * Apply a function to each member of a list. + */ + +void list_apply(list_t *plist,void (*fp)(void *)) +{ + list_t l; + + if (fp) + for (l = *plist; l; l = list_next(l)) + { + (*fp)(list_ptr(l)); + } +} + +/********************************************* + * Reverse a list. + */ + +list_t list_reverse(list_t l) +{ list_t r; + list_t ln; + + r = NULL; + while (l) + { ln = list_next(l); + list_next(l) = r; + r = l; + l = ln; + } + return r; +} + +/********************************************** + * Copy list of pointers into an array of pointers. + */ + +void list_copyinto(list_t l, void *pa) +{ + void **ppa = (void **)pa; + for (; l; l = list_next(l)) + *(ppa)++ = list_ptr(l); +} + +/********************************************** + * Insert item into list at nth position. + */ + +list_t list_insert(list_t *pl,void *ptr,int n) +{ + list_t list; + + while (n) + { + pl = &list_next(*pl); + n--; + } + list = list_alloc(fileline); + if (list) + { + list_next(list) = *pl; + *pl = list; + list_ptr(list) = ptr; + list->count = 1; + } + return list; +} + +#ifdef __cplusplus +void list_free(list_t *l) { list_free(l,FPNULL); } +#endif + diff --git a/tk/list.h b/tk/list.h new file mode 100644 index 00000000..0941f3d4 --- /dev/null +++ b/tk/list.h @@ -0,0 +1,291 @@ +/*_ list.h Wed May 16 1990 Modified by: Walter Bright */ +/* Copyright (C) 1986-1990 by Northwest Software */ +/* All Rights Reserved */ +/* Written by Walter Bright */ + +#ifndef LIST_H +#define LIST_H 1 + +#if __SC__ +#pragma once +#endif + +/* + * List is a complete package of functions to deal with singly linked + * lists of pointers or integers. + * Features: + * o Uses mem package. + * o Has loop-back tests. + * o Each item in the list can have multiple predecessors, enabling + * different lists to 'share' a common tail. + */ + +/***************** TYPEDEFS AND DEFINES *******************/ + +typedef struct LIST +{ + /* Do not access items in this struct directly, use the */ + /* functions designed for that purpose. */ + struct LIST *next; /* next element in list */ + int count; /* when 0, element may be deleted */ + union + { void *ptr; /* data pointer */ + int data; + } L; +} *list_t; /* pointer to a list entry */ + +/* FPNULL is a null function pointer designed to be an argument to + * list_free(). + */ + +typedef void (*list_free_fp) (void *); + +#define FPNULL ((list_free_fp) 0) + +/***************** PUBLIC VARIABLES **********************/ + +extern int list_inited; /* != 0 if list package is initialized */ + +/***************** PUBLIC FUNCTIONS **********************/ + +/******************************** + * Create link to existing list, that is, share the list with + * somebody else. + * Use: + * list_t list_link(list_t list); + * Returns: + * pointer to that list entry. + */ + +#define list_link(list) (((list) && (list)->count++),(list)) + +/******************************** + * Return pointer to next entry in list. + * Use: + * list_t list_next(list_t list); + */ + +#define list_next(list) ((list)->next) + +/******************************** + * Return pointer to previous item in list. + * Use: + * list_t list_prev(list_t start,list_t list); + */ + +/******************************** + * Return ptr from list entry. + * Use: + * void *list_ptr(list_t list); + */ + +#define list_ptr(list) ((list)->L.ptr) + +/******************************** + * Return integer item from list entry. + * Use: + * int list_data(list_t list); + */ + +#define list_data(list) ((list)->L.data) + +/******************************** + * Append integer item to list. + * Use: + * void list_appenddata(list_t *plist,int d); + */ + +#define list_appenddata(plist,d) (list_data(list_append((plist),NULL)) = (d)) + +/******************************** + * Prepend integer item to list. + * Use: + * void list_prependdata(list_t *plist,int d); + */ + +#define list_prependdata(plist,d) (list_data(list_prepend((plist),NULL)) = (d)) + +/********************** + * Initialize list package. + * void list_init(void); + * Output: + * list_inited = 1 + */ + +/******************* + * Terminate list package. + * void list_term(void); + * Output: + * list_inited = 0 + */ + +/******************** + * Free list. + * Use: + * void list_free(list_t *plist,void (*freeptr)(void *)); + * Input: + * plist Pointer to list to free + * freeptr Pointer to freeing function for the data pointer + * (use FPNULL if none) + * Output: + * *plist is NULL + */ + +/*************************** + * Remove ptr from the list pointed to by *plist. + * Use: + * void *list_subtract(list_t *plist,void *ptr); + * Output: + * *plist is updated to be the start of the new list + * Returns: + * NULL if *plist is NULL + * otherwise ptr + */ + +/*************************** + * Remove first element in list pointed to by *plist. + * void *list_pop(list_t *plist); + * Returns: + * First element, NULL if *plist is NULL + */ + +#define list_pop(plist) list_subtract((plist),list_ptr(*(plist))) + +/************************* + * Append ptr to *plist. + * Use: + * list_t list_append(list_t *plist,void *ptr); + * Returns: + * pointer to list item created. + * NULL if out of memory + */ + +/************************* + * Prepend ptr to *plist. + * Use: + * list_t list_prepend(list_t *plist,void *ptr); + * Returns: + * pointer to list item created (which is also the start of the list). + * NULL if out of memory + */ + +/************************* + * Count up and return number of items in list. + * Use: + * int list_nitems(list_t list); + * Returns: + * # of entries in list + */ + +/************************* + * Return nth list entry in list. + * Use: + * list_t list_nth(list_t list,int n); + */ + +/*********************** + * Return last list entry in list. + * Use: + * list_t list_last(list_t list); + */ + +/*********************** + * Copy a list and return it. + * Use: + * list_t list_copy(list_t list); + */ + +/************************ + * Compare two lists. If they have the same ptrs, return 1 else 0. + * Use: + * int list_equal(list_t list1,list_t list2); + */ + +/************************ + * Compare two lists using the specified comparison function. + * If they compare equal, return 0 else value returned by func. + * The comparison function is the same as used for qsort(). + */ + +int list_cmp (list_t list1,list_t list2,int (*func) (void *,void *)); + +/************************* + * Search for ptr in list. If found, return list entry that it is, else NULL. + * Use: + * list_t list_inlist(list_t list,void *ptr); + */ + +/************************* + * Concatenate two lists (l2 appended to l1). + * Output: + * *pl1 updated to be start of concatenated list. + * Returns: + * *pl1 + */ + +list_t list_cat (list_t *pl1, list_t l2); + +/************************* + * Build a list out of the NULL-terminated argument list. + * Returns: + * generated list + */ + +list_t list_build (void *p, ...); + +/*************************************** + * Apply a function to each member of a list. + */ + +void list_apply (list_t *plist,void (*fp)(void *)); + +/******************************************** + * Reverse a list. + */ + +list_t list_reverse (list_t); + +/********************************************** + * Copy list of pointers into an array of pointers. + */ + +void list_copyinto(list_t, void *); + +/********************************************** + * Insert item into list at nth position. + */ + +list_t list_insert(list_t *,void *,int n); + +/*********************** IMPLEMENTATION **********************/ + +extern void list_init (void), + list_term (void), + list_free (list_t *,void (*)(void *)); +extern void *list_subtract (list_t *,void *); +extern list_t +#if MEM_DEBUG +#define list_append(a,b) list_append_debug(a,b,__FILE__,__LINE__) + list_append_debug (list_t *,void *,char *,int), +#else + list_append (list_t *,void *), +#endif + list_prepend (list_t *,void *), + list_nth (list_t,int), + list_last (list_t), + list_prev (list_t,list_t), + list_inlist (list_t,void *), + list_copy (list_t); +#if __DMC__ +extern int __pascal list_nitems (list_t), + list_equal (list_t,list_t); +#else +extern int list_nitems (list_t), + list_equal (list_t,list_t); +#endif + +#ifdef __cplusplus +void list_free(list_t *l); // { list_free(l,FPNULL); } +#endif + +#endif /* LIST_H */ diff --git a/tk/mem.c b/tk/mem.c new file mode 100644 index 00000000..5851e4f1 --- /dev/null +++ b/tk/mem.c @@ -0,0 +1,901 @@ +/*_ mem.c */ +/* Memory management package */ +/* Written by Walter Bright */ + +#include +#if MSDOS || __OS2__ || __NT__ || _WIN32 +#include +#else +#define _near +#include +#include +#include +#endif +#include +#include + +#if __cplusplus +#if __DMC__ +#include +#else +#include +#endif +#endif + +#ifndef malloc +#if __SC__ || __DMC__ || _MSC_VER +#include +#else +#include +#endif +#endif + +#ifndef MEM_H +#include "mem.h" +#endif + +#ifndef MEM_NOMEMCOUNT +#define MEM_NOMEMCOUNT 0 +#endif + +#if !MEM_NONE + +#ifndef assert +#include +#endif + +#ifndef VAX11C +#ifdef BSDUNIX +#include +#else +#include +#endif +#else +extern char *strcpy(),*memcpy(); +extern int strlen(); +#endif /* VAX11C */ + +int mem_inited = 0; /* != 0 if initialized */ + +static int mem_behavior = MEM_ABORTMSG; +static int (*oom_fp)(void) = NULL; /* out-of-memory handler */ +static int mem_count; /* # of allocs that haven't been free'd */ +static int mem_scount; /* # of sallocs that haven't been free'd */ + +/* Determine where to send error messages */ +#if _WINDLL +void err_message(const char *,...); +#define PRINT err_message( +#elif MSDOS +#define PRINT printf( /* stderr can't be redirected with MS-DOS */ +#else +#define ferr stderr +#define PRINT fprintf(ferr, +#endif + +/*******************************/ + +void mem_setexception(enum MEM_E flag,...) +{ va_list ap; + typedef int (*fp_t)(void); + + mem_behavior = flag; + va_start(ap,flag); + oom_fp = (mem_behavior == MEM_CALLFP) ? va_arg(ap,fp_t) : 0; + va_end(ap); +#if MEM_DEBUG + assert(0 <= flag && flag <= MEM_RETRY); +#endif +} + +/************************* + * This is called when we're out of memory. + * Returns: + * 1: try again to allocate the memory + * 0: give up and return NULL + */ + +int mem_exception() +{ int behavior; + + behavior = mem_behavior; + while (1) + { + switch (behavior) + { + case MEM_ABORTMSG: +#if MSDOS || __OS2__ || __NT__ || _WIN32 + /* Avoid linking in buffered I/O */ + { static char msg[] = "Fatal error: out of memory\r\n"; + + write(1,msg,sizeof(msg) - 1); + } +#else + PRINT "Fatal error: out of memory\n"); +#endif + /* FALL-THROUGH */ + case MEM_ABORT: + exit(EXIT_FAILURE); + /* NOTREACHED */ + case MEM_CALLFP: + assert(oom_fp); + behavior = (*oom_fp)(); + break; + case MEM_RETNULL: + return 0; + case MEM_RETRY: + return 1; + default: + assert(0); + } + } +} + +/****************************/ + +#if MEM_DEBUG + +#undef mem_strdup + +char *mem_strdup(const char *s) +{ + return mem_strdup_debug(s,__FILE__,__LINE__); +} + +char *mem_strdup_debug(const char *s,const char *file,int line) +{ + char *p; + + p = s + ? (char *) mem_malloc_debug((unsigned) strlen(s) + 1,file,line) + : NULL; + return p ? strcpy(p,s) : p; +} +#else +char *mem_strdup(const char *s) +{ + char *p; + int len; + + if (s) + { len = strlen(s) + 1; + p = (char *) mem_malloc(len); + if (p) + return (char *)memcpy(p,s,len); + } + return NULL; +} + +#endif /* MEM_DEBUG */ + +/************* C++ Implementation ***************/ + +#if __cplusplus && !MEM_NONE +extern "C++" +{ + +/* Cause initialization and termination functions to be called */ +#if 0 +static struct cMemDebug +{ + cMemDebug() { mem_init(); } + ~cMemDebug() { mem_term(); } +} dummy; +#endif + +int __mem_line; +char *__mem_file; + +/******************** + */ + +#if __GNUC__ +int (*_new_handler)(void); +#else +void (*_new_handler)(void); +#endif + +/***************************** + * Replacement for the standard C++ library operator new(). + */ + +#if !MEM_NONEW + +#if __GNUC__ +void * operator new(size_t size) +#else +#undef new +void * __cdecl operator new(size_t size) +#endif +{ void *p; + + while (1) + { + if (size == 0) + size++; +#if MEM_DEBUG + assert(mem_inited); + p = mem_malloc_debug(size,__mem_file,__mem_line); +#else + p = mem_malloc((unsigned)size); +#endif + if (p != NULL || _new_handler == NULL) + break; + (*_new_handler)(); + } + return p; +} + +#if __GNUC__ +void * operator new[](size_t size) +#else +void * __cdecl operator new[](size_t size) +#endif +{ void *p; + + while (1) + { + if (size == 0) + size++; +#if MEM_DEBUG + assert(mem_inited); + p = mem_malloc_debug(size,__mem_file,__mem_line); +#else + p = mem_malloc((unsigned)size); +#endif + if (p != NULL || _new_handler == NULL) + break; + (*_new_handler)(); + } + return p; +} + +/*********************** + * Replacement for the standard C++ library operator delete(). + */ + +#undef delete +void __cdecl operator delete(void *p) +{ +#if MEM_DEBUG + assert(mem_inited); + mem_free_debug(p,__mem_file,__mem_line); +#else + mem_free(p); +#endif +} + +void __cdecl operator delete[](void *p) +{ +#if MEM_DEBUG + assert(mem_inited); + mem_free_debug(p,__mem_file,__mem_line); +#else + mem_free(p); +#endif +} +#endif +} +#endif + +#if MEM_DEBUG + +static long mem_maxalloc; /* max # of bytes allocated */ +static long mem_numalloc; /* current # of bytes allocated */ + +#define BEFOREVAL 0x4F464542 /* value to detect underrun */ +#define AFTERVAL 0x45544641 /* value to detect overrun */ + +#if SUN || SUN386 +static long afterval = AFTERVAL; /* so we can do &afterval */ +#endif + +/* The following should be selected to give maximum probability that */ +/* pointers loaded with these values will cause an obvious crash. On */ +/* Unix machines, a large value will cause a segment fault. */ +/* MALLOCVAL is the value to set malloc'd data to. */ + +#if MSDOS || __OS2__ || __NT__ || _WIN32 +#define BADVAL 0xFF +#define MALLOCVAL 0xEE +#else +#define BADVAL 0x7A +#define MALLOCVAL 0xEE +#endif + +/* Disable mapping macros */ +#undef mem_malloc +#undef mem_calloc +#undef mem_realloc +#undef mem_free + +/* Create a list of all alloc'ed pointers, retaining info about where */ +/* each alloc came from. This is a real memory and speed hog, but who */ +/* cares when you've got obscure pointer bugs. */ + +static struct mem_debug +{ + struct mem_debug *Mnext; /* next in list */ + struct mem_debug *Mprev; /* previous value in list */ + const char *Mfile; /* filename of where allocated */ + int Mline; /* line number of where allocated */ + unsigned Mnbytes; /* size of the allocation */ + unsigned long Mbeforeval; /* detect underrun of data */ + char data[1]; /* the data actually allocated */ +} mem_alloclist = +{ + (struct mem_debug *) NULL, + (struct mem_debug *) NULL, + NULL, + 11111, + 0, + BEFOREVAL, +#if !(linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4) + AFTERVAL +#endif +}; + +/* Determine allocation size of a mem_debug */ +#define mem_debug_size(n) (sizeof(struct mem_debug) - 1 + (n) + sizeof(AFTERVAL)) + +/* Convert from a void *to a mem_debug struct. */ +#define mem_ptrtodl(p) ((struct mem_debug *) ((char *)p - offsetof(struct mem_debug,data[0]))) + +/* Convert from a mem_debug struct to a mem_ptr. */ +#define mem_dltoptr(dl) ((void *) &((dl)->data[0])) + +/***************************** + * Set new value of file,line + */ + +void mem_setnewfileline( void *ptr, const char *fil, int lin) +{ + struct mem_debug *dl; + + dl = mem_ptrtodl(ptr); + dl->Mfile = fil; + dl->Mline = lin; +} + +/**************************** + * Print out struct mem_debug. + */ + +static void _near mem_printdl(struct mem_debug *dl) +{ + PRINT "alloc'd from file '%s' line %d nbytes %d ptr %p\n", + dl->Mfile,dl->Mline,dl->Mnbytes,(long)mem_dltoptr(dl)); +} + +/**************************** + * Print out file and line number. + */ + +static void _near mem_fillin(const char *fil, int lin) +{ + PRINT "File '%s' line %d\n",fil,lin); +#ifdef ferr + fflush(ferr); +#endif +} + +/**************************** + * If MEM_DEBUG is not on for some modules, these routines will get + * called. + */ + +void *mem_calloc(unsigned u) +{ + return mem_calloc_debug(u,__FILE__,__LINE__); +} + +void *mem_malloc(unsigned u) +{ + return mem_malloc_debug(u,__FILE__,__LINE__); +} + +void *mem_realloc(void *p, unsigned u) +{ + return mem_realloc_debug(p,u,__FILE__,__LINE__); +} + +void mem_free(void *p) +{ + mem_free_debug(p,__FILE__,__LINE__); +} + + +/**************************/ + +void mem_freefp(void *p) +{ + mem_free(p); +} + +/*********************** + * Debug versions of mem_calloc(), mem_free() and mem_realloc(). + */ + +void *mem_malloc_debug(unsigned n, const char *fil, int lin) +{ void *p; + + p = mem_calloc_debug(n,fil,lin); + if (p) + memset(p,MALLOCVAL,n); + return p; +} + +void *mem_calloc_debug(unsigned n, const char *fil, int lin) +{ + struct mem_debug *dl; + + do + dl = (struct mem_debug *) calloc(mem_debug_size(n),1); + while (dl == NULL && mem_exception()); + if (dl == NULL) + return NULL; + dl->Mfile = fil; + dl->Mline = lin; + dl->Mnbytes = n; + dl->Mbeforeval = BEFOREVAL; +#if SUN || SUN386 /* bus error if we store a long at an odd address */ + memcpy(&(dl->data[n]),&afterval,sizeof(AFTERVAL)); +#else + *(long *) &(dl->data[n]) = AFTERVAL; +#endif + + /* Add dl to start of allocation list */ + dl->Mnext = mem_alloclist.Mnext; + dl->Mprev = &mem_alloclist; + mem_alloclist.Mnext = dl; + if (dl->Mnext != NULL) + dl->Mnext->Mprev = dl; + + mem_count++; + mem_numalloc += n; + if (mem_numalloc > mem_maxalloc) + mem_maxalloc = mem_numalloc; + return mem_dltoptr(dl); +} + +void mem_free_debug(void *ptr, const char *fil, int lin) +{ + struct mem_debug *dl; + + if (ptr == NULL) + return; + if (mem_count <= 0) + { PRINT "More frees than allocs at "); + goto err; + } + dl = mem_ptrtodl(ptr); + if (dl->Mbeforeval != BEFOREVAL) + { + PRINT "Pointer x%lx underrun\n",(long)ptr); + PRINT "'%s'(%d)\n",fil,lin); + goto err2; + } +#if SUN || SUN386 /* Bus error if we read a long from an odd address */ + if (memcmp(&dl->data[dl->Mnbytes],&afterval,sizeof(AFTERVAL)) != 0) +#else + if (*(long *) &dl->data[dl->Mnbytes] != AFTERVAL) +#endif + { + PRINT "Pointer x%lx overrun\n",(long)ptr); + goto err2; + } + mem_numalloc -= dl->Mnbytes; + if (mem_numalloc < 0) + { PRINT "error: mem_numalloc = %ld, dl->Mnbytes = %d\n", + mem_numalloc,dl->Mnbytes); + goto err2; + } + + /* Remove dl from linked list */ + if (dl->Mprev) + dl->Mprev->Mnext = dl->Mnext; + if (dl->Mnext) + dl->Mnext->Mprev = dl->Mprev; + + /* Stomp on the freed storage to help detect references */ + /* after the storage was freed. */ + memset((void *) dl,BADVAL,sizeof(*dl) + dl->Mnbytes); + mem_count--; + + free((void *) dl); + return; + +err2: + mem_printdl(dl); +err: + PRINT "free'd from "); + mem_fillin(fil,lin); + assert(0); + /* NOTREACHED */ +} + +/******************* + * Debug version of mem_realloc(). + */ + +void *mem_realloc_debug(void *oldp, unsigned n, const char *fil, int lin) +{ void *p; + struct mem_debug *dl; + + if (n == 0) + { mem_free_debug(oldp,fil,lin); + p = NULL; + } + else if (oldp == NULL) + p = mem_malloc_debug(n,fil,lin); + else + { + p = mem_malloc_debug(n,fil,lin); + if (p != NULL) + { + dl = mem_ptrtodl(oldp); + if (dl->Mnbytes < n) + n = dl->Mnbytes; + memcpy(p,oldp,n); + mem_free_debug(oldp,fil,lin); + } + } + return p; +} + +/***************************/ + +static void mem_checkdl(struct mem_debug *dl) +{ void *p; +#if (__SC__ || __DMC__) && !_WIN32 + unsigned u; + + /* Take advantage of fact that SC's allocator stores the size of the + * alloc in the unsigned immediately preceding the allocation. + */ + u = ((unsigned *)dl)[-1] - sizeof(unsigned); + assert((u & (sizeof(unsigned) - 1)) == 0 && u >= mem_debug_size(dl->Mnbytes)); +#endif + p = mem_dltoptr(dl); + if (dl->Mbeforeval != BEFOREVAL) + { + PRINT "Pointer x%lx underrun\n",(long)p); + goto err2; + } +#if SUN || SUN386 /* Bus error if we read a long from an odd address */ + if (memcmp(&dl->data[dl->Mnbytes],&afterval,sizeof(AFTERVAL)) != 0) +#else + if (*(long *) &dl->data[dl->Mnbytes] != AFTERVAL) +#endif + { + PRINT "Pointer x%lx overrun\n",(long)p); + goto err2; + } + return; + +err2: + mem_printdl(dl); + assert(0); +} + +/***************************/ + +void mem_check() +{ register struct mem_debug *dl; + +#if (__SC__ || _MSC_VER) && !defined(malloc) + int i; + + i = _heapset(0xF4); + assert(i == _HEAPOK); +#endif + for (dl = mem_alloclist.Mnext; dl != NULL; dl = dl->Mnext) + mem_checkdl(dl); +} + +/***************************/ + +void mem_checkptr(void *p) +{ register struct mem_debug *dl; + + for (dl = mem_alloclist.Mnext; dl != NULL; dl = dl->Mnext) + { + if (p >= (void *) &(dl->data[0]) && + p < (void *)((char *)dl + sizeof(struct mem_debug)-1 + dl->Mnbytes)) + goto L1; + } + assert(0); + +L1: + mem_checkdl(dl); +} + +#else + +/***************************/ + +void *mem_malloc(unsigned numbytes) +{ void *p; + + if (numbytes == 0) + return NULL; + while (1) + { + p = malloc(numbytes); + if (p == NULL) + { if (mem_exception()) + continue; + } +#if !MEM_NOMEMCOUNT + else + mem_count++; +#endif + break; + } + /*printf("malloc(%d) = x%lx, mem_count = %d\n",numbytes,p,mem_count);*/ + return p; +} + +/***************************/ + +void *mem_calloc(unsigned numbytes) +{ void *p; + + if (numbytes == 0) + return NULL; + while (1) + { + p = calloc(numbytes,1); + if (p == NULL) + { if (mem_exception()) + continue; + } +#if !MEM_NOMEMCOUNT + else + mem_count++; +#endif + break; + } + /*printf("calloc(%d) = x%lx, mem_count = %d\n",numbytes,p,mem_count);*/ + return p; +} + +/***************************/ + +void *mem_realloc(void *oldmem_ptr,unsigned newnumbytes) +{ void *p; + + if (oldmem_ptr == NULL) + p = mem_malloc(newnumbytes); + else if (newnumbytes == 0) + { mem_free(oldmem_ptr); + p = NULL; + } + else + { + do + p = realloc(oldmem_ptr,newnumbytes); + while (p == NULL && mem_exception()); + } + /*printf("realloc(x%lx,%d) = x%lx, mem_count = %d\n",oldmem_ptr,newnumbytes,p,mem_count);*/ + return p; +} + +/***************************/ + +void mem_free(void *ptr) +{ + /*printf("free(x%lx) mem_count=%d\n",ptr,mem_count);*/ + if (ptr != NULL) + { +#if !MEM_NOMEMCOUNT + assert(mem_count != 0); + mem_count--; +#endif + free(ptr); + } +} + +/***************************/ +/* This is our low-rent fast storage allocator */ + +static char *heap; +static size_t heapleft; + +/***************************/ + +#if 0 && __SC__ && __INTSIZE == 4 && __I86__ && !_DEBUG_TRACE && _WIN32 && (SCC || SCPP || JAVA) + +__declspec(naked) void *mem_fmalloc(unsigned numbytes) +{ + __asm + { + mov EDX,4[ESP] + mov EAX,heap + add EDX,3 + mov ECX,heapleft + and EDX,~3 + je L5A + cmp EDX,ECX + ja L2D + sub ECX,EDX + add EDX,EAX + mov heapleft,ECX + mov heap,EDX + ret 4 + +L2D: push EBX + mov EBX,EDX +// add EDX,03FFFh +// and EDX,~03FFFh + add EDX,03C00h + mov heapleft,EDX +L3D: push heapleft + call mem_malloc + test EAX,EAX + mov heap,EAX + jne L18 + call mem_exception + test EAX,EAX + jne L3D + pop EBX +L5A: xor EAX,EAX + ret 4 + +L18: add heap,EBX + sub heapleft,EBX + pop EBX + ret 4 + } +} + +#else + +void *mem_fmalloc(unsigned numbytes) +{ void *p; + + //printf("fmalloc(%d)\n",numbytes); +#if defined(__llvm__) && (defined(__GNUC__) || defined(__clang__)) + // LLVM-GCC and Clang assume some types, notably elem (see DMD issue 6215), + // to be 16-byte aligned. Because we do not have any type information + // available here, we have to 16 byte-align everything. + numbytes = (numbytes + 0xF) & ~0xF; +#else + if (sizeof(size_t) == 2) + numbytes = (numbytes + 1) & ~1; /* word align */ + else + numbytes = (numbytes + 3) & ~3; /* dword align */ +#endif + + /* This ugly flow-of-control is so that the most common case + drops straight through. + */ + + if (!numbytes) + return NULL; + + if (numbytes <= heapleft) + { + L2: + p = (void *)heap; + heap += numbytes; + heapleft -= numbytes; + return p; + } + +#if 1 + heapleft = numbytes + 0x3C00; + if (heapleft >= 16372) + heapleft = numbytes; +#elif _WIN32 + heapleft = (numbytes + 0x3FFF) & ~0x3FFF; /* round to next boundary */ +#else + heapleft = 0x3F00; + assert(numbytes <= heapleft); +#endif +L1: + heap = (char *)malloc(heapleft); + if (!heap) + { if (mem_exception()) + goto L1; + return NULL; + } + goto L2; +} + +#endif + +/***************************/ + +void *mem_fcalloc(unsigned numbytes) +{ void *p; + + p = mem_fmalloc(numbytes); + return p ? memset(p,0,numbytes) : p; +} + +/***************************/ + +char *mem_fstrdup(const char *s) +{ + char *p; + int len; + + if (s) + { len = strlen(s) + 1; + p = (char *) mem_fmalloc(len); + if (p) + return (char *)memcpy(p,s,len); + } + return NULL; +} + +#endif + +/***************************/ + +void mem_init() +{ + if (mem_inited == 0) + { mem_count = 0; + mem_scount = 0; + oom_fp = NULL; + mem_behavior = MEM_ABORTMSG; +#if MEM_DEBUG + mem_numalloc = 0; + mem_maxalloc = 0; + mem_alloclist.Mnext = NULL; +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + *(long *) &(mem_alloclist.data[0]) = AFTERVAL; +#endif +#endif +#if (__ZTC__ || __SC__ || __DMC__) && !defined(malloc) + free(malloc(1)); /* initialize storage allocator */ +#endif +#if MEM_DEBUG && (__SC__ || _MSC_VER) && !defined(malloc) + { int i; + + i = _heapset(0xF4); + assert(i == _HEAPOK); + } +#endif + } + mem_inited++; +} + +/***************************/ + +void mem_term() +{ + if (mem_inited) + { +#if MEM_DEBUG + struct mem_debug *dl; + + for (dl = mem_alloclist.Mnext; dl; dl = dl->Mnext) + { PRINT "Unfreed pointer: "); + mem_printdl(dl); + } +#if 0 + PRINT "Max amount ever allocated == %ld bytes\n", + mem_maxalloc); +#endif +#if (__SC__ || _MSC_VER) && !defined(malloc) + { int i; + + i = _heapset(0xF4); + assert(i == _HEAPOK); + } +#endif +#else + if (mem_count) + PRINT "%d unfreed items\n",mem_count); + if (mem_scount) + PRINT "%d unfreed s items\n",mem_scount); +#endif /* MEM_DEBUG */ + assert(mem_count == 0 && mem_scount == 0); + } + mem_inited = 0; +} + +#endif /* !MEM_NONE */ diff --git a/tk/mem.h b/tk/mem.h new file mode 100644 index 00000000..f743cd15 --- /dev/null +++ b/tk/mem.h @@ -0,0 +1,271 @@ +/*_ mem.h */ +/* Copyright 1986-1997 by Walter Bright */ +/* All Rights Reserved */ +/* Written by Walter Bright */ + +#ifndef MEM_H +#define MEM_H 1 + +#if __SC__ +#pragma once +#endif + +/* + * Memory management routines. + * + * Compiling: + * + * #define MEM_DEBUG 1 when compiling to enable extended debugging + * features. + * + * #define MEM_NONE 1 to compile out mem, i.e. have it all drop + * directly to calls to malloc, free, etc. + * + * #define MEM_NOMEMCOUNT 1 to remove checks on the number of free's + * matching the number of alloc's. + * + * Features always enabled: + * + * o mem_init() is called at startup, and mem_term() at + * close, which checks to see that the number of alloc's is + * the same as the number of free's. + * o Behavior on out-of-memory conditions can be controlled + * via mem_setexception(). + * + * Extended debugging features: + * + * o Enabled by #define MEM_DEBUG 1 when compiling. + * o Check values are inserted before and after the alloc'ed data + * to detect pointer underruns and overruns. + * o Free'd pointers are checked against alloc'ed pointers. + * o Free'd storage is cleared to smoke out references to free'd data. + * o Realloc'd pointers are always changed, and the previous storage + * is cleared, to detect erroneous dependencies on the previous + * pointer. + * o The routine mem_checkptr() is provided to check an alloc'ed + * pointer. + */ + +/********************* GLOBAL VARIABLES *************************/ + +extern int mem_inited; /* != 0 if mem package is initialized. */ + /* Test this if you have other packages */ + /* that depend on mem being initialized */ + +/********************* PUBLIC FUNCTIONS *************************/ + +/*********************************** + * Set behavior when mem runs out of memory. + * Input: + * flag = MEM_ABORTMSG: Abort the program with the message + * 'Fatal error: out of memory' sent + * to stdout. This is the default behavior. + * MEM_ABORT: Abort the program with no message. + * MEM_RETNULL: Return NULL back to caller. + * MEM_CALLFP: Call application-specified function. + * fp must be supplied. + * fp Optional function pointer. Supplied if + * (flag == MEM_CALLFP). This function returns + * MEM_XXXXX, indicating what mem should do next. + * The function could do things like swap + * data out to disk to free up more memory. + * fp could also return: + * MEM_RETRY: Try again to allocate the space. Be + * careful not to go into an infinite loop. + * The type of fp is: + * int (*handler)(void) + */ + +#if !MEM_NONE +#if __SC__ || __DMC__ || __GNUC__ +enum MEM_E { MEM_ABORTMSG, MEM_ABORT, MEM_RETNULL, MEM_CALLFP, MEM_RETRY }; +void mem_setexception(enum MEM_E,...); +#else +#define MEM_ABORTMSG 0 +#define MEM_ABORT 1 +#define MEM_RETNULL 2 +#define MEM_CALLFP 3 +#define MEM_RETRY 4 +void mem_setexception(int,...); +#endif +#endif + +/**************************** + * Allocate space for string, copy string into it, and + * return pointer to the new string. + * This routine doesn't really belong here, but it is used so often + * that I gave up and put it here. + * Use: + * char *mem_strdup(const char *s); + * Returns: + * pointer to copied string if succussful. + * else returns NULL (if MEM_RETNULL) + */ + +char *mem_strdup(const char *); + +/************************** + * Function so we can have a pointer to function mem_free(). + * This is needed since mem_free is sometimes defined as a macro, + * and then the preprocessor screws up. + * The pointer to mem_free() is used frequently with the list package. + * Use: + * void mem_freefp(void *p); + */ + +/*************************** + * Check for errors. This routine does a consistency check on the + * storage allocator, looking for corrupted data. It should be called + * when the application has CPU cycles to burn. + * Use: + * void mem_check(void); + */ + +void mem_check(void); + +/*************************** + * Check ptr to see if it is in the range of allocated data. + * Cause assertion failure if it isn't. + */ + +void mem_checkptr(void *ptr); + +/*************************** + * Allocate and return a pointer to numbytes of storage. + * Use: + * void *mem_malloc(unsigned numbytes); + * void *mem_calloc(unsigned numbytes); allocated memory is cleared + * Input: + * numbytes Number of bytes to allocate + * Returns: + * if (numbytes > 0) + * pointer to allocated data, NULL if out of memory + * else + * return NULL + */ + +void *mem_malloc(unsigned); +void *mem_calloc(unsigned); + +/***************************** + * Reallocate memory. + * Use: + * void *mem_realloc(void *ptr,unsigned numbytes); + */ + +void *mem_realloc(void *,unsigned); + +/***************************** + * Free memory allocated by mem_malloc(), mem_calloc() or mem_realloc(). + * Use: + * void mem_free(void *ptr); + */ + +void mem_free(void *); + +/*************************** + * Initialize memory handler. + * Use: + * void mem_init(void); + * Output: + * mem_inited = 1 + */ + +void mem_init(void); + +/*************************** + * Terminate memory handler. Useful for checking for errors. + * Use: + * void mem_term(void); + * Output: + * mem_inited = 0 + */ + +void mem_term(void); + +/******************************* + * The mem_fxxx() functions are for allocating memory that will persist + * until program termination. The trick is that if the memory is never + * free'd, we can do a very fast allocation. If MEM_DEBUG is on, they + * act just like the regular mem functions, so it can be debugged. + */ + +#if MEM_NONE +#define mem_fmalloc(u) malloc(u) +#define mem_fcalloc(u) calloc((u),1) +#define mem_ffree(p) ((void)0) +#define mem_fstrdup(p) strdup(p) +#else +#if MEM_DEBUG +#define mem_fmalloc mem_malloc +#define mem_fcalloc mem_calloc +#define mem_ffree mem_free +#define mem_fstrdup mem_strdup +#else +void *mem_fmalloc(unsigned); +void *mem_fcalloc(unsigned); +#define mem_ffree(p) ((void)0) +char *mem_fstrdup(const char *); +#endif +#endif + +/*********************************** + * C++ stuff. + */ + +#if !MEM_NONE && MEM_DEBUG +#define mem_new !(__mem_line=__LINE__,__mem_file=__FILE__)? 0 : new +#define mem_delete (__mem_line=__LINE__,__mem_file=__FILE__), delete + +extern int __mem_line; +extern char *__mem_file; +#endif + +/* The following stuff forms the implementation rather than the + * definition, so ignore it. + */ + +#if MEM_NONE + +#define mem_inited 1 +#define mem_strdup(p) strdup(p) +#define mem_malloc(u) malloc(u) +#define mem_calloc(u) calloc((u),1) +#define mem_realloc(p,u) realloc((p),(u)) +#define mem_free(p) free(p) +#define mem_freefp free +#define mem_check() ((void)0) +#define mem_checkptr(p) ((void)(p)) +#define mem_init() ((void)0) +#define mem_term() ((void)0) + +#include + +#else + +#if MEM_DEBUG /* if creating debug version */ +#define mem_strdup(p) mem_strdup_debug((p),__FILE__,__LINE__) +#define mem_malloc(u) mem_malloc_debug((u),__FILE__,__LINE__) +#define mem_calloc(u) mem_calloc_debug((u),__FILE__,__LINE__) +#define mem_realloc(p,u) mem_realloc_debug((p),(u),__FILE__,__LINE__) +#define mem_free(p) mem_free_debug((p),__FILE__,__LINE__) + +char *mem_strdup_debug (const char *,const char *,int); +void *mem_calloc_debug (unsigned,const char *,int); +void *mem_malloc_debug (unsigned,const char *,int); +void *mem_realloc_debug (void *,unsigned,const char *,int); +void mem_free_debug (void *,const char *,int); +void mem_freefp (void *); + +void mem_setnewfileline (void *,const char *,int); + +#else + +#define mem_freefp mem_free +#define mem_check() +#define mem_checkptr(p) + +#endif /* MEM_DEBUG */ +#endif /* MEM_NONE */ + +#endif /* MEM_H */ diff --git a/tk/vec.c b/tk/vec.c new file mode 100644 index 00000000..9f8c04f5 --- /dev/null +++ b/tk/vec.c @@ -0,0 +1,655 @@ +/*_ vec.c Mon Oct 31 1994 */ +/* Copyright (C) 1986-2000 by Digital Mars */ +/* Written by Walter Bright */ +/* Bit vector package */ + +#include +#include +#ifndef assert +#include +#endif +#include "vec.h" +#include "mem.h" + +static int vec_count; /* # of vectors allocated */ +static int vec_initcount = 0; /* # of times package is initialized */ + +#define VECMAX 20 +static vec_t vecfreelist[VECMAX]; + +#if 1 +#define MASK(b) (1 << ((b) & VECMASK)) +#else +#define MASK(b) bmask[(b) & VECMASK] +static unsigned bmask[VECMASK + 1] = +{ + 1,2,4,8,0x10,0x20,0x40,0x80, + 0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000,0x8000, +#if __INTSIZE == 4 + 0x10000,0x20000,0x40000,0x80000,0x100000,0x200000,0x400000,0x800000, + 0x1000000,0x2000000,0x4000000,0x8000000, + 0x10000000,0x20000000,0x40000000,0x80000000 +#endif +}; +#endif + +/************************** + * Initialize package. + */ + +void vec_init() +{ + assert(sizeof(vec_base_t)==2&&VECSHIFT==4||sizeof(vec_base_t)==4&&VECSHIFT== 5); + if (vec_initcount++ == 0) + vec_count = 0; +} + +/************************** + * Terminate package. + */ + +void vec_term() +{ + if (--vec_initcount == 0) + { + +#ifdef DEBUG + if (vec_count != 0) + { + printf("vec_count = %d\n",vec_count); + assert(0); + } +#else + assert(vec_count == 0); +#endif +#if TERMCODE + int i; + for (i = 0; i < VECMAX; i++) + { void **v; + void **vn; + + for (v = (void **)vecfreelist[i]; v; v = vn) + { + vn = (void **)(*v); + mem_free(v); + } + vecfreelist[i] = NULL; + } +#endif + } +} + +/******************************** + * Allocate a vector given # of bits in it. + * Clear the vector. + */ + +vec_t vec_calloc(unsigned numbits) +{ vec_t v; + int dim; + + if (numbits == 0) + return (vec_t) NULL; + dim = (numbits + (VECBITS - 1)) >> VECSHIFT; + if (dim < VECMAX && (v = vecfreelist[dim]) != NULL) + { + vecfreelist[dim] = *(vec_t *)v; + v += 2; + switch (dim) + { + case 5: v[4] = 0; + case 4: v[3] = 0; + case 3: v[2] = 0; + case 2: v[1] = 0; + case 1: v[0] = 0; + break; + default: memset(v,0,dim * sizeof(vec_base_t)); + break; + } + goto L1; + } + else + { + v = (vec_t) mem_calloc((dim + 2) * sizeof(vec_base_t)); + } + if (v) + { + v += 2; + L1: + vec_dim(v) = dim; + vec_numbits(v) = numbits; + /*printf("vec_calloc(%d): v = %p vec_numbits = %d vec_dim = %d\n", + numbits,v,vec_numbits(v),vec_dim(v));*/ + vec_count++; + } + return v; +} + +/******************************** + * Allocate copy of existing vector. + */ + +vec_t vec_clone(vec_t v) +{ vec_t vc; + int dim; + unsigned nbytes; + + if (v) + { dim = vec_dim(v); + nbytes = (dim + 2) * sizeof(vec_base_t); + if (dim < VECMAX && (vc = vecfreelist[dim]) != NULL) + { + vecfreelist[dim] = *(vec_t *)vc; + goto L1; + } + else + { + vc = (vec_t) mem_calloc(nbytes); + } + if (vc) + { + L1: + memcpy(vc,v - 2,nbytes); + vec_count++; + v = vc + 2; + } + else + v = NULL; + } + return v; +} + +/************************** + * Free a vector. + */ + +void vec_free(vec_t v) +{ + /*printf("vec_free(%p)\n",v);*/ + if (v) + { int dim = vec_dim(v); + + v -= 2; + if (dim < VECMAX) + { + *(vec_t *)v = vecfreelist[dim]; + vecfreelist[dim] = v; + } + else + mem_free(v); + vec_count--; + } +} + +/************************** + * Realloc a vector to have numbits bits in it. + * Extra bits are set to 0. + */ + +vec_t vec_realloc(vec_t v,unsigned numbits) +{ vec_t newv; + unsigned vbits; + + /*printf("vec_realloc(%p,%d)\n",v,numbits);*/ + if (!v) + return vec_calloc(numbits); + if (!numbits) + { vec_free(v); + return NULL; + } + vbits = vec_numbits(v); + if (numbits == vbits) + return v; + newv = vec_calloc(numbits); + if (newv) + { unsigned nbytes; + + nbytes = (vec_dim(v) < vec_dim(newv)) ? vec_dim(v) : vec_dim(newv); + memcpy(newv,v,nbytes * sizeof(vec_base_t)); + vec_clearextrabits(newv); + } + vec_free(v); + return newv; +} + +/************************** + * Set bit b in vector v. + */ + +#ifndef vec_setbit + +#if _M_I86 && __INTSIZE == 4 && __SC__ +__declspec(naked) void __pascal vec_setbit(unsigned b,vec_t v) +{ + _asm + { + mov EAX,b-4[ESP] + mov ECX,v-4[ESP] + bts [ECX],EAX + ret 8 + } +} +#else +void vec_setbit(unsigned b,vec_t v) +{ +#ifdef DEBUG + if (!(v && b < vec_numbits(v))) + printf("vec_setbit(v = %p,b = %d): numbits = %d dim = %d\n", + v,b,v ? vec_numbits(v) : 0, v ? vec_dim(v) : 0); +#endif + assert(v && b < vec_numbits(v)); + *(v + (b >> VECSHIFT)) |= MASK(b); +} +#endif + +#endif + +/************************** + * Clear bit b in vector v. + */ + +#ifndef vec_clearbit + +#if _M_I86 && __INTSIZE == 4 && __SC__ +__declspec(naked) void __pascal vec_clearbit(unsigned b,vec_t v) +{ + _asm + { + mov EAX,b-4[ESP] + mov ECX,v-4[ESP] + btr [ECX],EAX + ret 8 + } +} +#else +void vec_clearbit(unsigned b,vec_t v) +{ + assert(v && b < vec_numbits(v)); + *(v + (b >> VECSHIFT)) &= ~MASK(b); +} +#endif + +#endif + +/************************** + * Test bit b in vector v. + */ + +#ifndef vec_testbit + +#if _M_I86 && __INTSIZE == 4 && __SC__ +__declspec(naked) int __pascal vec_testbit(unsigned b,vec_t v) +{ + _asm + { + mov EAX,v-4[ESP] + mov ECX,b-4[ESP] + test EAX,EAX + jz L1 + bt [EAX],ECX + sbb EAX,EAX + L1: ret 8 + } +} +#else +int vec_testbit(unsigned b,vec_t v) +{ + if (!v) + return 0; +#ifdef DEBUG + if (b >= vec_numbits(v)) + { printf("vec_testbit(v = %p,b = %d): numbits = %d dim = %d\n", + v,b,vec_numbits(v),vec_dim(v)); + b = (unsigned)-1; + } +#endif + assert(b < vec_numbits(v)); +#if __I86__ >= 3 && __SC__ + _asm + { +#if __INTSIZE == 4 + mov EAX,b + mov ECX,v + bt [ECX],EAX + sbb EAX,EAX +#elif __COMPACT__ || __LARGE__ || __VCM__ + mov AX,b + les BX,v + bt ES:[BX],AX + sbb AX,AX +#else + mov AX,b + mov CX,v + bt [CX],AX + sbb AX,AX +#endif + } +#ifdef DEBUG + { int x = _AX; + assert((x != 0) == ((*(v + (b >> VECSHIFT)) & MASK(b)) != 0)); + } +#endif +#else + return *(v + (b >> VECSHIFT)) & MASK(b); +#endif +} +#endif + +#endif + +/******************************** + * Find first set bit starting from b in vector v. + * If no bit is found, return vec_numbits(v). + */ + +unsigned vec_index(unsigned b,vec_t vec) +{ register unsigned starv; + register vec_t v,vtop; + unsigned bit; + + if (!vec) + return 0; + v = vec; + if (b < vec_numbits(v)) + { vtop = &vec[vec_dim(v)]; + bit = b & VECMASK; + if (bit != b) /* if not starting in first word */ + v += b >> VECSHIFT; + starv = *v >> bit; + while (1) + { + while (starv) + { if (starv & 1) + return b; + b++; + starv >>= 1; + } + b = (b + VECBITS) & ~VECMASK; /* round up to next word */ + if (++v >= vtop) + break; + starv = *v; + } + } + return vec_numbits(vec); +} + +/******************************** + * Compute v1 &= v2. + */ + +void vec_andass(vec_t v1,vec_t v2) +{ vec_t vtop; + + if (v1) + { + assert(v2); + assert(vec_numbits(v1)==vec_numbits(v2)); + vtop = &v1[vec_dim(v1)]; + for (; v1 < vtop; v1++,v2++) + *v1 &= *v2; + } + else + assert(!v2); +} + +/******************************** + * Compute v1 = v2 & v3. + */ + +void vec_and(vec_t v1,vec_t v2,vec_t v3) +{ vec_t vtop; + + if (v1) + { + assert(v2 && v3); + assert(vec_numbits(v1)==vec_numbits(v2) && vec_numbits(v1)==vec_numbits(v3)); + vtop = &v1[vec_dim(v1)]; + for (; v1 < vtop; v1++,v2++,v3++) + *v1 = *v2 & *v3; + } + else + assert(!v2 && !v3); +} + +/******************************** + * Compute v1 ^= v2. + */ + +void vec_xorass(vec_t v1,vec_t v2) +{ vec_t vtop; + + if (v1) + { + assert(v2); + assert(vec_numbits(v1)==vec_numbits(v2)); + vtop = &v1[vec_dim(v1)]; + for (; v1 < vtop; v1++,v2++) + *v1 ^= *v2; + } + else + assert(!v2); +} + +/******************************** + * Compute v1 = v2 ^ v3. + */ + +void vec_xor(vec_t v1,vec_t v2,vec_t v3) +{ vec_t vtop; + + if (v1) + { + assert(v2 && v3); + assert(vec_numbits(v1)==vec_numbits(v2) && vec_numbits(v1)==vec_numbits(v3)); + vtop = &v1[vec_dim(v1)]; + for (; v1 < vtop; v1++,v2++,v3++) + *v1 = *v2 ^ *v3; + } + else + assert(!v2 && !v3); +} + +/******************************** + * Compute v1 |= v2. + */ + +void vec_orass(vec_t v1,vec_t v2) +{ vec_t vtop; + + if (v1) + { +#ifdef DEBUG + assert(v2); + assert(vec_numbits(v1)==vec_numbits(v2)); +#endif + vtop = &v1[vec_dim(v1)]; +#if __INTSIZE == 2 && __I86__ && (__COMPACT__ || __LARGE__ || __VCM__) + _asm + { + push DS + lds SI,v2 + les DI,v1 + mov CX,word ptr vtop + cmp CX,DI + jz L1 + L2: mov AX,[SI] + add SI,2 + or ES:[DI],AX + add DI,2 + cmp DI,CX + jb L2 + L1: pop DS + #if __SC__ <= 0x610 + jmp Lret + #endif + } +#else + for (; v1 < vtop; v1++,v2++) + *v1 |= *v2; +#endif + } + else + assert(!v2); +} + +/******************************** + * Compute v1 = v2 | v3. + */ + +void vec_or(vec_t v1,vec_t v2,vec_t v3) +{ vec_t vtop; + + if (v1) + { + assert(v2 && v3); + assert(vec_numbits(v1)==vec_numbits(v2) && vec_numbits(v1)==vec_numbits(v3)); + vtop = &v1[vec_dim(v1)]; + for (; v1 < vtop; v1++,v2++,v3++) + *v1 = *v2 | *v3; + } + else + assert(!v2 && !v3); +} + +/******************************** + * Compute v1 -= v2. + */ + +void vec_subass(vec_t v1,vec_t v2) +{ vec_t vtop; + + if (v1) + { + assert(v2); + assert(vec_numbits(v1)==vec_numbits(v2)); + vtop = &v1[vec_dim(v1)]; + for (; v1 < vtop; v1++,v2++) + *v1 &= ~*v2; + } + else + assert(!v2); +} + +/******************************** + * Compute v1 = v2 - v3. + */ + +void vec_sub(vec_t v1,vec_t v2,vec_t v3) +{ vec_t vtop; + + if (v1) + { + assert(v2 && v3); + assert(vec_numbits(v1)==vec_numbits(v2) && vec_numbits(v1)==vec_numbits(v3)); + vtop = &v1[vec_dim(v1)]; + for (; v1 < vtop; v1++,v2++,v3++) + *v1 = *v2 & ~*v3; + } + else + assert(!v2 && !v3); +} + +/**************** + * Clear vector. + */ + +void vec_clear(vec_t v) +{ + if (v) + memset(v,0,sizeof(v[0]) * vec_dim(v)); +} + +/**************** + * Set vector. + */ + +void vec_set(vec_t v) +{ + if (v) + { memset(v,~0,sizeof(v[0]) * vec_dim(v)); + vec_clearextrabits(v); + } +} + +/*************** + * Copy vector. + */ + +void vec_copy(vec_t to,vec_t from) +{ + if (to != from) + { +#ifdef DEBUG + if (!(to && from && vec_numbits(to) == vec_numbits(from))) + printf("to = x%lx, from = x%lx, numbits(to) = %d, numbits(from) = %d\n", + (long)to,(long)from,to ? vec_numbits(to) : 0, from ? vec_numbits(from): 0); +#endif + assert(to && from && vec_numbits(to) == vec_numbits(from)); + memcpy(to,from,sizeof(to[0]) * vec_dim(to)); + } +} + +/**************** + * Return 1 if vectors are equal. + */ + +int vec_equal(vec_t v1,vec_t v2) +{ + if (v1 == v2) + return 1; + assert(v1 && v2 && vec_numbits(v1) == vec_numbits(v2)); + return !memcmp(v1,v2,sizeof(v1[0]) * vec_dim(v1)); +} + +/******************************** + * Return 1 if (v1 & v2) == 0 + */ + +int vec_disjoint(vec_t v1,vec_t v2) +{ vec_t vtop; + + assert(v1 && v2); + assert(vec_numbits(v1)==vec_numbits(v2)); + vtop = &v1[vec_dim(v1)]; + for (; v1 < vtop; v1++,v2++) + if (*v1 & *v2) + return 0; + return 1; +} + +/********************* + * Clear any extra bits in vector. + */ + +void vec_clearextrabits(vec_t v) +{ unsigned n; + + assert(v); + n = vec_numbits(v); + if (n & VECMASK) + v[vec_dim(v) - 1] &= MASK(n) - 1; +} + +/****************** + * Write out vector. + */ + +void vec_println(vec_t v) +{ +#ifdef DEBUG + vec_print(v); + fputc('\n',stdout); +#endif +} + +void vec_print(vec_t v) +{ +#ifdef DEBUG + printf(" Vec %p, numbits %d dim %d",v,vec_numbits(v),vec_dim(v)); + if (v) + { fputc('\t',stdout); + for (unsigned i = 0; i < vec_numbits(v); i++) + fputc((vec_testbit(i,v)) ? '1' : '0',stdout); + } +#endif +} diff --git a/tk/vec.h b/tk/vec.h new file mode 100644 index 00000000..77429c9c --- /dev/null +++ b/tk/vec.h @@ -0,0 +1,78 @@ +/*_ vec.h Mon Oct 31 1994 */ + +#ifndef VEC_H +#define VEC_H + +#if __SC__ +#pragma once +#endif + +typedef unsigned vec_base_t; /* base type of vector */ +typedef vec_base_t *vec_t; + +#define vec_numbits(v) ((v)[-1]) +#define vec_dim(v) ((v)[-2]) + +#define VECBITS (sizeof(vec_base_t)*8) /* # of bits per entry */ +#define VECMASK (VECBITS - 1) /* mask for bit position */ +#define VECSHIFT ((VECBITS == 16) ? 4 : 5) /* # of bits in VECMASK */ + +void vec_init (void); +void vec_term (void); +vec_t vec_calloc (unsigned numbits); +vec_t vec_clone (vec_t v); +void vec_free (vec_t v); +vec_t vec_realloc (vec_t v , unsigned numbits); +#if _M_I86 && __INTSIZE == 4 && __SC__ +void __pascal vec_setbit (unsigned b , vec_t v); +void __pascal vec_clearbit (unsigned b , vec_t v); +int __pascal vec_testbit (unsigned b , vec_t v); +#else +void vec_setbit (unsigned b , vec_t v); +void vec_clearbit (unsigned b , vec_t v); +int vec_testbit (unsigned b , vec_t v); +#endif +unsigned vec_index (unsigned b , vec_t vec); +void vec_andass (vec_t v1 , vec_t v2); +void vec_and (vec_t v1 , vec_t v2 , vec_t v3); +void vec_xorass (vec_t v1 , vec_t v2); +void vec_xor (vec_t v1 , vec_t v2 , vec_t v3); +void vec_orass (vec_t v1 , vec_t v2); +void vec_or (vec_t v1 , vec_t v2 , vec_t v3); +void vec_subass (vec_t v1 , vec_t v2); +void vec_sub (vec_t v1 , vec_t v2 , vec_t v3); +void vec_clear (vec_t v); +void vec_set (vec_t v); +void vec_copy (vec_t to , vec_t from); +int vec_equal (vec_t v1 , vec_t v2); +int vec_disjoint (vec_t v1 , vec_t v2); +void vec_clearextrabits (vec_t v); +void vec_print (vec_t v); +void vec_println (vec_t v); + +#if _M_I86 && __INTSIZE == 4 +#define vec_setclear(b,vs,vc) { \ + __asm mov EAX,b \ + __asm mov ECX,vs \ + __asm bts [ECX],EAX \ + __asm mov ECX,vc \ + __asm btr [ECX],EAX \ + } +#else +#define vec_setclear(b,vs,vc) (vec_setbit((b),(vs)),vec_clearbit((b),(vc))) +#endif + +// Loop through all the bits that are set in vector v of size t: +#define foreach(i,t,v) for((i)=0;((i)=vec_index((i),(v))), (i) < (t); (i)++) + +#if __DMC__ +// Digital Mars inlines some bit operations +#include + +#define vec_setbit(b, v) _inline_bts(v, b) +#define vec_clearbit(b, v) _inline_btr(v, b) +#define vec_testbit(b, v) (v && _inline_bt(v, b)) +#endif + +#endif /* VEC_H */ + diff --git a/tocsym.c b/tocsym.c new file mode 100644 index 00000000..1d614a1c --- /dev/null +++ b/tocsym.c @@ -0,0 +1,804 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include + +#if __sun&&__SVR4 +#include +#endif + +#include "mars.h" +#include "module.h" +#include "mtype.h" +#include "declaration.h" +#include "statement.h" +#include "enum.h" +#include "aggregate.h" +#include "init.h" +#include "attrib.h" +#include "lexer.h" +#include "dsymbol.h" +#include "id.h" + +#include "rmem.h" + +// Back end +#include "cc.h" +#include "global.h" +#include "oper.h" +#include "code.h" +#include "type.h" +#include "dt.h" +#include "cgcv.h" +#include "outbuf.h" +#include "irstate.h" + +void slist_add(Symbol *s); +void slist_reset(); + +Classsym *fake_classsym(Identifier *id); + +/********************************* SymbolDeclaration ****************************/ + +SymbolDeclaration::SymbolDeclaration(Loc loc, Symbol *s, StructDeclaration *dsym) + : Declaration(new Identifier(s->Sident, TOKidentifier)) +{ + this->loc = loc; + sym = s; + this->dsym = dsym; + storage_class |= STCconst; +} + +Symbol *SymbolDeclaration::toSymbol() +{ + return sym; +} + +/************************************* + * Helper + */ + +Symbol *Dsymbol::toSymbolX(const char *prefix, int sclass, type *t, const char *suffix) +{ + Symbol *s; + char *id; + const char *n; + size_t nlen; + + //printf("Dsymbol::toSymbolX('%s')\n", prefix); + n = mangle(); + assert(n); + nlen = strlen(n); +#if 0 + if (nlen > 2 && n[0] == '_' && n[1] == 'D') + { + nlen -= 2; + n += 2; + } +#endif + id = (char *) alloca(2 + nlen + sizeof(size_t) * 3 + strlen(prefix) + strlen(suffix) + 1); + sprintf(id,"_D%s%zu%s%s", n, strlen(prefix), prefix, suffix); +#if 0 + if (global.params.isWindows && + (type_mangle(t) == mTYman_c || type_mangle(t) == mTYman_std)) + id++; // Windows C mangling will put the '_' back in +#endif + s = symbol_name(id, sclass, t); + //printf("-Dsymbol::toSymbolX() %s\n", id); + return s; +} + +/************************************* + */ + +Symbol *Dsymbol::toSymbol() +{ + printf("Dsymbol::toSymbol() '%s', kind = '%s'\n", toChars(), kind()); +#ifdef DEBUG + halt(); +#endif + assert(0); // BUG: implement + return NULL; +} + +/********************************* + * Generate import symbol from symbol. + */ + +Symbol *Dsymbol::toImport() +{ + if (!isym) + { + if (!csym) + csym = toSymbol(); + isym = toImport(csym); + } + return isym; +} + +/************************************* + */ + +Symbol *Dsymbol::toImport(Symbol *sym) +{ + char *id; + char *n; + Symbol *s; + type *t; + + //printf("Dsymbol::toImport('%s')\n", sym->Sident); + n = sym->Sident; + id = (char *) alloca(6 + strlen(n) + 1 + sizeof(type_paramsize(sym->Stype))*3 + 1); + if (sym->Stype->Tmangle == mTYman_std && tyfunc(sym->Stype->Tty)) + { + sprintf(id,"_imp__%s@%lu",n,(unsigned long)type_paramsize(sym->Stype)); + } + else if (sym->Stype->Tmangle == mTYman_d) + sprintf(id,"_imp_%s",n); + else + sprintf(id,"_imp__%s",n); + t = type_alloc(TYnptr | mTYconst); + t->Tnext = sym->Stype; + t->Tnext->Tcount++; + t->Tmangle = mTYman_c; + t->Tcount++; + s = symbol_calloc(id); + s->Stype = t; + s->Sclass = SCextern; + s->Sfl = FLextern; + slist_add(s); + return s; +} + +/************************************* + */ + +Symbol *VarDeclaration::toSymbol() +{ + //printf("VarDeclaration::toSymbol(%s)\n", toChars()); + //if (needThis()) *(char*)0=0; + assert(!needThis()); + if (!csym) + { Symbol *s; + TYPE *t; + const char *id; + + if (isDataseg()) + id = mangle(); + else + id = ident->toChars(); + s = symbol_calloc(id); + + if (storage_class & (STCout | STCref)) + { + if (global.params.symdebug && storage_class & STCparameter) + { + t = type_alloc(TYnptr); // should be TYref, but problems in back end + t->Tnext = type->toCtype(); + t->Tnext->Tcount++; + } + else + t = type_fake(TYnptr); + } + else if (storage_class & STClazy) + t = type_fake(TYdelegate); // Tdelegate as C type + else if (isParameter()) + t = type->toCParamtype(); + else + t = type->toCtype(); + t->Tcount++; + + if (isDataseg()) + { + if (isThreadlocal()) + { /* Thread local storage + */ + TYPE *ts = t; + ts->Tcount++; // make sure a different t is allocated + type_setty(&t, t->Tty | mTYthread); + ts->Tcount--; + + if (global.params.vtls) + { + char *p = loc.toChars(); + fprintf(stdmsg, "%s: %s is thread local\n", p ? p : "", toChars()); + if (p) + mem.free(p); + } + } + s->Sclass = SCextern; + s->Sfl = FLextern; + slist_add(s); + /* if it's global or static, then it needs to have a qualified but unmangled name. + * This gives some explanation of the separation in treating name mangling. + * It applies to PDB format, but should apply to CV as PDB derives from CV. + * http://msdn.microsoft.com/en-us/library/ff553493(VS.85).aspx + */ + s->prettyIdent = toPrettyChars(); + } + else + { + s->Sclass = SCauto; + s->Sfl = FLauto; + + if (nestedrefs.dim) + { + /* Symbol is accessed by a nested function. Make sure + * it is not put in a register, and that the optimizer + * assumes it is modified across function calls and pointer + * dereferences. + */ + //printf("\tnested ref, not register\n"); + type_setcv(&t, t->Tty | mTYvolatile); + } + } + + if (ident == Id::va_argsave) + /* __va_argsave is set outside of the realm of the optimizer, + * so we tell the optimizer to leave it alone + */ + type_setcv(&t, t->Tty | mTYvolatile); + + mangle_t m = 0; + switch (linkage) + { + case LINKwindows: + m = mTYman_std; + break; + + case LINKpascal: + m = mTYman_pas; + break; + + case LINKc: + m = mTYman_c; + break; + + case LINKd: + m = mTYman_d; + break; + + case LINKcpp: + m = mTYman_cpp; + break; + + default: + printf("linkage = %d\n", linkage); + assert(0); + } + type_setmangle(&t, m); + s->Stype = t; + + csym = s; + } + return csym; +} + +/************************************* + */ + +Symbol *ClassInfoDeclaration::toSymbol() +{ + return cd->toSymbol(); +} + +/************************************* + */ + +Symbol *ModuleInfoDeclaration::toSymbol() +{ + return mod->toSymbol(); +} + +/************************************* + */ + +Symbol *TypeInfoDeclaration::toSymbol() +{ + //printf("TypeInfoDeclaration::toSymbol(%s), linkage = %d\n", toChars(), linkage); + return VarDeclaration::toSymbol(); +} + +/************************************* + */ + +Symbol *TypeInfoClassDeclaration::toSymbol() +{ + //printf("TypeInfoClassDeclaration::toSymbol(%s), linkage = %d\n", toChars(), linkage); + assert(tinfo->ty == Tclass); + TypeClass *tc = (TypeClass *)tinfo; + return tc->sym->toSymbol(); +} + +/************************************* + */ + +Symbol *FuncAliasDeclaration::toSymbol() +{ + return funcalias->toSymbol(); +} + +/************************************* + */ + +Symbol *FuncDeclaration::toSymbol() +{ + if (!csym) + { Symbol *s; + TYPE *t; + const char *id; + +#if 0 + id = ident->toChars(); +#else + id = mangle(); +#endif + //printf("FuncDeclaration::toSymbol(%s %s)\n", kind(), toChars()); + //printf("\tid = '%s'\n", id); + //printf("\ttype = %s\n", type->toChars()); + s = symbol_calloc(id); + slist_add(s); + + { + s->prettyIdent = toPrettyChars(); + s->Sclass = SCglobal; + symbol_func(s); + func_t *f = s->Sfunc; + if (isVirtual()) + f->Fflags |= Fvirtual; + else if (isMember2()) + f->Fflags |= Fstatic; + f->Fstartline.Slinnum = loc.linnum; + f->Fstartline.Sfilename = (char *)loc.filename; + if (endloc.linnum) + { f->Fendline.Slinnum = endloc.linnum; + f->Fendline.Sfilename = (char *)endloc.filename; + } + else + { f->Fendline.Slinnum = loc.linnum; + f->Fendline.Sfilename = (char *)loc.filename; + } + t = type->toCtype(); + } + + mangle_t msave = t->Tmangle; + if (isMain()) + { + t->Tty = TYnfunc; + t->Tmangle = mTYman_c; + } + else + { + switch (linkage) + { + case LINKwindows: + t->Tmangle = mTYman_std; + break; + + case LINKpascal: + t->Tty = TYnpfunc; + t->Tmangle = mTYman_pas; + break; + + case LINKc: + t->Tmangle = mTYman_c; + break; + + case LINKd: + t->Tmangle = mTYman_d; + break; + + case LINKcpp: + { t->Tmangle = mTYman_cpp; +#if TARGET_WINDOS + if (isThis()) + t->Tty = TYmfunc; +#endif + s->Sflags |= SFLpublic; + Dsymbol *parent = toParent(); + ClassDeclaration *cd = parent->isClassDeclaration(); + if (cd) + { + ::type *tc = cd->type->toCtype(); + s->Sscope = tc->Tnext->Ttag; + } + break; + } + default: + printf("linkage = %d\n", linkage); + assert(0); + } + } + if (msave) + assert(msave == t->Tmangle); + //printf("Tty = %x, mangle = x%x\n", t->Tty, t->Tmangle); + t->Tcount++; + s->Stype = t; + //s->Sfielddef = this; + + csym = s; + } + return csym; +} + +/************************************* + */ + +Symbol *FuncDeclaration::toThunkSymbol(int offset) +{ + Symbol *sthunk; + + toSymbol(); + +#if 0 + char *id; + char *n; + type *t; + + n = sym->Sident; + id = (char *) alloca(8 + 5 + strlen(n) + 1); + sprintf(id,"_thunk%d__%s", offset, n); + s = symbol_calloc(id); + slist_add(s); + s->Stype = csym->Stype; + s->Stype->Tcount++; +#endif + sthunk = symbol_generate(SCstatic, csym->Stype); + sthunk->Sflags |= SFLimplem; + cod3_thunk(sthunk, csym, 0, TYnptr, -offset, -1, 0); + return sthunk; +} + + +/**************************************** + * Create a static symbol we can hang DT initializers onto. + */ + +Symbol *static_sym() +{ + Symbol *s; + type *t; + + t = type_alloc(TYint); + t->Tcount++; + s = symbol_calloc("internal"); + s->Sclass = SCstatic; + s->Sfl = FLextern; + s->Sflags |= SFLnodebug; + s->Stype = t; +#if ELFOBJ || MACHOBJ + s->Sseg = DATA; +#endif + slist_add(s); + return s; +} + +/************************************** + * Fake a struct symbol. + */ + +Classsym *fake_classsym(Identifier *id) +{ TYPE *t; + Classsym *scc; + + scc = (Classsym *)symbol_calloc(id->toChars()); + scc->Sclass = SCstruct; + scc->Sstruct = struct_calloc(); + scc->Sstruct->Sstructalign = 8; + //scc->Sstruct->ptrtype = TYnptr; + scc->Sstruct->Sflags = STRglobal; + + t = type_alloc(TYstruct); + t->Tflags |= TFsizeunknown | TFforward; + t->Ttag = scc; // structure tag name + assert(t->Tmangle == 0); + t->Tmangle = mTYman_d; + t->Tcount++; + scc->Stype = t; + slist_add(scc); + return scc; +} + +/************************************* + * Create the "ClassInfo" symbol + */ + +static Classsym *scc; + +Symbol *ClassDeclaration::toSymbol() +{ + if (!csym) + { + Symbol *s; + + if (!scc) + scc = fake_classsym(Id::ClassInfo); + + s = toSymbolX("__Class", SCextern, scc->Stype, "Z"); + s->Sfl = FLextern; + s->Sflags |= SFLnodebug; + csym = s; + slist_add(s); + } + return csym; +} + +/************************************* + * Create the "InterfaceInfo" symbol + */ + +Symbol *InterfaceDeclaration::toSymbol() +{ + if (!csym) + { + Symbol *s; + + if (!scc) + scc = fake_classsym(Id::ClassInfo); + + s = toSymbolX("__Interface", SCextern, scc->Stype, "Z"); + s->Sfl = FLextern; + s->Sflags |= SFLnodebug; + csym = s; + slist_add(s); + } + return csym; +} + +/************************************* + * Create the "ModuleInfo" symbol + */ + +Symbol *Module::toSymbol() +{ + if (!csym) + { + if (!scc) + scc = fake_classsym(Id::ClassInfo); + + Symbol *s = toSymbolX("__ModuleInfo", SCextern, scc->Stype, "Z"); + s->Sfl = FLextern; + s->Sflags |= SFLnodebug; + csym = s; + slist_add(s); + } + return csym; +} + +/************************************* + * This is accessible via the ClassData, but since it is frequently + * needed directly (like for rtti comparisons), make it directly accessible. + */ + +Symbol *ClassDeclaration::toVtblSymbol() +{ + if (!vtblsym) + { + Symbol *s; + TYPE *t; + + if (!csym) + toSymbol(); + + t = type_alloc(TYnptr | mTYconst); + t->Tnext = tsvoid; + t->Tnext->Tcount++; + t->Tmangle = mTYman_d; + s = toSymbolX("__vtbl", SCextern, t, "Z"); + s->Sflags |= SFLnodebug; + s->Sfl = FLextern; + vtblsym = s; + slist_add(s); + } + return vtblsym; +} + +/********************************** + * Create the static initializer for the struct/class. + */ + +Symbol *AggregateDeclaration::toInitializer() +{ + Symbol *s; + Classsym *stag; + + if (!sinit) + { + stag = fake_classsym(Id::ClassInfo); + s = toSymbolX("__init", SCextern, stag->Stype, "Z"); + s->Sfl = FLextern; + s->Sflags |= SFLnodebug; + slist_add(s); + sinit = s; + } + return sinit; +} + +Symbol *TypedefDeclaration::toInitializer() +{ + Symbol *s; + Classsym *stag; + + if (!sinit) + { + stag = fake_classsym(Id::ClassInfo); + s = toSymbolX("__init", SCextern, stag->Stype, "Z"); + s->Sfl = FLextern; + s->Sflags |= SFLnodebug; + slist_add(s); + sinit = s; + } + return sinit; +} + +Symbol *EnumDeclaration::toInitializer() +{ + Symbol *s; + Classsym *stag; + + if (!sinit) + { + stag = fake_classsym(Id::ClassInfo); + Identifier *ident_save = ident; + if (!ident) + ident = Lexer::uniqueId("__enum"); + s = toSymbolX("__init", SCextern, stag->Stype, "Z"); + ident = ident_save; + s->Sfl = FLextern; + s->Sflags |= SFLnodebug; + slist_add(s); + sinit = s; + } + return sinit; +} + + +/****************************************** + */ + +Symbol *Module::toModuleAssert() +{ + if (!massert) + { + type *t; + + t = type_alloc(TYjfunc); + t->Tflags |= TFprototype | TFfixed; + t->Tmangle = mTYman_d; + t->Tnext = tsvoid; + tsvoid->Tcount++; + + massert = toSymbolX("__assert", SCextern, t, "FiZv"); + massert->Sfl = FLextern; + massert->Sflags |= SFLnodebug; + slist_add(massert); + } + return massert; +} + +Symbol *Module::toModuleUnittest() +{ + if (!munittest) + { + type *t; + + t = type_alloc(TYjfunc); + t->Tflags |= TFprototype | TFfixed; + t->Tmangle = mTYman_d; + t->Tnext = tsvoid; + tsvoid->Tcount++; + + munittest = toSymbolX("__unittest_fail", SCextern, t, "FiZv"); + munittest->Sfl = FLextern; + munittest->Sflags |= SFLnodebug; + slist_add(munittest); + } + return munittest; +} + +/****************************************** + */ + +Symbol *Module::toModuleArray() +{ + if (!marray) + { + type *t; + + t = type_alloc(TYjfunc); + t->Tflags |= TFprototype | TFfixed; + t->Tmangle = mTYman_d; + t->Tnext = tsvoid; + tsvoid->Tcount++; + + marray = toSymbolX("__array", SCextern, t, "Z"); + marray->Sfl = FLextern; + marray->Sflags |= SFLnodebug; + slist_add(marray); + } + return marray; +} + +/******************************************** + * Determine the right symbol to look up + * an associative array element. + * Input: + * flags 0 don't add value signature + * 1 add value signature + */ + +Symbol *TypeAArray::aaGetSymbol(const char *func, int flags) +#if __DMC__ + __in + { + assert(func); + assert((flags & ~1) == 0); + } + __out (result) + { + assert(result); + } + __body +#endif + { + // Dumb linear symbol table - should use associative array! + static Symbols *sarray = NULL; + + //printf("aaGetSymbol(func = '%s', flags = %d, key = %p)\n", func, flags, key); +#if 0 + OutBuffer buf; + key->toKeyBuffer(&buf); + + sz = next->size(); // it's just data, so we only care about the size + sz = (sz + 3) & ~3; // reduce proliferation of library routines + char *id = (char *)alloca(3 + strlen(func) + buf.offset + sizeof(sz) * 3 + 1); + buf.writeByte(0); + if (flags & 1) + sprintf(id, "_aa%s%s%d", func, buf.data, sz); + else + sprintf(id, "_aa%s%s", func, buf.data); +#else + char *id = (char *)alloca(3 + strlen(func) + 1); + sprintf(id, "_aa%s", func); +#endif + if (!sarray) + sarray = new Symbols(); + + // See if symbol is already in sarray + for (size_t i = 0; i < sarray->dim; i++) + { Symbol *s = (*sarray)[i]; + if (strcmp(id, s->Sident) == 0) + return s; // use existing Symbol + } + + // Create new Symbol + + Symbol *s = symbol_calloc(id); + slist_add(s); + s->Sclass = SCextern; + s->Ssymnum = -1; + symbol_func(s); + + type *t = type_alloc(TYnfunc); + t->Tflags = TFprototype | TFfixed; + t->Tmangle = mTYman_c; + t->Tparamtypes = NULL; + t->Tnext = next->toCtype(); + t->Tnext->Tcount++; + t->Tcount++; + s->Stype = t; + + sarray->push(s); // remember it + return s; + } + diff --git a/toctype.c b/toctype.c new file mode 100644 index 00000000..db41b3b4 --- /dev/null +++ b/toctype.c @@ -0,0 +1,517 @@ + +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// http://www.dsource.org/projects/dmd/browser/trunk/src/toctype.c +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include + +#if __sun&&__SVR4 +#include +#endif + +#include "mars.h" +#include "module.h" +#include "mtype.h" +#include "declaration.h" +#include "statement.h" +#include "enum.h" +#include "aggregate.h" +#include "init.h" +#include "attrib.h" +#include "id.h" +#include "import.h" +#include "template.h" + +#include "rmem.h" +#include "cc.h" +#include "global.h" +#include "oper.h" +#include "code.h" +#include "type.h" +#include "dt.h" +#include "cgcv.h" +#include "outbuf.h" +#include "irstate.h" + +void out_config_init(); +void slist_add(Symbol *s); +void slist_reset(); + + +/*************************************** + * Convert from D type to C type. + * This is done so C debug info can be generated. + */ + +type *Type::toCtype() +{ + if (!ctype) + { ctype = type_fake(totym()); + ctype->Tcount++; + } + return ctype; +} + +type *Type::toCParamtype() +{ + return toCtype(); +} + +type *TypeSArray::toCParamtype() +{ +#if SARRAYVALUE + return toCtype(); +#else + // arrays are passed as pointers + return next->pointerTo()->toCtype(); +#endif +} + +type *TypeSArray::toCtype() +{ + if (!ctype) + { type *tn; + + tn = next->toCtype(); + ctype = type_allocn(TYarray, tn); + ctype->Tdim = dim->toInteger(); + } + return ctype; +} + +type *TypeDArray::toCtype() +{ type *t; + + if (ctype) + return ctype; + + if (0 && global.params.symdebug) + { + /* Create a C type out of: + * struct _Array_T { size_t length; T* data; } + */ + Symbol *s; + char *id; + + assert(next->deco); + id = (char *) alloca(7 + strlen(next->deco) + 1); + sprintf(id, "_Array_%s", next->deco); + s = symbol_calloc(id); + s->Sclass = SCstruct; + s->Sstruct = struct_calloc(); + s->Sstruct->Sflags |= 0; + s->Sstruct->Salignsize = alignsize(); + s->Sstruct->Sstructalign = global.structalign; + s->Sstruct->Sstructsize = size(0); + slist_add(s); + + Symbol *s1 = symbol_name("length", SCmember, Type::tsize_t->toCtype()); + list_append(&s->Sstruct->Sfldlst, s1); + + Symbol *s2 = symbol_name("data", SCmember, next->pointerTo()->toCtype()); + s2->Smemoff = Type::tsize_t->size(); + list_append(&s->Sstruct->Sfldlst, s2); + + t = type_alloc(TYstruct); + t->Ttag = (Classsym *)s; // structure tag name + t->Tcount++; + s->Stype = t; + } + else + { + if (global.params.symdebug == 1) + { + // Generate D symbolic debug info, rather than C + t = type_allocn(TYdarray, next->toCtype()); + } + else + t = type_fake(TYdarray); + } + t->Tcount++; + ctype = t; + return t; +} + + +type *TypeAArray::toCtype() +{ type *t; + + if (ctype) + return ctype; + + if (0 && global.params.symdebug) + { + /* An associative array is represented by: + * struct AArray { size_t length; void* ptr; } + */ + + static Symbol *s; + + if (!s) + { + s = symbol_calloc("_AArray"); + s->Sclass = SCstruct; + s->Sstruct = struct_calloc(); + s->Sstruct->Sflags |= 0; + s->Sstruct->Salignsize = alignsize(); + s->Sstruct->Sstructalign = global.structalign; + s->Sstruct->Sstructsize = size(0); + slist_add(s); + + Symbol *s1 = symbol_name("length", SCmember, Type::tsize_t->toCtype()); + list_append(&s->Sstruct->Sfldlst, s1); + + Symbol *s2 = symbol_name("data", SCmember, Type::tvoidptr->toCtype()); + s2->Smemoff = Type::tsize_t->size(); + list_append(&s->Sstruct->Sfldlst, s2); + } + + t = type_alloc(TYstruct); + t->Ttag = (Classsym *)s; // structure tag name + t->Tcount++; + s->Stype = t; + } + else + { + if (global.params.symdebug == 1) + { + /* Generate D symbolic debug info, rather than C + * Tnext: element type + * Tkey: key type + */ + t = type_allocn(TYaarray, next->toCtype()); + t->Tkey = index->toCtype(); + t->Tkey->Tcount++; + } + else + t = type_fake(TYaarray); + } + t->Tcount++; + ctype = t; + return t; +} + + +type *TypePointer::toCtype() +{ type *tn; + type *t; + + //printf("TypePointer::toCtype() %s\n", toChars()); + if (ctype) + return ctype; + + if (1 || global.params.symdebug) + { /* Need to always do this, otherwise C++ name mangling + * goes awry. + */ + t = type_alloc(TYnptr); + ctype = t; + tn = next->toCtype(); + t->Tnext = tn; + tn->Tcount++; + } + else + t = type_fake(totym()); + t->Tcount++; + ctype = t; + return t; +} + +type *TypeFunction::toCtype() +{ type *t; + + if (ctype) + return ctype; + + if (1) + { + param_t *paramtypes = NULL; + size_t nparams = Parameter::dim(parameters); + for (size_t i = 0; i < nparams; i++) + { Parameter *arg = Parameter::getNth(parameters, i); + type *tp = arg->type->toCtype(); + if (arg->storageClass & (STCout | STCref)) + { // C doesn't have reference types, so it's really a pointer + // to the parameter type + tp = type_allocn(TYref, tp); + } + param_append_type(¶mtypes,tp); + } + tym_t tyf = totym(); + t = type_alloc(tyf); + t->Tflags |= TFprototype; + if (varargs != 1) + t->Tflags |= TFfixed; + ctype = t; + assert(next); // function return type should exist + t->Tnext = next->toCtype(); + t->Tnext->Tcount++; + t->Tparamtypes = paramtypes; + } + ctype = t; + return t; +} + +type *TypeDelegate::toCtype() +{ type *t; + + if (ctype) + return ctype; + + if (0 && global.params.symdebug) + { + /* A delegate consists of: + * _Delegate { void* frameptr; Function *funcptr; } + */ + + static Symbol *s; + + if (!s) + { + s = symbol_calloc("_Delegate"); + s->Sclass = SCstruct; + s->Sstruct = struct_calloc(); + s->Sstruct->Sflags |= 0; + s->Sstruct->Salignsize = alignsize(); + s->Sstruct->Sstructalign = global.structalign; + s->Sstruct->Sstructsize = size(0); + slist_add(s); + + Symbol *s1 = symbol_name("frameptr", SCmember, Type::tvoidptr->toCtype()); + list_append(&s->Sstruct->Sfldlst, s1); + + Symbol *s2 = symbol_name("funcptr", SCmember, Type::tvoidptr->toCtype()); + s2->Smemoff = Type::tvoidptr->size(); + list_append(&s->Sstruct->Sfldlst, s2); + } + + t = type_alloc(TYstruct); + t->Ttag = (Classsym *)s; // structure tag name + t->Tcount++; + s->Stype = t; + } + else + { + if (global.params.symdebug == 1) + { + // Generate D symbolic debug info, rather than C + t = type_allocn(TYdelegate, next->toCtype()); + } + else + t = type_fake(TYdelegate); + } + + t->Tcount++; + ctype = t; + return t; +} + + +type *TypeStruct::toCtype() +{ + if (ctype) + return ctype; + + //printf("TypeStruct::toCtype() '%s'\n", sym->toChars()); + type *t = type_alloc(TYstruct); + Type *tm = mutableOf(); + if (tm->ctype) + { + Symbol *s = tm->ctype->Ttag; + t->Ttag = (Classsym *)s; // structure tag name + t->Tcount++; + // Add modifiers + switch (mod) + { + case 0: + assert(0); + break; + case MODconst: + case MODwild: + t->Tty |= mTYconst; + break; + case MODimmutable: + t->Tty |= mTYimmutable; + break; + case MODshared: + t->Tty |= mTYshared; + break; + case MODshared | MODwild: + case MODshared | MODconst: + t->Tty |= mTYshared | mTYconst; + break; + default: + assert(0); + } + ctype = t; + } + else + { + Symbol *s = symbol_calloc(sym->toPrettyChars()); + s->Sclass = SCstruct; + s->Sstruct = struct_calloc(); + s->Sstruct->Sflags |= 0; + s->Sstruct->Salignsize = sym->alignsize; + s->Sstruct->Sstructalign = sym->alignsize; + s->Sstruct->Sstructsize = sym->structsize; + + if (sym->isUnionDeclaration()) + s->Sstruct->Sflags |= STRunion; + + t->Ttag = (Classsym *)s; // structure tag name + t->Tcount++; + s->Stype = t; + slist_add(s); + tm->ctype = t; + ctype = t; + + /* Add in fields of the struct + * (after setting ctype to avoid infinite recursion) + */ + if (global.params.symdebug) + for (size_t i = 0; i < sym->fields.dim; i++) + { VarDeclaration *v = sym->fields.tdata()[i]; + + Symbol *s2 = symbol_name(v->ident->toChars(), SCmember, v->type->toCtype()); + s2->Smemoff = v->offset; + list_append(&s->Sstruct->Sfldlst, s2); + } + } + + //printf("t = %p, Tflags = x%x\n", t, t->Tflags); + return t; +} + +type *TypeEnum::toCtype() +{ + if (ctype) + return ctype; + + //printf("TypeEnum::toCtype() '%s'\n", sym->toChars()); + type *t; + Type *tm = mutableOf(); + if (tm->ctype && tybasic(tm->ctype->Tty) == TYenum) + { + Symbol *s = tm->ctype->Ttag; + assert(s); + t = type_alloc(TYenum); + t->Ttag = (Classsym *)s; // enum tag name + t->Tcount++; + t->Tnext = tm->ctype->Tnext; + t->Tnext->Tcount++; + // Add modifiers + switch (mod) + { + case 0: + assert(0); + break; + case MODconst: + case MODwild: + t->Tty |= mTYconst; + break; + case MODimmutable: + t->Tty |= mTYimmutable; + break; + case MODshared: + t->Tty |= mTYshared; + break; + case MODshared | MODwild: + case MODshared | MODconst: + t->Tty |= mTYshared | mTYconst; + break; + default: + assert(0); + } + ctype = t; + } + else if (sym->memtype->toBasetype()->ty == Tint32) + { + Symbol *s = symbol_calloc(sym->toPrettyChars()); + s->Sclass = SCenum; + s->Senum = (enum_t *) MEM_PH_CALLOC(sizeof(enum_t)); + s->Senum->SEflags |= SENforward; // forward reference + slist_add(s); + + t = type_alloc(TYenum); + t->Ttag = (Classsym *)s; // enum tag name + t->Tcount++; + t->Tnext = sym->memtype->toCtype(); + t->Tnext->Tcount++; + s->Stype = t; + slist_add(s); + tm->ctype = t; + ctype = t; + } + else + { + t = ctype = sym->memtype->toCtype(); + } + + //printf("t = %p, Tflags = x%x\n", t, t->Tflags); + return t; +} + +type *TypeTypedef::toCtype() +{ + return sym->basetype->toCtype(); +} + +type *TypeTypedef::toCParamtype() +{ + return sym->basetype->toCParamtype(); +} + +type *TypeClass::toCtype() +{ type *t; + Symbol *s; + + //printf("TypeClass::toCtype() %s\n", toChars()); + if (ctype) + return ctype; + + /* Need this symbol to do C++ name mangling + */ + const char *name = sym->isCPPinterface() ? sym->ident->toChars() + : sym->toPrettyChars(); + s = symbol_calloc(name); + s->Sclass = SCstruct; + s->Sstruct = struct_calloc(); + s->Sstruct->Sflags |= STRclass; + s->Sstruct->Salignsize = sym->alignsize; + s->Sstruct->Sstructalign = sym->structalign; + s->Sstruct->Sstructsize = sym->structsize; + + t = type_alloc(TYstruct); + t->Ttag = (Classsym *)s; // structure tag name + t->Tcount++; + s->Stype = t; + slist_add(s); + + t = type_allocn(TYnptr, t); + + t->Tcount++; + ctype = t; + + /* Add in fields of the class + * (after setting ctype to avoid infinite recursion) + */ + if (global.params.symdebug) + for (size_t i = 0; i < sym->fields.dim; i++) + { VarDeclaration *v = sym->fields.tdata()[i]; + + Symbol *s2 = symbol_name(v->ident->toChars(), SCmember, v->type->toCtype()); + s2->Smemoff = v->offset; + list_append(&s->Sstruct->Sfldlst, s2); + } + + return t; +} + diff --git a/tocvdebug.c b/tocvdebug.c new file mode 100644 index 00000000..e54bfeb0 --- /dev/null +++ b/tocvdebug.c @@ -0,0 +1,821 @@ + +// Copyright (c) 2004-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com + +#include +#include +#include +#include + +#include "mars.h" +#include "module.h" +#include "mtype.h" +#include "declaration.h" +#include "statement.h" +#include "enum.h" +#include "aggregate.h" +#include "init.h" +#include "attrib.h" +#include "id.h" +#include "import.h" +#include "template.h" + +#include "rmem.h" +#include "cc.h" +#include "global.h" +#include "oper.h" +#include "code.h" +#include "type.h" +#include "dt.h" +#include "cv4.h" +#include "cgcv.h" +#include "outbuf.h" +#include "irstate.h" + +/* The CV4 debug format is defined in: + * "CV4 Symbolic Debug Information Specification" + * rev 3.1 March 5, 1993 + * Languages Business Unit + * Microsoft + */ + +/****************************** + * CV4 pg. 25 + * Convert D protection attribute to cv attribute. + */ + +unsigned PROTtoATTR(enum PROT prot) +{ + unsigned attribute; + + switch (prot) + { + case PROTprivate: attribute = 1; break; + case PROTpackage: attribute = 2; break; + case PROTprotected: attribute = 2; break; + case PROTpublic: attribute = 3; break; + case PROTexport: attribute = 3; break; + + case PROTundefined: + case PROTnone: + default: + //printf("prot = %d\n", prot); + assert(0); + } + return attribute; +} + +unsigned cv4_memfunctypidx(FuncDeclaration *fd) +{ type *t; + debtyp_t *d; + unsigned char *p; + AggregateDeclaration *ad; + + //printf("cv4_memfunctypidx(fd = '%s')\n", fd->toChars()); + t = fd->type->toCtype(); + ad = fd->isMember2(); + if (ad) + { + unsigned nparam; + idx_t paramidx; + idx_t thisidx; + unsigned char call; + + // It's a member function, which gets a special type record + + if (fd->isStatic()) + thisidx = dttab4[TYvoid]; + else + { + assert(ad->handle); + thisidx = cv4_typidx(ad->handle->toCtype()); + } + + paramidx = cv4_arglist(t,&nparam); + call = cv4_callconv(t); + + d = debtyp_alloc(18); + p = d->data; + TOWORD(p,LF_MFUNCTION); + TOWORD(p + 2,cv4_typidx(t->Tnext)); + TOWORD(p + 4,cv4_typidx(ad->type->toCtype())); + TOWORD(p + 6,thisidx); + p[8] = call; + p[9] = 0; // reserved + TOWORD(p + 10,nparam); + TOWORD(p + 12,paramidx); + TOLONG(p + 14,0); // thisadjust + + return cv_debtyp(d); + } + return cv4_typidx(t); +} + +unsigned cv4_Denum(EnumDeclaration *e) +{ + debtyp_t *d,*dt; + unsigned nfields,fnamelen; + unsigned len; + unsigned property; + unsigned attribute; + const char *id; + idx_t typidx; + + //dbg_printf("cv4_Denum(%s)\n", e->toChars()); + property = 0; + if (!e->members || !e->memtype) + property |= 0x80; // enum is forward referenced + + id = e->toPrettyChars(); + len = 10; + d = debtyp_alloc(len + cv_stringbytes(id)); + TOWORD(d->data,LF_ENUM); + TOWORD(d->data + 4,e->memtype ? cv4_typidx(e->memtype->toCtype()) : 0); + TOWORD(d->data + 8,property); + len += cv_namestring(d->data + len,id); + + d->length = 0; // so cv_debtyp() will allocate new + typidx = cv_debtyp(d); + d->length = len; // restore length + + // Compute the number of fields, and the length of the fieldlist record + nfields = 0; + fnamelen = 2; + if (!property) + { + for (size_t i = 0; i < e->members->dim; i++) + { EnumMember *sf = (e->members->tdata()[i])->isEnumMember(); + dinteger_t value; + + if (sf) + { + value = sf->value->toInteger(); + unsigned fnamelen1 = fnamelen; + // store only member's simple name + fnamelen += 4 + cv4_numericbytes(value) + cv_stringbytes(sf->toChars()); + + /* Optlink dies on longer ones, so just truncate + */ + if (fnamelen > 0xB000) // 0xB000 found by trial and error + { fnamelen = fnamelen1; // back up + break; // and skip the rest + } + + nfields++; + } + } + } + + TOWORD(d->data + 2,nfields); + + // If forward reference, then field list is 0 + if (property) + { + TOWORD(d->data + 6,0); + return typidx; + } + + // Generate fieldlist type record + dt = debtyp_alloc(fnamelen); + TOWORD(dt->data,LF_FIELDLIST); + + // And fill it in + unsigned j = 2; + unsigned fieldi = 0; + for (size_t i = 0; i < e->members->dim; i++) + { EnumMember *sf = (e->members->tdata()[i])->isEnumMember(); + dinteger_t value; + + if (sf) + { + fieldi++; + if (fieldi > nfields) + break; // chop off the rest + + value = sf->value->toInteger(); + TOWORD(dt->data + j,LF_ENUMERATE); + attribute = 0; + TOWORD(dt->data + j + 2,attribute); + cv4_storenumeric(dt->data + j + 4,value); + j += 4 + cv4_numericbytes(value); + // store only member's simple name + j += cv_namestring(dt->data + j, sf->toChars()); + + // If enum is not a member of a class, output enum members as constants +// if (!isclassmember(s)) +// { +// cv4_outsym(sf); +// } + } + } + assert(j == fnamelen); + TOWORD(d->data + 6,cv_debtyp(dt)); + +// cv4_outsym(s); + return typidx; +} + +/* ==================================================================== */ + +/**************************** + * Emit symbolic debug info in CV format. + */ + +void TypedefDeclaration::toDebug() +{ + //printf("TypedefDeclaration::toDebug('%s')\n", toChars()); + + assert(config.fulltypes >= CV4); + + // If it is a member, it is handled by cvMember() + if (!isMember()) + { + if (basetype->ty == Ttuple) + return; + + unsigned length; + const char *id = toPrettyChars(); + idx_t typidx = cv4_typidx(basetype->toCtype()); + unsigned len = strlen(id); + unsigned char *debsym = (unsigned char *) alloca(39 + IDOHD + len); + + // Output a 'user-defined type' for the tag name + TOWORD(debsym + 2,S_UDT); + TOIDX(debsym + 4,typidx); + length = 2 + 2 + cgcv.sz_idx; + length += cv_namestring(debsym + length,id); + TOWORD(debsym,length - 2); + + assert(length <= 40 + len); + obj_write_bytes(SegData[DEBSYM],length,debsym); + } +} + + +void EnumDeclaration::toDebug() +{ + //printf("EnumDeclaration::toDebug('%s')\n", toChars()); + + assert(config.fulltypes >= CV4); + + // If it is a member, it is handled by cvMember() + if (!isMember()) + { + unsigned length; + const char *id = toPrettyChars(); + idx_t typidx = cv4_Denum(this); + unsigned len = strlen(id); + unsigned char *debsym = (unsigned char *) alloca(39 + IDOHD + len); + + // Output a 'user-defined type' for the tag name + TOWORD(debsym + 2,S_UDT); + TOIDX(debsym + 4,typidx); + length = 2 + 2 + cgcv.sz_idx; + length += cv_namestring(debsym + length,id); + TOWORD(debsym,length - 2); + + assert(length <= 40 + len); + obj_write_bytes(SegData[DEBSYM],length,debsym); + } +} + + +void StructDeclaration::toDebug() +{ + unsigned leaf; + unsigned property; + unsigned nfields; + unsigned fnamelen; + const char *id; + targ_size_t size; + unsigned numidx; + debtyp_t *d,*dt; + unsigned len; + int count; // COUNT field in LF_CLASS + unsigned char *p; + idx_t typidx = 0; + + //printf("StructDeclaration::toDebug('%s')\n", toChars()); + + assert(config.fulltypes >= CV4); + if (isAnonymous()) + return /*0*/; + + if (typidx) // if reference already generated + return /*typidx*/; // use already existing reference + + property = 0; + if (!members) + { size = 0; + property |= 0x80; // forward reference + } + else + size = structsize; + + if (parent->isAggregateDeclaration()) // if class is nested + property |= 8; +// if (st->Sctor || st->Sdtor) +// property |= 2; // class has ctors and/or dtors +// if (st->Sopoverload) +// property |= 4; // class has overloaded operators +// if (st->Scastoverload) +// property |= 0x40; // class has casting methods +// if (st->Sopeq && !(st->Sopeq->Sfunc->Fflags & Fnodebug)) +// property |= 0x20; // class has overloaded assignment + + id = toPrettyChars(); + numidx = isUnionDeclaration() ? 8 : 12; + len = numidx + cv4_numericbytes(size); + d = debtyp_alloc(len + cv_stringbytes(id)); + cv4_storenumeric(d->data + numidx,size); + len += cv_namestring(d->data + len,id); + + leaf = isUnionDeclaration() ? LF_UNION : LF_STRUCTURE; + if (!isUnionDeclaration()) + { + TOWORD(d->data + 8,0); // dList + TOWORD(d->data + 10,0); // vshape is 0 (no virtual functions) + } + TOWORD(d->data,leaf); + + // Assign a number to prevent infinite recursion if a struct member + // references the same struct. + d->length = 0; // so cv_debtyp() will allocate new + typidx = cv_debtyp(d); + d->length = len; // restore length + + if (!members) // if reference only + { + TOWORD(d->data + 2,0); // count: number of fields is 0 + TOWORD(d->data + 4,0); // field list is 0 + TOWORD(d->data + 6,property); + return /*typidx*/; + } + + // Compute the number of fields, and the length of the fieldlist record + nfields = 0; + fnamelen = 2; + + count = nfields; + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + int nwritten; + + nwritten = s->cvMember(NULL); + if (nwritten) + { + fnamelen += nwritten; + nfields++; + count++; + } + } + + TOWORD(d->data + 2,count); + TOWORD(d->data + 6,property); + + // Generate fieldlist type record + dt = debtyp_alloc(fnamelen); + p = dt->data; + + // And fill it in + TOWORD(p,LF_FIELDLIST); + p += 2; + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + + p += s->cvMember(p); + } + + //dbg_printf("fnamelen = %d, p-dt->data = %d\n",fnamelen,p-dt->data); + assert(p - dt->data == fnamelen); + TOWORD(d->data + 4,cv_debtyp(dt)); + +// cv4_outsym(s); + + unsigned char *debsym; + unsigned length; + + len = strlen(id); + debsym = (unsigned char *) alloca(39 + IDOHD + len); + + // Output a 'user-defined type' for the tag name + TOWORD(debsym + 2,S_UDT); + TOIDX(debsym + 4,typidx); + length = 2 + 2 + cgcv.sz_idx; + length += cv_namestring(debsym + length,id); + TOWORD(debsym,length - 2); + + assert(length <= 40 + len); + obj_write_bytes(SegData[DEBSYM],length,debsym); + +// return typidx; +} + + +void ClassDeclaration::toDebug() +{ + unsigned leaf; + unsigned property; + unsigned nfields; + unsigned fnamelen; + const char *id; + targ_size_t size; + unsigned numidx; + debtyp_t *d,*dt; + unsigned len; + int i; + int count; // COUNT field in LF_CLASS + unsigned char *p; + idx_t typidx = 0; + + //printf("ClassDeclaration::toDebug('%s')\n", toChars()); + + assert(config.fulltypes >= CV4); + if (isAnonymous()) + return /*0*/; + + if (typidx) // if reference already generated + return /*typidx*/; // use already existing reference + + property = 0; + if (!members) + { size = 0; + property |= 0x80; // forward reference + } + else + size = structsize; + + if (parent->isAggregateDeclaration()) // if class is nested + property |= 8; + if (ctor || dtors.dim) + property |= 2; // class has ctors and/or dtors +// if (st->Sopoverload) +// property |= 4; // class has overloaded operators +// if (st->Scastoverload) +// property |= 0x40; // class has casting methods +// if (st->Sopeq && !(st->Sopeq->Sfunc->Fflags & Fnodebug)) +// property |= 0x20; // class has overloaded assignment + + id = isCPPinterface() ? ident->toChars() : toPrettyChars(); + numidx = isUnionDeclaration() ? 8 : 12; + len = numidx + cv4_numericbytes(size); + d = debtyp_alloc(len + cv_stringbytes(id)); + cv4_storenumeric(d->data + numidx,size); + len += cv_namestring(d->data + len,id); + + leaf = LF_CLASS; + TOWORD(d->data + 8,0); // dList + + if (1) + { debtyp_t *vshape; + unsigned char descriptor; + + size_t n = vtbl.dim; // number of virtual functions + if (n == 0) + { + TOWORD(d->data + 10,0); // vshape is 0 + } + else + { + vshape = debtyp_alloc(4 + (n + 1) / 2); + TOWORD(vshape->data,LF_VTSHAPE); + TOWORD(vshape->data + 2,1); + + n = 0; + descriptor = 0; + for (size_t i = 0; i < vtbl.dim; i++) + { FuncDeclaration *fd = (FuncDeclaration *)vtbl.tdata()[i]; + + //if (intsize == 4) + descriptor |= 5; + vshape->data[4 + n / 2] = descriptor; + descriptor <<= 4; + n++; + } + TOWORD(d->data + 10,cv_debtyp(vshape)); // vshape + } + } + else + TOWORD(d->data + 10,0); // vshape is 0 (no virtual functions) + + TOWORD(d->data,leaf); + + // Assign a number to prevent infinite recursion if a struct member + // references the same struct. + d->length = 0; // so cv_debtyp() will allocate new + typidx = cv_debtyp(d); + d->length = len; // restore length + + if (!members) // if reference only + { + TOWORD(d->data + 2,0); // count: number of fields is 0 + TOWORD(d->data + 4,0); // field list is 0 + TOWORD(d->data + 6,property); + return /*typidx*/; + } + + // Compute the number of fields, and the length of the fieldlist record + nfields = 0; + fnamelen = 2; + + // Add in base classes + for (size_t i = 0; i < baseclasses->dim; i++) + { BaseClass *bc = baseclasses->tdata()[i]; + + nfields++; + fnamelen += 6 + cv4_numericbytes(bc->offset); + } + + count = nfields; + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + int nwritten; + + nwritten = s->cvMember(NULL); + if (nwritten) + { + fnamelen += nwritten; + nfields++; + count++; + } + } + + TOWORD(d->data + 2,count); + TOWORD(d->data + 6,property); + + // Generate fieldlist type record + dt = debtyp_alloc(fnamelen); + p = dt->data; + + // And fill it in + TOWORD(p,LF_FIELDLIST); + p += 2; + + // Add in base classes + for (size_t i = 0; i < baseclasses->dim; i++) + { BaseClass *bc = baseclasses->tdata()[i]; + idx_t typidx; + unsigned attribute; + + typidx = cv4_typidx(bc->base->type->toCtype()->Tnext); + + attribute = PROTtoATTR(bc->protection); + + TOWORD(p,LF_BCLASS); + TOWORD(p + 2,typidx); + TOWORD(p + 4,attribute); + p += 6; + + cv4_storenumeric(p, bc->offset); + p += cv4_numericbytes(bc->offset); + } + + + + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *s = members->tdata()[i]; + + p += s->cvMember(p); + } + + //dbg_printf("fnamelen = %d, p-dt->data = %d\n",fnamelen,p-dt->data); + assert(p - dt->data == fnamelen); + TOWORD(d->data + 4,cv_debtyp(dt)); + +// cv4_outsym(s); + + unsigned char *debsym; + unsigned length; + + len = strlen(id); + debsym = (unsigned char *) alloca(39 + IDOHD + len); + + // Output a 'user-defined type' for the tag name + TOWORD(debsym + 2,S_UDT); + TOIDX(debsym + 4,typidx); + length = 2 + 2 + cgcv.sz_idx; + length += cv_namestring(debsym + length,id); + TOWORD(debsym,length - 2); + + assert(length <= 40 + len); + obj_write_bytes(SegData[DEBSYM],length,debsym); + +// return typidx; +} + + +/* ===================================================================== */ + +/***************************************** + * Insert CV info into *p. + * Returns: + * number of bytes written, or that would be written if p==NULL + */ + +int Dsymbol::cvMember(unsigned char *p) +{ + return 0; +} + + +int TypedefDeclaration::cvMember(unsigned char *p) +{ + char *id; + idx_t typidx; + int nwritten = 0; + + //printf("TypedefDeclaration::cvMember() '%s'\n", toChars()); + id = toChars(); + + if (!p) + { + nwritten = 4 + cv_stringbytes(id); + } + else + { + TOWORD(p,LF_NESTTYPE); + typidx = cv4_typidx(basetype->toCtype()); + TOWORD(p + 2,typidx); + nwritten = 4 + cv_namestring(p + 4, id); + } + return nwritten; +} + + +int EnumDeclaration::cvMember(unsigned char *p) +{ + char *id; + idx_t typidx; + int nwritten = 0; + + //printf("EnumDeclaration::cvMember() '%s'\n", toChars()); + id = toChars(); + + if (!p) + { + nwritten = 4 + cv_stringbytes(id); + } + else + { + TOWORD(p,LF_NESTTYPE); + typidx = cv4_Denum(this); + TOWORD(p + 2,typidx); + nwritten = 4 + cv_namestring(p + 4, id); + } + return nwritten; +} + + +int FuncDeclaration::cvMember(unsigned char *p) +{ + char *id; + idx_t typidx; + unsigned attribute; + int nwritten = 0; + debtyp_t *d; + + //printf("FuncDeclaration::cvMember() '%s'\n", toChars()); + + if (!type) // if not compiled in, + return 0; // skip it + + id = toChars(); + + if (!p) + { + nwritten = 6 + cv_stringbytes(id); + } + else + { + int count; + int mlen; + unsigned char *q; + + count = 0; + mlen = 2; + { + if (introducing) + mlen += 4; + mlen += cgcv.sz_idx * 2; + count++; + } + + // Allocate and fill it in + d = debtyp_alloc(mlen); + q = d->data; + TOWORD(q,LF_METHODLIST); + q += 2; +// for (s = sf; s; s = s->Sfunc->Foversym) + { + attribute = PROTtoATTR(prot()); + + /* 0*4 vanilla method + * 1*4 virtual method + * 2*4 static method + * 3*4 friend method + * 4*4 introducing virtual method + * 5*4 pure virtual method + * 6*4 pure introducing virtual method + * 7*4 reserved + */ + + if (isStatic()) + attribute |= 2*4; + else if (isVirtual()) + { + if (introducing) + { + if (isAbstract()) + attribute |= 6*4; + else + attribute |= 4*4; + } + else + { + if (isAbstract()) + attribute |= 5*4; + else + attribute |= 1*4; + } + } + else + attribute |= 0*4; + + TOIDX(q,attribute); + q += cgcv.sz_idx; + TOIDX(q, cv4_memfunctypidx(this)); + q += cgcv.sz_idx; + if (introducing) + { TOLONG(q, vtblIndex * PTRSIZE); + q += 4; + } + } + assert(q - d->data == mlen); + + typidx = cv_debtyp(d); + if (typidx) + { + TOWORD(p,LF_METHOD); + TOWORD(p + 2,count); + nwritten = 4; + TOIDX(p + nwritten, typidx); + nwritten += cgcv.sz_idx; + nwritten += cv_namestring(p + nwritten, id); + } + } + return nwritten; +} + +int VarDeclaration::cvMember(unsigned char *p) +{ + char *id; + idx_t typidx; + unsigned attribute; + int nwritten = 0; + + //printf("VarDeclaration::cvMember(p = %p) '%s'\n", p, toChars()); + + if (type->toBasetype()->ty == Ttuple) + return 0; + + id = toChars(); + + if (!p) + { + if (storage_class & STCfield) + { + nwritten += 6 + + cv4_numericbytes(offset) + cv_stringbytes(id); + } + else if (isStatic()) + { + nwritten += 6 + cv_stringbytes(id); + } + } + else if (storage_class & STCfield) + { + TOWORD(p,LF_MEMBER); + typidx = cv_typidx(type->toCtype()); + attribute = PROTtoATTR(prot()); + assert((attribute & ~3) == 0); + TOWORD(p + 2,typidx); + TOWORD(p + 4,attribute); + cv4_storenumeric(p + 6, offset); + nwritten = 6 + cv4_numericbytes( offset); + nwritten += cv_namestring(p + nwritten, id); + } + else if (isStatic()) + { + TOWORD(p,LF_STMEMBER); + typidx = cv_typidx(type->toCtype()); + attribute = PROTtoATTR(prot()); + assert((attribute & ~3) == 0); + TOWORD(p + 2,typidx); + TOWORD(p + 4,attribute); + nwritten = 6 + cv_namestring(p + 6, id); + } + return nwritten; +} + diff --git a/todt.c b/todt.c new file mode 100644 index 00000000..12a60333 --- /dev/null +++ b/todt.c @@ -0,0 +1,1051 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +/* A dt_t is a simple structure representing data to be added + * to the data segment of the output object file. As such, + * it is a list of initialized bytes, 0 data, and offsets from + * other symbols. + * Each D symbol and type can be converted into a dt_t so it can + * be written to the data segment. + */ + +#include +#include +#include +#include +#include + +#include "lexer.h" +#include "mtype.h" +#include "expression.h" +#include "init.h" +#include "enum.h" +#include "aggregate.h" +#include "declaration.h" + + +// Back end +#include "cc.h" +#include "el.h" +#include "oper.h" +#include "global.h" +#include "code.h" +#include "type.h" +#include "dt.h" + +extern Symbol *static_sym(); + +typedef ArrayBase Dts; + +/* ================================================================ */ + +dt_t *Initializer::toDt() +{ + assert(0); + return NULL; +} + + +dt_t *VoidInitializer::toDt() +{ /* Void initializers are set to 0, just because we need something + * to set them to in the static data segment. + */ + dt_t *dt = NULL; + + dtnzeros(&dt, type->size()); + return dt; +} + + +dt_t *StructInitializer::toDt() +{ + Dts dts; + dt_t *dt; + dt_t *d; + dt_t **pdtend; + unsigned offset; + + //printf("StructInitializer::toDt('%s')\n", toChars()); + dts.setDim(ad->fields.dim); + dts.zero(); + + for (size_t i = 0; i < vars.dim; i++) + { + VarDeclaration *v = vars.tdata()[i]; + Initializer *val = value.tdata()[i]; + + //printf("vars[%d] = %s\n", i, v->toChars()); + + for (size_t j = 0; 1; j++) + { + assert(j < dts.dim); + //printf(" adfield[%d] = %s\n", j, (ad->fields.tdata()[j])->toChars()); + if (ad->fields.tdata()[j] == v) + { + if (dts.tdata()[j]) + error(loc, "field %s of %s already initialized", v->toChars(), ad->toChars()); + dts.tdata()[j] = val->toDt(); + break; + } + } + } + + dt = NULL; + pdtend = &dt; + offset = 0; + for (size_t j = 0; j < dts.dim; j++) + { + VarDeclaration *v = ad->fields.tdata()[j]; + + d = dts.tdata()[j]; + if (!d) + { // An instance specific initializer was not provided. + // Look to see if there's a default initializer from the + // struct definition + if (v->init) + { + d = v->init->toDt(); + } + else if (v->offset >= offset) + { + unsigned k; + unsigned offset2 = v->offset + v->type->size(); + // Make sure this field does not overlap any explicitly + // initialized field. + for (k = j + 1; 1; k++) + { + if (k == dts.dim) // didn't find any overlap + { + v->type->toDt(&d); + break; + } + VarDeclaration *v2 = ad->fields.tdata()[k]; + + if (v2->offset < offset2 && dts.tdata()[k]) + break; // overlap + } + } + } + if (d) + { + if (v->offset < offset) + error(loc, "duplicate union initialization for %s", v->toChars()); + else + { unsigned sz = dt_size(d); + unsigned vsz = v->type->size(); + unsigned voffset = v->offset; + + if (sz > vsz) + { assert(v->type->ty == Tsarray && vsz == 0); + error(loc, "zero length array %s has non-zero length initializer", v->toChars()); + } + + unsigned dim = 1; + for (Type *vt = v->type->toBasetype(); + vt->ty == Tsarray; + vt = vt->nextOf()->toBasetype()) + { TypeSArray *tsa = (TypeSArray *)vt; + dim *= tsa->dim->toInteger(); + } + //printf("sz = %d, dim = %d, vsz = %d\n", sz, dim, vsz); + assert(sz == vsz || sz * dim <= vsz); + + for (size_t i = 0; i < dim; i++) + { + if (offset < voffset) + pdtend = dtnzeros(pdtend, voffset - offset); + if (!d) + { + if (v->init) + d = v->init->toDt(); + else + v->type->toDt(&d); + } + pdtend = dtcat(pdtend, d); + d = NULL; + offset = voffset + sz; + voffset += vsz / dim; + if (sz == vsz) + break; + } + } + } + } + if (offset < ad->structsize) + dtnzeros(pdtend, ad->structsize - offset); + + return dt; +} + + +dt_t *ArrayInitializer::toDt() +{ + //printf("ArrayInitializer::toDt('%s')\n", toChars()); + Type *tb = type->toBasetype(); + Type *tn = tb->nextOf()->toBasetype(); + + Dts dts; + unsigned size; + unsigned length; + dt_t *dt; + dt_t *d; + dt_t **pdtend; + + //printf("\tdim = %d\n", dim); + dts.setDim(dim); + dts.zero(); + + size = tn->size(); + + length = 0; + for (size_t i = 0; i < index.dim; i++) + { Expression *idx; + Initializer *val; + + idx = index.tdata()[i]; + if (idx) + length = idx->toInteger(); + //printf("\tindex[%d] = %p, length = %u, dim = %u\n", i, idx, length, dim); + + assert(length < dim); + val = value.tdata()[i]; + dt = val->toDt(); + if (dts.tdata()[length]) + error(loc, "duplicate initializations for index %d", length); + dts.tdata()[length] = dt; + length++; + } + + Expression *edefault = tb->nextOf()->defaultInit(); + + unsigned n = 1; + for (Type *tbn = tn; tbn->ty == Tsarray; tbn = tbn->nextOf()->toBasetype()) + { TypeSArray *tsa = (TypeSArray *)tbn; + + n *= tsa->dim->toInteger(); + } + + d = NULL; + pdtend = &d; + for (size_t i = 0; i < dim; i++) + { + dt = dts.tdata()[i]; + if (dt) + pdtend = dtcat(pdtend, dt); + else + { + for (size_t j = 0; j < n; j++) + pdtend = edefault->toDt(pdtend); + } + } + switch (tb->ty) + { + case Tsarray: + { unsigned tadim; + TypeSArray *ta = (TypeSArray *)tb; + + tadim = ta->dim->toInteger(); + if (dim < tadim) + { + if (edefault->isBool(FALSE)) + // pad out end of array + pdtend = dtnzeros(pdtend, size * (tadim - dim)); + else + { + for (size_t i = dim; i < tadim; i++) + { for (size_t j = 0; j < n; j++) + pdtend = edefault->toDt(pdtend); + } + } + } + else if (dim > tadim) + { +#ifdef DEBUG + printf("1: "); +#endif + error(loc, "too many initializers, %d, for array[%d]", dim, tadim); + } + break; + } + + case Tpointer: + case Tarray: + { // Create symbol, and then refer to it + Symbol *s = static_sym(); + s->Sdt = d; + outdata(s); + + d = NULL; + if (tb->ty == Tarray) + dtsize_t(&d, dim); + dtxoff(&d, s, 0, TYnptr); + break; + } + + default: + assert(0); + } + return d; +} + + +dt_t *ArrayInitializer::toDtBit() +{ +#if DMDV1 + unsigned size; + unsigned length; + unsigned tadim; + dt_t *d; + dt_t **pdtend; + Type *tb = type->toBasetype(); + + //printf("ArrayInitializer::toDtBit('%s')\n", toChars()); + + Bits databits; + Bits initbits; + + if (tb->ty == Tsarray) + { + /* The 'dim' for ArrayInitializer is only the maximum dimension + * seen in the initializer, not the type. So, for static arrays, + * use instead the dimension of the type in order + * to get the whole thing. + */ + dinteger_t value = ((TypeSArray*)tb)->dim->toInteger(); + tadim = value; + assert(tadim == value); // truncation overflow should already be checked + databits.resize(tadim); + initbits.resize(tadim); + } + else + { + databits.resize(dim); + initbits.resize(dim); + } + + /* The default initializer may be something other than zero. + */ + if (tb->nextOf()->defaultInit()->toInteger()) + databits.set(); + + size = sizeof(databits.tdata()[0]); + + length = 0; + for (size_t i = 0; i < index.dim; i++) + { Expression *idx; + Initializer *val; + Expression *eval; + + idx = index.tdata()[i]; + if (idx) + { dinteger_t value; + value = idx->toInteger(); + length = value; + if (length != value) + { error(loc, "index overflow %llu", value); + length = 0; + } + } + assert(length < dim); + + val = value.tdata()[i]; + eval = val->toExpression(); + if (initbits.test(length)) + error(loc, "duplicate initializations for index %d", length); + initbits.set(length); + if (eval->toInteger()) // any non-zero value is boolean 'true' + databits.set(length); + else + databits.clear(length); // boolean 'false' + length++; + } + + d = NULL; + pdtend = dtnbytes(&d, databits.allocdim * size, (char *)databits.data); + switch (tb->ty) + { + case Tsarray: + { + if (dim > tadim) + { + error(loc, "too many initializers, %d, for array[%d]", dim, tadim); + } + else + { + tadim = (tadim + 31) / 32; + if (databits.allocdim < tadim) + pdtend = dtnzeros(pdtend, size * (tadim - databits.allocdim)); // pad out end of array + } + break; + } + + case Tpointer: + case Tarray: + // Create symbol, and then refer to it + Symbol *s; + s = static_sym(); + s->Sdt = d; + outdata(s); + + d = NULL; + if (tb->ty == Tarray) + dtsize_t(&d, dim); + dtxoff(&d, s, 0, TYnptr); + break; + + default: + assert(0); + } + return d; +#else + return NULL; +#endif +} + + +dt_t *ExpInitializer::toDt() +{ + //printf("ExpInitializer::toDt() %s\n", exp->toChars()); + dt_t *dt = NULL; + + exp = exp->optimize(WANTvalue); + exp->toDt(&dt); + return dt; +} + +/* ================================================================ */ + +dt_t **Expression::toDt(dt_t **pdt) +{ +#ifdef DEBUG + printf("Expression::toDt() %d\n", op); + dump(0); +#endif + error("non-constant expression %s", toChars()); + pdt = dtnzeros(pdt, 1); + return pdt; +} + +dt_t **IntegerExp::toDt(dt_t **pdt) +{ unsigned sz; + + //printf("IntegerExp::toDt() %d\n", op); + sz = type->size(); + if (value == 0) + pdt = dtnzeros(pdt, sz); + else + pdt = dtnbytes(pdt, sz, (char *)&value); + return pdt; +} + +static char zeropad[6]; + +dt_t **RealExp::toDt(dt_t **pdt) +{ + d_float32 fvalue; + d_float64 dvalue; + d_float80 evalue; + + //printf("RealExp::toDt(%Lg)\n", value); + switch (type->toBasetype()->ty) + { + case Tfloat32: + case Timaginary32: + fvalue = value; + pdt = dtnbytes(pdt,4,(char *)&fvalue); + break; + + case Tfloat64: + case Timaginary64: + dvalue = value; + pdt = dtnbytes(pdt,8,(char *)&dvalue); + break; + + case Tfloat80: + case Timaginary80: + evalue = value; + pdt = dtnbytes(pdt,REALSIZE - REALPAD,(char *)&evalue); + pdt = dtnbytes(pdt,REALPAD,zeropad); + assert(REALPAD <= sizeof(zeropad)); + break; + + default: + printf("%s\n", toChars()); + type->print(); + assert(0); + break; + } + return pdt; +} + +dt_t **ComplexExp::toDt(dt_t **pdt) +{ + //printf("ComplexExp::toDt() '%s'\n", toChars()); + d_float32 fvalue; + d_float64 dvalue; + d_float80 evalue; + + switch (type->toBasetype()->ty) + { + case Tcomplex32: + fvalue = creall(value); + pdt = dtnbytes(pdt,4,(char *)&fvalue); + fvalue = cimagl(value); + pdt = dtnbytes(pdt,4,(char *)&fvalue); + break; + + case Tcomplex64: + dvalue = creall(value); + pdt = dtnbytes(pdt,8,(char *)&dvalue); + dvalue = cimagl(value); + pdt = dtnbytes(pdt,8,(char *)&dvalue); + break; + + case Tcomplex80: + evalue = creall(value); + pdt = dtnbytes(pdt,REALSIZE - REALPAD,(char *)&evalue); + pdt = dtnbytes(pdt,REALPAD,zeropad); + evalue = cimagl(value); + pdt = dtnbytes(pdt,REALSIZE - REALPAD,(char *)&evalue); + pdt = dtnbytes(pdt,REALPAD,zeropad); + break; + + default: + assert(0); + break; + } + return pdt; +} + +dt_t **NullExp::toDt(dt_t **pdt) +{ + assert(type); + return dtnzeros(pdt, type->size()); +} + +dt_t **StringExp::toDt(dt_t **pdt) +{ + //printf("StringExp::toDt() '%s', type = %s\n", toChars(), type->toChars()); + Type *t = type->toBasetype(); + + // BUG: should implement some form of static string pooling + switch (t->ty) + { + case Tarray: + dtsize_t(pdt, len); + pdt = dtabytes(pdt, TYnptr, 0, (len + 1) * sz, (char *)string); + break; + + case Tsarray: + { TypeSArray *tsa = (TypeSArray *)type; + dinteger_t dim; + + pdt = dtnbytes(pdt, len * sz, (const char *)string); + if (tsa->dim) + { + dim = tsa->dim->toInteger(); + if (len < dim) + { + // Pad remainder with 0 + pdt = dtnzeros(pdt, (dim - len) * tsa->next->size()); + } + } + break; + } + case Tpointer: + pdt = dtabytes(pdt, TYnptr, 0, (len + 1) * sz, (char *)string); + break; + + default: + printf("StringExp::toDt(type = %s)\n", type->toChars()); + assert(0); + } + return pdt; +} + +dt_t **ArrayLiteralExp::toDt(dt_t **pdt) +{ + //printf("ArrayLiteralExp::toDt() '%s', type = %s\n", toChars(), type->toChars()); + + dt_t *d; + dt_t **pdtend; + + d = NULL; + pdtend = &d; + for (size_t i = 0; i < elements->dim; i++) + { Expression *e = elements->tdata()[i]; + + pdtend = e->toDt(pdtend); + } + Type *t = type->toBasetype(); + + switch (t->ty) + { + case Tsarray: + pdt = dtcat(pdt, d); + break; + + case Tpointer: + case Tarray: + if (t->ty == Tarray) + dtsize_t(pdt, elements->dim); + if (d) + { + // Create symbol, and then refer to it + Symbol *s; + s = static_sym(); + s->Sdt = d; + outdata(s); + + dtxoff(pdt, s, 0, TYnptr); + } + else + dtsize_t(pdt, 0); + + break; + + default: + assert(0); + } + return pdt; +} + +dt_t **StructLiteralExp::toDt(dt_t **pdt) +{ + Dts dts; + dt_t *dt; + dt_t *d; + unsigned offset; + + //printf("StructLiteralExp::toDt() %s)\n", toChars()); + dts.setDim(sd->fields.dim); + dts.zero(); + assert(elements->dim <= sd->fields.dim); + + for (size_t i = 0; i < elements->dim; i++) + { + Expression *e = elements->tdata()[i]; + if (!e) + continue; + dt = NULL; + e->toDt(&dt); + dts.tdata()[i] = dt; + } + + offset = 0; + for (size_t j = 0; j < dts.dim; j++) + { + VarDeclaration *v = sd->fields.tdata()[j]; + + d = dts.tdata()[j]; + if (!d) + { // An instance specific initializer was not provided. + // Look to see if there's a default initializer from the + // struct definition + if (v->init) + { + d = v->init->toDt(); + } + else if (v->offset >= offset) + { + unsigned offset2 = v->offset + v->type->size(); + // Make sure this field (v) does not overlap any explicitly + // initialized field. + for (size_t k = j + 1; 1; k++) + { + if (k == dts.dim) // didn't find any overlap + { + v->type->toDt(&d); + break; + } + VarDeclaration *v2 = sd->fields.tdata()[k]; + + if (v2->offset < offset2 && dts.tdata()[k]) + break; // overlap + } + } + } + if (d) + { + if (v->offset < offset) + error("duplicate union initialization for %s", v->toChars()); + else + { unsigned sz = dt_size(d); + unsigned vsz = v->type->size(); + unsigned voffset = v->offset; + + if (sz > vsz) + { assert(v->type->ty == Tsarray && vsz == 0); + error("zero length array %s has non-zero length initializer", v->toChars()); + } + + unsigned dim = 1; + Type *vt; + for (vt = v->type->toBasetype(); + vt->ty == Tsarray; + vt = vt->nextOf()->toBasetype()) + { TypeSArray *tsa = (TypeSArray *)vt; + dim *= tsa->dim->toInteger(); + } + //printf("sz = %d, dim = %d, vsz = %d\n", sz, dim, vsz); + assert(sz == vsz || sz * dim <= vsz); + + for (size_t i = 0; i < dim; i++) + { + if (offset < voffset) + pdt = dtnzeros(pdt, voffset - offset); + if (!d) + { + if (v->init) + d = v->init->toDt(); + else + vt->toDt(&d); + } + pdt = dtcat(pdt, d); + d = NULL; + offset = voffset + sz; + voffset += vsz / dim; + if (sz == vsz) + break; + } + } + } + } + if (offset < sd->structsize) + pdt = dtnzeros(pdt, sd->structsize - offset); + + return pdt; +} + + +dt_t **SymOffExp::toDt(dt_t **pdt) +{ + Symbol *s; + + //printf("SymOffExp::toDt('%s')\n", var->toChars()); + assert(var); + if (!(var->isDataseg() || var->isCodeseg()) || + var->needThis() || + var->isThreadlocal()) + { +#ifdef DEBUG + printf("SymOffExp::toDt()\n"); +#endif + error("non-constant expression %s", toChars()); + return pdt; + } + s = var->toSymbol(); + return dtxoff(pdt, s, offset, TYnptr); +} + +dt_t **VarExp::toDt(dt_t **pdt) +{ + //printf("VarExp::toDt() %d\n", op); + for (; *pdt; pdt = &((*pdt)->DTnext)) + ; + + VarDeclaration *v = var->isVarDeclaration(); + if (v && (v->isConst() || v->isImmutable()) && + type->toBasetype()->ty != Tsarray && v->init) + { + if (v->inuse) + { + error("recursive reference %s", toChars()); + return pdt; + } + v->inuse++; + *pdt = v->init->toDt(); + v->inuse--; + return pdt; + } + SymbolDeclaration *sd = var->isSymbolDeclaration(); + if (sd && sd->dsym) + { + sd->dsym->toDt(pdt); + return pdt; + } +#ifdef DEBUG + printf("VarExp::toDt(), kind = %s\n", var->kind()); +#endif + error("non-constant expression %s", toChars()); + pdt = dtnzeros(pdt, 1); + return pdt; +} + +dt_t **FuncExp::toDt(dt_t **pdt) +{ + //printf("FuncExp::toDt() %d\n", op); + Symbol *s = fd->toSymbol(); + if (fd->isNested()) + { error("non-constant nested delegate literal expression %s", toChars()); + return NULL; + } + fd->toObjFile(0); + return dtxoff(pdt, s, 0, TYnptr); +} + +/* ================================================================= */ + +// Generate the data for the static initializer. + +void ClassDeclaration::toDt(dt_t **pdt) +{ + //printf("ClassDeclaration::toDt(this = '%s')\n", toChars()); + + // Put in first two members, the vtbl[] and the monitor + dtxoff(pdt, toVtblSymbol(), 0, TYnptr); + dtsize_t(pdt, 0); // monitor + + // Put in the rest + toDt2(pdt, this); + + //printf("-ClassDeclaration::toDt(this = '%s')\n", toChars()); +} + +void ClassDeclaration::toDt2(dt_t **pdt, ClassDeclaration *cd) +{ + unsigned offset; + dt_t *dt; + unsigned csymoffset; + +#define LOG 0 + +#if LOG + printf("ClassDeclaration::toDt2(this = '%s', cd = '%s')\n", toChars(), cd->toChars()); +#endif + if (baseClass) + { + baseClass->toDt2(pdt, cd); + offset = baseClass->structsize; + } + else + { + offset = PTRSIZE * 2; + } + + // Note equivalence of this loop to struct's + for (size_t i = 0; i < fields.dim; i++) + { + VarDeclaration *v = fields.tdata()[i]; + Initializer *init; + + //printf("\t\tv = '%s' v->offset = %2d, offset = %2d\n", v->toChars(), v->offset, offset); + dt = NULL; + init = v->init; + if (init) + { //printf("\t\t%s has initializer %s\n", v->toChars(), init->toChars()); + ExpInitializer *ei = init->isExpInitializer(); + Type *tb = v->type->toBasetype(); + if (ei && tb->ty == Tsarray) + ((TypeSArray *)tb)->toDtElem(&dt, ei->exp); + else + dt = init->toDt(); + } + else if (v->offset >= offset) + { //printf("\t\tdefault initializer\n"); + v->type->toDt(&dt); + } + if (dt) + { + if (v->offset < offset) + error("duplicated union initialization for %s", v->toChars()); + else + { + if (offset < v->offset) + dtnzeros(pdt, v->offset - offset); + dtcat(pdt, dt); + offset = v->offset + v->type->size(); + } + } + } + + // Interface vptr initializations + toSymbol(); // define csym + + for (size_t i = 0; i < vtblInterfaces->dim; i++) + { BaseClass *b = vtblInterfaces->tdata()[i]; + +#if 1 || INTERFACE_VIRTUAL + for (ClassDeclaration *cd2 = cd; 1; cd2 = cd2->baseClass) + { + assert(cd2); + csymoffset = cd2->baseVtblOffset(b); + if (csymoffset != ~0) + { + if (offset < b->offset) + dtnzeros(pdt, b->offset - offset); + dtxoff(pdt, cd2->toSymbol(), csymoffset, TYnptr); + break; + } + } +#else + csymoffset = baseVtblOffset(b); + assert(csymoffset != ~0); + dtxoff(pdt, csym, csymoffset, TYnptr); +#endif + offset = b->offset + PTRSIZE; + } + + if (offset < structsize) + dtnzeros(pdt, structsize - offset); + +#undef LOG +} + +void StructDeclaration::toDt(dt_t **pdt) +{ + unsigned offset; + dt_t *dt; + + //printf("StructDeclaration::toDt(), this='%s'\n", toChars()); + offset = 0; + + // Note equivalence of this loop to class's + for (size_t i = 0; i < fields.dim; i++) + { + VarDeclaration *v = fields.tdata()[i]; + //printf("\tfield '%s' voffset %d, offset = %d\n", v->toChars(), v->offset, offset); + dt = NULL; + int sz; + + if (v->storage_class & STCref) + { + sz = PTRSIZE; + if (v->offset >= offset) + dtnzeros(&dt, sz); + } + else + { + sz = v->type->size(); + Initializer *init = v->init; + if (init) + { //printf("\t\thas initializer %s\n", init->toChars()); + ExpInitializer *ei = init->isExpInitializer(); + Type *tb = v->type->toBasetype(); + if (ei && tb->ty == Tsarray) + ((TypeSArray *)tb)->toDtElem(&dt, ei->exp); + else + dt = init->toDt(); + } + else if (v->offset >= offset) + v->type->toDt(&dt); + } + if (dt) + { + if (v->offset < offset) + error("overlapping initialization for struct %s.%s", toChars(), v->toChars()); + else + { + if (offset < v->offset) + dtnzeros(pdt, v->offset - offset); + dtcat(pdt, dt); + offset = v->offset + sz; + } + } + } + + if (offset < structsize) + dtnzeros(pdt, structsize - offset); + + dt_optimize(*pdt); +} + +/* ================================================================= */ + +dt_t **Type::toDt(dt_t **pdt) +{ + //printf("Type::toDt()\n"); + Expression *e = defaultInit(); + return e->toDt(pdt); +} + +dt_t **TypeSArray::toDt(dt_t **pdt) +{ + return toDtElem(pdt, NULL); +} + +dt_t **TypeSArray::toDtElem(dt_t **pdt, Expression *e) +{ + unsigned len; + + //printf("TypeSArray::toDtElem()\n"); + len = dim->toInteger(); + if (len) + { + while (*pdt) + pdt = &((*pdt)->DTnext); + Type *tnext = next; + Type *tbn = tnext->toBasetype(); + while (tbn->ty == Tsarray && (!e || tbn != e->type->nextOf())) + { TypeSArray *tsa = (TypeSArray *)tbn; + + len *= tsa->dim->toInteger(); + tnext = tbn->nextOf(); + tbn = tnext->toBasetype(); + } + if (!e) // if not already supplied + e = tnext->defaultInit(); // use default initializer + e->toDt(pdt); + dt_optimize(*pdt); + if (e->op == TOKstring) + len /= ((StringExp *)e)->len; + if (e->op == TOKarrayliteral) + len /= ((ArrayLiteralExp *)e)->elements->dim; + if ((*pdt)->dt == DT_azeros && !(*pdt)->DTnext) + { + (*pdt)->DTazeros *= len; + pdt = &((*pdt)->DTnext); + } + else if ((*pdt)->dt == DT_1byte && (*pdt)->DTonebyte == 0 && !(*pdt)->DTnext) + { + (*pdt)->dt = DT_azeros; + (*pdt)->DTazeros = len; + pdt = &((*pdt)->DTnext); + } + else + { + for (size_t i = 1; i < len; i++) + { + if (tbn->ty == Tstruct) + { pdt = tnext->toDt(pdt); + while (*pdt) + pdt = &((*pdt)->DTnext); + } + else + pdt = e->toDt(pdt); + } + } + } + return pdt; +} + +dt_t **TypeStruct::toDt(dt_t **pdt) +{ + sym->toDt(pdt); + return pdt; +} + +dt_t **TypeTypedef::toDt(dt_t **pdt) +{ + if (sym->init) + { + dt_t *dt = sym->init->toDt(); + + while (*pdt) + pdt = &((*pdt)->DTnext); + *pdt = dt; + return pdt; + } + sym->basetype->toDt(pdt); + return pdt; +} + + + diff --git a/toelfdebug.c b/toelfdebug.c new file mode 100644 index 00000000..19a07552 --- /dev/null +++ b/toelfdebug.c @@ -0,0 +1,103 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 2004-2007 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#include +#include +#include +#include + +#include "mars.h" +#include "module.h" +#include "mtype.h" +#include "declaration.h" +#include "statement.h" +#include "enum.h" +#include "aggregate.h" +#include "init.h" +#include "attrib.h" +#include "id.h" +#include "import.h" +#include "template.h" + +#include "rmem.h" +#include "cc.h" +#include "global.h" +#include "oper.h" +#include "code.h" +#include "type.h" +#include "dt.h" +#include "cv4.h" +#include "cgcv.h" +#include "outbuf.h" +#include "irstate.h" + +/**************************** + * Emit symbolic debug info in Dwarf2 format. + */ + +void TypedefDeclaration::toDebug() +{ + //printf("TypedefDeclaration::toDebug('%s')\n", toChars()); +} + + +void EnumDeclaration::toDebug() +{ + //printf("EnumDeclaration::toDebug('%s')\n", toChars()); +} + + +void StructDeclaration::toDebug() +{ +} + + +void ClassDeclaration::toDebug() +{ +} + + +/* ===================================================================== */ + +/***************************************** + * Insert CV info into *p. + * Returns: + * number of bytes written, or that would be written if p==NULL + */ + +int Dsymbol::cvMember(unsigned char *p) +{ + return 0; +} + + +int TypedefDeclaration::cvMember(unsigned char *p) +{ + return 0; +} + + +int EnumDeclaration::cvMember(unsigned char *p) +{ + return 0; +} + + +int FuncDeclaration::cvMember(unsigned char *p) +{ + return 0; +} + +int VarDeclaration::cvMember(unsigned char *p) +{ + return 0; +} + diff --git a/toir.c b/toir.c new file mode 100644 index 00000000..b1ee5108 --- /dev/null +++ b/toir.c @@ -0,0 +1,912 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +/* Code to help convert to the intermediate representation + * of the compiler back end. + */ + +#include +#include +#include +#include + +#include "lexer.h" +#include "expression.h" +#include "mtype.h" +#include "dsymbol.h" +#include "declaration.h" +#include "enum.h" +#include "aggregate.h" +#include "attrib.h" +#include "module.h" +#include "init.h" +#include "template.h" + +#include "mem.h" // for mem_malloc + +#include "cc.h" +#include "el.h" +#include "oper.h" +#include "global.h" +#include "code.h" +#include "type.h" +#include "dt.h" +#include "irstate.h" +#include "id.h" +#include "type.h" +#include "toir.h" + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +/********************************************* + * Produce elem which increments the usage count for a particular line. + * Used to implement -cov switch (coverage analysis). + */ + +elem *incUsageElem(IRState *irs, Loc loc) +{ + unsigned linnum = loc.linnum; + + if (!irs->blx->module->cov || !linnum || + loc.filename != irs->blx->module->srcfile->toChars()) + return NULL; + + //printf("cov = %p, covb = %p, linnum = %u\n", irs->blx->module->cov, irs->blx->module->covb, p, linnum); + + linnum--; // from 1-based to 0-based + + /* Set bit in covb[] indicating this is a valid code line number + */ + unsigned *p = irs->blx->module->covb; + if (p) // covb can be NULL if it has already been written out to its .obj file + { + p += linnum / (sizeof(*p) * 8); + *p |= 1 << (linnum & (sizeof(*p) * 8 - 1)); + } + + elem *e; + e = el_ptr(irs->blx->module->cov); + e = el_bin(OPadd, TYnptr, e, el_long(TYuint, linnum * 4)); + e = el_una(OPind, TYuint, e); + e = el_bin(OPaddass, TYuint, e, el_long(TYuint, 1)); + return e; +} + +/****************************************** + * Return elem that evaluates to the static frame pointer for function fd. + * If fd is a member function, the returned expression will compute the value + * of fd's 'this' variable. + * This routine is critical for implementing nested functions. + */ + +elem *getEthis(Loc loc, IRState *irs, Dsymbol *fd) +{ + elem *ethis; + FuncDeclaration *thisfd = irs->getFunc(); + Dsymbol *fdparent = fd->toParent2(); + + //printf("getEthis(thisfd = '%s', fd = '%s', fdparent = '%s')\n", thisfd->toPrettyChars(), fd->toPrettyChars(), fdparent->toPrettyChars()); + if (fdparent == thisfd || + /* These two are compiler generated functions for the in and out contracts, + * and are called from an overriding function, not just the one they're + * nested inside, so this hack is so they'll pass + */ + fd->ident == Id::require || fd->ident == Id::ensure) + { /* Going down one nesting level, i.e. we're calling + * a nested function from its enclosing function. + */ +#if DMDV2 + if (irs->sclosure) + ethis = el_var(irs->sclosure); + else +#endif + if (irs->sthis) + { // We have a 'this' pointer for the current function + ethis = el_var(irs->sthis); + + /* If no variables in the current function's frame are + * referenced by nested functions, then we can 'skip' + * adding this frame into the linked list of stack + * frames. + */ + if (thisfd->hasNestedFrameRefs()) + { /* Local variables are referenced, can't skip. + * Address of 'this' gives the 'this' for the nested + * function + */ + ethis = el_una(OPaddr, TYnptr, ethis); + } + } + else + { /* No 'this' pointer for current function, + * use NULL if no references to the current function's frame + */ + ethis = el_long(TYnptr, 0); + if (thisfd->hasNestedFrameRefs()) + { /* OPframeptr is an operator that gets the frame pointer + * for the current function, i.e. for the x86 it gets + * the value of EBP + */ + ethis->Eoper = OPframeptr; + } + } +//if (fdparent != thisfd) ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYint, 0x18)); + } + else + { + if (!irs->sthis) // if no frame pointer for this function + { + fd->error(loc, "is a nested function and cannot be accessed from %s", irs->getFunc()->toPrettyChars()); + ethis = el_long(TYnptr, 0); // error recovery + } + else + { + ethis = el_var(irs->sthis); + Dsymbol *s = thisfd; + while (fd != s) + { /* Go up a nesting level, i.e. we need to find the 'this' + * of an enclosing function. + * Our 'enclosing function' may also be an inner class. + */ + + //printf("\ts = '%s'\n", s->toChars()); + thisfd = s->isFuncDeclaration(); + if (thisfd) + { /* Enclosing function is a function. + */ + if (fdparent == s->toParent2()) + break; + if (thisfd->isNested()) + { + FuncDeclaration *p = s->toParent2()->isFuncDeclaration(); + if (!p || p->hasNestedFrameRefs()) + ethis = el_una(OPind, TYnptr, ethis); + } + else if (thisfd->vthis) + { + } + else + { // Error should have been caught by front end + assert(0); + } + } + else + { /* Enclosed by an aggregate. That means the current + * function must be a member function of that aggregate. + */ + ClassDeclaration *cd; + StructDeclaration *sd; + AggregateDeclaration *ad = s->isAggregateDeclaration(); + if (!ad) + goto Lnoframe; + cd = s->isClassDeclaration(); + if (cd && fd->isClassDeclaration() && + fd->isClassDeclaration()->isBaseOf(cd, NULL)) + break; + sd = s->isStructDeclaration(); + if (fd == sd) + break; + if (!ad->isNested() || !ad->vthis) + { + Lnoframe: + irs->getFunc()->error(loc, "cannot get frame pointer to %s", fd->toChars()); + return el_long(TYnptr, 0); // error recovery + } + ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYsize_t, ad->vthis->offset)); + ethis = el_una(OPind, TYnptr, ethis); + if (fdparent == s->toParent2()) + break; + if (fd == s->toParent2()) + { + /* Remember that frames for functions that have no + * nested references are skipped in the linked list + * of frames. + */ + if (s->toParent2()->isFuncDeclaration()->hasNestedFrameRefs()) + ethis = el_una(OPind, TYnptr, ethis); + break; + } + if (s->toParent2()->isFuncDeclaration()) + { + /* Remember that frames for functions that have no + * nested references are skipped in the linked list + * of frames. + */ + if (s->toParent2()->isFuncDeclaration()->hasNestedFrameRefs()) + ethis = el_una(OPind, TYnptr, ethis); + } + } + s = s->toParent2(); + assert(s); + } + } + } +#if 0 + printf("ethis:\n"); + elem_print(ethis); + printf("\n"); +#endif + return ethis; +} + + +/************************* + * Initialize the hidden aggregate member, vthis, with + * the context pointer. + * Returns: + * *(ey + ad.vthis.offset) = this; + */ +#if DMDV2 +elem *setEthis(Loc loc, IRState *irs, elem *ey, AggregateDeclaration *ad) +{ + elem *ethis; + FuncDeclaration *thisfd = irs->getFunc(); + int offset = 0; + Dsymbol *cdp = ad->toParent2(); // class/func we're nested in + + //printf("setEthis(ad = %s, cdp = %s, thisfd = %s)\n", ad->toChars(), cdp->toChars(), thisfd->toChars()); + + if (cdp == thisfd) + { /* Class we're new'ing is a local class in this function: + * void thisfd() { class ad { } } + */ + if (irs->sclosure) + ethis = el_var(irs->sclosure); + else if (irs->sthis) + { + if (thisfd->hasNestedFrameRefs()) + { + ethis = el_ptr(irs->sthis); + } + else + ethis = el_var(irs->sthis); + } + else + { + ethis = el_long(TYnptr, 0); + if (thisfd->hasNestedFrameRefs()) + { + ethis->Eoper = OPframeptr; + } + } + } + else if (thisfd->vthis && + (cdp == thisfd->toParent2() || + (cdp->isClassDeclaration() && + cdp->isClassDeclaration()->isBaseOf(thisfd->toParent2()->isClassDeclaration(), &offset) + ) + ) + ) + { /* Class we're new'ing is at the same level as thisfd + */ + assert(offset == 0); // BUG: should handle this case + ethis = el_var(irs->sthis); + } + else + { + ethis = getEthis(loc, irs, ad->toParent2()); + ethis = el_una(OPaddr, TYnptr, ethis); + } + + ey = el_bin(OPadd, TYnptr, ey, el_long(TYsize_t, ad->vthis->offset)); + ey = el_una(OPind, TYnptr, ey); + ey = el_bin(OPeq, TYnptr, ey, ethis); + return ey; +} +#endif + +/******************************************* + * Convert intrinsic function to operator. + * Returns that operator, -1 if not an intrinsic function. + */ + +int intrinsic_op(char *name) +{ + //printf("intrinsic_op(%s)\n", name); + static const char *std_namearray[] = + { +#if DMDV1 + "4math3cosFeZe", + "4math3sinFeZe", + "4math4fabsFeZe", + "4math4rintFeZe", + "4math4sqrtFdZd", + "4math4sqrtFeZe", + "4math4sqrtFfZf", + "4math4yl2xFeeZe", + "4math5ldexpFeiZe", + "4math6rndtolFeZl", + "4math6yl2xp1FeeZe", + + "9intrinsic2btFPkkZi", + "9intrinsic3bsfFkZi", + "9intrinsic3bsrFkZi", + "9intrinsic3btcFPkkZi", + "9intrinsic3btrFPkkZi", + "9intrinsic3btsFPkkZi", + "9intrinsic3inpFkZh", + "9intrinsic4inplFkZk", + "9intrinsic4inpwFkZt", + "9intrinsic4outpFkhZh", + "9intrinsic5bswapFkZk", + "9intrinsic5outplFkkZk", + "9intrinsic5outpwFktZt", +#elif DMDV2 + /* The names are mangled differently because of the pure and + * nothrow attributes. + */ + "4math3cosFNaNbNfeZe", + "4math3sinFNaNbNfeZe", + "4math4fabsFNaNbNfeZe", + "4math4rintFNaNbNfeZe", + "4math4sqrtFNaNbNfdZd", + "4math4sqrtFNaNbNfeZe", + "4math4sqrtFNaNbNffZf", + "4math4yl2xFNaNbNfeeZe", + "4math5ldexpFNaNbNfeiZe", + "4math6rndtolFNaNbNfeZl", + "4math6yl2xp1FNaNbNfeeZe", + + "9intrinsic2btFNaNbxPkkZi", + "9intrinsic3bsfFNaNbkZi", + "9intrinsic3bsrFNaNbkZi", + "9intrinsic3btcFNbPkkZi", + "9intrinsic3btrFNbPkkZi", + "9intrinsic3btsFNbPkkZi", + "9intrinsic3inpFNbkZh", + "9intrinsic4inplFNbkZk", + "9intrinsic4inpwFNbkZt", + "9intrinsic4outpFNbkhZh", + "9intrinsic5bswapFNaNbkZk", + "9intrinsic5outplFNbkkZk", + "9intrinsic5outpwFNbktZt", +#endif + }; + static const char *std_namearray64[] = + { +#if DMDV1 + "4math3cosFeZe", + "4math3sinFeZe", + "4math4fabsFeZe", + "4math4rintFeZe", + "4math4sqrtFdZd", + "4math4sqrtFeZe", + "4math4sqrtFfZf", + "4math4yl2xFeeZe", + "4math5ldexpFeiZe", + "4math6rndtolFeZl", + "4math6yl2xp1FeeZe", + + "9intrinsic2btFPmmZi", + "9intrinsic3bsfFkZi", + "9intrinsic3bsrFkZi", + "9intrinsic3btcFPmmZi", + "9intrinsic3btrFPmmZi", + "9intrinsic3btsFPmmZi", + "9intrinsic3inpFkZh", + "9intrinsic4inplFkZk", + "9intrinsic4inpwFkZt", + "9intrinsic4outpFkhZh", + "9intrinsic5bswapFkZk", + "9intrinsic5outplFkkZk", + "9intrinsic5outpwFktZt", +#elif DMDV2 + /* The names are mangled differently because of the pure and + * nothrow attributes. + */ + "4math3cosFNaNbNfeZe", + "4math3sinFNaNbNfeZe", + "4math4fabsFNaNbNfeZe", + "4math4rintFNaNbNfeZe", + "4math4sqrtFNaNbNfdZd", + "4math4sqrtFNaNbNfeZe", + "4math4sqrtFNaNbNffZf", + "4math4yl2xFNaNbNfeeZe", + "4math5ldexpFNaNbNfeiZe", + "4math6rndtolFNaNbNfeZl", + "4math6yl2xp1FNaNbNfeeZe", + + "9intrinsic2btFNaNbxPmmZi", + "9intrinsic3bsfFNaNbmZi", + "9intrinsic3bsrFNaNbmZi", + "9intrinsic3btcFNbPmmZi", + "9intrinsic3btrFNbPmmZi", + "9intrinsic3btsFNbPmmZi", + "9intrinsic3inpFNbkZh", + "9intrinsic4inplFNbkZk", + "9intrinsic4inpwFNbkZt", + "9intrinsic4outpFNbkhZh", + "9intrinsic5bswapFNaNbkZk", + "9intrinsic5outplFNbkkZk", + "9intrinsic5outpwFNbktZt", +#endif + }; + static unsigned char std_ioptab[] = + { + OPcos, + OPsin, + OPabs, + OPrint, + OPsqrt, + OPsqrt, + OPsqrt, + OPyl2x, + OPscale, + OPrndtol, + OPyl2xp1, + + OPbt, + OPbsf, + OPbsr, + OPbtc, + OPbtr, + OPbts, + OPinp, + OPinp, + OPinp, + OPoutp, + OPbswap, + OPoutp, + OPoutp, + }; + +#ifdef DMDV2 + static const char *core_namearray[] = + { + "4math3cosFNaNbNfeZe", + "4math3sinFNaNbNfeZe", + "4math4fabsFNaNbNfeZe", + "4math4rintFNaNbNfeZe", + "4math4sqrtFNaNbNfdZd", + "4math4sqrtFNaNbNfeZe", + "4math4sqrtFNaNbNffZf", + "4math4yl2xFNaNbNfeeZe", + "4math5ldexpFNaNbNfeiZe", + "4math6rndtolFNaNbNfeZl", + "4math6yl2xp1FNaNbNfeeZe", + + "4simd6__simdFNaNbNfE4core4simd3XMMNhG16vNhG16vZNhG16v", + + "5bitop2btFNaNbxPkkZi", + "5bitop3bsfFNaNbkZi", + "5bitop3bsrFNaNbkZi", + "5bitop3btcFNbPkkZi", + "5bitop3btrFNbPkkZi", + "5bitop3btsFNbPkkZi", + "5bitop3inpFNbkZh", + "5bitop4inplFNbkZk", + "5bitop4inpwFNbkZt", + "5bitop4outpFNbkhZh", + "5bitop5bswapFNaNbkZk", + "5bitop5outplFNbkkZk", + "5bitop5outpwFNbktZt", + }; + static const char *core_namearray64[] = + { + "4math3cosFNaNbNfeZe", + "4math3sinFNaNbNfeZe", + "4math4fabsFNaNbNfeZe", + "4math4rintFNaNbNfeZe", + "4math4sqrtFNaNbNfdZd", + "4math4sqrtFNaNbNfeZe", + "4math4sqrtFNaNbNffZf", + "4math4yl2xFNaNbNfeeZe", + "4math5ldexpFNaNbNfeiZe", + "4math6rndtolFNaNbNfeZl", + "4math6yl2xp1FNaNbNfeeZe", + + "4simd6__simdFNaNbNfE4core4simd3XMMNhG16vNhG16vZNhG16v", + + "5bitop2btFNaNbxPmmZi", + "5bitop3bsfFNaNbmZi", + "5bitop3bsrFNaNbmZi", + "5bitop3btcFNbPmmZi", + "5bitop3btrFNbPmmZi", + "5bitop3btsFNbPmmZi", + "5bitop3inpFNbkZh", + "5bitop4inplFNbkZk", + "5bitop4inpwFNbkZt", + "5bitop4outpFNbkhZh", + "5bitop5bswapFNaNbkZk", + "5bitop5outplFNbkkZk", + "5bitop5outpwFNbktZt", + }; + static unsigned char core_ioptab[] = + { + OPcos, + OPsin, + OPabs, + OPrint, + OPsqrt, + OPsqrt, + OPsqrt, + OPyl2x, + OPscale, + OPrndtol, + OPyl2xp1, + + OPvector, + + OPbt, + OPbsf, + OPbsr, + OPbtc, + OPbtr, + OPbts, + OPinp, + OPinp, + OPinp, + OPoutp, + OPbswap, + OPoutp, + OPoutp, + }; +#endif + +#ifdef DEBUG + assert(sizeof(std_namearray) == sizeof(std_namearray64)); + assert(sizeof(std_namearray) / sizeof(char *) == sizeof(std_ioptab)); + for (size_t i = 0; i < sizeof(std_namearray) / sizeof(char *) - 1; i++) + { + if (strcmp(std_namearray[i], std_namearray[i + 1]) >= 0) + { + printf("std_namearray[%ld] = '%s'\n", (long)i, std_namearray[i]); + assert(0); + } + } + assert(sizeof(std_namearray64) / sizeof(char *) == sizeof(std_ioptab)); + for (size_t i = 0; i < sizeof(std_namearray64) / sizeof(char *) - 1; i++) + { + if (strcmp(std_namearray64[i], std_namearray64[i + 1]) >= 0) + { + printf("std_namearray64[%ld] = '%s'\n", (long)i, std_namearray64[i]); + assert(0); + } + } +#ifdef DMDV2 + assert(sizeof(core_namearray) == sizeof(core_namearray64)); + assert(sizeof(core_namearray) / sizeof(char *) == sizeof(core_ioptab)); + for (size_t i = 0; i < sizeof(core_namearray) / sizeof(char *) - 1; i++) + { + if (strcmp(core_namearray[i], core_namearray[i + 1]) >= 0) + { + printf("core_namearray[%ld] = '%s'\n", (long)i, core_namearray[i]); + assert(0); + } + } + assert(sizeof(core_namearray64) / sizeof(char *) == sizeof(core_ioptab)); + for (size_t i = 0; i < sizeof(core_namearray64) / sizeof(char *) - 1; i++) + { + if (strcmp(core_namearray64[i], core_namearray64[i + 1]) >= 0) + { + printf("core_namearray64[%ld] = '%s'\n", (long)i, core_namearray64[i]); + assert(0); + } + } +#endif +#endif + size_t length = strlen(name); + + if (length > 10 && + (name[7] == 'm' || name[7] == 'i') && + !memcmp(name, "_D3std", 6)) + { + int i = binary(name + 6, I64 ? std_namearray64 : std_namearray, sizeof(std_namearray) / sizeof(char *)); + return (i == -1) ? i : std_ioptab[i]; + } +#ifdef DMDV2 + if (length > 12 && + (name[8] == 'm' || name[8] == 'b' || name[8] == 's') && + !memcmp(name, "_D4core", 7)) + { + int i = binary(name + 7, I64 ? core_namearray64 : core_namearray, sizeof(core_namearray) / sizeof(char *)); + return (i == -1) ? i : core_ioptab[i]; + } +#endif + return -1; +} + + +/************************************** + * Given an expression e that is an array, + * determine and set the 'length' variable. + * Input: + * lengthVar Symbol of 'length' variable + * &e expression that is the array + * t1 Type of the array + * Output: + * e is rewritten to avoid side effects + * Returns: + * expression that initializes 'length' + */ + +elem *resolveLengthVar(VarDeclaration *lengthVar, elem **pe, Type *t1) +{ + //printf("resolveLengthVar()\n"); + elem *einit = NULL; + + if (lengthVar && !(lengthVar->storage_class & STCconst)) + { elem *elength; + Symbol *slength; + + if (t1->ty == Tsarray) + { TypeSArray *tsa = (TypeSArray *)t1; + dinteger_t length = tsa->dim->toInteger(); + + elength = el_long(TYsize_t, length); + goto L3; + } + else if (t1->ty == Tarray) + { + elength = *pe; + *pe = el_same(&elength); + elength = el_una(I64 ? OP128_64 : OP64_32, TYsize_t, elength); + + L3: + slength = lengthVar->toSymbol(); + //symbol_add(slength); + + einit = el_bin(OPeq, TYsize_t, el_var(slength), elength); + } + } + return einit; +} + +/************************************* + * Closures are implemented by taking the local variables that + * need to survive the scope of the function, and copying them + * into a gc allocated chuck of memory. That chunk, called the + * closure here, is inserted into the linked list of stack + * frames instead of the usual stack frame. + * + * buildClosure() inserts code just after the function prolog + * is complete. It allocates memory for the closure, allocates + * a local variable (sclosure) to point to it, inserts into it + * the link to the enclosing frame, and copies into it the parameters + * that are referred to in nested functions. + * In VarExp::toElem and SymOffExp::toElem, when referring to a + * variable that is in a closure, takes the offset from sclosure rather + * than from the frame pointer. + * + * getEthis() and NewExp::toElem need to use sclosure, if set, rather + * than the current frame pointer. + */ + +#if DMDV2 + +void FuncDeclaration::buildClosure(IRState *irs) +{ + if (needsClosure()) + { // Generate closure on the heap + // BUG: doesn't capture variadic arguments passed to this function + +#if DMDV2 + /* BUG: doesn't handle destructors for the local variables. + * The way to do it is to make the closure variables the fields + * of a class object: + * class Closure + * { vtbl[] + * monitor + * ptr to destructor + * sthis + * ... closure variables ... + * ~this() { call destructor } + * } + */ +#endif + //printf("FuncDeclaration::buildClosure()\n"); + Symbol *sclosure; + sclosure = symbol_name("__closptr",SCauto,Type::tvoidptr->toCtype()); + sclosure->Sflags |= SFLtrue | SFLfree; + symbol_add(sclosure); + irs->sclosure = sclosure; + + unsigned offset = PTRSIZE; // leave room for previous sthis + for (size_t i = 0; i < closureVars.dim; i++) + { VarDeclaration *v = closureVars.tdata()[i]; + assert(v->isVarDeclaration()); + +#if DMDV2 + if (v->needsAutoDtor()) + /* Because the value needs to survive the end of the scope! + */ + v->error("has scoped destruction, cannot build closure"); + if (v->isargptr) + /* See Bugzilla 2479 + * This is actually a bug, but better to produce a nice + * message at compile time rather than memory corruption at runtime + */ + v->error("cannot reference variadic arguments from closure"); +#endif + /* Align and allocate space for v in the closure + * just like AggregateDeclaration::addField() does. + */ + unsigned memsize; + unsigned memalignsize; + unsigned xalign; +#if DMDV2 + if (v->storage_class & STClazy) + { + /* Lazy variables are really delegates, + * so give same answers that TypeDelegate would + */ + memsize = PTRSIZE * 2; + memalignsize = memsize; + xalign = global.structalign; + } + else if (v->isRef() || v->isOut()) + { // reference parameters are just pointers + memsize = PTRSIZE; + memalignsize = memsize; + xalign = global.structalign; + } + else +#endif + { + memsize = v->type->size(); + memalignsize = v->type->alignsize(); + xalign = v->type->memalign(global.structalign); + } + AggregateDeclaration::alignmember(xalign, memalignsize, &offset); + v->offset = offset; + offset += memsize; + + /* Can't do nrvo if the variable is put in a closure, since + * what the shidden points to may no longer exist. + */ + if (nrvo_can && nrvo_var == v) + { + nrvo_can = 0; + } + } + // offset is now the size of the closure + + // Allocate memory for the closure + elem *e; + e = el_long(TYsize_t, offset); + e = el_bin(OPcall, TYnptr, el_var(rtlsym[RTLSYM_ALLOCMEMORY]), e); + + // Assign block of memory to sclosure + // sclosure = allocmemory(sz); + e = el_bin(OPeq, TYvoid, el_var(sclosure), e); + + // Set the first element to sthis + // *(sclosure + 0) = sthis; + elem *ethis; + if (irs->sthis) + ethis = el_var(irs->sthis); + else + ethis = el_long(TYnptr, 0); + elem *ex = el_una(OPind, TYnptr, el_var(sclosure)); + ex = el_bin(OPeq, TYnptr, ex, ethis); + e = el_combine(e, ex); + + // Copy function parameters into closure + for (size_t i = 0; i < closureVars.dim; i++) + { VarDeclaration *v = closureVars.tdata()[i]; + + if (!v->isParameter()) + continue; + tym_t tym = v->type->totym(); + if ( +#if !SARRAYVALUE + v->type->toBasetype()->ty == Tsarray || +#endif + v->isOut() || v->isRef()) + tym = TYnptr; // reference parameters are just pointers +#if DMDV2 + else if (v->storage_class & STClazy) + tym = TYdelegate; +#endif + ex = el_bin(OPadd, TYnptr, el_var(sclosure), el_long(TYsize_t, v->offset)); + ex = el_una(OPind, tym, ex); + if (tybasic(ex->Ety) == TYstruct || tybasic(ex->Ety) == TYarray) + { + ::type *t = v->type->toCtype(); + ex->ET = t; + ex = el_bin(OPstreq, tym, ex, el_var(v->toSymbol())); + ex->ET = t; + } + else + ex = el_bin(OPeq, tym, ex, el_var(v->toSymbol())); + + e = el_combine(e, ex); + } + + block_appendexp(irs->blx->curblock, e); + } +} + +#endif + +/*************************** + * Determine return style of function - whether in registers or + * through a hidden pointer to the caller's stack. + */ + +enum RET TypeFunction::retStyle() +{ + //printf("TypeFunction::retStyle() %s\n", toChars()); +#if DMDV2 + if (isref) + return RETregs; // returns a pointer +#endif + + Type *tn = next->toBasetype(); + Type *tns = tn; + d_uns64 sz = tn->size(); + +#if SARRAYVALUE + if (tn->ty == Tsarray) + { + do + { + tns = tns->nextOf()->toBasetype(); + } while (tns->ty == Tsarray); + if (tns->ty != Tstruct) + { + if (global.params.isLinux && linkage != LINKd) + ; + else + { + switch (sz) + { case 1: + case 2: + case 4: + case 8: + return RETregs; // return small structs in regs + // (not 3 byte structs!) + default: + break; + } + } + return RETstack; + } + } +#endif + + if (tns->ty == Tstruct) + { StructDeclaration *sd = ((TypeStruct *)tn)->sym; + if (global.params.isLinux && linkage != LINKd) + ; +#if DMDV2 + else if (sd->dtor || sd->cpctor) + ; +#endif + else + { + switch (sz) + { case 1: + case 2: + case 4: + case 8: + return RETregs; // return small structs in regs + // (not 3 byte structs!) + default: + break; + } + } + return RETstack; + } + else if ((global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isSolaris) && + linkage == LINKc && + tn->iscomplex()) + { + if (tn->ty == Tcomplex32) + return RETregs; // in EDX:EAX, not ST1:ST0 + else + return RETstack; + } + else + return RETregs; +} + + diff --git a/toir.h b/toir.h new file mode 100644 index 00000000..246d6128 --- /dev/null +++ b/toir.h @@ -0,0 +1,21 @@ + +// Copyright (c) 1999-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +/* Code to help convert to the intermediate representation + * of the compiler back end. + * It's specific to the Digital Mars back end, but can serve + * as a guide to hooking up to other back ends. + */ + +elem *incUsageElem(IRState *irs, Loc loc); +elem *getEthis(Loc loc, IRState *irs, Dsymbol *fd); +elem *setEthis(Loc loc, IRState *irs, elem *ey, AggregateDeclaration *ad); +int intrinsic_op(char *name); +elem *resolveLengthVar(VarDeclaration *lengthVar, elem **pe, Type *t1); + diff --git a/toobj.c b/toobj.c new file mode 100644 index 00000000..a8071e87 --- /dev/null +++ b/toobj.c @@ -0,0 +1,1408 @@ + +// Copyright (c) 1999-2012 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include + +#include "mars.h" +#include "module.h" +#include "mtype.h" +#include "declaration.h" +#include "statement.h" +#include "enum.h" +#include "aggregate.h" +#include "init.h" +#include "attrib.h" +#include "id.h" +#include "import.h" +#include "template.h" + +#include "rmem.h" +#include "cc.h" +#include "global.h" +#include "oper.h" +#include "code.h" +#include "type.h" +#include "dt.h" +#include "cgcv.h" +#include "outbuf.h" +#include "irstate.h" + +void obj_lzext(Symbol *s1,Symbol *s2); + +/* ================================================================== */ + +// Put out instance of ModuleInfo for this Module + +void Module::genmoduleinfo() +{ + //printf("Module::genmoduleinfo() %s\n", toChars()); + + Symbol *msym = toSymbol(); +#if DMDV2 + unsigned sizeof_ModuleInfo = 16 * PTRSIZE; +#else + unsigned sizeof_ModuleInfo = 14 * PTRSIZE; +#endif +#if !MODULEINFO_IS_STRUCT + sizeof_ModuleInfo -= 2 * PTRSIZE; +#endif + //printf("moduleinfo size = x%x\n", sizeof_ModuleInfo); + + ////////////////////////////////////////////// + + csym->Sclass = SCglobal; + csym->Sfl = FLdata; + +#if 1 + dt_t *dt = NULL; + ClassDeclarations aclasses; + + //printf("members->dim = %d\n", members->dim); + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *member = members->tdata()[i]; + + //printf("\tmember '%s'\n", member->toChars()); + member->addLocalClass(&aclasses); + } + + // importedModules[] + size_t aimports_dim = aimports.dim; + for (size_t i = 0; i < aimports.dim; i++) + { Module *m = aimports.tdata()[i]; + if (!m->needmoduleinfo) + aimports_dim--; + } + + FuncDeclaration *sgetmembers = findGetMembers(); + + // These must match the values in druntime/src/object_.d + #define MIstandalone 4 + #define MItlsctor 8 + #define MItlsdtor 0x10 + #define MIctor 0x20 + #define MIdtor 0x40 + #define MIxgetMembers 0x80 + #define MIictor 0x100 + #define MIunitTest 0x200 + #define MIimportedModules 0x400 + #define MIlocalClasses 0x800 + #define MInew 0x80000000 // it's the "new" layout + + unsigned flags = MInew; + if (sctor) + flags |= MItlsctor; + if (sdtor) + flags |= MItlsdtor; + if (ssharedctor) + flags |= MIctor; + if (sshareddtor) + flags |= MIdtor; + if (sgetmembers) + flags |= MIxgetMembers; + if (sictor) + flags |= MIictor; + if (stest) + flags |= MIunitTest; + if (aimports_dim) + flags |= MIimportedModules; + if (aclasses.dim) + flags |= MIlocalClasses; + + if (!needmoduleinfo) + flags |= MIstandalone; + + dtdword(&dt, flags); // n.flags + dtdword(&dt, 0); // n.index + + if (flags & MItlsctor) + dtxoff(&dt, sctor, 0, TYnptr); + if (flags & MItlsdtor) + dtxoff(&dt, sdtor, 0, TYnptr); + if (flags & MIctor) + dtxoff(&dt, ssharedctor, 0, TYnptr); + if (flags & MIdtor) + dtxoff(&dt, sshareddtor, 0, TYnptr); + if (flags & MIxgetMembers) + dtxoff(&dt, sgetmembers->toSymbol(), 0, TYnptr); + if (flags & MIictor) + dtxoff(&dt, sictor, 0, TYnptr); + if (flags & MIunitTest) + dtxoff(&dt, stest, 0, TYnptr); + if (flags & MIimportedModules) + { + dtsize_t(&dt, aimports_dim); + for (size_t i = 0; i < aimports.dim; i++) + { Module *m = aimports[i]; + + if (m->needmoduleinfo) + { Symbol *s = m->toSymbol(); + + /* Weak references don't pull objects in from the library, + * they resolve to 0 if not pulled in by something else. + * Don't pull in a module just because it was imported. + */ +#if !OMFOBJ // Optlink crashes with weak symbols at EIP 41AFE7, 402000 + s->Sflags |= SFLweak; +#endif + dtxoff(&dt, s, 0, TYnptr); + } + } + } + if (flags & MIlocalClasses) + { + dtsize_t(&dt, aclasses.dim); + for (size_t i = 0; i < aclasses.dim; i++) + { + ClassDeclaration *cd = aclasses.tdata()[i]; + dtxoff(&dt, cd->toSymbol(), 0, TYnptr); + } + } + + // Put out module name as a 0-terminated string, to save bytes + nameoffset = dt_size(dt); + const char *name = toPrettyChars(); + namelen = strlen(name); + dtnbytes(&dt, namelen + 1, name); + //printf("nameoffset = x%x\n", nameoffset); +#else + /* The layout is: + { + void **vptr; + monitor_t monitor; + char[] name; // class name + ModuleInfo importedModules[]; + ClassInfo localClasses[]; + uint flags; // initialization state + void *ctor; + void *dtor; + void *unitTest; + const(MemberInfo[]) function(string) xgetMembers; // module getMembers() function + void *ictor; + void *sharedctor; + void *shareddtor; + uint index; + void*[1] reserved; + } + */ + dt_t *dt = NULL; + +#if !MODULEINFO_IS_STRUCT + if (moduleinfo) + dtxoff(&dt, moduleinfo->toVtblSymbol(), 0, TYnptr); // vtbl for ModuleInfo + else + { //printf("moduleinfo is null\n"); + dtdword(&dt, 0); // BUG: should be an assert() + } + dtdword(&dt, 0); // monitor +#endif + + // name[] + const char *name = toPrettyChars(); + size_t namelen = strlen(name); + dtdword(&dt, namelen); + dtabytes(&dt, TYnptr, 0, namelen + 1, name); + + ClassDeclarations aclasses; + + //printf("members->dim = %d\n", members->dim); + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *member = members->tdata()[i]; + + //printf("\tmember '%s'\n", member->toChars()); + member->addLocalClass(&aclasses); + } + + // importedModules[] + int aimports_dim = aimports.dim; + for (size_t i = 0; i < aimports.dim; i++) + { Module *m = aimports.tdata()[i]; + if (!m->needModuleInfo()) + aimports_dim--; + } + dtdword(&dt, aimports_dim); + if (aimports_dim) + dtxoff(&dt, csym, sizeof_ModuleInfo, TYnptr); + else + dtdword(&dt, 0); + + // localClasses[] + dtdword(&dt, aclasses.dim); + if (aclasses.dim) + dtxoff(&dt, csym, sizeof_ModuleInfo + aimports_dim * PTRSIZE, TYnptr); + else + dtdword(&dt, 0); + + if (needmoduleinfo) + dtdword(&dt, 8); // flags + else + dtdword(&dt, 8 | MIstandalone); // flags + + if (ssharedctor) + dtxoff(&dt, ssharedctor, 0, TYnptr); + else + dtdword(&dt, 0); + + if (sshareddtor) + dtxoff(&dt, sshareddtor, 0, TYnptr); + else + dtdword(&dt, 0); + + if (stest) + dtxoff(&dt, stest, 0, TYnptr); + else + dtdword(&dt, 0); + +#if DMDV2 + FuncDeclaration *sgetmembers = findGetMembers(); + if (sgetmembers) + dtxoff(&dt, sgetmembers->toSymbol(), 0, TYnptr); + else +#endif + dtdword(&dt, 0); // xgetMembers + + if (sictor) + dtxoff(&dt, sictor, 0, TYnptr); + else + dtdword(&dt, 0); + +#if DMDV2 + if (sctor) + dtxoff(&dt, sctor, 0, TYnptr); + else + dtdword(&dt, 0); + + if (sdtor) + dtxoff(&dt, sdtor, 0, TYnptr); + else + dtdword(&dt, 0); + + dtdword(&dt, 0); // index + + // void*[1] reserved; + dtdword(&dt, 0); +#endif + ////////////////////////////////////////////// + + for (size_t i = 0; i < aimports.dim; i++) + { Module *m = aimports.tdata()[i]; + + if (m->needModuleInfo()) + { Symbol *s = m->toSymbol(); + + /* Weak references don't pull objects in from the library, + * they resolve to 0 if not pulled in by something else. + * Don't pull in a module just because it was imported. + */ +#if !OMFOBJ // Optlink crashes with weak symbols at EIP 41AFE7, 402000 + s->Sflags |= SFLweak; +#endif + dtxoff(&dt, s, 0, TYnptr); + } + } + + for (size_t i = 0; i < aclasses.dim; i++) + { + ClassDeclaration *cd = aclasses.data()[i]; + dtxoff(&dt, cd->toSymbol(), 0, TYnptr); + } +#endif + + csym->Sdt = dt; +#if ELFOBJ || MACHOBJ + // Cannot be CONST because the startup code sets flag bits in it + csym->Sseg = DATA; +#endif + outdata(csym); + + ////////////////////////////////////////////// + + obj_moduleinfo(msym); +} + +/* ================================================================== */ + +void Dsymbol::toObjFile(int multiobj) +{ + //printf("Dsymbol::toObjFile('%s')\n", toChars()); + // ignore +} + +/* ================================================================== */ + +void ClassDeclaration::toObjFile(int multiobj) +{ + unsigned offset; + Symbol *sinit; + enum_SC scclass; + + //printf("ClassDeclaration::toObjFile('%s')\n", toChars()); + + if (type->ty == Terror) + { error("had semantic errors when compiling"); + return; + } + + if (!members) + return; + + if (multiobj && !hasStaticCtorOrDtor()) + { obj_append(this); + return; + } + + if (global.params.symdebug) + toDebug(); + + assert(!scope); // semantic() should have been run to completion + + scclass = SCglobal; + if (inTemplateInstance()) + scclass = SCcomdat; + + // Put out the members + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *member = (*members)[i]; + /* There might be static ctors in the members, and they cannot + * be put in separate obj files. + */ + member->toObjFile(multiobj); + } + +#if 0 + // Build destructor by aggregating dtors[] + Symbol *sdtor; + switch (dtors.dim) + { case 0: + // No destructors for this class + sdtor = NULL; + break; + + case 1: + // One destructor, just use it directly + sdtor = dtors[0]->toSymbol(); + break; + + default: + { /* Build a destructor that calls all the + * other destructors in dtors[]. + */ + + elem *edtor = NULL; + + // Declare 'this' pointer for our new destructor + Symbol *sthis = symbol_calloc("this"); + sthis->Stype = type_fake(TYnptr); + sthis->Stype->Tcount++; + sthis->Sclass = SCfastpar; + sthis->Spreg = AX; + sthis->Sfl = FLauto; + + // Call each of the destructors in dtors[] + // in reverse order + for (size_t i = 0; i < dtors.dim; i++) + { DtorDeclaration *d = dtors[i]; + Symbol *s = d->toSymbol(); + elem *e = el_bin(OPcall, TYvoid, el_var(s), el_var(sthis)); + edtor = el_combine(e, edtor); + } + + // Create type for the function + ::type *t = type_alloc(TYjfunc); + t->Tflags |= TFprototype | TFfixed; + t->Tmangle = mTYman_d; + t->Tnext = tsvoid; + tsvoid->Tcount++; + + // Create the function, sdtor, and write it out + localgot = NULL; + sdtor = toSymbolX("__dtor", SCglobal, t, "FZv"); + block *b = block_calloc(); + b->BC = BCret; + b->Belem = edtor; + sdtor->Sfunc->Fstartblock = b; + cstate.CSpsymtab = &sdtor->Sfunc->Flocsym; + symbol_add(sthis); + writefunc(sdtor); + } + } +#endif + + // Generate C symbols + toSymbol(); + toVtblSymbol(); + sinit = toInitializer(); + + ////////////////////////////////////////////// + + // Generate static initializer + sinit->Sclass = scclass; + sinit->Sfl = FLdata; +#if ELFOBJ // Burton + sinit->Sseg = CDATA; +#endif +#if MACHOBJ + sinit->Sseg = DATA; +#endif + toDt(&sinit->Sdt); + outdata(sinit); + + ////////////////////////////////////////////// + + // Put out the TypeInfo + type->getTypeInfo(NULL); + //type->vtinfo->toObjFile(multiobj); + + ////////////////////////////////////////////// + + // Put out the ClassInfo + csym->Sclass = scclass; + csym->Sfl = FLdata; + + /* The layout is: + { + void **vptr; + monitor_t monitor; + byte[] initializer; // static initialization data + char[] name; // class name + void *[] vtbl; + Interface[] interfaces; + ClassInfo *base; // base class + void *destructor; + void *invariant; // class invariant + uint flags; + void *deallocator; + OffsetTypeInfo[] offTi; + void *defaultConstructor; + const(MemberInfo[]) function(string) xgetMembers; // module getMembers() function + //TypeInfo typeinfo; + } + */ + dt_t *dt = NULL; + unsigned classinfo_size = global.params.is64bit ? CLASSINFO_SIZE_64 : CLASSINFO_SIZE; // must be ClassInfo.size + offset = classinfo_size; + if (classinfo) + { + if (classinfo->structsize != classinfo_size) + { +#ifdef DEBUG + printf("CLASSINFO_SIZE = x%x, classinfo->structsize = x%x\n", offset, classinfo->structsize); +#endif + error("mismatch between dmd and object.d or object.di found. Check installation and import paths with -v compiler switch."); + fatal(); + } + } + + if (classinfo) + dtxoff(&dt, classinfo->toVtblSymbol(), 0, TYnptr); // vtbl for ClassInfo + else + dtsize_t(&dt, 0); // BUG: should be an assert() + dtsize_t(&dt, 0); // monitor + + // initializer[] + assert(structsize >= 8); + dtsize_t(&dt, structsize); // size + dtxoff(&dt, sinit, 0, TYnptr); // initializer + + // name[] + const char *name = ident->toChars(); + size_t namelen = strlen(name); + if (!(namelen > 9 && memcmp(name, "TypeInfo_", 9) == 0)) + { name = toPrettyChars(); + namelen = strlen(name); + } + dtsize_t(&dt, namelen); + dtabytes(&dt, TYnptr, 0, namelen + 1, name); + + // vtbl[] + dtsize_t(&dt, vtbl.dim); + dtxoff(&dt, vtblsym, 0, TYnptr); + + // interfaces[] + dtsize_t(&dt, vtblInterfaces->dim); + if (vtblInterfaces->dim) + dtxoff(&dt, csym, offset, TYnptr); // (*) + else + dtsize_t(&dt, 0); + + // base + if (baseClass) + dtxoff(&dt, baseClass->toSymbol(), 0, TYnptr); + else + dtsize_t(&dt, 0); + + // destructor + if (dtor) + dtxoff(&dt, dtor->toSymbol(), 0, TYnptr); + else + dtsize_t(&dt, 0); + + // invariant + if (inv) + dtxoff(&dt, inv->toSymbol(), 0, TYnptr); + else + dtsize_t(&dt, 0); + + // flags + int flags = 4 | isCOMclass(); +#if DMDV2 + flags |= 16; +#endif + flags |= 32; + if (ctor) + flags |= 8; + if (isabstract) + flags |= 64; + for (ClassDeclaration *cd = this; cd; cd = cd->baseClass) + { + if (cd->members) + { + for (size_t i = 0; i < cd->members->dim; i++) + { + Dsymbol *sm = cd->members->tdata()[i]; + //printf("sm = %s %s\n", sm->kind(), sm->toChars()); + if (sm->hasPointers()) + goto L2; + } + } + } + flags |= 2; // no pointers + L2: + dtsize_t(&dt, flags); + + + // deallocator + if (aggDelete) + dtxoff(&dt, aggDelete->toSymbol(), 0, TYnptr); + else + dtsize_t(&dt, 0); + + // offTi[] + dtsize_t(&dt, 0); + dtsize_t(&dt, 0); // null for now, fix later + + // defaultConstructor + if (defaultCtor) + dtxoff(&dt, defaultCtor->toSymbol(), 0, TYnptr); + else + dtsize_t(&dt, 0); + +#if DMDV2 + FuncDeclaration *sgetmembers = findGetMembers(); + if (sgetmembers) + dtxoff(&dt, sgetmembers->toSymbol(), 0, TYnptr); + else + dtsize_t(&dt, 0); // module getMembers() function +#endif + + //dtxoff(&dt, type->vtinfo->toSymbol(), 0, TYnptr); // typeinfo + + ////////////////////////////////////////////// + + // Put out vtblInterfaces->tdata()[]. Must immediately follow csym, because + // of the fixup (*) + + offset += vtblInterfaces->dim * (4 * PTRSIZE); + for (size_t i = 0; i < vtblInterfaces->dim; i++) + { BaseClass *b = vtblInterfaces->tdata()[i]; + ClassDeclaration *id = b->base; + + /* The layout is: + * struct Interface + * { + * ClassInfo *interface; + * void *[] vtbl; + * ptrdiff_t offset; + * } + */ + + // Fill in vtbl[] + b->fillVtbl(this, &b->vtbl, 1); + + dtxoff(&dt, id->toSymbol(), 0, TYnptr); // ClassInfo + + // vtbl[] + dtsize_t(&dt, id->vtbl.dim); + dtxoff(&dt, csym, offset, TYnptr); + + dtsize_t(&dt, b->offset); // this offset + + offset += id->vtbl.dim * PTRSIZE; + } + + // Put out the vtblInterfaces->tdata()[].vtbl[] + // This must be mirrored with ClassDeclaration::baseVtblOffset() + //printf("putting out %d interface vtbl[]s for '%s'\n", vtblInterfaces->dim, toChars()); + for (size_t i = 0; i < vtblInterfaces->dim; i++) + { BaseClass *b = vtblInterfaces->tdata()[i]; + ClassDeclaration *id = b->base; + + //printf(" interface[%d] is '%s'\n", i, id->toChars()); + size_t j = 0; + if (id->vtblOffset()) + { + // First entry is ClassInfo reference + //dtxoff(&dt, id->toSymbol(), 0, TYnptr); + + // First entry is struct Interface reference + dtxoff(&dt, csym, classinfo_size + i * (4 * PTRSIZE), TYnptr); + j = 1; + } + assert(id->vtbl.dim == b->vtbl.dim); + for (; j < id->vtbl.dim; j++) + { + assert(j < b->vtbl.dim); +#if 0 + Object *o = b->vtbl.tdata()[j]; + if (o) + { + printf("o = %p\n", o); + assert(o->dyncast() == DYNCAST_DSYMBOL); + Dsymbol *s = (Dsymbol *)o; + printf("s->kind() = '%s'\n", s->kind()); + } +#endif + FuncDeclaration *fd = b->vtbl.tdata()[j]; + if (fd) + dtxoff(&dt, fd->toThunkSymbol(b->offset), 0, TYnptr); + else + dtsize_t(&dt, 0); + } + } + +#if 1 + // Put out the overriding interface vtbl[]s. + // This must be mirrored with ClassDeclaration::baseVtblOffset() + //printf("putting out overriding interface vtbl[]s for '%s' at offset x%x\n", toChars(), offset); + ClassDeclaration *cd; + FuncDeclarations bvtbl; + + for (cd = this->baseClass; cd; cd = cd->baseClass) + { + for (size_t k = 0; k < cd->vtblInterfaces->dim; k++) + { BaseClass *bs = cd->vtblInterfaces->tdata()[k]; + + if (bs->fillVtbl(this, &bvtbl, 0)) + { + //printf("\toverriding vtbl[] for %s\n", bs->base->toChars()); + ClassDeclaration *id = bs->base; + + size_t j = 0; + if (id->vtblOffset()) + { + // First entry is ClassInfo reference + //dtxoff(&dt, id->toSymbol(), 0, TYnptr); + + // First entry is struct Interface reference + dtxoff(&dt, cd->toSymbol(), classinfo_size + k * (4 * PTRSIZE), TYnptr); + j = 1; + } + + for (; j < id->vtbl.dim; j++) + { + FuncDeclaration *fd; + + assert(j < bvtbl.dim); + fd = bvtbl.tdata()[j]; + if (fd) + dtxoff(&dt, fd->toThunkSymbol(bs->offset), 0, TYnptr); + else + dtsize_t(&dt, 0); + } + } + } + } +#endif +#if INTERFACE_VIRTUAL + // Put out the overriding interface vtbl[]s. + // This must be mirrored with ClassDeclaration::baseVtblOffset() + //printf("putting out overriding interface vtbl[]s for '%s' at offset x%x\n", toChars(), offset); + for (size_t i = 0; i < vtblInterfaces->dim; i++) + { BaseClass *b = vtblInterfaces->tdata()[i]; + ClassDeclaration *cd; + + for (cd = this->baseClass; cd; cd = cd->baseClass) + { + for (size_t k = 0; k < cd->vtblInterfaces->dim; k++) + { BaseClass *bs = cd->vtblInterfaces->tdata()[k]; + + if (b->base == bs->base) + { + //printf("\toverriding vtbl[] for %s\n", b->base->toChars()); + ClassDeclaration *id = b->base; + + size_t j = 0; + if (id->vtblOffset()) + { + // First entry is ClassInfo reference + //dtxoff(&dt, id->toSymbol(), 0, TYnptr); + + // First entry is struct Interface reference + dtxoff(&dt, cd->toSymbol(), classinfo_size + k * (4 * PTRSIZE), TYnptr); + j = 1; + } + + for (; j < id->vtbl.dim; j++) + { + assert(j < b->vtbl.dim); + FuncDeclaration *fd = b->vtbl.tdata()[j]; + if (fd) + dtxoff(&dt, fd->toThunkSymbol(bs->offset), 0, TYnptr); + else + dtsize_t(&dt, 0); + } + } + } + } + } +#endif + + + csym->Sdt = dt; +#if ELFOBJ || MACHOBJ // Burton + // ClassInfo cannot be const data, because we use the monitor on it + csym->Sseg = DATA; +#endif + outdata(csym); + if (isExport()) + obj_export(csym,0); + + ////////////////////////////////////////////// + + // Put out the vtbl[] + //printf("putting out %s.vtbl[]\n", toChars()); + dt = NULL; + dtxoff(&dt, csym, 0, TYnptr); // first entry is ClassInfo reference + for (size_t i = 1; i < vtbl.dim; i++) + { + FuncDeclaration *fd = vtbl.tdata()[i]->isFuncDeclaration(); + + //printf("\tvtbl[%d] = %p\n", i, fd); + if (fd && (fd->fbody || !isAbstract())) + { + // Ensure function has a return value (Bugzilla 4869) + if (fd->type->ty == Tfunction && !((TypeFunction *)fd->type)->next) + { + assert(fd->scope); + fd->semantic3(fd->scope); + } + + Symbol *s = fd->toSymbol(); + +#if DMDV2 + if (isFuncHidden(fd)) + { /* fd is hidden from the view of this class. + * If fd overlaps with any function in the vtbl[], then + * issue 'hidden' error. + */ + for (size_t j = 1; j < vtbl.dim; j++) + { if (j == i) + continue; + FuncDeclaration *fd2 = vtbl.tdata()[j]->isFuncDeclaration(); + if (!fd2->ident->equals(fd->ident)) + continue; + if (fd->leastAsSpecialized(fd2) || fd2->leastAsSpecialized(fd)) + { + if (!global.params.useDeprecated) + { + TypeFunction *tf = (TypeFunction *)fd->type; + if (tf->ty == Tfunction) + error("use of %s%s hidden by %s is deprecated\n", fd->toPrettyChars(), Parameter::argsTypesToChars(tf->parameters, tf->varargs), toChars()); + else + error("use of %s hidden by %s is deprecated\n", fd->toPrettyChars(), toChars()); + } + s = rtlsym[RTLSYM_DHIDDENFUNC]; + break; + } + } + } +#endif + dtxoff(&dt, s, 0, TYnptr); + } + else + dtsize_t(&dt, 0); + } + vtblsym->Sdt = dt; + vtblsym->Sclass = scclass; + vtblsym->Sfl = FLdata; +#if ELFOBJ + vtblsym->Sseg = CDATA; +#endif +#if MACHOBJ + vtblsym->Sseg = DATA; +#endif + outdata(vtblsym); + if (isExport()) + obj_export(vtblsym,0); +} + +/****************************************** + * Get offset of base class's vtbl[] initializer from start of csym. + * Returns ~0 if not this csym. + */ + +unsigned ClassDeclaration::baseVtblOffset(BaseClass *bc) +{ + unsigned csymoffset; + + //printf("ClassDeclaration::baseVtblOffset('%s', bc = %p)\n", toChars(), bc); + csymoffset = global.params.is64bit ? CLASSINFO_SIZE_64 : CLASSINFO_SIZE; // must be ClassInfo.size + csymoffset += vtblInterfaces->dim * (4 * PTRSIZE); + + for (size_t i = 0; i < vtblInterfaces->dim; i++) + { + BaseClass *b = vtblInterfaces->tdata()[i]; + + if (b == bc) + return csymoffset; + csymoffset += b->base->vtbl.dim * PTRSIZE; + } + +#if 1 + // Put out the overriding interface vtbl[]s. + // This must be mirrored with ClassDeclaration::baseVtblOffset() + //printf("putting out overriding interface vtbl[]s for '%s' at offset x%x\n", toChars(), offset); + ClassDeclaration *cd; + FuncDeclarations bvtbl; + + for (cd = this->baseClass; cd; cd = cd->baseClass) + { + for (size_t k = 0; k < cd->vtblInterfaces->dim; k++) + { BaseClass *bs = cd->vtblInterfaces->tdata()[k]; + + if (bs->fillVtbl(this, NULL, 0)) + { + if (bc == bs) + { //printf("\tcsymoffset = x%x\n", csymoffset); + return csymoffset; + } + csymoffset += bs->base->vtbl.dim * PTRSIZE; + } + } + } +#endif +#if INTERFACE_VIRTUAL + for (size_t i = 0; i < vtblInterfaces->dim; i++) + { BaseClass *b = vtblInterfaces->tdata()[i]; + ClassDeclaration *cd; + + for (cd = this->baseClass; cd; cd = cd->baseClass) + { + //printf("\tbase class %s\n", cd->toChars()); + for (size_t k = 0; k < cd->vtblInterfaces->dim; k++) + { BaseClass *bs = cd->vtblInterfaces->tdata()[k]; + + if (bc == bs) + { //printf("\tcsymoffset = x%x\n", csymoffset); + return csymoffset; + } + if (b->base == bs->base) + csymoffset += bs->base->vtbl.dim * PTRSIZE; + } + } + } +#endif + + return ~0; +} + +/* ================================================================== */ + +void InterfaceDeclaration::toObjFile(int multiobj) +{ + enum_SC scclass; + + //printf("InterfaceDeclaration::toObjFile('%s')\n", toChars()); + + if (type->ty == Terror) + { error("had semantic errors when compiling"); + return; + } + + if (!members) + return; + + if (global.params.symdebug) + toDebug(); + + scclass = SCglobal; + if (inTemplateInstance()) + scclass = SCcomdat; + + // Put out the members + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *member = members->tdata()[i]; + + member->toObjFile(0); + } + + // Generate C symbols + toSymbol(); + + ////////////////////////////////////////////// + + // Put out the TypeInfo + type->getTypeInfo(NULL); + type->vtinfo->toObjFile(multiobj); + + ////////////////////////////////////////////// + + // Put out the ClassInfo + csym->Sclass = scclass; + csym->Sfl = FLdata; + + /* The layout is: + { + void **vptr; + monitor_t monitor; + byte[] initializer; // static initialization data + char[] name; // class name + void *[] vtbl; + Interface[] interfaces; + Object *base; // base class + void *destructor; + void *invariant; // class invariant + uint flags; + void *deallocator; + OffsetTypeInfo[] offTi; + void *defaultConstructor; +#if DMDV2 + const(MemberInfo[]) function(string) xgetMembers; // module getMembers() function +#endif + //TypeInfo typeinfo; + } + */ + dt_t *dt = NULL; + + if (classinfo) + dtxoff(&dt, classinfo->toVtblSymbol(), 0, TYnptr); // vtbl for ClassInfo + else + dtsize_t(&dt, 0); // BUG: should be an assert() + dtsize_t(&dt, 0); // monitor + + // initializer[] + dtsize_t(&dt, 0); // size + dtsize_t(&dt, 0); // initializer + + // name[] + const char *name = toPrettyChars(); + size_t namelen = strlen(name); + dtsize_t(&dt, namelen); + dtabytes(&dt, TYnptr, 0, namelen + 1, name); + + // vtbl[] + dtsize_t(&dt, 0); + dtsize_t(&dt, 0); + + // vtblInterfaces->tdata()[] + unsigned offset; + dtsize_t(&dt, vtblInterfaces->dim); + if (vtblInterfaces->dim) + { + offset = global.params.is64bit ? CLASSINFO_SIZE_64 : CLASSINFO_SIZE; // must be ClassInfo.size + if (classinfo) + { + if (classinfo->structsize != offset) + { + error("mismatch between dmd and object.d or object.di found. Check installation and import paths with -v compiler switch."); + fatal(); + } + } + dtxoff(&dt, csym, offset, TYnptr); // (*) + } + else + { offset = 0; + dtsize_t(&dt, 0); + } + + // base + assert(!baseClass); + dtsize_t(&dt, 0); + + // dtor + dtsize_t(&dt, 0); + + // invariant + dtsize_t(&dt, 0); + + // flags + dtsize_t(&dt, 4 | isCOMinterface() | 32); + + // deallocator + dtsize_t(&dt, 0); + + // offTi[] + dtsize_t(&dt, 0); + dtsize_t(&dt, 0); // null for now, fix later + + // defaultConstructor + dtsize_t(&dt, 0); + +#if DMDV2 + // xgetMembers + dtsize_t(&dt, 0); +#endif + + //dtxoff(&dt, type->vtinfo->toSymbol(), 0, TYnptr); // typeinfo + + ////////////////////////////////////////////// + + // Put out vtblInterfaces->tdata()[]. Must immediately follow csym, because + // of the fixup (*) + + offset += vtblInterfaces->dim * (4 * PTRSIZE); + for (size_t i = 0; i < vtblInterfaces->dim; i++) + { BaseClass *b = vtblInterfaces->tdata()[i]; + ClassDeclaration *id = b->base; + + // ClassInfo + dtxoff(&dt, id->toSymbol(), 0, TYnptr); + + // vtbl[] + dtsize_t(&dt, 0); + dtsize_t(&dt, 0); + + // this offset + dtsize_t(&dt, b->offset); + } + + csym->Sdt = dt; +#if ELFOBJ + csym->Sseg = CDATA; +#endif +#if MACHOBJ + csym->Sseg = DATA; +#endif + outdata(csym); + if (isExport()) + obj_export(csym,0); +} + +/* ================================================================== */ + +void StructDeclaration::toObjFile(int multiobj) +{ + //printf("StructDeclaration::toObjFile('%s')\n", toChars()); + + if (type->ty == Terror) + { error("had semantic errors when compiling"); + return; + } + + if (multiobj && !hasStaticCtorOrDtor()) + { obj_append(this); + return; + } + + // Anonymous structs/unions only exist as part of others, + // do not output forward referenced structs's + if (!isAnonymous() && members) + { + if (global.params.symdebug) + toDebug(); + + type->getTypeInfo(NULL); // generate TypeInfo + + if (1) + { + // Generate static initializer + toInitializer(); +#if 0 + sinit->Sclass = SCcomdat; +#else + if (inTemplateInstance()) + { + sinit->Sclass = SCcomdat; + } + else + { + sinit->Sclass = SCglobal; + } +#endif + sinit->Sfl = FLdata; + toDt(&sinit->Sdt); + +#if OMFOBJ + /* For OMF, common blocks aren't pulled in from the library. + */ + /* ELF comdef's generate multiple + * definition errors for them from the gnu linker. + * Need to figure out how to generate proper comdef's for ELF. + */ + // See if we can convert a comdat to a comdef, + // which saves on exe file space. + if (0 && // causes multiple def problems with COMMON in one file and COMDAT in library + sinit->Sclass == SCcomdat && + sinit->Sdt && + sinit->Sdt->dt == DT_azeros && + sinit->Sdt->DTnext == NULL && + !global.params.multiobj) + { + sinit->Sclass = SCglobal; + sinit->Sdt->dt = DT_common; + } +#endif + +#if ELFOBJ + sinit->Sseg = CDATA; +#endif +#if MACHOBJ + sinit->Sseg = DATA; +#endif + outdata(sinit); + } + + // Put out the members + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *member = (*members)[i]; + /* There might be static ctors in the members, and they cannot + * be put in separate obj files. + */ + member->toObjFile(multiobj); + } + } +} + +/* ================================================================== */ + +void VarDeclaration::toObjFile(int multiobj) +{ + Symbol *s; + unsigned sz; + Dsymbol *parent; + + //printf("VarDeclaration::toObjFile(%p '%s' type=%s) protection %d\n", this, toChars(), type->toChars(), protection); + //printf("\talign = %d\n", type->alignsize()); + + if (type->ty == Terror) + { error("had semantic errors when compiling"); + return; + } + + if (aliassym) + { toAlias()->toObjFile(0); + return; + } + +#if DMDV2 + // Do not store variables we cannot take the address of + if (!canTakeAddressOf()) + { + return; + } +#endif + + if (isDataseg() && !(storage_class & STCextern)) + { + s = toSymbol(); + sz = type->size(); + + parent = this->toParent(); +#if DMDV1 /* private statics should still get a global symbol, in case + * another module inlines a function that references it. + */ + if (/*protection == PROTprivate ||*/ + !parent || parent->ident == NULL || parent->isFuncDeclaration()) + { + s->Sclass = SCstatic; + } + else +#endif + { + if (storage_class & STCcomdat) + s->Sclass = SCcomdat; + else + s->Sclass = SCglobal; + + do + { + /* Global template data members need to be in comdat's + * in case multiple .obj files instantiate the same + * template with the same types. + */ + if (parent->isTemplateInstance() && !parent->isTemplateMixin()) + { +#if DMDV1 + /* These symbol constants have already been copied, + * so no reason to output them. + * Note that currently there is no way to take + * the address of such a const. + */ + if (isConst() && type->toBasetype()->ty != Tsarray && + init && init->isExpInitializer()) + return; +#endif + s->Sclass = SCcomdat; + break; + } + parent = parent->parent; + } while (parent); + } + s->Sfl = FLdata; + + if (init) + { s->Sdt = init->toDt(); + + // Look for static array that is block initialized + Type *tb; + ExpInitializer *ie = init->isExpInitializer(); + + tb = type->toBasetype(); + if (tb->ty == Tsarray && ie && + !tb->nextOf()->equals(ie->exp->type->toBasetype()->nextOf()) && + ie->exp->implicitConvTo(tb->nextOf()) + ) + { + size_t dim = ((TypeSArray *)tb)->dim->toInteger(); + + // Duplicate Sdt 'dim-1' times, as we already have the first one + dt_t **pdt = &s->Sdt; + while (--dim > 0) + { + pdt = ie->exp->toDt(pdt); + } + } + } + else if (storage_class & STCextern) + { + s->Sclass = SCextern; + s->Sfl = FLextern; + s->Sdt = NULL; + // BUG: if isExport(), shouldn't we make it dllimport? + return; + } + else + { + type->toDt(&s->Sdt); + } + dt_optimize(s->Sdt); + + // See if we can convert a comdat to a comdef, + // which saves on exe file space. + if (s->Sclass == SCcomdat && + s->Sdt && + s->Sdt->dt == DT_azeros && + s->Sdt->DTnext == NULL && + !isThreadlocal()) + { + s->Sclass = SCglobal; + s->Sdt->dt = DT_common; + } + +#if ELFOBJ || MACHOBJ // Burton + if (s->Sdt && s->Sdt->dt == DT_azeros && s->Sdt->DTnext == NULL) + s->Sseg = UDATA; + else + s->Sseg = DATA; +#endif + if (sz) + { outdata(s); + if (isExport()) + obj_export(s,0); + } + } +} + +/* ================================================================== */ + +void TypedefDeclaration::toObjFile(int multiobj) +{ + //printf("TypedefDeclaration::toObjFile('%s')\n", toChars()); + + if (type->ty == Terror) + { error("had semantic errors when compiling"); + return; + } + + if (global.params.symdebug) + toDebug(); + + type->getTypeInfo(NULL); // generate TypeInfo + + TypeTypedef *tc = (TypeTypedef *)type; + if (type->isZeroInit() || !tc->sym->init) + ; + else + { + enum_SC scclass = SCglobal; + if (inTemplateInstance()) + scclass = SCcomdat; + + // Generate static initializer + toInitializer(); + sinit->Sclass = scclass; + sinit->Sfl = FLdata; +#if ELFOBJ // Burton + sinit->Sseg = CDATA; +#endif +#if MACHOBJ + sinit->Sseg = DATA; +#endif + sinit->Sdt = tc->sym->init->toDt(); + outdata(sinit); + } +} + +/* ================================================================== */ + +void EnumDeclaration::toObjFile(int multiobj) +{ + //printf("EnumDeclaration::toObjFile('%s')\n", toChars()); + + if (type->ty == Terror) + { error("had semantic errors when compiling"); + return; + } + +#if DMDV2 + if (isAnonymous()) + return; +#endif + + if (global.params.symdebug) + toDebug(); + + type->getTypeInfo(NULL); // generate TypeInfo + + TypeEnum *tc = (TypeEnum *)type; + if (!tc->sym->defaultval || type->isZeroInit()) + ; + else + { + enum_SC scclass = SCglobal; + if (inTemplateInstance()) + scclass = SCcomdat; + + // Generate static initializer + toInitializer(); + sinit->Sclass = scclass; + sinit->Sfl = FLdata; +#if ELFOBJ // Burton + sinit->Sseg = CDATA; +#endif +#if MACHOBJ + sinit->Sseg = DATA; +#endif +#if DMDV1 + dtnbytes(&sinit->Sdt, tc->size(0), (char *)&tc->sym->defaultval); + //sinit->Sdt = tc->sym->init->toDt(); +#endif +#if DMDV2 + tc->sym->defaultval->toDt(&sinit->Sdt); +#endif + outdata(sinit); + } +} + + + diff --git a/total.h b/total.h new file mode 100644 index 00000000..42f38765 --- /dev/null +++ b/total.h @@ -0,0 +1,46 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_TOTAL_H +#define DMD_TOTAL_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include +#include +#include +#include +#include + +#include "root.h" +#include "stringtable.h" + +#include "arraytypes.h" +#include "mars.h" +#include "lexer.h" +#include "parse.h" +#include "identifier.h" +#include "enum.h" +#include "aggregate.h" +#include "mtype.h" +#include "expression.h" +#include "declaration.h" +#include "statement.h" +#include "scope.h" +#include "import.h" +#include "module.h" +#include "id.h" +#include "cond.h" +#include "version.h" +#include "lib.h" + +#endif /* DMD_TOTAL_H */ diff --git a/traits.c b/traits.c new file mode 100644 index 00000000..e49fd553 --- /dev/null +++ b/traits.c @@ -0,0 +1,549 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include +#include + +#include "rmem.h" + +//#include "port.h" +#include "mtype.h" +#include "init.h" +#include "expression.h" +#include "template.h" +#include "utf.h" +#include "enum.h" +#include "scope.h" +#include "statement.h" +#include "declaration.h" +#include "aggregate.h" +#include "import.h" +#include "id.h" +#include "dsymbol.h" +#include "module.h" +#include "attrib.h" +#include "hdrgen.h" +#include "parse.h" + +#define LOGSEMANTIC 0 + +#if DMDV2 + +/************************************************ + * Delegate to be passed to overloadApply() that looks + * for functions matching a trait. + */ + +struct Ptrait +{ + Expression *e1; + Expressions *exps; // collected results + Identifier *ident; // which trait we're looking for +}; + +static int fptraits(void *param, FuncDeclaration *f) +{ Ptrait *p = (Ptrait *)param; + + if (p->ident == Id::getVirtualFunctions && !f->isVirtual()) + return 0; + + if (p->ident == Id::getVirtualMethods && !f->isVirtualMethod()) + return 0; + + Expression *e; + + if (p->e1->op == TOKdotvar) + { DotVarExp *dve = (DotVarExp *)p->e1; + e = new DotVarExp(0, dve->e1, f); + } + else + e = new DsymbolExp(0, f); + p->exps->push(e); + return 0; +} + +/************************ TraitsExp ************************************/ + +Expression *TraitsExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("TraitsExp::semantic() %s\n", toChars()); +#endif + if (ident != Id::compiles && ident != Id::isSame && + ident != Id::identifier) + { + TemplateInstance::semanticTiargs(loc, sc, args, 1); + } + size_t dim = args ? args->dim : 0; + Declaration *d; + +#define ISTYPE(cond) \ + for (size_t i = 0; i < dim; i++) \ + { Type *t = getType(args->tdata()[i]); \ + if (!t) \ + goto Lfalse; \ + if (!(cond)) \ + goto Lfalse; \ + } \ + if (!dim) \ + goto Lfalse; \ + goto Ltrue; + +#define ISDSYMBOL(cond) \ + for (size_t i = 0; i < dim; i++) \ + { Dsymbol *s = getDsymbol(args->tdata()[i]); \ + if (!s) \ + goto Lfalse; \ + if (!(cond)) \ + goto Lfalse; \ + } \ + if (!dim) \ + goto Lfalse; \ + goto Ltrue; + + + + if (ident == Id::isArithmetic) + { + ISTYPE(t->isintegral() || t->isfloating()) + } + else if (ident == Id::isFloating) + { + ISTYPE(t->isfloating()) + } + else if (ident == Id::isIntegral) + { + ISTYPE(t->isintegral()) + } + else if (ident == Id::isScalar) + { + ISTYPE(t->isscalar()) + } + else if (ident == Id::isUnsigned) + { + ISTYPE(t->isunsigned()) + } + else if (ident == Id::isAssociativeArray) + { + ISTYPE(t->toBasetype()->ty == Taarray) + } + else if (ident == Id::isStaticArray) + { + ISTYPE(t->toBasetype()->ty == Tsarray) + } + else if (ident == Id::isAbstractClass) + { + ISTYPE(t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->isAbstract()) + } + else if (ident == Id::isFinalClass) + { + ISTYPE(t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal) + } + else if (ident == Id::isAbstractFunction) + { + FuncDeclaration *f; + ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isAbstract()) + } + else if (ident == Id::isVirtualFunction) + { + FuncDeclaration *f; + ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isVirtual()) + } + else if (ident == Id::isVirtualMethod) + { + FuncDeclaration *f; + ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isVirtualMethod()) + } + else if (ident == Id::isFinalFunction) + { + FuncDeclaration *f; + ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isFinal()) + } +#if DMDV2 + else if (ident == Id::isStaticFunction) + { + FuncDeclaration *f; + ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && !f->needThis() && !f->isNested()) + } + else if (ident == Id::isRef) + { + ISDSYMBOL((d = s->isDeclaration()) != NULL && d->isRef()) + } + else if (ident == Id::isOut) + { + ISDSYMBOL((d = s->isDeclaration()) != NULL && d->isOut()) + } + else if (ident == Id::isLazy) + { + ISDSYMBOL((d = s->isDeclaration()) != NULL && d->storage_class & STClazy) + } + else if (ident == Id::identifier) + { // Get identifier for symbol as a string literal + + // Specify 0 for the flags argument to semanticTiargs() so that + // a symbol should not be folded to a constant. + TemplateInstance::semanticTiargs(loc, sc, args, 0); + + if (dim != 1) + goto Ldimerror; + Object *o = args->tdata()[0]; + Dsymbol *s = getDsymbol(o); + if (!s || !s->ident) + { + error("argument %s has no identifier", o->toChars()); + goto Lfalse; + } + StringExp *se = new StringExp(loc, s->ident->toChars()); + return se->semantic(sc); + } + else if (ident == Id::parent) + { + if (dim != 1) + goto Ldimerror; + Object *o = args->tdata()[0]; + Dsymbol *s = getDsymbol(o); + if (s) + s = s->toParent(); + if (!s) + { + error("argument %s has no parent", o->toChars()); + goto Lfalse; + } + return (new DsymbolExp(loc, s))->semantic(sc); + } + +#endif + else if (ident == Id::hasMember || + ident == Id::getMember || + ident == Id::getOverloads || + ident == Id::getVirtualMethods || + ident == Id::getVirtualFunctions) + { + if (dim != 2) + goto Ldimerror; + Object *o = args->tdata()[0]; + Expression *e = isExpression(args->tdata()[1]); + if (!e) + { error("expression expected as second argument of __traits %s", ident->toChars()); + goto Lfalse; + } + e = e->optimize(WANTvalue | WANTinterpret); + StringExp *se = e->toString(); + if (!se || se->length() == 0) + { error("string expected as second argument of __traits %s instead of %s", ident->toChars(), e->toChars()); + goto Lfalse; + } + se = se->toUTF8(sc); + if (se->sz != 1) + { error("string must be chars"); + goto Lfalse; + } + Identifier *id = Lexer::idPool((char *)se->string); + + Type *t = isType(o); + e = isExpression(o); + Dsymbol *s = isDsymbol(o); + if (t) + e = typeDotIdExp(loc, t, id); + else if (e) + e = new DotIdExp(loc, e, id); + else if (s) + { e = new DsymbolExp(loc, s); + e = new DotIdExp(loc, e, id); + } + else + { error("invalid first argument"); + goto Lfalse; + } + + if (ident == Id::hasMember) + { + if (t) + { + Dsymbol *sym = t->toDsymbol(sc); + if (sym) + { + Dsymbol *sm = sym->search(loc, id, 0); + if (sm) + goto Ltrue; + } + } + + /* Take any errors as meaning it wasn't found + */ + Scope *sc2 = sc->push(); + //sc2->inHasMember++; + e = e->trySemantic(sc2); + sc2->pop(); + if (!e) + goto Lfalse; + else + goto Ltrue; + } + else if (ident == Id::getMember) + { + e = e->semantic(sc); + return e; + } + else if (ident == Id::getVirtualFunctions || + ident == Id::getVirtualMethods || + ident == Id::getOverloads) + { + unsigned errors = global.errors; + Expression *ex = e; + e = e->semantic(sc); + if (errors < global.errors) + error("%s cannot be resolved", ex->toChars()); + + /* Create tuple of functions of e + */ + //e->dump(0); + Expressions *exps = new Expressions(); + FuncDeclaration *f; + if (e->op == TOKvar) + { VarExp *ve = (VarExp *)e; + f = ve->var->isFuncDeclaration(); + } + else if (e->op == TOKdotvar) + { DotVarExp *dve = (DotVarExp *)e; + f = dve->var->isFuncDeclaration(); + } + else + f = NULL; + Ptrait p; + p.exps = exps; + p.e1 = e; + p.ident = ident; + overloadApply(f, fptraits, &p); + + TupleExp *tup = new TupleExp(loc, exps); + return tup->semantic(sc); + } + else + assert(0); + } + else if (ident == Id::classInstanceSize) + { + if (dim != 1) + goto Ldimerror; + Object *o = args->tdata()[0]; + Dsymbol *s = getDsymbol(o); + ClassDeclaration *cd; + if (!s || (cd = s->isClassDeclaration()) == NULL) + { + error("first argument is not a class"); + goto Lfalse; + } + return new IntegerExp(loc, cd->structsize, Type::tsize_t); + } + else if (ident == Id::allMembers || ident == Id::derivedMembers) + { + if (dim != 1) + goto Ldimerror; + Object *o = args->tdata()[0]; + Dsymbol *s = getDsymbol(o); + ScopeDsymbol *sd; + if (!s) + { + error("argument has no members"); + goto Lfalse; + } + if ((sd = s->isScopeDsymbol()) == NULL) + { + error("%s %s has no members", s->kind(), s->toChars()); + goto Lfalse; + } + + // use a struct as local function + struct PushIdentsDg + { + static int dg(void *ctx, size_t n, Dsymbol *sm) + { + if (!sm) + return 1; + //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars()); + if (sm->ident) + { + //printf("\t%s\n", sm->ident->toChars()); + Identifiers *idents = (Identifiers *)ctx; + + /* Skip if already present in idents[] + */ + for (size_t j = 0; j < idents->dim; j++) + { Identifier *id = idents->tdata()[j]; + if (id == sm->ident) + return 0; +#ifdef DEBUG + // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop. + assert(strcmp(id->toChars(), sm->ident->toChars()) != 0); +#endif + } + + idents->push(sm->ident); + } + return 0; + } + }; + + Identifiers *idents = new Identifiers; + ScopeDsymbol::foreach(sd->members, &PushIdentsDg::dg, idents); + + ClassDeclaration *cd = sd->isClassDeclaration(); + if (cd && ident == Id::allMembers) + { + struct PushBaseMembers + { + static void dg(ClassDeclaration *cd, Identifiers *idents) + { + for (size_t i = 0; i < cd->baseclasses->dim; i++) + { ClassDeclaration *cb = (*cd->baseclasses)[i]->base; + ScopeDsymbol::foreach(cb->members, &PushIdentsDg::dg, idents); + if (cb->baseclasses->dim) + dg(cb, idents); + } + } + }; + PushBaseMembers::dg(cd, idents); + } + + // Turn Identifiers into StringExps reusing the allocated array + assert(sizeof(Expressions) == sizeof(Identifiers)); + Expressions *exps = (Expressions *)idents; + for (size_t i = 0; i < idents->dim; i++) + { Identifier *id = idents->tdata()[i]; + StringExp *se = new StringExp(loc, id->toChars()); + exps->tdata()[i] = se; + } + +#if DMDV1 + Expression *e = new ArrayLiteralExp(loc, exps); +#endif +#if DMDV2 + /* Making this a tuple is more flexible, as it can be statically unrolled. + * To make an array literal, enclose __traits in [ ]: + * [ __traits(allMembers, ...) ] + */ + Expression *e = new TupleExp(loc, exps); +#endif + e = e->semantic(sc); + return e; + } + else if (ident == Id::compiles) + { + /* Determine if all the objects - types, expressions, or symbols - + * compile without error + */ + if (!dim) + goto Lfalse; + + for (size_t i = 0; i < dim; i++) + { Object *o = args->tdata()[i]; + Expression *e; + + unsigned errors = global.startGagging(); + + Type *t = isType(o); + if (t) + { Dsymbol *s; + t->resolve(loc, sc, &e, &t, &s); + if (t) + t->semantic(loc, sc); + else if (e) + { e = e->semantic(sc); + e = e->optimize(WANTvalue); + } + } + else + { e = isExpression(o); + if (e) + { e = e->semantic(sc); + e = e->optimize(WANTvalue); + } + } + + if (global.endGagging(errors)) + { + goto Lfalse; + } + } + goto Ltrue; + } + else if (ident == Id::isSame) + { /* Determine if two symbols are the same + */ + if (dim != 2) + goto Ldimerror; + TemplateInstance::semanticTiargs(loc, sc, args, 0); + Object *o1 = args->tdata()[0]; + Object *o2 = args->tdata()[1]; + Dsymbol *s1 = getDsymbol(o1); + Dsymbol *s2 = getDsymbol(o2); + + //printf("isSame: %s, %s\n", o1->toChars(), o2->toChars()); +#if 0 + printf("o1: %p\n", o1); + printf("o2: %p\n", o2); + if (!s1) + { Expression *ea = isExpression(o1); + if (ea) + printf("%s\n", ea->toChars()); + Type *ta = isType(o1); + if (ta) + printf("%s\n", ta->toChars()); + goto Lfalse; + } + else + printf("%s %s\n", s1->kind(), s1->toChars()); +#endif + if (!s1 && !s2) + { Expression *ea1 = isExpression(o1); + Expression *ea2 = isExpression(o2); + if (ea1 && ea2) + { + if (ea1->equals(ea2)) + goto Ltrue; + } + } + + if (!s1 || !s2) + goto Lfalse; + + s1 = s1->toAlias(); + s2 = s2->toAlias(); + + if (s1 == s2) + goto Ltrue; + else + goto Lfalse; + } + else + { error("unrecognized trait %s", ident->toChars()); + goto Lfalse; + } + + return NULL; + +Ldimerror: + error("wrong number of arguments %d", dim); + goto Lfalse; + + +Lfalse: + return new IntegerExp(loc, 0, Type::tbool); + +Ltrue: + return new IntegerExp(loc, 1, Type::tbool); +} + + +#endif diff --git a/typinf.c b/typinf.c new file mode 100644 index 00000000..ceb768a1 --- /dev/null +++ b/typinf.c @@ -0,0 +1,949 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +//#include "mem.h" + +#include "mars.h" +#include "module.h" +#include "mtype.h" +#include "scope.h" +#include "init.h" +#include "expression.h" +#include "attrib.h" +#include "declaration.h" +#include "template.h" +#include "id.h" +#include "enum.h" +#include "import.h" +#include "aggregate.h" + +#ifndef TARGET_NET +#include "rmem.h" +#include "cc.h" +#include "global.h" +#include "oper.h" +#include "code.h" +#include "type.h" +#include "dt.h" +#include "cgcv.h" +#include "outbuf.h" +#include "irstate.h" +#endif + +extern Symbol *static_sym(); + +/******************************************* + * Get a canonicalized form of the TypeInfo for use with the internal + * runtime library routines. Canonicalized in that static arrays are + * represented as dynamic arrays, enums are represented by their + * underlying type, etc. This reduces the number of TypeInfo's needed, + * so we can use the custom internal ones more. + */ + +Expression *Type::getInternalTypeInfo(Scope *sc) +{ TypeInfoDeclaration *tid; + Expression *e; + Type *t; + static TypeInfoDeclaration *internalTI[TMAX]; + + //printf("Type::getInternalTypeInfo() %s\n", toChars()); + t = toBasetype(); + switch (t->ty) + { + case Tsarray: +#if 0 + // convert to corresponding dynamic array type + t = t->nextOf()->mutableOf()->arrayOf(); +#endif + break; + + case Tclass: + if (((TypeClass *)t)->sym->isInterfaceDeclaration()) + break; + goto Linternal; + + case Tarray: + // convert to corresponding dynamic array type + t = t->nextOf()->mutableOf()->arrayOf(); + if (t->nextOf()->ty != Tclass) + break; + goto Linternal; + + case Tfunction: + case Tdelegate: + case Tpointer: + Linternal: + tid = internalTI[t->ty]; + if (!tid) + { tid = new TypeInfoDeclaration(t, 1); + internalTI[t->ty] = tid; + } + e = new VarExp(0, tid); + e = e->addressOf(sc); + e->type = tid->type; // do this so we don't get redundant dereference + return e; + + default: + break; + } + //printf("\tcalling getTypeInfo() %s\n", t->toChars()); + return t->getTypeInfo(sc); +} + + +/**************************************************** + * Get the exact TypeInfo. + */ + +Expression *Type::getTypeInfo(Scope *sc) +{ + //printf("Type::getTypeInfo() %p, %s\n", this, toChars()); + if (!Type::typeinfo) + { + error(0, "TypeInfo not found. object.d may be incorrectly installed or corrupt, compile with -v switch"); + fatal(); + } + + Type *t = merge2(); // do this since not all Type's are merge'd + if (!t->vtinfo) + { +#if DMDV2 + if (t->isShared()) // does both 'shared' and 'shared const' + t->vtinfo = new TypeInfoSharedDeclaration(t); + else if (t->isConst()) + t->vtinfo = new TypeInfoConstDeclaration(t); + else if (t->isImmutable()) + t->vtinfo = new TypeInfoInvariantDeclaration(t); + else if (t->isWild()) + t->vtinfo = new TypeInfoWildDeclaration(t); + else +#endif + t->vtinfo = t->getTypeInfoDeclaration(); + assert(t->vtinfo); + vtinfo = t->vtinfo; + + /* If this has a custom implementation in std/typeinfo, then + * do not generate a COMDAT for it. + */ + if (!t->builtinTypeInfo()) + { // Generate COMDAT + if (sc) // if in semantic() pass + { // Find module that will go all the way to an object file + Module *m = sc->module->importedFrom; + m->members->push(t->vtinfo); + } + else // if in obj generation pass + { + t->vtinfo->toObjFile(global.params.multiobj); + } + } + } + if (!vtinfo) + vtinfo = t->vtinfo; // Types aren't merged, but we can share the vtinfo's + Expression *e = new VarExp(0, t->vtinfo); + e = e->addressOf(sc); + e->type = t->vtinfo->type; // do this so we don't get redundant dereference + return e; +} + +TypeInfoDeclaration *Type::getTypeInfoDeclaration() +{ + //printf("Type::getTypeInfoDeclaration() %s\n", toChars()); + return new TypeInfoDeclaration(this, 0); +} + +TypeInfoDeclaration *TypeTypedef::getTypeInfoDeclaration() +{ + return new TypeInfoTypedefDeclaration(this); +} + +TypeInfoDeclaration *TypePointer::getTypeInfoDeclaration() +{ + return new TypeInfoPointerDeclaration(this); +} + +TypeInfoDeclaration *TypeDArray::getTypeInfoDeclaration() +{ + return new TypeInfoArrayDeclaration(this); +} + +TypeInfoDeclaration *TypeSArray::getTypeInfoDeclaration() +{ + return new TypeInfoStaticArrayDeclaration(this); +} + +TypeInfoDeclaration *TypeAArray::getTypeInfoDeclaration() +{ + return new TypeInfoAssociativeArrayDeclaration(this); +} + +TypeInfoDeclaration *TypeStruct::getTypeInfoDeclaration() +{ + return new TypeInfoStructDeclaration(this); +} + +TypeInfoDeclaration *TypeClass::getTypeInfoDeclaration() +{ + if (sym->isInterfaceDeclaration()) + return new TypeInfoInterfaceDeclaration(this); + else + return new TypeInfoClassDeclaration(this); +} + +TypeInfoDeclaration *TypeVector::getTypeInfoDeclaration() +{ + return new TypeInfoVectorDeclaration(this); +} + +TypeInfoDeclaration *TypeEnum::getTypeInfoDeclaration() +{ + return new TypeInfoEnumDeclaration(this); +} + +TypeInfoDeclaration *TypeFunction::getTypeInfoDeclaration() +{ + return new TypeInfoFunctionDeclaration(this); +} + +TypeInfoDeclaration *TypeDelegate::getTypeInfoDeclaration() +{ + return new TypeInfoDelegateDeclaration(this); +} + +TypeInfoDeclaration *TypeTuple::getTypeInfoDeclaration() +{ + return new TypeInfoTupleDeclaration(this); +} + +#ifndef TARGET_NET +/**************************************************** + */ + +#if 1 + +void TypeInfoDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoDeclaration::toDt() %s\n", toChars()); + dtxoff(pdt, Type::typeinfo->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo + dtsize_t(pdt, 0); // monitor +} + +#if DMDV2 +void TypeInfoConstDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoConstDeclaration::toDt() %s\n", toChars()); + dtxoff(pdt, Type::typeinfoconst->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Const + dtsize_t(pdt, 0); // monitor + Type *tm = tinfo->mutableOf(); + tm = tm->merge(); + tm->getTypeInfo(NULL); + dtxoff(pdt, tm->vtinfo->toSymbol(), 0, TYnptr); +} + +void TypeInfoInvariantDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoInvariantDeclaration::toDt() %s\n", toChars()); + dtxoff(pdt, Type::typeinfoinvariant->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Invariant + dtsize_t(pdt, 0); // monitor + Type *tm = tinfo->mutableOf(); + tm = tm->merge(); + tm->getTypeInfo(NULL); + dtxoff(pdt, tm->vtinfo->toSymbol(), 0, TYnptr); +} + +void TypeInfoSharedDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoSharedDeclaration::toDt() %s\n", toChars()); + dtxoff(pdt, Type::typeinfoshared->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Shared + dtsize_t(pdt, 0); // monitor + Type *tm = tinfo->unSharedOf(); + tm = tm->merge(); + tm->getTypeInfo(NULL); + dtxoff(pdt, tm->vtinfo->toSymbol(), 0, TYnptr); +} + +void TypeInfoWildDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoWildDeclaration::toDt() %s\n", toChars()); + dtxoff(pdt, Type::typeinfowild->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Wild + dtsize_t(pdt, 0); // monitor + Type *tm = tinfo->mutableOf(); + tm = tm->merge(); + tm->getTypeInfo(NULL); + dtxoff(pdt, tm->vtinfo->toSymbol(), 0, TYnptr); +} + +#endif + +void TypeInfoTypedefDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoTypedefDeclaration::toDt() %s\n", toChars()); + + dtxoff(pdt, Type::typeinfotypedef->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Typedef + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Ttypedef); + + TypeTypedef *tc = (TypeTypedef *)tinfo; + TypedefDeclaration *sd = tc->sym; + //printf("basetype = %s\n", sd->basetype->toChars()); + + /* Put out: + * TypeInfo base; + * char[] name; + * void[] m_init; + */ + + sd->basetype = sd->basetype->merge(); + sd->basetype->getTypeInfo(NULL); // generate vtinfo + assert(sd->basetype->vtinfo); + dtxoff(pdt, sd->basetype->vtinfo->toSymbol(), 0, TYnptr); // TypeInfo for basetype + + const char *name = sd->toPrettyChars(); + size_t namelen = strlen(name); + dtsize_t(pdt, namelen); + dtabytes(pdt, TYnptr, 0, namelen + 1, name); + + // void[] init; + if (tinfo->isZeroInit() || !sd->init) + { // 0 initializer, or the same as the base type + dtsize_t(pdt, 0); // init.length + dtsize_t(pdt, 0); // init.ptr + } + else + { + dtsize_t(pdt, sd->type->size()); // init.length + dtxoff(pdt, sd->toInitializer(), 0, TYnptr); // init.ptr + } +} + +void TypeInfoEnumDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoEnumDeclaration::toDt()\n"); + dtxoff(pdt, Type::typeinfoenum->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Enum + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Tenum); + + TypeEnum *tc = (TypeEnum *)tinfo; + EnumDeclaration *sd = tc->sym; + + /* Put out: + * TypeInfo base; + * char[] name; + * void[] m_init; + */ + + if (sd->memtype) + { sd->memtype->getTypeInfo(NULL); + dtxoff(pdt, sd->memtype->vtinfo->toSymbol(), 0, TYnptr); // TypeInfo for enum members + } + else + dtsize_t(pdt, 0); + + const char *name = sd->toPrettyChars(); + size_t namelen = strlen(name); + dtsize_t(pdt, namelen); + dtabytes(pdt, TYnptr, 0, namelen + 1, name); + + // void[] init; + if (!sd->defaultval || tinfo->isZeroInit()) + { // 0 initializer, or the same as the base type + dtsize_t(pdt, 0); // init.length + dtsize_t(pdt, 0); // init.ptr + } + else + { + dtsize_t(pdt, sd->type->size()); // init.length + dtxoff(pdt, sd->toInitializer(), 0, TYnptr); // init.ptr + } +} + +void TypeInfoPointerDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoPointerDeclaration::toDt()\n"); + dtxoff(pdt, Type::typeinfopointer->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Pointer + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Tpointer); + + TypePointer *tc = (TypePointer *)tinfo; + + tc->next->getTypeInfo(NULL); + dtxoff(pdt, tc->next->vtinfo->toSymbol(), 0, TYnptr); // TypeInfo for type being pointed to +} + +void TypeInfoArrayDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoArrayDeclaration::toDt()\n"); + dtxoff(pdt, Type::typeinfoarray->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Array + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Tarray); + + TypeDArray *tc = (TypeDArray *)tinfo; + + tc->next->getTypeInfo(NULL); + dtxoff(pdt, tc->next->vtinfo->toSymbol(), 0, TYnptr); // TypeInfo for array of type +} + +void TypeInfoStaticArrayDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoStaticArrayDeclaration::toDt()\n"); + dtxoff(pdt, Type::typeinfostaticarray->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_StaticArray + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Tsarray); + + TypeSArray *tc = (TypeSArray *)tinfo; + + tc->next->getTypeInfo(NULL); + dtxoff(pdt, tc->next->vtinfo->toSymbol(), 0, TYnptr); // TypeInfo for array of type + + dtsize_t(pdt, tc->dim->toInteger()); // length +} + +void TypeInfoVectorDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoVectorDeclaration::toDt()\n"); + dtxoff(pdt, Type::typeinfovector->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Vector + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Tvector); + + TypeVector *tc = (TypeVector *)tinfo; + + tc->basetype->getTypeInfo(NULL); + dtxoff(pdt, tc->basetype->vtinfo->toSymbol(), 0, TYnptr); // TypeInfo for equivalent static array +} + +void TypeInfoAssociativeArrayDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoAssociativeArrayDeclaration::toDt()\n"); + dtxoff(pdt, Type::typeinfoassociativearray->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_AssociativeArray + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Taarray); + + TypeAArray *tc = (TypeAArray *)tinfo; + + tc->next->getTypeInfo(NULL); + dtxoff(pdt, tc->next->vtinfo->toSymbol(), 0, TYnptr); // TypeInfo for array of type + + tc->index->getTypeInfo(NULL); + dtxoff(pdt, tc->index->vtinfo->toSymbol(), 0, TYnptr); // TypeInfo for array of type + +#if DMDV2 + tc->getImpl()->type->getTypeInfo(NULL); + dtxoff(pdt, tc->getImpl()->type->vtinfo->toSymbol(), 0, TYnptr); // impl +#endif +} + +void TypeInfoFunctionDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoFunctionDeclaration::toDt()\n"); + dtxoff(pdt, Type::typeinfofunction->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Function + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Tfunction); + + TypeFunction *tc = (TypeFunction *)tinfo; + + tc->next->getTypeInfo(NULL); + dtxoff(pdt, tc->next->vtinfo->toSymbol(), 0, TYnptr); // TypeInfo for function return value + + const char *name = tinfo->deco; + assert(name); + size_t namelen = strlen(name); + dtsize_t(pdt, namelen); + dtabytes(pdt, TYnptr, 0, namelen + 1, name); +} + +void TypeInfoDelegateDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoDelegateDeclaration::toDt()\n"); + dtxoff(pdt, Type::typeinfodelegate->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Delegate + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Tdelegate); + + TypeDelegate *tc = (TypeDelegate *)tinfo; + + tc->next->nextOf()->getTypeInfo(NULL); + dtxoff(pdt, tc->next->nextOf()->vtinfo->toSymbol(), 0, TYnptr); // TypeInfo for delegate return value + + const char *name = tinfo->deco; + assert(name); + size_t namelen = strlen(name); + dtsize_t(pdt, namelen); + dtabytes(pdt, TYnptr, 0, namelen + 1, name); +} + +void TypeInfoStructDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoStructDeclaration::toDt() '%s'\n", toChars()); + + unsigned offset = Type::typeinfostruct->structsize; + + dtxoff(pdt, Type::typeinfostruct->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Struct + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Tstruct); + + TypeStruct *tc = (TypeStruct *)tinfo; + StructDeclaration *sd = tc->sym; + + /* Put out: + * char[] name; + * void[] init; + * hash_t function(in void*) xtoHash; + * bool function(in void*, in void*) xopEquals; + * int function(in void*, in void*) xopCmp; + * string function(const(void)*) xtoString; + * uint m_flags; + * xgetMembers; + * xdtor; + * xpostblit; + * uint m_align; + * version (X86_64) + * TypeInfo m_arg1; + * TypeInfo m_arg2; + * + * name[] + */ + + const char *name = sd->toPrettyChars(); + size_t namelen = strlen(name); + dtsize_t(pdt, namelen); + //dtabytes(pdt, TYnptr, 0, namelen + 1, name); + dtxoff(pdt, toSymbol(), offset, TYnptr); + offset += namelen + 1; + + // void[] init; + dtsize_t(pdt, sd->structsize); // init.length + if (sd->zeroInit) + dtsize_t(pdt, 0); // NULL for 0 initialization + else + dtxoff(pdt, sd->toInitializer(), 0, TYnptr); // init.ptr + + FuncDeclaration *fd; + FuncDeclaration *fdx; + Dsymbol *s; + + static TypeFunction *tftohash; + static TypeFunction *tftostring; + + if (!tftohash) + { + Scope sc; + + tftohash = new TypeFunction(NULL, Type::thash_t, 0, LINKd); + tftohash->mod = MODconst; + tftohash = (TypeFunction *)tftohash->semantic(0, &sc); + + tftostring = new TypeFunction(NULL, Type::tchar->invariantOf()->arrayOf(), 0, LINKd); + tftostring = (TypeFunction *)tftostring->semantic(0, &sc); + } + + TypeFunction *tfcmpptr; + { + Scope sc; + Parameters *arguments = new Parameters; +#if STRUCTTHISREF + // arg type is ref const T + Parameter *arg = new Parameter(STCref, tc->constOf(), NULL, NULL); +#else + // arg type is const T* + Parameter *arg = new Parameter(STCin, tc->pointerTo(), NULL, NULL); +#endif + + arguments->push(arg); + tfcmpptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); + tfcmpptr->mod = MODconst; + tfcmpptr = (TypeFunction *)tfcmpptr->semantic(0, &sc); + } + + s = search_function(sd, Id::tohash); + fdx = s ? s->isFuncDeclaration() : NULL; + if (fdx) + { fd = fdx->overloadExactMatch(tftohash); + if (fd) + dtxoff(pdt, fd->toSymbol(), 0, TYnptr); + else + //fdx->error("must be declared as extern (D) uint toHash()"); + dtsize_t(pdt, 0); + } + else + dtsize_t(pdt, 0); + + if (sd->xeq) + dtxoff(pdt, sd->xeq->toSymbol(), 0, TYnptr); + else + dtsize_t(pdt, 0); + + s = search_function(sd, Id::cmp); + fdx = s ? s->isFuncDeclaration() : NULL; + if (fdx) + { + //printf("test1 %s, %s, %s\n", fdx->toChars(), fdx->type->toChars(), tfeqptr->toChars()); + fd = fdx->overloadExactMatch(tfcmpptr); + if (fd) + { dtxoff(pdt, fd->toSymbol(), 0, TYnptr); + //printf("test2\n"); + } + else + //fdx->error("must be declared as extern (D) int %s(%s*)", fdx->toChars(), sd->toChars()); + dtsize_t(pdt, 0); + } + else + dtsize_t(pdt, 0); + + s = search_function(sd, Id::tostring); + fdx = s ? s->isFuncDeclaration() : NULL; + if (fdx) + { fd = fdx->overloadExactMatch(tftostring); + if (fd) + dtxoff(pdt, fd->toSymbol(), 0, TYnptr); + else + //fdx->error("must be declared as extern (D) char[] toString()"); + dtsize_t(pdt, 0); + } + else + dtsize_t(pdt, 0); + + // uint m_flags; + dtsize_t(pdt, tc->hasPointers()); + +#if DMDV2 + // xgetMembers + FuncDeclaration *sgetmembers = sd->findGetMembers(); + if (sgetmembers) + dtxoff(pdt, sgetmembers->toSymbol(), 0, TYnptr); + else + dtsize_t(pdt, 0); // xgetMembers + + // xdtor + FuncDeclaration *sdtor = sd->dtor; + if (sdtor) + dtxoff(pdt, sdtor->toSymbol(), 0, TYnptr); + else + dtsize_t(pdt, 0); // xdtor + + // xpostblit + FuncDeclaration *spostblit = sd->postblit; + if (spostblit && !(spostblit->storage_class & STCdisable)) + dtxoff(pdt, spostblit->toSymbol(), 0, TYnptr); + else + dtsize_t(pdt, 0); // xpostblit +#endif + + // uint m_align; + dtsize_t(pdt, tc->alignsize()); + + if (global.params.is64bit) + { + TypeTuple *tup = tc->toArgTypes(); + assert(tup->arguments->dim <= 2); + for (size_t i = 0; i < 2; i++) + { + if (i < tup->arguments->dim) + { + Type *targ = (tup->arguments->tdata()[i])->type; + targ = targ->merge(); + targ->getTypeInfo(NULL); + dtxoff(pdt, targ->vtinfo->toSymbol(), 0, TYnptr); // m_argi + } + else + dtsize_t(pdt, 0); // m_argi + } + } + + // name[] + dtnbytes(pdt, namelen + 1, name); +} + +void TypeInfoClassDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoClassDeclaration::toDt() %s\n", tinfo->toChars()); +#if DMDV1 + dtxoff(pdt, Type::typeinfoclass->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfoClass + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Tclass); + + TypeClass *tc = (TypeClass *)tinfo; + Symbol *s; + + if (!tc->sym->vclassinfo) + tc->sym->vclassinfo = new ClassInfoDeclaration(tc->sym); + s = tc->sym->vclassinfo->toSymbol(); + dtxoff(pdt, s, 0, TYnptr); // ClassInfo for tinfo +#else + assert(0); +#endif +} + +void TypeInfoInterfaceDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoInterfaceDeclaration::toDt() %s\n", tinfo->toChars()); + dtxoff(pdt, Type::typeinfointerface->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfoInterface + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Tclass); + + TypeClass *tc = (TypeClass *)tinfo; + Symbol *s; + + if (!tc->sym->vclassinfo) +#if DMDV1 + tc->sym->vclassinfo = new ClassInfoDeclaration(tc->sym); +#else + tc->sym->vclassinfo = new TypeInfoClassDeclaration(tc); +#endif + s = tc->sym->vclassinfo->toSymbol(); + dtxoff(pdt, s, 0, TYnptr); // ClassInfo for tinfo +} + +void TypeInfoTupleDeclaration::toDt(dt_t **pdt) +{ + //printf("TypeInfoTupleDeclaration::toDt() %s\n", tinfo->toChars()); + dtxoff(pdt, Type::typeinfotypelist->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfoInterface + dtsize_t(pdt, 0); // monitor + + assert(tinfo->ty == Ttuple); + + TypeTuple *tu = (TypeTuple *)tinfo; + + size_t dim = tu->arguments->dim; + dtsize_t(pdt, dim); // elements.length + + dt_t *d = NULL; + for (size_t i = 0; i < dim; i++) + { Parameter *arg = tu->arguments->tdata()[i]; + Expression *e = arg->type->getTypeInfo(NULL); + e = e->optimize(WANTvalue); + e->toDt(&d); + } + + Symbol *s; + s = static_sym(); + s->Sdt = d; + outdata(s); + + dtxoff(pdt, s, 0, TYnptr); // elements.ptr +} + +void TypeInfoDeclaration::toObjFile(int multiobj) +{ + Symbol *s; + unsigned sz; + Dsymbol *parent; + + //printf("TypeInfoDeclaration::toObjFile(%p '%s') protection %d\n", this, toChars(), protection); + + if (multiobj) + { + obj_append(this); + return; + } + + s = toSymbol(); + sz = type->size(); + + parent = this->toParent(); + s->Sclass = SCcomdat; + s->Sfl = FLdata; + + toDt(&s->Sdt); + + dt_optimize(s->Sdt); + + // See if we can convert a comdat to a comdef, + // which saves on exe file space. + if (s->Sclass == SCcomdat && + s->Sdt->dt == DT_azeros && + s->Sdt->DTnext == NULL) + { + s->Sclass = SCglobal; + s->Sdt->dt = DT_common; + } + +#if ELFOBJ || MACHOBJ // Burton + if (s->Sdt && s->Sdt->dt == DT_azeros && s->Sdt->DTnext == NULL) + s->Sseg = UDATA; + else + s->Sseg = DATA; +#endif + outdata(s); + if (isExport()) + obj_export(s,0); +} + +#endif +#endif // TARGET_NET + +/* ========================================================================= */ + +/* These decide if there's an instance for them already in std.typeinfo, + * because then the compiler doesn't need to build one. + */ + +int Type::builtinTypeInfo() +{ + return 0; +} + +int TypeBasic::builtinTypeInfo() +{ +#if DMDV2 + return mod ? 0 : 1; +#else + return 1; +#endif +} + +int TypeDArray::builtinTypeInfo() +{ +#if DMDV2 + return !mod && (next->isTypeBasic() != NULL && !next->mod || + // strings are so common, make them builtin + next->ty == Tchar && next->mod == MODimmutable); +#else + return next->isTypeBasic() != NULL; +#endif +} + +int TypeClass::builtinTypeInfo() +{ + /* This is statically put out with the ClassInfo, so + * claim it is built in so it isn't regenerated by each module. + */ +#if DMDV2 + return mod ? 0 : 1; +#else + return 1; +#endif +} + +/* ========================================================================= */ + +/*************************************** + * Create a static array of TypeInfo references + * corresponding to an array of Expression's. + * Used to supply hidden _arguments[] value for variadic D functions. + */ + +Expression *createTypeInfoArray(Scope *sc, Expression *exps[], unsigned dim) +{ +#if 1 + /* Get the corresponding TypeInfo_Tuple and + * point at its elements[]. + */ + + /* Create the TypeTuple corresponding to the types of args[] + */ + Parameters *args = new Parameters; + args->setDim(dim); + for (size_t i = 0; i < dim; i++) + { Parameter *arg = new Parameter(STCin, exps[i]->type, NULL, NULL); + args->tdata()[i] = arg; + } + TypeTuple *tup = new TypeTuple(args); + Expression *e = tup->getTypeInfo(sc); + e = e->optimize(WANTvalue); + assert(e->op == TOKsymoff); // should be SymOffExp + +#if BREAKABI + /* + * Should just pass a reference to TypeInfo_Tuple instead, + * but that would require existing code to be recompiled. + * Source compatibility can be maintained by computing _arguments[] + * at the start of the called function by offseting into the + * TypeInfo_Tuple reference. + */ + +#else + // Advance to elements[] member of TypeInfo_Tuple + SymOffExp *se = (SymOffExp *)e; + se->offset += PTRSIZE + PTRSIZE; + + // Set type to TypeInfo[]* + se->type = Type::typeinfo->type->arrayOf()->pointerTo(); + + // Indirect to get the _arguments[] value + e = new PtrExp(0, se); + e->type = se->type->next; +#endif + return e; +#else + /* Improvements: + * 1) create an array literal instead, + * as it would eliminate the extra dereference of loading the + * static variable. + */ + + ArrayInitializer *ai = new ArrayInitializer(0); + VarDeclaration *v; + Type *t; + Expression *e; + OutBuffer buf; + Identifier *id; + char *name; + + // Generate identifier for _arguments[] + buf.writestring("_arguments_"); + for (int i = 0; i < dim; i++) + { t = exps[i]->type; + t->toDecoBuffer(&buf); + } + buf.writeByte(0); + id = Lexer::idPool((char *)buf.data); + + Module *m = sc->module; + Dsymbol *s = m->symtab->lookup(id); + + if (s && s->parent == m) + { // Use existing one + v = s->isVarDeclaration(); + assert(v); + } + else + { // Generate new one + + for (int i = 0; i < dim; i++) + { t = exps[i]->type; + e = t->getTypeInfo(sc); + ai->addInit(new IntegerExp(i), new ExpInitializer(0, e)); + } + + t = Type::typeinfo->type->arrayOf(); + ai->type = t; + v = new VarDeclaration(0, t, id, ai); + m->members->push(v); + m->symtabInsert(v); + sc = sc->push(); + sc->linkage = LINKc; + sc->stc = STCstatic | STCcomdat; + ai->semantic(sc, t); + v->semantic(sc); + v->parent = m; + sc = sc->pop(); + } + e = new VarExp(0, v); + e = e->semantic(sc); + return e; +#endif +} + + + diff --git a/unialpha.c b/unialpha.c new file mode 100644 index 00000000..5c407180 --- /dev/null +++ b/unialpha.c @@ -0,0 +1,323 @@ + +// Copyright (c) 2003 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include + +/******************************* + * Return !=0 if unicode alpha. + * Use table from C99 Appendix D. + */ + +int isUniAlpha(unsigned u) +{ + static unsigned short table[][2] = + { + { 0x00AA, 0x00AA }, + { 0x00B5, 0x00B5 }, + { 0x00B7, 0x00B7 }, + { 0x00BA, 0x00BA }, + { 0x00C0, 0x00D6 }, + { 0x00D8, 0x00F6 }, + { 0x00F8, 0x01F5 }, + { 0x01FA, 0x0217 }, + { 0x0250, 0x02A8 }, + { 0x02B0, 0x02B8 }, + { 0x02BB, 0x02BB }, + { 0x02BD, 0x02C1 }, + { 0x02D0, 0x02D1 }, + { 0x02E0, 0x02E4 }, + { 0x037A, 0x037A }, + { 0x0386, 0x0386 }, + { 0x0388, 0x038A }, + { 0x038C, 0x038C }, + { 0x038E, 0x03A1 }, + { 0x03A3, 0x03CE }, + { 0x03D0, 0x03D6 }, + { 0x03DA, 0x03DA }, + { 0x03DC, 0x03DC }, + { 0x03DE, 0x03DE }, + { 0x03E0, 0x03E0 }, + { 0x03E2, 0x03F3 }, + { 0x0401, 0x040C }, + { 0x040E, 0x044F }, + { 0x0451, 0x045C }, + { 0x045E, 0x0481 }, + { 0x0490, 0x04C4 }, + { 0x04C7, 0x04C8 }, + { 0x04CB, 0x04CC }, + { 0x04D0, 0x04EB }, + { 0x04EE, 0x04F5 }, + { 0x04F8, 0x04F9 }, + { 0x0531, 0x0556 }, + { 0x0559, 0x0559 }, + { 0x0561, 0x0587 }, + { 0x05B0, 0x05B9 }, + { 0x05BB, 0x05BD }, + { 0x05BF, 0x05BF }, + { 0x05C1, 0x05C2 }, + { 0x05D0, 0x05EA }, + { 0x05F0, 0x05F2 }, + { 0x0621, 0x063A }, + { 0x0640, 0x0652 }, + { 0x0660, 0x0669 }, + { 0x0670, 0x06B7 }, + { 0x06BA, 0x06BE }, + { 0x06C0, 0x06CE }, + { 0x06D0, 0x06DC }, + { 0x06E5, 0x06E8 }, + { 0x06EA, 0x06ED }, + { 0x06F0, 0x06F9 }, + { 0x0901, 0x0903 }, + { 0x0905, 0x0939 }, + { 0x093D, 0x093D }, + { 0x093E, 0x094D }, + { 0x0950, 0x0952 }, + { 0x0958, 0x0963 }, + { 0x0966, 0x096F }, + { 0x0981, 0x0983 }, + { 0x0985, 0x098C }, + { 0x098F, 0x0990 }, + { 0x0993, 0x09A8 }, + { 0x09AA, 0x09B0 }, + { 0x09B2, 0x09B2 }, + { 0x09B6, 0x09B9 }, + { 0x09BE, 0x09C4 }, + { 0x09C7, 0x09C8 }, + { 0x09CB, 0x09CD }, + { 0x09DC, 0x09DD }, + { 0x09DF, 0x09E3 }, + { 0x09E6, 0x09EF }, + { 0x09F0, 0x09F1 }, + { 0x0A02, 0x0A02 }, + { 0x0A05, 0x0A0A }, + { 0x0A0F, 0x0A10 }, + { 0x0A13, 0x0A28 }, + { 0x0A2A, 0x0A30 }, + { 0x0A32, 0x0A33 }, + { 0x0A35, 0x0A36 }, + { 0x0A38, 0x0A39 }, + { 0x0A3E, 0x0A42 }, + { 0x0A47, 0x0A48 }, + { 0x0A4B, 0x0A4D }, + { 0x0A59, 0x0A5C }, + { 0x0A5E, 0x0A5E }, + { 0x0A66, 0x0A6F }, + { 0x0A74, 0x0A74 }, + { 0x0A81, 0x0A83 }, + { 0x0A85, 0x0A8B }, + { 0x0A8D, 0x0A8D }, + { 0x0A8F, 0x0A91 }, + { 0x0A93, 0x0AA8 }, + { 0x0AAA, 0x0AB0 }, + { 0x0AB2, 0x0AB3 }, + { 0x0AB5, 0x0AB9 }, + { 0x0ABD, 0x0AC5 }, + { 0x0AC7, 0x0AC9 }, + { 0x0ACB, 0x0ACD }, + { 0x0AD0, 0x0AD0 }, + { 0x0AE0, 0x0AE0 }, + { 0x0AE6, 0x0AEF }, + { 0x0B01, 0x0B03 }, + { 0x0B05, 0x0B0C }, + { 0x0B0F, 0x0B10 }, + { 0x0B13, 0x0B28 }, + { 0x0B2A, 0x0B30 }, + { 0x0B32, 0x0B33 }, + { 0x0B36, 0x0B39 }, + { 0x0B3D, 0x0B3D }, + { 0x0B3E, 0x0B43 }, + { 0x0B47, 0x0B48 }, + { 0x0B4B, 0x0B4D }, + { 0x0B5C, 0x0B5D }, + { 0x0B5F, 0x0B61 }, + { 0x0B66, 0x0B6F }, + { 0x0B82, 0x0B83 }, + { 0x0B85, 0x0B8A }, + { 0x0B8E, 0x0B90 }, + { 0x0B92, 0x0B95 }, + { 0x0B99, 0x0B9A }, + { 0x0B9C, 0x0B9C }, + { 0x0B9E, 0x0B9F }, + { 0x0BA3, 0x0BA4 }, + { 0x0BA8, 0x0BAA }, + { 0x0BAE, 0x0BB5 }, + { 0x0BB7, 0x0BB9 }, + { 0x0BBE, 0x0BC2 }, + { 0x0BC6, 0x0BC8 }, + { 0x0BCA, 0x0BCD }, + { 0x0BE7, 0x0BEF }, + { 0x0C01, 0x0C03 }, + { 0x0C05, 0x0C0C }, + { 0x0C0E, 0x0C10 }, + { 0x0C12, 0x0C28 }, + { 0x0C2A, 0x0C33 }, + { 0x0C35, 0x0C39 }, + { 0x0C3E, 0x0C44 }, + { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, + { 0x0C60, 0x0C61 }, + { 0x0C66, 0x0C6F }, + { 0x0C82, 0x0C83 }, + { 0x0C85, 0x0C8C }, + { 0x0C8E, 0x0C90 }, + { 0x0C92, 0x0CA8 }, + { 0x0CAA, 0x0CB3 }, + { 0x0CB5, 0x0CB9 }, + { 0x0CBE, 0x0CC4 }, + { 0x0CC6, 0x0CC8 }, + { 0x0CCA, 0x0CCD }, + { 0x0CDE, 0x0CDE }, + { 0x0CE0, 0x0CE1 }, + { 0x0CE6, 0x0CEF }, + { 0x0D02, 0x0D03 }, + { 0x0D05, 0x0D0C }, + { 0x0D0E, 0x0D10 }, + { 0x0D12, 0x0D28 }, + { 0x0D2A, 0x0D39 }, + { 0x0D3E, 0x0D43 }, + { 0x0D46, 0x0D48 }, + { 0x0D4A, 0x0D4D }, + { 0x0D60, 0x0D61 }, + { 0x0D66, 0x0D6F }, + { 0x0E01, 0x0E3A }, + { 0x0E40, 0x0E5B }, +// { 0x0E50, 0x0E59 }, + { 0x0E81, 0x0E82 }, + { 0x0E84, 0x0E84 }, + { 0x0E87, 0x0E88 }, + { 0x0E8A, 0x0E8A }, + { 0x0E8D, 0x0E8D }, + { 0x0E94, 0x0E97 }, + { 0x0E99, 0x0E9F }, + { 0x0EA1, 0x0EA3 }, + { 0x0EA5, 0x0EA5 }, + { 0x0EA7, 0x0EA7 }, + { 0x0EAA, 0x0EAB }, + { 0x0EAD, 0x0EAE }, + { 0x0EB0, 0x0EB9 }, + { 0x0EBB, 0x0EBD }, + { 0x0EC0, 0x0EC4 }, + { 0x0EC6, 0x0EC6 }, + { 0x0EC8, 0x0ECD }, + { 0x0ED0, 0x0ED9 }, + { 0x0EDC, 0x0EDD }, + { 0x0F00, 0x0F00 }, + { 0x0F18, 0x0F19 }, + { 0x0F20, 0x0F33 }, + { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, + { 0x0F39, 0x0F39 }, + { 0x0F3E, 0x0F47 }, + { 0x0F49, 0x0F69 }, + { 0x0F71, 0x0F84 }, + { 0x0F86, 0x0F8B }, + { 0x0F90, 0x0F95 }, + { 0x0F97, 0x0F97 }, + { 0x0F99, 0x0FAD }, + { 0x0FB1, 0x0FB7 }, + { 0x0FB9, 0x0FB9 }, + { 0x10A0, 0x10C5 }, + { 0x10D0, 0x10F6 }, + { 0x1E00, 0x1E9B }, + { 0x1EA0, 0x1EF9 }, + { 0x1F00, 0x1F15 }, + { 0x1F18, 0x1F1D }, + { 0x1F20, 0x1F45 }, + { 0x1F48, 0x1F4D }, + { 0x1F50, 0x1F57 }, + { 0x1F59, 0x1F59 }, + { 0x1F5B, 0x1F5B }, + { 0x1F5D, 0x1F5D }, + { 0x1F5F, 0x1F7D }, + { 0x1F80, 0x1FB4 }, + { 0x1FB6, 0x1FBC }, + { 0x1FBE, 0x1FBE }, + { 0x1FC2, 0x1FC4 }, + { 0x1FC6, 0x1FCC }, + { 0x1FD0, 0x1FD3 }, + { 0x1FD6, 0x1FDB }, + { 0x1FE0, 0x1FEC }, + { 0x1FF2, 0x1FF4 }, + { 0x1FF6, 0x1FFC }, + { 0x203F, 0x2040 }, + { 0x207F, 0x207F }, + { 0x2102, 0x2102 }, + { 0x2107, 0x2107 }, + { 0x210A, 0x2113 }, + { 0x2115, 0x2115 }, + { 0x2118, 0x211D }, + { 0x2124, 0x2124 }, + { 0x2126, 0x2126 }, + { 0x2128, 0x2128 }, + { 0x212A, 0x2131 }, + { 0x2133, 0x2138 }, + { 0x2160, 0x2182 }, + { 0x3005, 0x3007 }, + { 0x3021, 0x3029 }, + { 0x3041, 0x3093 }, + { 0x309B, 0x309C }, + { 0x30A1, 0x30F6 }, + { 0x30FB, 0x30FC }, + { 0x3105, 0x312C }, + { 0x4E00, 0x9FA5 }, + { 0xAC00, 0xD7A3 }, + }; + +#ifdef DEBUG + for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++) + { + //printf("%x\n", table[i][0]); + assert(table[i][0] <= table[i][1]); + if (i < sizeof(table) / sizeof(table[0]) - 1) + assert(table[i][1] < table[i + 1][0]); + } +#endif + + if (u > 0xD7A3) + goto Lisnot; + + // Binary search + int mid; + int low; + int high; + + low = 0; + high = sizeof(table) / sizeof(table[0]) - 1; + while (low <= high) + { + mid = (low + high) >> 1; + if (u < table[mid][0]) + high = mid - 1; + else if (u > table[mid][1]) + low = mid + 1; + else + goto Lis; + } + +Lisnot: +#ifdef DEBUG + for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++) + { + assert(u < table[i][0] || u > table[i][1]); + } +#endif + return 0; + +Lis: +#ifdef DEBUG + for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++) + { + if (u >= table[i][0] && u <= table[i][1]) + return 1; + } + assert(0); // should have been in table +#endif + return 1; +} + diff --git a/unittests.c b/unittests.c new file mode 100644 index 00000000..1b3c2770 --- /dev/null +++ b/unittests.c @@ -0,0 +1,17 @@ + +#include + +#include "mars.h" + +void unittest_speller(); +void unittest_importHint(); +void unittest_aa(); + +void unittests() +{ +#if UNITTEST + unittest_speller(); + unittest_importHint(); + unittest_aa(); +#endif +} diff --git a/utf.c b/utf.c new file mode 100644 index 00000000..78f8cd4a --- /dev/null +++ b/utf.c @@ -0,0 +1,320 @@ +// utf.c +// Copyright (c) 2003-2009 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +// Description of UTF-8 at: +// http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + +#include +#include +#include + +#include "utf.h" + +int utf_isValidDchar(dchar_t c) +{ + return c < 0xD800 || + (c > 0xDFFF && c <= 0x10FFFF && c != 0xFFFE && c != 0xFFFF); +} + +static const unsigned char UTF8stride[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0xFF,0xFF, +}; + +/** + * stride() returns the length of a UTF-8 sequence starting at index i + * in string s. + * Returns: + * The number of bytes in the UTF-8 sequence or + * 0xFF meaning s[i] is not the start of of UTF-8 sequence. + */ + +unsigned stride(unsigned char* s, size_t i) +{ + unsigned result = UTF8stride[s[i]]; + return result; +} + +/******************************************** + * Decode a single UTF-8 character sequence. + * Returns: + * NULL success + * !=NULL error message string + */ + +const char *utf_decodeChar(unsigned char *s, size_t len, size_t *pidx, dchar_t *presult) +{ + dchar_t V; + size_t i = *pidx; + unsigned char u = s[i]; + + //printf("utf_decodeChar(s = %02x, %02x, %02x len = %d)\n", u, s[1], s[2], len); + + assert(i >= 0 && i < len); + + if (u & 0x80) + { unsigned n; + unsigned char u2; + + /* The following encodings are valid, except for the 5 and 6 byte + * combinations: + * 0xxxxxxx + * 110xxxxx 10xxxxxx + * 1110xxxx 10xxxxxx 10xxxxxx + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + for (n = 1; ; n++) + { + if (n > 4) + goto Lerr; // only do the first 4 of 6 encodings + if (((u << n) & 0x80) == 0) + { + if (n == 1) + goto Lerr; + break; + } + } + + // Pick off (7 - n) significant bits of B from first byte of octet + V = (dchar_t)(u & ((1 << (7 - n)) - 1)); + + if (i + (n - 1) >= len) + goto Lerr; // off end of string + + /* The following combinations are overlong, and illegal: + * 1100000x (10xxxxxx) + * 11100000 100xxxxx (10xxxxxx) + * 11110000 1000xxxx (10xxxxxx 10xxxxxx) + * 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx) + * 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx) + */ + u2 = s[i + 1]; + if ((u & 0xFE) == 0xC0 || + (u == 0xE0 && (u2 & 0xE0) == 0x80) || + (u == 0xF0 && (u2 & 0xF0) == 0x80) || + (u == 0xF8 && (u2 & 0xF8) == 0x80) || + (u == 0xFC && (u2 & 0xFC) == 0x80)) + goto Lerr; // overlong combination + + for (unsigned j = 1; j != n; j++) + { + u = s[i + j]; + if ((u & 0xC0) != 0x80) + goto Lerr; // trailing bytes are 10xxxxxx + V = (V << 6) | (u & 0x3F); + } + if (!utf_isValidDchar(V)) + goto Lerr; + i += n; + } + else + { + V = (dchar_t) u; + i++; + } + + assert(utf_isValidDchar(V)); + *pidx = i; + *presult = V; + return NULL; + + Lerr: + *presult = (dchar_t) s[i]; + *pidx = i + 1; + return "invalid UTF-8 sequence"; +} + +/*************************************************** + * Validate a UTF-8 string. + * Returns: + * NULL success + * !=NULL error message string + */ + +const char *utf_validateString(unsigned char *s, size_t len) +{ + size_t idx; + const char *err = NULL; + dchar_t dc; + + for (idx = 0; idx < len; ) + { + err = utf_decodeChar(s, len, &idx, &dc); + if (err) + break; + } + return err; +} + + +/******************************************** + * Decode a single UTF-16 character sequence. + * Returns: + * NULL success + * !=NULL error message string + */ + + +const char *utf_decodeWchar(unsigned short *s, size_t len, size_t *pidx, dchar_t *presult) +{ + const char *msg; + size_t i = *pidx; + unsigned u = s[i]; + + assert(i >= 0 && i < len); + if (u & ~0x7F) + { if (u >= 0xD800 && u <= 0xDBFF) + { unsigned u2; + + if (i + 1 == len) + { msg = "surrogate UTF-16 high value past end of string"; + goto Lerr; + } + u2 = s[i + 1]; + if (u2 < 0xDC00 || u2 > 0xDFFF) + { msg = "surrogate UTF-16 low value out of range"; + goto Lerr; + } + u = ((u - 0xD7C0) << 10) + (u2 - 0xDC00); + i += 2; + } + else if (u >= 0xDC00 && u <= 0xDFFF) + { msg = "unpaired surrogate UTF-16 value"; + goto Lerr; + } + else if (u == 0xFFFE || u == 0xFFFF) + { msg = "illegal UTF-16 value"; + goto Lerr; + } + else + i++; + } + else + { + i++; + } + + assert(utf_isValidDchar(u)); + *pidx = i; + *presult = (dchar_t)u; + return NULL; + + Lerr: + *presult = (dchar_t)s[i]; + *pidx = i + 1; + return msg; +} + +void utf_encodeChar(unsigned char *s, dchar_t c) +{ + if (c <= 0x7F) + { + s[0] = (char) c; + } + else if (c <= 0x7FF) + { + s[0] = (char)(0xC0 | (c >> 6)); + s[1] = (char)(0x80 | (c & 0x3F)); + } + else if (c <= 0xFFFF) + { + s[0] = (char)(0xE0 | (c >> 12)); + s[1] = (char)(0x80 | ((c >> 6) & 0x3F)); + s[2] = (char)(0x80 | (c & 0x3F)); + } + else if (c <= 0x10FFFF) + { + s[0] = (char)(0xF0 | (c >> 18)); + s[1] = (char)(0x80 | ((c >> 12) & 0x3F)); + s[2] = (char)(0x80 | ((c >> 6) & 0x3F)); + s[3] = (char)(0x80 | (c & 0x3F)); + } + else + assert(0); +} + +void utf_encodeWchar(unsigned short *s, dchar_t c) +{ + if (c <= 0xFFFF) + { + s[0] = (wchar_t) c; + } + else + { + s[0] = (wchar_t) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); + s[1] = (wchar_t) (((c - 0x10000) & 0x3FF) + 0xDC00); + } +} + + +/** + * Returns the code length of c in the encoding. + * The code is returned in character count, not in bytes. + */ + +int utf_codeLengthChar(dchar_t c) +{ + return + c <= 0x7F ? 1 + : c <= 0x7FF ? 2 + : c <= 0xFFFF ? 3 + : c <= 0x10FFFF ? 4 + : (assert(false), 6); +} + +int utf_codeLengthWchar(dchar_t c) +{ + return c <= 0xFFFF ? 1 : 2; +} + +/** + * Returns the code length of c in the encoding. + * sz is the encoding: 1 = utf8, 2 = utf16, 4 = utf32. + * The code is returned in character count, not in bytes. + */ +int utf_codeLength(int sz, dchar_t c) +{ + if (sz == 1) + return utf_codeLengthChar(c); + if (sz == 2) + return utf_codeLengthWchar(c); + assert(sz == 4); + return 1; +} + +void utf_encode(int sz, void *s, dchar_t c) +{ + if (sz == 1) + utf_encodeChar((unsigned char *)s, c); + else if (sz == 2) + utf_encodeWchar((unsigned short *)s, c); + else + { + assert(sz == 4); + memcpy((unsigned char *)s, &c, sz); + } +} + diff --git a/utf.h b/utf.h new file mode 100644 index 00000000..22d8d3eb --- /dev/null +++ b/utf.h @@ -0,0 +1,35 @@ +// Compiler implementation of the D programming language +// utf.h +// Copyright (c) 2003-2010 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_UTF_H +#define DMD_UTF_H + + +typedef unsigned dchar_t; + +int utf_isValidDchar(dchar_t c); + +const char *utf_decodeChar(unsigned char *s, size_t len, size_t *pidx, dchar_t *presult); +const char *utf_decodeWchar(unsigned short *s, size_t len, size_t *pidx, dchar_t *presult); + +const char *utf_validateString(unsigned char *s, size_t len); + +extern int isUniAlpha(dchar_t); + +void utf_encodeChar(unsigned char *s, dchar_t c); +void utf_encodeWchar(unsigned short *s, dchar_t c); + +int utf_codeLengthChar(dchar_t c); +int utf_codeLengthWchar(dchar_t c); + +int utf_codeLength(int sz, dchar_t c); +void utf_encode(int sz, void *s, dchar_t c); + +#endif diff --git a/util.c b/util.c new file mode 100644 index 00000000..77ecf8e8 --- /dev/null +++ b/util.c @@ -0,0 +1,342 @@ +/* + * Some portions copyright (c) 1984-1993 by Symantec + * Copyright (c) 1999-2011 by Digital Mars + * All Rights Reserved + * http://www.digitalmars.com + * Written by Walter Bright + * + * This source file is made available for personal use + * only. The license is in /dmd/src/dmd/backendlicense.txt + * For any other uses, please contact Digital Mars. + */ + +// Utility subroutines + +#include +#include +#include +#include +#include + +#include "cc.h" +#include "global.h" +#include "mem.h" +#include "token.h" +#if SCPP || MARS +#include "el.h" +#endif + +#if _WIN32 && __DMC__ +//#include "scdll.h" +#include +#endif + +static char __file__[] = __FILE__; /* for tassert.h */ +#include "tassert.h" + +void *ph_malloc(size_t nbytes); +void *ph_calloc(size_t nbytes); +void ph_free(void *p); +void *ph_realloc(void *p , size_t nbytes); + + +void util_exit(int exitcode); + +void file_progress() +{ +} + +/******************************* + * Alternative assert failure. + */ + +void util_assert(char *file,int line) +{ + fflush(stdout); + printf("Internal error: %s %d\n",file,line); + err_exit(); +} + +/**************************** + * Clean up and exit program. + */ + +void err_exit() +{ + util_exit(EXIT_FAILURE); +} + +/******************************** + * Clean up and exit program. + */ + +void err_break() +{ + util_exit(255); +} + + +/**************************** + * Clean up and exit program. + */ + +void util_exit(int exitcode) +{ + exit(exitcode); /* terminate abnormally */ +} + + +#if _WIN32 + +volatile int controlc_saw; + +/******************************** + * Control C interrupts go here. + */ + +static void __cdecl controlc_handler(void) +{ + //printf("saw controlc\n"); + controlc_saw = 1; +} + +/********************************* + * Trap control C interrupts. + */ + +void _STI_controlc() +{ + //printf("_STI_controlc()\n"); + _controlc_handler = controlc_handler; + controlc_open(); /* trap control C */ +} + +void _STD_controlc() +{ + //printf("_STD_controlc()\n"); + controlc_close(); +} + + +/*********************************** + * Send progress report. + */ + +void util_progress() +{ + if (controlc_saw) + err_break(); +} + +void util_progress(int linnum) +{ + if (controlc_saw) + err_break(); +} + +#endif + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +void util_progress() +{ +} + +void util_progress(int linnum) +{ +} +#endif + +/********************************** + * Binary string search. + * Input: + * p -> string of characters + * tab array of pointers to strings + * n = number of pointers in the array + * Returns: + * index (0..n-1) into tab[] if we found a string match + * else -1 + */ + +#if TX86 && __INTSIZE == 4 && __DMC__ && !_DEBUG_TRACE + +int binary(const char *p, const char **table,int high) +{ +#define len high // reuse parameter storage + _asm + { + +;First find the length of the identifier. + xor EAX,EAX ;Scan for a 0. + mov EDI,p + mov ECX,EAX + dec ECX ;Longest possible string. + repne scasb + mov EDX,high ;EDX = high + not ECX ;length of the id including '/0', stays in ECX + dec EDX ;high-- + js short Lnotfound + dec EAX ;EAX = -1, so that eventually EBX = low (0) + mov len,ECX + + even +L4D: mov EBX,EAX ;EBX (low) = mid + inc EBX ;low = mid + 1 + cmp EBX,EDX + jg Lnotfound + + even +L15: lea EAX,[EBX + EDX] ;EAX = EBX + EDX + +;Do the string compare. + + mov EDI,table + sar EAX,1 ;mid = (low + high) >> 1; + mov ESI,p + mov EDI,DS:[4*EAX+EDI] ;Load table[mid] + mov ECX,len ;length of id + repe cmpsb + + je short L63 ;return mid if equal + jns short L4D ;if (cond < 0) + lea EDX,-1[EAX] ;high = mid - 1 + cmp EBX,EDX + jle L15 + +Lnotfound: + mov EAX,-1 ;Return -1. + + even +L63: + } +#undef len +} + +#else + +int binary(const char *p, const char __near * __near *table,int high) +{ int low,mid; + signed char cond; + char cp; + + low = 0; + high--; + cp = *p; + p++; + while (low <= high) + { mid = (low + high) >> 1; + if ((cond = table[mid][0] - cp) == 0) + cond = strcmp(table[mid] + 1,p); + if (cond > 0) + high = mid - 1; + else if (cond < 0) + low = mid + 1; + else + return mid; /* match index */ + } + return -1; +} + +#endif + +/********************** + * If c is a power of 2, return that power else -1. + */ + +int ispow2(unsigned long long c) +{ int i; + + if (c == 0 || (c & (c - 1))) + i = -1; + else + for (i = 0; c >>= 1; i++) + ; + return i; +} + +/*************************** + */ + +#define UTIL_PH 1 + +#if _WIN32 +void *util_malloc(unsigned n,unsigned size) +{ +#if 0 && MEM_DEBUG + void *p; + + p = mem_malloc(n * size); + //dbg_printf("util_calloc(%d) = %p\n",n * size,p); + return p; +#elif UTIL_PH + return ph_malloc(n * size); +#else + size_t nbytes = (size_t)n * (size_t)size; + void *p = malloc(nbytes); + if (!p && nbytes) + err_nomem(); + return p; +#endif +} +#endif + +/*************************** + */ + +#if _WIN32 +void *util_calloc(unsigned n,unsigned size) +{ +#if 0 && MEM_DEBUG + void *p; + + p = mem_calloc(n * size); + //dbg_printf("util_calloc(%d) = %p\n",n * size,p); + return p; +#elif UTIL_PH + return ph_calloc(n * size); +#else + size_t nbytes = (size_t) n * (size_t) size; + void *p = calloc(n,size); + if (!p && nbytes) + err_nomem(); + return p; +#endif +} +#endif + +/*************************** + */ + +#if _WIN32 +void util_free(void *p) +{ + //dbg_printf("util_free(%p)\n",p); +#if 0 && MEM_DEBUG + mem_free(p); +#elif UTIL_PH + ph_free(p); +#else + free(p); +#endif +} +#endif + +/*************************** + */ + +#if _WIN32 +void *util_realloc(void *oldp,unsigned n,unsigned size) +{ +#if 0 && MEM_DEBUG + //dbg_printf("util_realloc(%p,%d)\n",oldp,n * size); + return mem_realloc(oldp,n * size); +#elif UTIL_PH + return ph_realloc(oldp,n * size); +#else + size_t nbytes = (size_t) n * (size_t) size; + void *p = realloc(oldp,nbytes); + if (!p && nbytes) + err_nomem(); + return p; +#endif +} +#endif diff --git a/version.c b/version.c new file mode 100644 index 00000000..37f33c06 --- /dev/null +++ b/version.c @@ -0,0 +1,181 @@ + +// Copyright (c) 1999-2005 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include + +#include "root.h" + +#include "identifier.h" +#include "dsymbol.h" +#include "cond.h" +#include "version.h" +#include "module.h" + +/* ================================================== */ + +/* DebugSymbol's happen for statements like: + * debug = identifier; + * debug = integer; + */ + +DebugSymbol::DebugSymbol(Loc loc, Identifier *ident) + : Dsymbol(ident) +{ + this->loc = loc; +} + +DebugSymbol::DebugSymbol(Loc loc, unsigned level) + : Dsymbol() +{ + this->level = level; + this->loc = loc; +} + +Dsymbol *DebugSymbol::syntaxCopy(Dsymbol *s) +{ + assert(!s); + DebugSymbol *ds = new DebugSymbol(loc, ident); + ds->level = level; + return ds; +} + +int DebugSymbol::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +{ + //printf("DebugSymbol::addMember('%s') %s\n", sd->toChars(), toChars()); + Module *m; + + // Do not add the member to the symbol table, + // just make sure subsequent debug declarations work. + m = sd->isModule(); + if (ident) + { + if (!m) + error("declaration must be at module level"); + else + { + if (findCondition(m->debugidsNot, ident)) + error("defined after use"); + if (!m->debugids) + m->debugids = new Strings(); + m->debugids->push(ident->toChars()); + } + } + else + { + if (!m) + error("level declaration must be at module level"); + else + m->debuglevel = level; + } + return 0; +} + +void DebugSymbol::semantic(Scope *sc) +{ + //printf("DebugSymbol::semantic() %s\n", toChars()); +} + +void DebugSymbol::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("debug = "); + if (ident) + buf->writestring(ident->toChars()); + else + buf->printf("%u", level); + buf->writestring(";"); + buf->writenl(); +} + +const char *DebugSymbol::kind() +{ + return "debug"; +} + +/* ================================================== */ + +/* VersionSymbol's happen for statements like: + * version = identifier; + * version = integer; + */ + +VersionSymbol::VersionSymbol(Loc loc, Identifier *ident) + : Dsymbol(ident) +{ + this->loc = loc; +} + +VersionSymbol::VersionSymbol(Loc loc, unsigned level) + : Dsymbol() +{ + this->level = level; + this->loc = loc; +} + +Dsymbol *VersionSymbol::syntaxCopy(Dsymbol *s) +{ + assert(!s); + VersionSymbol *ds = new VersionSymbol(loc, ident); + ds->level = level; + return ds; +} + +int VersionSymbol::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +{ + //printf("VersionSymbol::addMember('%s') %s\n", sd->toChars(), toChars()); + Module *m; + + // Do not add the member to the symbol table, + // just make sure subsequent debug declarations work. + m = sd->isModule(); + if (ident) + { + VersionCondition::checkPredefined(loc, ident->toChars()); + if (!m) + error("declaration must be at module level"); + else + { + if (findCondition(m->versionidsNot, ident)) + error("defined after use"); + if (!m->versionids) + m->versionids = new Strings(); + m->versionids->push(ident->toChars()); + } + } + else + { + if (!m) + error("level declaration must be at module level"); + else + m->versionlevel = level; + } + return 0; +} + +void VersionSymbol::semantic(Scope *sc) +{ +} + +void VersionSymbol::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("version = "); + if (ident) + buf->writestring(ident->toChars()); + else + buf->printf("%u", level); + buf->writestring(";"); + buf->writenl(); +} + +const char *VersionSymbol::kind() +{ + return "version"; +} + + diff --git a/version.h b/version.h new file mode 100644 index 00000000..b5ae51d2 --- /dev/null +++ b/version.h @@ -0,0 +1,51 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_VERSION_H +#define DMD_VERSION_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +#include "dsymbol.h" + +struct OutBuffer; +struct HdrGenState; + +struct DebugSymbol : Dsymbol +{ + unsigned level; + + DebugSymbol(Loc loc, Identifier *ident); + DebugSymbol(Loc loc, unsigned level); + Dsymbol *syntaxCopy(Dsymbol *); + + int addMember(Scope *sc, ScopeDsymbol *s, int memnum); + void semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + const char *kind(); +}; + +struct VersionSymbol : Dsymbol +{ + unsigned level; + + VersionSymbol(Loc loc, Identifier *ident); + VersionSymbol(Loc loc, unsigned level); + Dsymbol *syntaxCopy(Dsymbol *); + + int addMember(Scope *sc, ScopeDsymbol *s, int memnum); + void semantic(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + const char *kind(); +}; + +#endif /* DMD_VERSION_H */ diff --git a/win32.mak b/win32.mak new file mode 100644 index 00000000..1b1ab046 --- /dev/null +++ b/win32.mak @@ -0,0 +1,583 @@ +#_ win32.mak +# Copyright (C) 1999-2011 by Digital Mars, http://www.digitalmars.com +# Written by Walter Bright +# All Rights Reserved +# Build dmd with Digital Mars C++ compiler +# http://www.digitalmars.com/ctg/sc.html +# This makefile is designed to be used with Digital Mars make.exe +# http://www.digitalmars.com/ctg/make.html +# which should be in \dm\bin or in \dmd\windows\bin + +D= +DMDSVN=\svnproj\dmd\trunk\src +#DMDSVN=\svnproj\dmd\branches\dmd-1.x\src +SCROOT=$D\dm +INCLUDE=$(SCROOT)\include +CC=dmc +LIBNT=$(SCROOT)\lib +SNN=$(SCROOT)\lib\snn +DIR=\dmd2 +CP=cp + +C=backend +TK=tk +ROOT=root + +MAKE=make -fwin32.mak C=$C TK=$(TK) ROOT=$(ROOT) + +TARGET=dmd +XFLG= +MODEL=n +OPT= +DEBUG=-gl -D -DUNITTEST +#PREC=-H -HItotal.h -HO +PREC= +LFLAGS= + +LINKN=$(SCROOT)\bin\link /de + +CFLAGS=-I$(ROOT);$(INCLUDE) $(XFLG) $(OPT) $(DEBUG) -cpp +MFLAGS=-I$C;$(TK) $(OPT) -DMARS -cpp $(DEBUG) -e -wx + +# Makerules: +.c.obj: + $(CC) -c $(CFLAGS) $(PREC) $* + +.asm.obj: + $(CC) -c $(CFLAGS) $* + +defaulttarget: debdmd + +################ RELEASES ######################### + +release: + $(MAKE) clean + $(MAKE) dmd + $(MAKE) clean + +################ NT COMMAND LINE RELEASE ######################### + +trace: + $(MAKE) OPT=-o "DEBUG=-gt -Nc" LFLAGS=-L/ma/co/delexe dmd.exe + +dmd: + $(MAKE) OPT=-o "DEBUG=" LFLAGS=-L/delexe dmd.exe +# $(MAKE) OPT=-o "DEBUG=" LFLAGS=-L/ma/co/delexe dmd.exe + +################ NT COMMAND LINE DEBUG ######################### + +debdmd: + $(MAKE) OPT= "DEBUG=-D -g -DUNITTEST" LFLAGS=-L/ma/co dmd.exe + +######################################### + +# D front end + +OBJ1= mars.obj enum.obj struct.obj dsymbol.obj import.obj id.obj \ + staticassert.obj identifier.obj mtype.obj expression.obj \ + optimize.obj template.obj lexer.obj declaration.obj cast.obj \ + init.obj func.obj utf.obj unialpha.obj parse.obj statement.obj \ + constfold.obj version.obj inifile.obj typinf.obj \ + module.obj scope.obj dump.obj cond.obj inline.obj opover.obj \ + entity.obj class.obj mangle.obj attrib.obj impcnvtab.obj \ + link.obj access.obj doc.obj macro.obj hdrgen.obj delegatize.obj \ + interpret.obj traits.obj aliasthis.obj intrange.obj \ + builtin.obj clone.obj libomf.obj arrayop.obj irstate.obj \ + glue.obj msc.obj ph.obj tk.obj s2ir.obj todt.obj e2ir.obj tocsym.obj \ + util.obj eh.obj toobj.obj toctype.obj tocvdebug.obj toir.obj \ + json.obj unittests.obj imphint.obj argtypes.obj apply.obj canthrow.obj \ + sideeffect.obj + +# from C/C++ compiler optimizer and back end + +OBJ8= go.obj gdag.obj gother.obj gflow.obj gloop.obj var.obj el.obj \ + newman.obj glocal.obj os.obj nteh.obj evalu8.obj cgcs.obj \ + rtlsym.obj html.obj cgelem.obj cgen.obj cgreg.obj out.obj \ + blockopt.obj cgobj.obj cg.obj cgcv.obj type.obj dt.obj \ + debug.obj code.obj cg87.obj cgxmm.obj cgsched.obj ee.obj csymbol.obj \ + cgcod.obj cod1.obj cod2.obj cod3.obj cod4.obj cod5.obj outbuf.obj \ + bcomplex.obj iasm.obj ptrntab.obj aa.obj ti_achar.obj md5.obj \ + ti_pvoid.obj + +# from ROOT + +GCOBJS=rmem.obj +#GCOBJS=dmgcmem.obj bits.obj win32.obj gc.obj + +ROOTOBJS= lstring.obj array.obj gnuc.obj man.obj root.obj port.obj \ + stringtable.obj dchar.obj response.obj async.obj speller.obj aav.obj \ + $(GCOBJS) + +OBJS= $(OBJ1) $(OBJ8) $(ROOTOBJS) + +SRCS= mars.c enum.c struct.c dsymbol.c import.c idgen.c impcnvgen.c utf.h \ + utf.c entity.c identifier.c mtype.c expression.c optimize.c \ + template.h template.c lexer.c declaration.c cast.c \ + cond.h cond.c link.c aggregate.h staticassert.h parse.c statement.c \ + constfold.c version.h version.c inifile.c iasm.c staticassert.c \ + module.c scope.c dump.c init.h init.c attrib.h attrib.c opover.c \ + eh.c toctype.c class.c mangle.c tocsym.c func.c inline.c \ + access.c complex_t.h unialpha.c irstate.h irstate.c glue.c msc.c \ + ph.c tk.c s2ir.c todt.c e2ir.c util.c toobj.c cppmangle.c \ + identifier.h parse.h scope.h enum.h import.h intrange.h \ + typinf.c tocvdebug.c toelfdebug.c mars.h module.h mtype.h dsymbol.h \ + declaration.h lexer.h expression.h statement.h doc.h doc.c \ + macro.h macro.c hdrgen.h hdrgen.c arraytypes.h \ + delegatize.c toir.h toir.c interpret.c traits.c builtin.c \ + clone.c lib.h libomf.c libelf.c libmach.c arrayop.c intrange.c \ + aliasthis.h aliasthis.c json.h json.c unittests.c imphint.c argtypes.c \ + apply.c canthrow.c sideeffect.c + +# From C++ compiler + +BACKSRC= $C\cdef.h $C\cc.h $C\oper.h $C\ty.h $C\optabgen.c \ + $C\global.h $C\code.h $C\type.h $C\dt.h $C\cgcv.h \ + $C\el.h $C\iasm.h $C\rtlsym.h $C\html.h \ + $C\bcomplex.c $C\blockopt.c $C\cg.c $C\cg87.c $C\cgxmm.c \ + $C\cgcod.c $C\cgcs.c $C\cgcv.c $C\cgelem.c $C\cgen.c $C\cgobj.c \ + $C\cgreg.c $C\var.c \ + $C\cgsched.c $C\cod1.c $C\cod2.c $C\cod3.c $C\cod4.c $C\cod5.c \ + $C\code.c $C\symbol.c $C\debug.c $C\dt.c $C\ee.c $C\el.c \ + $C\evalu8.c $C\go.c $C\gflow.c $C\gdag.c \ + $C\gother.c $C\glocal.c $C\gloop.c $C\html.c $C\newman.c \ + $C\nteh.c $C\os.c $C\out.c $C\outbuf.c $C\ptrntab.c $C\rtlsym.c \ + $C\type.c $C\melf.h $C\mach.h $C\bcomplex.h \ + $C\cdeflnx.h $C\outbuf.h $C\token.h $C\tassert.h \ + $C\elfobj.c $C\cv4.h $C\dwarf2.h $C\exh.h $C\go.h \ + $C\dwarf.c $C\dwarf.h $C\cppman.c $C\machobj.c \ + $C\strtold.c $C\aa.h $C\aa.c $C\tinfo.h $C\ti_achar.c \ + $C\md5.h $C\md5.c $C\ti_pvoid.c $C\xmm.h \ + $C\backend.txt + +# From TK + +TKSRC= $(TK)\filespec.h $(TK)\mem.h $(TK)\list.h $(TK)\vec.h \ + $(TK)\filespec.c $(TK)\mem.c $(TK)\vec.c $(TK)\list.c + +# From root + +ROOTSRC= $(ROOT)\dchar.h $(ROOT)\dchar.c $(ROOT)\lstring.h \ + $(ROOT)\lstring.c $(ROOT)\root.h $(ROOT)\root.c $(ROOT)\array.c \ + $(ROOT)\rmem.h $(ROOT)\rmem.c $(ROOT)\port.h \ + $(ROOT)\stringtable.h $(ROOT)\stringtable.c \ + $(ROOT)\gnuc.h $(ROOT)\gnuc.c $(ROOT)\man.c $(ROOT)\port.c \ + $(ROOT)\response.c $(ROOT)\async.h $(ROOT)\async.c \ + $(ROOT)\speller.h $(ROOT)\speller.c \ + $(ROOT)\aav.h $(ROOT)\aav.c \ + $(ROOT)\dmgcmem.c $(ROOT)\gc\bits.c $(ROOT)\gc\gc.c $(ROOT)\gc\gc.h $(ROOT)\gc\mscbitops.h \ + $(ROOT)\gc\bits.h $(ROOT)\gc\gccbitops.h $(ROOT)\gc\linux.c $(ROOT)\gc\os.h \ + $(ROOT)\gc\win32.c + +MAKEFILES=win32.mak posix.mak + +######################################### + +$(TARGET).exe : $(OBJS) win32.mak + dmc -o$(TARGET).exe $(OBJS) -cpp -mn -Ar $(LFLAGS) + + +##################### INCLUDE MACROS ##################### + +CCH= +#TOTALH=$(CCH) total.sym +TOTALH=$(CCH) id.h +CH= $C\cc.h $C\global.h $C\oper.h $C\code.h $C\type.h $C\dt.h $C\cgcv.h $C\el.h $C\iasm.h + +##################### GENERATED SOURCE ##################### + +msgs.h msgs.c sj1041.msg sj1036.msg sj1031.msg : msgsx.exe + msgsx + +msgsx.exe : msgsx.c + dmc msgsx -mn -D$(TARGET) $(DEFINES) $(WINLIBS) + +elxxx.c cdxxx.c optab.c debtab.c fltables.c tytab.c : \ + $C\cdef.h $C\cc.h $C\oper.h $C\ty.h $C\optabgen.c + dmc -cpp -ooptabgen.exe $C\optabgen -DMARS -I$(TK) $(WINLIBS) #-L$(LINKS) + optabgen + +impcnvtab.c : impcnvgen.c + $(CC) -I$(ROOT) -cpp impcnvgen + impcnvgen + +id.h id.c : idgen.c + dmc -cpp idgen + idgen + +##################### SPECIAL BUILDS ##################### + +total.sym : $(ROOT)\root.h mars.h lexer.h parse.h enum.h dsymbol.h \ + mtype.h expression.h attrib.h init.h cond.h version.h \ + declaration.h statement.h scope.h import.h module.h id.h \ + template.h aggregate.h arraytypes.h lib.h total.h + $(CC) -c $(CFLAGS) -HFtotal.sym total.h + +impcnvtab.obj : mtype.h impcnvtab.c + $(CC) -c -I$(ROOT) -cpp impcnvtab + +iasm.obj : $(CH) $(TOTALH) $C\iasm.h iasm.c + $(CC) -c $(MFLAGS) -I$(ROOT) iasm + +bcomplex.obj : $C\bcomplex.c + $(CC) -c $(MFLAGS) $C\bcomplex + +aa.obj : $C\tinfo.h $C\aa.h $C\aa.c + $(CC) -c $(MFLAGS) -I. $C\aa + +blockopt.obj : $C\blockopt.c + $(CC) -c $(MFLAGS) $C\blockopt + +cg.obj : $C\cg.c + $(CC) -c $(MFLAGS) -I. $C\cg + +cg87.obj : $C\cg87.c + $(CC) -c $(MFLAGS) $C\cg87 + +cgcod.obj : $C\cgcod.c + $(CC) -c $(MFLAGS) -I. $C\cgcod + +cgcs.obj : $C\cgcs.c + $(CC) -c $(MFLAGS) $C\cgcs + +cgcv.obj : $C\cgcv.c + $(CC) -c $(MFLAGS) $C\cgcv + +cgelem.obj : $C\rtlsym.h $C\cgelem.c + $(CC) -c $(MFLAGS) -I. $C\cgelem + +cgen.obj : $C\rtlsym.h $C\cgen.c + $(CC) -c $(MFLAGS) $C\cgen + +cgobj.obj : $C\md5.h $C\cgobj.c + $(CC) -c $(MFLAGS) $C\cgobj + +cgreg.obj : $C\cgreg.c + $(CC) -c $(MFLAGS) $C\cgreg + +cgsched.obj : $C\rtlsym.h $C\cgsched.c + $(CC) -c $(MFLAGS) $C\cgsched + +cgxmm.obj : $C\xmm.h $C\cgxmm.c + $(CC) -c $(MFLAGS) $C\cgxmm + +cod1.obj : $C\rtlsym.h $C\cod1.c + $(CC) -c $(MFLAGS) $C\cod1 + +cod2.obj : $C\rtlsym.h $C\cod2.c + $(CC) -c $(MFLAGS) $C\cod2 + +cod3.obj : $C\rtlsym.h $C\cod3.c + $(CC) -c $(MFLAGS) $C\cod3 + +cod4.obj : $C\cod4.c + $(CC) -c $(MFLAGS) $C\cod4 + +cod5.obj : $C\cod5.c + $(CC) -c $(MFLAGS) $C\cod5 + +code.obj : $C\code.c + $(CC) -c $(MFLAGS) $C\code + +irstate.obj : irstate.h irstate.c + $(CC) -c $(MFLAGS) -I$(ROOT) irstate + +csymbol.obj : $C\symbol.c + $(CC) -c $(MFLAGS) $C\symbol -ocsymbol.obj + +debug.obj : $C\debug.c + $(CC) -c $(MFLAGS) -I. $C\debug + +dt.obj : $C\dt.h $C\dt.c + $(CC) -c $(MFLAGS) $C\dt + +ee.obj : $C\ee.c + $(CC) -c $(MFLAGS) $C\ee + +eh.obj : $C\cc.h $C\code.h $C\type.h $C\dt.h eh.c + $(CC) -c $(MFLAGS) eh + +el.obj : $C\rtlsym.h $C\el.h $C\el.c + $(CC) -c $(MFLAGS) $C\el + +evalu8.obj : $C\evalu8.c + $(CC) -c $(MFLAGS) $C\evalu8 + +go.obj : $C\go.c + $(CC) -c $(MFLAGS) $C\go + +gflow.obj : $C\gflow.c + $(CC) -c $(MFLAGS) $C\gflow + +gdag.obj : $C\gdag.c + $(CC) -c $(MFLAGS) $C\gdag + +gother.obj : $C\gother.c + $(CC) -c $(MFLAGS) $C\gother + +glocal.obj : $C\rtlsym.h $C\glocal.c + $(CC) -c $(MFLAGS) $C\glocal + +gloop.obj : $C\gloop.c + $(CC) -c $(MFLAGS) $C\gloop + +glue.obj : $(CH) $(TOTALH) $C\rtlsym.h mars.h module.h glue.c + $(CC) -c $(MFLAGS) -I$(ROOT) glue + +html.obj : $(CH) $(TOTALH) $C\html.h $C\html.c + $(CC) -c -I$(ROOT) $(MFLAGS) $C\html + +imphint.obj : imphint.c + $(CC) -c $(CFLAGS) $* + +mars.obj : $(TOTALH) module.h mars.h mars.c + $(CC) -c $(CFLAGS) $(PREC) $* -Ae + +md5.obj : $C\md5.h $C\md5.c + $(CC) -c $(MFLAGS) $C\md5 + +module.obj : $(TOTALH) $C\html.h module.c + $(CC) -c $(CFLAGS) -I$C $(PREC) module.c + +msc.obj : $(CH) mars.h msc.c + $(CC) -c $(MFLAGS) msc + +newman.obj : $(CH) $C\newman.c + $(CC) -c $(MFLAGS) $C\newman + +nteh.obj : $C\rtlsym.h $C\nteh.c + $(CC) -c $(MFLAGS) $C\nteh + +os.obj : $C\os.c + $(CC) -c $(MFLAGS) $C\os + +out.obj : $C\out.c + $(CC) -c $(MFLAGS) $C\out + +outbuf.obj : $C\outbuf.h $C\outbuf.c + $(CC) -c $(MFLAGS) $C\outbuf + +ph.obj : ph.c + $(CC) -c $(MFLAGS) ph + +ptrntab.obj : $C\iasm.h $C\ptrntab.c + $(CC) -c $(MFLAGS) $C\ptrntab + +rtlsym.obj : $C\rtlsym.h $C\rtlsym.c + $(CC) -c $(MFLAGS) $C\rtlsym + +ti_achar.obj : $C\tinfo.h $C\ti_achar.c + $(CC) -c $(MFLAGS) -I. $C\ti_achar + +ti_pvoid.obj : $C\tinfo.h $C\ti_pvoid.c + $(CC) -c $(MFLAGS) -I. $C\ti_pvoid + +toctype.obj : $(CH) $(TOTALH) $C\rtlsym.h mars.h module.h toctype.c + $(CC) -c $(MFLAGS) -I$(ROOT) toctype + +tocvdebug.obj : $(CH) $(TOTALH) $C\rtlsym.h mars.h module.h tocvdebug.c + $(CC) -c $(MFLAGS) -I$(ROOT) tocvdebug + +toobj.obj : $(CH) $(TOTALH) mars.h module.h toobj.c + $(CC) -c $(MFLAGS) -I$(ROOT) toobj + +type.obj : $C\type.c + $(CC) -c $(MFLAGS) $C\type + +typinf.obj : $(CH) $(TOTALH) $C\rtlsym.h mars.h module.h typinf.c + $(CC) -c $(MFLAGS) -I$(ROOT) typinf + +todt.obj : mtype.h expression.h $C\dt.h todt.c + $(CC) -c -I$(ROOT) $(MFLAGS) todt + +s2ir.obj : $C\rtlsym.h statement.h s2ir.c + $(CC) -c -I$(ROOT) $(MFLAGS) s2ir + +e2ir.obj : $C\rtlsym.h expression.h toir.h e2ir.c + $(CC) -c -I$(ROOT) $(MFLAGS) e2ir + +toir.obj : $C\rtlsym.h expression.h toir.h toir.c + $(CC) -c -I$(ROOT) $(MFLAGS) toir + +tocsym.obj : $(CH) $(TOTALH) mars.h module.h tocsym.c + $(CC) -c $(MFLAGS) -I$(ROOT) tocsym + +unittests.obj : $(TOTALH) unittests.c + $(CC) -c $(CFLAGS) $(PREC) $* + +util.obj : util.c + $(CC) -c $(MFLAGS) util + +var.obj : $C\var.c optab.c + $(CC) -c $(MFLAGS) -I. $C\var + + +tk.obj : tk.c + $(CC) -c $(MFLAGS) tk.c + +# ROOT + +aav.obj : $(ROOT)\aav.h $(ROOT)\aav.c + $(CC) -c $(CFLAGS) $(ROOT)\aav.c + +array.obj : $(ROOT)\array.c + $(CC) -c $(CFLAGS) $(ROOT)\array.c + +async.obj : $(ROOT)\async.h $(ROOT)\async.c + $(CC) -c $(CFLAGS) $(ROOT)\async.c + +dchar.obj : $(ROOT)\dchar.c + $(CC) -c $(CFLAGS) $(ROOT)\dchar.c + +dmgcmem.obj : $(ROOT)\dmgcmem.c + $(CC) -c $(CFLAGS) $(ROOT)\dmgcmem.c + +gnuc.obj : $(ROOT)\gnuc.c + $(CC) -c $(CFLAGS) $(ROOT)\gnuc.c + +lstring.obj : $(ROOT)\lstring.c + $(CC) -c $(CFLAGS) $(ROOT)\lstring.c + +man.obj : $(ROOT)\man.c + $(CC) -c $(CFLAGS) $(ROOT)\man.c + +rmem.obj : $(ROOT)\rmem.c + $(CC) -c $(CFLAGS) $(ROOT)\rmem.c + +port.obj : $(ROOT)\port.c + $(CC) -c $(CFLAGS) $(ROOT)\port.c + +root.obj : $(ROOT)\root.c + $(CC) -c $(CFLAGS) $(ROOT)\root.c + +response.obj : $(ROOT)\response.c + $(CC) -c $(CFLAGS) $(ROOT)\response.c + +speller.obj : $(ROOT)\speller.h $(ROOT)\speller.c + $(CC) -c $(CFLAGS) $(ROOT)\speller.c + +stringtable.obj : $(ROOT)\stringtable.c + $(CC) -c $(CFLAGS) $(ROOT)\stringtable.c + +# ROOT/GC + +bits.obj : $(ROOT)\gc\bits.h $(ROOT)\gc\bits.c + $(CC) -c $(CFLAGS) -I$(ROOT)\gc $(ROOT)\gc\bits.c + +gc.obj : $(ROOT)\gc\bits.h $(ROOT)\gc\os.h $(ROOT)\gc\gc.h $(ROOT)\gc\gc.c + $(CC) -c $(CFLAGS) -I$(ROOT)\gc $(ROOT)\gc\gc.c + +win32.obj : $(ROOT)\gc\os.h $(ROOT)\gc\win32.c + $(CC) -c $(CFLAGS) -I$(ROOT)\gc $(ROOT)\gc\win32.c + + +################# Source file dependencies ############### + +access.obj : $(TOTALH) enum.h aggregate.h init.h attrib.h access.c +aliasthis.obj : $(TOTALH) aliasthis.h aliasthis.c +apply.obj : $(TOTALH) apply.c +argtypes.obj : $(TOTALH) mtype.h argtypes.c +arrayop.obj : $(TOTALH) identifier.h declaration.h arrayop.c +attrib.obj : $(TOTALH) dsymbol.h identifier.h declaration.h attrib.h attrib.c +builtin.obj : $(TOTALH) builtin.c +canthrow.obj : $(TOTALH) canthrow.c +cast.obj : $(TOTALH) expression.h mtype.h cast.c +class.obj : $(TOTALH) enum.h class.c +clone.obj : $(TOTALH) clone.c +constfold.obj : $(TOTALH) expression.h constfold.c +cond.obj : $(TOTALH) identifier.h declaration.h cond.h cond.c +declaration.obj : $(TOTALH) identifier.h attrib.h declaration.h declaration.c expression.h +delegatize.obj : $(TOTALH) delegatize.c +doc.obj : $(TOTALH) doc.h doc.c +enum.obj : $(TOTALH) dsymbol.h identifier.h enum.h enum.c +expression.obj : $(TOTALH) expression.h expression.c +func.obj : $(TOTALH) identifier.h attrib.h declaration.h func.c +hdrgen.obj : $(TOTALH) hdrgen.h hdrgen.c +id.obj : $(TOTALH) id.h id.c +identifier.obj : $(TOTALH) identifier.h identifier.c +import.obj : $(TOTALH) dsymbol.h import.h import.c +inifile.obj : $(TOTALH) inifile.c +init.obj : $(TOTALH) init.h init.c +inline.obj : $(TOTALH) inline.c +interpret.obj : $(TOTALH) interpret.c declaration.h expression.h +intrange.obj : $(TOTALH) intrange.h intrange.c +json.obj : $(TOTALH) json.h json.c +lexer.obj : $(TOTALH) lexer.c +libomf.obj : $(TOTALH) lib.h libomf.c +link.obj : $(TOTALH) link.c +macro.obj : $(TOTALH) macro.h macro.c +mangle.obj : $(TOTALH) dsymbol.h declaration.h mangle.c +#module.obj : $(TOTALH) mars.h $C\html.h module.h module.c +opover.obj : $(TOTALH) expression.h opover.c +optimize.obj : $(TOTALH) expression.h optimize.c +parse.obj : $(TOTALH) attrib.h lexer.h parse.h parse.c +scope.obj : $(TOTALH) scope.h scope.c +sideeffect.obj : $(TOTALH) sideeffect.c +statement.obj : $(TOTALH) statement.h statement.c expression.h +staticassert.obj : $(TOTALH) staticassert.h staticassert.c +struct.obj : $(TOTALH) identifier.h enum.h struct.c +traits.obj : $(TOTALH) traits.c +dsymbol.obj : $(TOTALH) identifier.h dsymbol.h dsymbol.c +mtype.obj : $(TOTALH) mtype.h mtype.c +#typinf.obj : $(TOTALH) mtype.h typinf.c +utf.obj : utf.h utf.c +template.obj : $(TOTALH) template.h template.c +version.obj : $(TOTALH) identifier.h dsymbol.h cond.h version.h version.c + +################### Utilities ################ + +clean: + del *.obj + del total.sym + del msgs.h msgs.c + del elxxx.c cdxxx.c optab.c debtab.c fltables.c tytab.c + del impcnvtab.c + +zip : detab tolf $(MAKEFILES) + del dmdsrc.zip + zip32 dmdsrc $(MAKEFILES) + zip32 dmdsrc $(SRCS) + zip32 dmdsrc $(BACKSRC) + zip32 dmdsrc $(TKSRC) + zip32 dmdsrc $(ROOTSRC) + +################### Detab ################ + +detab: + detab $(SRCS) $(ROOTSRC) $(TKSRC) $(BACKSRC) + +tolf: + tolf $(SRCS) $(ROOTSRC) $(TKSRC) $(BACKSRC) $(MAKEFILES) + +################### Install ################ + +install: detab install2 + +install2: + copy dmd.exe $(DIR)\windows\bin\ + copy phobos\phobos.lib $(DIR)\windows\lib + $(CP) $(SRCS) $(DIR)\src\dmd\ + $(CP) $(ROOTSRC) $(DIR)\src\dmd\root\ + $(CP) $(TKSRC) $(DIR)\src\dmd\tk\ + $(CP) $(BACKSRC) $(DIR)\src\dmd\backend\ + $(CP) $(MAKEFILES) $(DIR)\src\dmd\ + copy gpl.txt $(DIR)\src\dmd\ + copy readme.txt $(DIR)\src\dmd\ + copy artistic.txt $(DIR)\src\dmd\ + copy backendlicense.txt $(DIR)\src\dmd\ + +################### Write to SVN ################ + +svn: detab tolf svn2 + +svn2: + $(CP) $(SRCS) $(DMDSVN)\ + $(CP) $(ROOTSRC) $(DMDSVN)\root\ + $(CP) $(TKSRC) $(DMDSVN)\tk\ + $(CP) $(BACKSRC) $(DMDSVN)\backend\ + $(CP) $(MAKEFILES) $(DMDSVN)\ + copy gpl.txt $(DMDSVN)\ + copy readme.txt $(DMDSVN)\ + copy artistic.txt $(DMDSVN)\ + copy backendlicense.txt $(DMDSVN)\ + +###################################