Annotation of 42BSD/usr.bin/f77/src/f77pass1/optloop.c, revision 1.1

1.1     ! root        1: #include "defs.h"
        !             2: #include "optim.h"
        !             3: 
        !             4: 
        !             5: #define SCFREE   0
        !             6: #define SCSAFE   1
        !             7: 
        !             8: 
        !             9: 
        !            10: typedef
        !            11:   struct varblock
        !            12:     {
        !            13:       struct varblock *next;
        !            14:       field vstg;
        !            15:       int memno;       /* holds memalloc for TTEMP */
        !            16:       short sets;
        !            17:       short uses;
        !            18:       field setfirst;
        !            19:     } VARBLOCK;
        !            20: 
        !            21: typedef VARBLOCK *Varp;
        !            22: 
        !            23: #define TABLESIZE 59
        !            24: 
        !            25: LOCAL Varp table[TABLESIZE];
        !            26: 
        !            27: 
        !            28: 
        !            29: LOCAL Varp mkbucket(vstg,memno)
        !            30: field vstg;
        !            31: int memno;
        !            32: 
        !            33: {
        !            34:   Varp q;
        !            35: 
        !            36:   q = ALLOC(varblock);
        !            37:   q->vstg = vstg;
        !            38:   q->memno = memno;
        !            39:   return q;
        !            40: }
        !            41: 
        !            42: 
        !            43: 
        !            44: LOCAL Varp lookup(p)
        !            45: tagptr p;
        !            46: 
        !            47: {
        !            48: int vstg, memno;
        !            49: int key;
        !            50: Varp q, r;
        !            51: 
        !            52: switch (p->tag)
        !            53:        {
        !            54:        case TTEMP:
        !            55:                vstg = 0;
        !            56:                memno = (int) p->tempblock.memalloc;
        !            57:                break;
        !            58: 
        !            59:        case TADDR:
        !            60:                vstg = p->addrblock.vstg;
        !            61:                memno = p->addrblock.memno;
        !            62:                break;
        !            63: 
        !            64:        default:
        !            65:                badtag ("lookup",p->tag);
        !            66:        }
        !            67: key = memno % TABLESIZE;
        !            68: q = table[key];
        !            69: 
        !            70: if (q)
        !            71:        {
        !            72:        for (; q; r = q, q = q->next)
        !            73:                if ((q->vstg == vstg) && (q->memno == memno))
        !            74:                        return q;
        !            75:        return r->next = mkbucket(vstg,memno);
        !            76:        }
        !            77: else
        !            78:        return table[key] = mkbucket(vstg,memno);
        !            79: }
        !            80: 
        !            81: 
        !            82: 
        !            83: LOCAL freetable()
        !            84: 
        !            85: {
        !            86:   int i;
        !            87:   Varp p, q;
        !            88: 
        !            89:   for (i = 0; i < TABLESIZE; i++)
        !            90:     if (table[i])
        !            91:       {
        !            92:        p = table[i];
        !            93:        table[i] = NULL;
        !            94: 
        !            95:        while (p)
        !            96:          {
        !            97:            q = p->next;
        !            98:            free((char *) p);
        !            99:            p = q;
        !           100:          }
        !           101:       }
        !           102: }
        !           103: 
        !           104: 
        !           105: 
        !           106: Slotp newcode;
        !           107: Slotp dohead, doend;
        !           108: LOCAL Slotp first, last;
        !           109: LOCAL commonset;
        !           110: LOCAL int comocount;   /* count of number of code motions done */
        !           111: 
        !           112: 
        !           113: optloops()
        !           114: 
        !           115: {
        !           116: int    match;
        !           117: Slotp  nextslot;
        !           118: Slotp  sl1,sl2;
        !           119: Slotp  lastlabslot;
        !           120: int    lab;
        !           121: 
        !           122: if (! optimflag) return;
        !           123: if (debugflag[6]) return;
        !           124: 
        !           125: lastlabslot = NULL;
        !           126: comocount = 0;
        !           127: for (sl1 = firstslot; sl1; sl1 = nextslot)
        !           128:        {
        !           129:        nextslot = sl1->next;
        !           130:        switch (sl1->type)
        !           131:            {
        !           132:            case SKLABEL:
        !           133:                lastlabslot = sl1;
        !           134:                break;
        !           135: 
        !           136:            case SKGOTO:
        !           137:                if (lastlabslot && sl1->label == lastlabslot->label)
        !           138:                        {
        !           139:                        lab = newlabel ();
        !           140:                        first = optinsert (SKLABEL,0,lab,0,lastlabslot->next);
        !           141:                        last = sl1;
        !           142:                        last->label = lab;
        !           143:                        optloop ();
        !           144:                        }
        !           145:                break;
        !           146: 
        !           147:            case SKDOHEAD:
        !           148:                match = 0;
        !           149:                for (sl2 = sl1; sl2; sl2 = sl2->next)
        !           150:                        {
        !           151:                        if (sl2->type == SKDOHEAD) match++;
        !           152:                        else if (sl2->type == SKENDDO) match--;
        !           153:                        if (match == 0) break;
        !           154:                        }
        !           155:                if (sl2)
        !           156:                        last = sl2;
        !           157:                else
        !           158:                        fatal ("unmatched do in code buffer");
        !           159:                if (sl2->type != SKENDDO)
        !           160:                        fatal ("internal error in optloops");
        !           161: 
        !           162:                /*  last now points to the SKENDDO slot; the SKNULL slot
        !           163:                 *  is reached through last->nullslot
        !           164:                 */
        !           165:                last = (Slotp) last->nullslot;
        !           166: 
        !           167:                first = sl1;
        !           168: 
        !           169:                optloop ();
        !           170:                break;
        !           171: 
        !           172:            default:
        !           173:                break;
        !           174:            }
        !           175:        }
        !           176: 
        !           177: if (debugflag[0])
        !           178:        fprintf (diagfile,"%d code motion%s performed\n",comocount,
        !           179:                (comocount==1 ? "" : "s") );
        !           180: return;
        !           181: }
        !           182: 
        !           183: 
        !           184: 
        !           185: optloop()
        !           186: 
        !           187: {
        !           188: newcode = NULL;
        !           189: 
        !           190: modify();
        !           191: 
        !           192: return;
        !           193: }
        !           194: 
        !           195: 
        !           196: LOCAL modify()
        !           197: 
        !           198: {
        !           199:   Slotp sp;
        !           200:   int s;
        !           201: 
        !           202:   scanvars();
        !           203: 
        !           204:   for (sp = first; sp != last->next; sp = sp->next)
        !           205:     switch (sp->type)
        !           206:       {
        !           207:       case SKEQ:
        !           208:        s = anex(sp->expr);
        !           209:        if (s == SCSAFE)
        !           210:          removesafe (&sp->expr);
        !           211:        break;
        !           212: 
        !           213:       case SKARIF:
        !           214:       case SKASGOTO:
        !           215:       case SKCALL:
        !           216:       case SKCMGOTO:
        !           217:       case SKIFN:
        !           218:       case SKSTOP:
        !           219:       case SKRETURN:
        !           220:       case SKPAUSE:
        !           221:       case SKIOIFN:
        !           222:        s = anex(sp->expr);
        !           223:        if (s == SCSAFE)
        !           224:          removesafe(&sp->expr);
        !           225:        break;
        !           226: 
        !           227:       default:
        !           228:        break;
        !           229:       }
        !           230: 
        !           231:   freetable();
        !           232:   return;
        !           233: }
        !           234: 
        !           235: 
        !           236: LOCAL scanvars()
        !           237: 
        !           238: {
        !           239:   Slotp sp;
        !           240:   Varp varinfo;
        !           241:   int i;
        !           242:   Varp p;
        !           243: 
        !           244:   commonset = NO;
        !           245: 
        !           246:   for (sp = first; sp != last->next; sp = sp->next)
        !           247:     {
        !           248:       switch (sp->type)
        !           249:        {
        !           250:        case SKARIF:
        !           251:        case SKASGOTO:
        !           252:        case SKCALL:
        !           253:        case SKCMGOTO:
        !           254:        case SKIFN:
        !           255:        case SKSTOP:
        !           256:        case SKRETURN:
        !           257:        case SKPAUSE:
        !           258:        case SKIOIFN:
        !           259:        case SKEQ:
        !           260:          setsuses(sp->expr);
        !           261:          break;
        !           262: 
        !           263:        default:
        !           264:          break;
        !           265:        }
        !           266:     }
        !           267: 
        !           268:   if (commonset)
        !           269:     for (i = 0; i < TABLESIZE; i++)
        !           270:       for (p = table[i]; p; p = p->next)
        !           271:        if (p->vstg == STGCOMMON)
        !           272:          {
        !           273:            p->sets++;
        !           274:            p->setfirst = NO;
        !           275:          }
        !           276: }
        !           277: 
        !           278: 
        !           279: LOCAL setsuses(p)
        !           280: expptr p;
        !           281: 
        !           282: {
        !           283:   Addrp lhs;
        !           284:   Varp varinfo;
        !           285:   chainp args;
        !           286: 
        !           287:   if (!p) return;
        !           288: 
        !           289:   switch (p->tag)
        !           290:     {
        !           291:     case TEXPR:
        !           292:       switch (p->exprblock.opcode)
        !           293:        {
        !           294:        default:
        !           295:          setsuses(p->exprblock.leftp);
        !           296:          setsuses(p->exprblock.rightp);
        !           297:          setsuses(p->exprblock.vleng);
        !           298:          break;
        !           299: 
        !           300:        case OPASSIGN:
        !           301:          switch (p->exprblock.leftp->tag)
        !           302:            {
        !           303:            case TTEMP:
        !           304:              lhs = (Addrp) p->exprblock.leftp;
        !           305:              goto taddr;
        !           306: 
        !           307:            case TADDR:
        !           308:              lhs = (Addrp) p->exprblock.leftp;
        !           309:              setsuses(lhs->memoffset);
        !           310:              setsuses(lhs->vleng);
        !           311:            taddr:
        !           312:              setsuses(p->exprblock.rightp);
        !           313:              setsuses(p->exprblock.vleng);
        !           314:              varinfo = lookup(lhs);
        !           315:              varinfo->sets++;
        !           316:               if (varinfo->uses == 0)
        !           317:                varinfo->setfirst = YES;
        !           318:              break;
        !           319: 
        !           320:            default:
        !           321:              fatal("O6:  l-value expected");
        !           322:            }
        !           323:          break;
        !           324: 
        !           325:        case OPSTAREQ:
        !           326:        case OPPLUSEQ:
        !           327:          switch (p->exprblock.leftp->tag)
        !           328:            {
        !           329:            case TADDR:
        !           330:              lhs = (Addrp) p->exprblock.leftp;
        !           331:              break;
        !           332:            case TTEMP:
        !           333:              lhs = (Addrp) p->exprblock.leftp;
        !           334:              break;
        !           335:            default:
        !           336:              fatal("O7:  l-value expected");
        !           337:            }
        !           338:          setsuses(p->exprblock.leftp);
        !           339:          setsuses(p->exprblock.rightp);
        !           340:          setsuses(p->exprblock.vleng);
        !           341:          varinfo = lookup(lhs);
        !           342:          varinfo->sets++;
        !           343:          break;
        !           344: 
        !           345:        case OPCALL:
        !           346:          if (p->exprblock.leftp->tag != TADDR)
        !           347:            fatal("O8:  subprogram expected");
        !           348:          setsuses(p->exprblock.rightp);
        !           349:          setsuses(p->exprblock.vleng);
        !           350:          if (p->exprblock.leftp->addrblock.vstg != STGEXT) break;
        !           351:          commonset = YES;
        !           352:          if (p->exprblock.rightp == NULL) break;
        !           353:          args = p->exprblock.rightp->listblock.listp;
        !           354:          for (; args; args = args->nextp)
        !           355:            if (args->datap->tag == TADDR)
        !           356:              {
        !           357:                lhs = (Addrp) args->datap;
        !           358:                switch (lhs->vstg)
        !           359:                  {
        !           360:                  case STGARG:
        !           361:                  case STGAUTO:
        !           362:                  case STGBSS:
        !           363:                  case STGINIT:
        !           364:                  case STGCOMMON:
        !           365:                  case STGEQUIV:
        !           366:                  case STGREG:
        !           367:                  case STGPREG:
        !           368:                    varinfo = lookup(lhs);
        !           369:                    varinfo->sets++;
        !           370:                  }
        !           371:              }
        !           372:            else if (args->datap->tag == TTEMP)
        !           373:              {
        !           374:                lhs = (Addrp) args->datap;
        !           375:                varinfo = lookup (lhs);
        !           376:                varinfo->sets++;
        !           377:              }
        !           378:          break;
        !           379:         }
        !           380: 
        !           381:       return;
        !           382: 
        !           383:     case TTEMP:
        !           384:       varinfo = lookup((Addrp) p);
        !           385:       varinfo->uses++;
        !           386:       return;
        !           387: 
        !           388:     case TADDR:
        !           389:       setsuses(p->addrblock.memoffset);
        !           390:       setsuses(p->addrblock.vleng);
        !           391:       varinfo = lookup((Addrp) p);
        !           392:       varinfo->uses++;
        !           393:       return;
        !           394: 
        !           395:     case TLIST:
        !           396:       for (args = p->listblock.listp; args; args = args->nextp)
        !           397:        setsuses(args->datap);
        !           398: 
        !           399:     case TCONST:
        !           400:     case TERROR:
        !           401:       return;
        !           402: 
        !           403:     default:
        !           404:       fatal("O9:  bad tag value");
        !           405:     }
        !           406: }
        !           407: 
        !           408: 
        !           409: LOCAL int anex(p)
        !           410: expptr p;
        !           411: 
        !           412: {
        !           413:   int s1, s2, s3;
        !           414:   expptr q;
        !           415:   Varp varinfo;
        !           416:   chainp ch;
        !           417:   int setfirst;
        !           418:   expptr expr;
        !           419: 
        !           420: 
        !           421:   if (p == ENULL)
        !           422:     return SCSAFE;
        !           423: 
        !           424:   switch (p->tag)
        !           425:     {
        !           426:     case TCONST:
        !           427:       return SCSAFE;
        !           428: 
        !           429:     case TLIST:
        !           430:       for (ch = p->listblock.listp; ch; ch = ch->nextp)
        !           431:        {
        !           432:          s1 = anex (ch->datap);
        !           433:          if (s1 == SCSAFE)
        !           434:            removesafe (&ch->datap);
        !           435:        }
        !           436:       return SCFREE;
        !           437: 
        !           438:     case TEXPR:
        !           439:       s1 = anex(p->exprblock.leftp);
        !           440:       s2 = anex(p->exprblock.rightp);
        !           441:       s3 = anex(p->exprblock.vleng);
        !           442: 
        !           443:       switch (p->exprblock.opcode)
        !           444:        {
        !           445:        case OPASSIGN:
        !           446:          expr = p->exprblock.leftp;
        !           447:          varinfo = lookup(expr);
        !           448:          setfirst = varinfo->setfirst && (varinfo->sets == 1);
        !           449:          if (expr->tag == TTEMP && setfirst &&
        !           450:                s2 == SCSAFE && s3 == SCSAFE)
        !           451:            {
        !           452:              movefrtemp (expr);
        !           453:              return SCSAFE;
        !           454:            }
        !           455:          else
        !           456:            {
        !           457:              if (s2 == SCSAFE) removesafe (&p->exprblock.rightp);
        !           458:              if (s3 == SCSAFE) removesafe (&p->exprblock.vleng);
        !           459:              return SCFREE;
        !           460:            }
        !           461: 
        !           462:        case OPNEG:
        !           463:        case OPNOT:
        !           464:        case OPABS:
        !           465:        case OPADDR:
        !           466:        case OPBITNOT:
        !           467:          if ((s2 == SCSAFE) && (s3 == SCSAFE))
        !           468:            return s1;
        !           469:          else
        !           470:            return SCFREE;
        !           471: 
        !           472:        case OPCONV:
        !           473:          if ((s2 != SCSAFE) || (s3 != SCSAFE))
        !           474:            return SCFREE;
        !           475: 
        !           476:          if (ISINT(p->exprblock.vtype))
        !           477:            return s1;
        !           478:          if (ISINT(p->exprblock.leftp->headblock.vtype))
        !           479:            return s1;
        !           480: 
        !           481:          return SCFREE;
        !           482: 
        !           483: 
        !           484:        case OPSTAR:
        !           485:          if (ISINT(p->exprblock.vtype))
        !           486:            goto safeop;
        !           487: 
        !           488:          if (safefactor(p->exprblock.leftp) ||
        !           489:              safefactor(p->exprblock.rightp))
        !           490:            goto safeop;
        !           491: 
        !           492:          goto floatop;
        !           493: 
        !           494: 
        !           495:        case OPPLUS:
        !           496:        case OPMINUS:
        !           497:          if (ISINT(p->exprblock.vtype))
        !           498:            goto safeop;
        !           499: 
        !           500:        floatop:
        !           501:          if (!(ISREAL(p->exprblock.vtype) || ISCOMPLEX(p->exprblock.vtype)))
        !           502:            return SCFREE;
        !           503: 
        !           504:          switch (s1)
        !           505:            {
        !           506:            case SCSAFE:
        !           507:              removesafe(&p->exprblock.leftp);
        !           508:              if (s2 == SCSAFE)
        !           509:                removesafe(&p->exprblock.leftp);
        !           510:              return SCFREE;
        !           511: 
        !           512:            case SCFREE:
        !           513:              if (s2 == SCSAFE)
        !           514:                removesafe(&p->exprblock.rightp);
        !           515:              return SCFREE;
        !           516:            }
        !           517: 
        !           518:        case OPOR:
        !           519:        case OPAND:
        !           520:        case OPEQV:
        !           521:        case OPNEQV:
        !           522:        case OPLT:
        !           523:        case OPEQ:
        !           524:        case OPGT:
        !           525:        case OPLE:
        !           526:        case OPNE:
        !           527:        case OPGE:
        !           528:        case OPLSHIFT:
        !           529:        case OPMIN:
        !           530:        case OPMAX:
        !           531:        case OPBITOR:
        !           532:        case OPBITAND:
        !           533:        case OPBITXOR:
        !           534:        case OPRSHIFT:
        !           535:        safeop:
        !           536:          if ((p->exprblock.vleng != ENULL) && ( ! ISCONST(p->exprblock.vleng)))
        !           537:            return SCFREE;
        !           538: 
        !           539:          switch (s1)
        !           540:            {
        !           541:            case SCSAFE:
        !           542:                if (s2 == SCFREE) removesafe (&p->exprblock.leftp);
        !           543:                return s2;
        !           544: 
        !           545:            case SCFREE:
        !           546:                if (s2 == SCSAFE) removesafe (&p->exprblock.rightp);
        !           547:                return SCFREE;
        !           548:            }
        !           549: 
        !           550:        default:
        !           551:          if (s1 == SCSAFE) removesafe(&p->exprblock.leftp);
        !           552:          if (s2 == SCSAFE) removesafe(&p->exprblock.rightp);
        !           553:          if (s3 == SCSAFE) removesafe(&p->exprblock.vleng);
        !           554:          return SCFREE;
        !           555:        }
        !           556: 
        !           557: 
        !           558:     case TTEMP:
        !           559:       varinfo = lookup(p);
        !           560:       if (varinfo->sets == 0)
        !           561:        return SCSAFE;
        !           562:       else
        !           563:        return SCFREE;
        !           564: 
        !           565:     case TADDR:
        !           566:       s1 = anex(p->addrblock.memoffset);
        !           567:       s2 = anex(p->addrblock.vleng);
        !           568: 
        !           569:       varinfo = lookup(p);
        !           570: 
        !           571:       if (varinfo->sets == 0)
        !           572:        switch (s1)
        !           573:          {
        !           574:          case SCSAFE:
        !           575:                if (s2 == SCFREE) removesafe(&p->addrblock.memoffset);
        !           576:                return s2;
        !           577: 
        !           578:          case SCFREE:
        !           579:                if (s2 == SCSAFE) removesafe(&p->addrblock.vleng);
        !           580:                return SCFREE;
        !           581:          }
        !           582: 
        !           583:       if (s1 == SCSAFE) removesafe(&p->addrblock.memoffset);
        !           584:       if (s2 == SCSAFE) removesafe(&p->addrblock.vleng);
        !           585:       return SCFREE;
        !           586:     
        !           587:        
        !           588:     default:
        !           589:       return SCFREE;
        !           590:     }
        !           591: }
        !           592: 
        !           593: 
        !           594: LOCAL safefactor(p)
        !           595: expptr p;
        !           596: 
        !           597: {
        !           598:   if ( ! ISCONST(p))
        !           599:     return NO;
        !           600: 
        !           601:   if (ISINT(p->constblock.vtype))
        !           602:     if (abs(p->constblock.const.ci) <= 1)
        !           603:       return YES;
        !           604: 
        !           605:   if (ISREAL(p->constblock.vtype))
        !           606:     if (abs(p->constblock.const.cd[0]) <= 1.0)
        !           607:       return YES;
        !           608: 
        !           609:   return NO;
        !           610: }
        !           611: 
        !           612: 
        !           613: LOCAL int worthcost(p)
        !           614: expptr p;
        !           615: 
        !           616: {
        !           617:   int cost;
        !           618:   chainp q;
        !           619:   expptr memoffset,vleng;
        !           620: 
        !           621:   if (p == ENULL)
        !           622:     return NO;
        !           623: 
        !           624:   switch (p->tag)
        !           625:     {
        !           626:     case TCONST:
        !           627:       return NO;
        !           628: 
        !           629:     case TTEMP:
        !           630:       return NO;
        !           631: 
        !           632:     case TADDR:
        !           633:       if ((memoffset = p->addrblock.memoffset) && ! ISCONST(memoffset))
        !           634:        return YES;
        !           635:       else if ((vleng = p->addrblock.vleng) && ! ISCONST(vleng))
        !           636:        return YES;
        !           637:       else
        !           638:        return NO;
        !           639: 
        !           640:     case TEXPR:
        !           641:       return YES;
        !           642: 
        !           643:     case TLIST:
        !           644:       cost = 0;
        !           645:       for (q = p->listblock.listp; q; q = q->nextp)
        !           646:        {
        !           647:        if (worthcost ((expptr) q->datap))
        !           648:          return YES;
        !           649:        cost++;
        !           650:        }
        !           651:       return (cost>2 ? YES : NO);
        !           652: 
        !           653:     default:
        !           654:       return NO;
        !           655:     }
        !           656: }
        !           657: 
        !           658: 
        !           659: LOCAL removesafe(refexpr)
        !           660: expptr *refexpr;
        !           661: 
        !           662: {
        !           663:   expptr ep;
        !           664:   Tempp ap;
        !           665:   Slotp newslot;
        !           666: 
        !           667:   extern Addrp gettemp();
        !           668: 
        !           669:   ep = *refexpr;
        !           670:   if (! worthcost(ep))
        !           671:     return;
        !           672: 
        !           673:   if (ep->tag == TEXPR && ep->exprblock.opcode == OPASSIGN)
        !           674:     {
        !           675:       if (ep->exprblock.leftp->tag != TTEMP)
        !           676:        fatal ("non-TEMP in assignment to be moved in optloop");
        !           677: 
        !           678:       newslot = optinsert (SKEQ, ep, 0, 0, first);
        !           679:       *refexpr = ep->exprblock.leftp;
        !           680:     }
        !           681:   else
        !           682:     {
        !           683:       ap = (Tempp) gettemp(ep);
        !           684:       newslot = optinsert (SKEQ, mkexpr(OPASSIGN,cpexpr(ap),ep), 0, 0, first);
        !           685:       *refexpr = (expptr) ap;
        !           686:       optinsert (SKFRTEMP,ap->memalloc,0,0,last->next);
        !           687:     }
        !           688: 
        !           689:   comocount++;
        !           690:   if (!newcode)
        !           691:     newcode = newslot;
        !           692: 
        !           693:   return;
        !           694: }
        !           695: 
        !           696: 
        !           697: LOCAL Addrp gettemp(p)
        !           698: expptr p;
        !           699: 
        !           700: {
        !           701:   return mktemp(p->headblock.vtype, p->headblock.vleng);
        !           702: }
        !           703: 
        !           704: 
        !           705: 
        !           706: LOCAL movefrtemp (expr)
        !           707: Tempp  expr;
        !           708: 
        !           709: {
        !           710:   Slotp        s;
        !           711: 
        !           712:   if (expr->tag != TTEMP)
        !           713:     badtag ("movefrtemp",expr->tag);
        !           714: 
        !           715:   for (s = first; s; s = s->next)
        !           716:     if (s->type == SKFRTEMP && s->expr == (expptr) expr->memalloc)
        !           717:       {
        !           718:        removeslot (s);
        !           719:        insertslot (s,last->next);
        !           720:        return;
        !           721:       }
        !           722: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.