From caad8cde58410f8cfbc039522829ad053f943703 Mon Sep 17 00:00:00 2001 From: Alexey Prokhin Date: Thu, 5 Apr 2012 11:10:48 +0400 Subject: [PATCH] Squashed 'dmd2/' content from commit 10017d5 git-subtree-dir: dmd2 git-subtree-split: 10017d50eaaff4ecdc37a0153b6c37ea0b004c81 --- access.c | 412 ++ aggregate.h | 320 ++ aliasthis.c | 79 + aliasthis.h | 41 + apply.c | 165 + argtypes.c | 193 + arrayop.c | 648 +++ arraytypes.h | 77 + artistic.txt | 117 + attrib.c | 1524 ++++++ attrib.h | 189 + backend/aa.c | 498 ++ backend/aa.h | 109 + backend/backend.txt | 80 + backend/bcomplex.c | 292 + backend/bcomplex.h | 36 + backend/blockopt.c | 2204 ++++++++ backend/cc.h | 1557 ++++++ backend/cdef.h | 1104 ++++ backend/cdeflnx.h | 85 + backend/cg.c | 61 + backend/cg87.c | 3638 +++++++++++++ backend/cgcod.c | 2527 +++++++++ backend/cgcs.c | 683 +++ backend/cgcv.c | 2705 +++++++++ backend/cgcv.h | 76 + backend/cgelem.c | 4522 +++++++++++++++ backend/cgen.c | 781 +++ backend/cgobj.c | 3680 +++++++++++++ backend/cgreg.c | 1013 ++++ backend/cgsched.c | 3206 +++++++++++ backend/cgxmm.c | 780 +++ backend/cod1.c | 3990 ++++++++++++++ backend/cod2.c | 4997 +++++++++++++++++ backend/cod3.c | 6458 ++++++++++++++++++++++ backend/cod4.c | 3438 ++++++++++++ backend/cod5.c | 207 + backend/code.c | 136 + backend/code.h | 1049 ++++ backend/cppman.c | 746 +++ backend/cv4.h | 126 + backend/debug.c | 415 ++ backend/dt.c | 383 ++ backend/dt.h | 114 + backend/dwarf.c | 2531 +++++++++ backend/dwarf.h | 18 + backend/dwarf2.h | 470 ++ backend/ee.c | 124 + backend/el.c | 3326 ++++++++++++ backend/el.h | 219 + backend/elfobj.c | 3111 +++++++++++ backend/evalu8.c | 2089 +++++++ backend/exh.h | 47 + backend/gdag.c | 852 +++ backend/gflow.c | 1721 ++++++ backend/global.h | 589 ++ backend/glocal.c | 741 +++ backend/gloop.c | 3628 +++++++++++++ backend/go.c | 368 ++ backend/go.h | 98 + backend/gother.c | 1781 ++++++ backend/html.c | 771 +++ backend/html.h | 50 + backend/iasm.h | 430 ++ backend/mach.h | 323 ++ backend/machobj.c | 2757 ++++++++++ backend/md5.c | 277 + backend/md5.h | 61 + backend/melf.h | 381 ++ backend/newman.c | 1707 ++++++ backend/nteh.c | 915 ++++ backend/oper.h | 414 ++ backend/optabgen.c | 1131 ++++ backend/os.c | 953 ++++ backend/out.c | 1534 ++++++ backend/outbuf.c | 298 + backend/outbuf.h | 179 + backend/ptrntab.c | 5736 ++++++++++++++++++++ backend/rtlsym.c | 125 + backend/rtlsym.h | 155 + backend/strtold.c | 712 +++ backend/symbol.c | 2307 ++++++++ backend/tassert.h | 54 + backend/ti_achar.c | 61 + backend/ti_pvoid.c | 54 + backend/tinfo.h | 52 + backend/token.h | 458 ++ backend/ty.h | 377 ++ backend/type.c | 1480 +++++ backend/type.h | 209 + backend/var.c | 235 + backend/xmm.h | 314 ++ backendlicense.txt | 42 + builtin.c | 234 + canthrow.c | 189 + cast.c | 2574 +++++++++ class.c | 1648 ++++++ clone.c | 694 +++ complex_t.h | 74 + cond.c | 399 ++ cond.h | 105 + constfold.c | 1868 +++++++ cppmangle.c | 454 ++ declaration.c | 2258 ++++++++ declaration.h | 889 +++ delegatize.c | 140 + doc.c | 2226 ++++++++ doc.h | 20 + dsymbol.c | 1451 +++++ dsymbol.h | 348 ++ dump.c | 152 + e2ir.c | 5146 ++++++++++++++++++ eh.c | 344 ++ entity.c | 2391 ++++++++ enum.c | 425 ++ enum.h | 91 + expression.c | 12367 ++++++++++++++++++++++++++++++++++++++++++ expression.h | 1697 ++++++ func.c | 4069 ++++++++++++++ glue.c | 1234 +++++ gpl.txt | 248 + hdrgen.c | 100 + hdrgen.h | 34 + iasm.c | 4851 +++++++++++++++++ identifier.c | 102 + identifier.h | 40 + idgen.c | 412 ++ impcnvgen.c | 427 ++ imphint.c | 88 + import.c | 397 ++ import.h | 66 + inifile.c | 332 ++ init.c | 897 +++ init.h | 130 + inline.c | 1797 ++++++ interpret.c | 6496 ++++++++++++++++++++++ intrange.c | 1105 ++++ intrange.h | 149 + irstate.c | 191 + irstate.h | 59 + json.c | 462 ++ json.h | 24 + lexer.c | 3211 +++++++++++ lexer.h | 316 ++ lib.h | 54 + libelf.c | 773 +++ libmach.c | 798 +++ libomf.c | 959 ++++ link.c | 636 +++ macro.c | 449 ++ macro.h | 45 + mangle.c | 272 + mars.c | 1594 ++++++ mars.h | 441 ++ module.c | 1190 ++++ module.h | 194 + msc.c | 408 ++ mtype.c | 9040 ++++++++++++++++++++++++++++++ mtype.h | 992 ++++ objfile.h | 61 + opover.c | 1611 ++++++ optimize.c | 1182 ++++ parse.c | 6706 +++++++++++++++++++++++ parse.h | 187 + ph.c | 346 ++ posix.mak | 674 +++ readme.txt | 24 + root/aav.c | 188 + root/aav.h | 11 + root/array.c | 257 + root/async.c | 325 ++ root/async.h | 33 + root/dchar.c | 482 ++ root/dchar.h | 194 + root/dmgcmem.c | 499 ++ root/gc/bits.c | 43 + root/gc/bits.h | 83 + root/gc/gc.c | 2223 ++++++++ root/gc/gc.h | 68 + root/gc/gccbitops.h | 69 + root/gc/linux.c | 96 + root/gc/mscbitops.h | 25 + root/gc/os.h | 25 + root/gc/testgc.c | 300 + root/gc/win32.c | 72 + root/gnuc.c | 55 + root/gnuc.h | 8 + root/lstring.c | 63 + root/lstring.h | 72 + root/man.c | 100 + root/port.c | 792 +++ root/port.h | 81 + root/response.c | 290 + root/rmem.c | 155 + root/rmem.h | 51 + root/root.c | 2089 +++++++ root/root.h | 417 ++ root/speller.c | 257 + root/speller.h | 7 + root/stringtable.c | 139 + root/stringtable.h | 48 + root/thread.h | 12 + s2ir.c | 1722 ++++++ scope.c | 399 ++ scope.h | 122 + sideeffect.c | 250 + statement.c | 5100 +++++++++++++++++ statement.h | 904 +++ staticassert.c | 126 + staticassert.h | 41 + struct.c | 717 +++ template.c | 6189 +++++++++++++++++++++ template.h | 375 ++ tk.c | 30 + tk/filespec.c | 424 ++ tk/filespec.h | 136 + tk/list.c | 462 ++ tk/list.h | 291 + tk/mem.c | 901 +++ tk/mem.h | 271 + tk/vec.c | 655 +++ tk/vec.h | 78 + tocsym.c | 804 +++ toctype.c | 517 ++ tocvdebug.c | 821 +++ todt.c | 1051 ++++ toelfdebug.c | 103 + toir.c | 912 ++++ toir.h | 21 + toobj.c | 1408 +++++ total.h | 46 + traits.c | 549 ++ typinf.c | 949 ++++ unialpha.c | 323 ++ unittests.c | 17 + utf.c | 320 ++ utf.h | 35 + util.c | 342 ++ version.c | 181 + version.h | 51 + win32.mak | 583 ++ 241 files changed, 233638 insertions(+) create mode 100644 access.c create mode 100644 aggregate.h create mode 100644 aliasthis.c create mode 100644 aliasthis.h create mode 100644 apply.c create mode 100644 argtypes.c create mode 100644 arrayop.c create mode 100644 arraytypes.h create mode 100644 artistic.txt create mode 100644 attrib.c create mode 100644 attrib.h create mode 100644 backend/aa.c create mode 100644 backend/aa.h create mode 100644 backend/backend.txt create mode 100644 backend/bcomplex.c create mode 100644 backend/bcomplex.h create mode 100644 backend/blockopt.c create mode 100644 backend/cc.h create mode 100644 backend/cdef.h create mode 100644 backend/cdeflnx.h create mode 100644 backend/cg.c create mode 100644 backend/cg87.c create mode 100644 backend/cgcod.c create mode 100644 backend/cgcs.c create mode 100644 backend/cgcv.c create mode 100644 backend/cgcv.h create mode 100644 backend/cgelem.c create mode 100644 backend/cgen.c create mode 100644 backend/cgobj.c create mode 100644 backend/cgreg.c create mode 100644 backend/cgsched.c create mode 100644 backend/cgxmm.c create mode 100644 backend/cod1.c create mode 100644 backend/cod2.c create mode 100644 backend/cod3.c create mode 100644 backend/cod4.c create mode 100644 backend/cod5.c create mode 100644 backend/code.c create mode 100644 backend/code.h create mode 100644 backend/cppman.c create mode 100644 backend/cv4.h create mode 100644 backend/debug.c create mode 100644 backend/dt.c create mode 100644 backend/dt.h create mode 100644 backend/dwarf.c create mode 100644 backend/dwarf.h create mode 100644 backend/dwarf2.h create mode 100644 backend/ee.c create mode 100644 backend/el.c create mode 100644 backend/el.h create mode 100644 backend/elfobj.c create mode 100644 backend/evalu8.c create mode 100644 backend/exh.h create mode 100644 backend/gdag.c create mode 100644 backend/gflow.c create mode 100644 backend/global.h create mode 100644 backend/glocal.c create mode 100644 backend/gloop.c create mode 100644 backend/go.c create mode 100644 backend/go.h create mode 100644 backend/gother.c create mode 100644 backend/html.c create mode 100644 backend/html.h create mode 100644 backend/iasm.h create mode 100644 backend/mach.h create mode 100644 backend/machobj.c create mode 100644 backend/md5.c create mode 100644 backend/md5.h create mode 100644 backend/melf.h create mode 100644 backend/newman.c create mode 100644 backend/nteh.c create mode 100644 backend/oper.h create mode 100644 backend/optabgen.c create mode 100644 backend/os.c create mode 100644 backend/out.c create mode 100644 backend/outbuf.c create mode 100644 backend/outbuf.h create mode 100644 backend/ptrntab.c create mode 100644 backend/rtlsym.c create mode 100644 backend/rtlsym.h create mode 100644 backend/strtold.c create mode 100644 backend/symbol.c create mode 100644 backend/tassert.h create mode 100644 backend/ti_achar.c create mode 100644 backend/ti_pvoid.c create mode 100644 backend/tinfo.h create mode 100644 backend/token.h create mode 100644 backend/ty.h create mode 100644 backend/type.c create mode 100644 backend/type.h create mode 100644 backend/var.c create mode 100644 backend/xmm.h create mode 100644 backendlicense.txt create mode 100644 builtin.c create mode 100644 canthrow.c create mode 100644 cast.c create mode 100644 class.c create mode 100644 clone.c create mode 100644 complex_t.h create mode 100644 cond.c create mode 100644 cond.h create mode 100644 constfold.c create mode 100644 cppmangle.c create mode 100644 declaration.c create mode 100644 declaration.h create mode 100644 delegatize.c create mode 100644 doc.c create mode 100644 doc.h create mode 100644 dsymbol.c create mode 100644 dsymbol.h create mode 100644 dump.c create mode 100644 e2ir.c create mode 100644 eh.c create mode 100644 entity.c create mode 100644 enum.c create mode 100644 enum.h create mode 100644 expression.c create mode 100644 expression.h create mode 100644 func.c create mode 100644 glue.c create mode 100644 gpl.txt create mode 100644 hdrgen.c create mode 100644 hdrgen.h create mode 100644 iasm.c create mode 100644 identifier.c create mode 100644 identifier.h create mode 100644 idgen.c create mode 100644 impcnvgen.c create mode 100644 imphint.c create mode 100644 import.c create mode 100644 import.h create mode 100644 inifile.c create mode 100644 init.c create mode 100644 init.h create mode 100644 inline.c create mode 100644 interpret.c create mode 100644 intrange.c create mode 100644 intrange.h create mode 100644 irstate.c create mode 100644 irstate.h create mode 100644 json.c create mode 100644 json.h create mode 100644 lexer.c create mode 100644 lexer.h create mode 100644 lib.h create mode 100644 libelf.c create mode 100644 libmach.c create mode 100644 libomf.c create mode 100644 link.c create mode 100644 macro.c create mode 100644 macro.h create mode 100644 mangle.c create mode 100644 mars.c create mode 100644 mars.h create mode 100644 module.c create mode 100644 module.h create mode 100644 msc.c create mode 100644 mtype.c create mode 100644 mtype.h create mode 100644 objfile.h create mode 100644 opover.c create mode 100644 optimize.c create mode 100644 parse.c create mode 100644 parse.h create mode 100644 ph.c create mode 100644 posix.mak create mode 100644 readme.txt create mode 100644 root/aav.c create mode 100644 root/aav.h create mode 100644 root/array.c create mode 100644 root/async.c create mode 100644 root/async.h create mode 100644 root/dchar.c create mode 100644 root/dchar.h create mode 100644 root/dmgcmem.c create mode 100644 root/gc/bits.c create mode 100644 root/gc/bits.h create mode 100644 root/gc/gc.c create mode 100644 root/gc/gc.h create mode 100644 root/gc/gccbitops.h create mode 100644 root/gc/linux.c create mode 100644 root/gc/mscbitops.h create mode 100644 root/gc/os.h create mode 100644 root/gc/testgc.c create mode 100644 root/gc/win32.c create mode 100644 root/gnuc.c create mode 100644 root/gnuc.h create mode 100644 root/lstring.c create mode 100644 root/lstring.h create mode 100644 root/man.c create mode 100644 root/port.c create mode 100644 root/port.h create mode 100644 root/response.c create mode 100644 root/rmem.c create mode 100644 root/rmem.h create mode 100644 root/root.c create mode 100644 root/root.h create mode 100644 root/speller.c create mode 100644 root/speller.h create mode 100644 root/stringtable.c create mode 100644 root/stringtable.h create mode 100644 root/thread.h create mode 100644 s2ir.c create mode 100644 scope.c create mode 100644 scope.h create mode 100644 sideeffect.c create mode 100644 statement.c create mode 100644 statement.h create mode 100644 staticassert.c create mode 100644 staticassert.h create mode 100644 struct.c create mode 100644 template.c create mode 100644 template.h create mode 100644 tk.c create mode 100644 tk/filespec.c create mode 100644 tk/filespec.h create mode 100644 tk/list.c create mode 100644 tk/list.h create mode 100644 tk/mem.c create mode 100644 tk/mem.h create mode 100644 tk/vec.c create mode 100644 tk/vec.h create mode 100644 tocsym.c create mode 100644 toctype.c create mode 100644 tocvdebug.c create mode 100644 todt.c create mode 100644 toelfdebug.c create mode 100644 toir.c create mode 100644 toir.h create mode 100644 toobj.c create mode 100644 total.h create mode 100644 traits.c create mode 100644 typinf.c create mode 100644 unialpha.c create mode 100644 unittests.c create mode 100644 utf.c create mode 100644 utf.h create mode 100644 util.c create mode 100644 version.c create mode 100644 version.h create mode 100644 win32.mak 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)\ + +###################################