|
|
1.1 root 1: /* @(#) optim.c: 1.3 1/12/84 */
2:
3: # include "mfile1.h"
4:
5: # define ISCON(p) (p->in.op==ICON)
6:
7: int opdebug = 0;
8: NODE *doptim();
9:
10: NODE *
11: aadjust( p, adj )
12: register NODE *p;
13: register adj;
14: {
15: /* try to adjust p by adj bits */
16: register NODE *q;
17: adj = BITOOR(adj);
18: switch( p->tn.op )
19: {
20: case ICON:
21: p->tn.lval += adj;
22: return( p );
23: default:
24: /* construct a + node */
25: mkplus:
26: return( block( PLUS, p, bcon(adj), p->fn.type,
27: p->fn.cdim, p->fn.csiz ) );
28: case PLUS:
29: q = p->in.right;
30: if( q->tn.op != ICON ) goto mkplus;
31: q->tn.lval += adj;
32: return( p );
33: case MINUS:
34: q = p->in.right;
35: if( q->tn.op != ICON ) goto mkplus;
36: q->tn.lval -= adj;
37: return( p );
38: }
39: }
40:
41: adjust( p, adj )
42: register NODE *p;
43: register adj;
44: {
45: /* handle adjustment of scalars by adj bits */
46:
47: switch( p->tn.op )
48: {
49: case NAME:
50: case VAUTO:
51: case VPARAM:
52: p->tn.lval += BITOOR(adj);
53: return( 1 );
54: case STAR:
55: p->in.left = aadjust( p->in.left, adj );
56: return( 1 );
57: default:
58: return( 0 );
59: }
60: }
61:
62: # ifdef NOSIMPSTR
63: # ifndef MYSIMPSTR
64: simpstr( d, s )
65: {
66: return( STRTY );
67: }
68: # endif
69: # else
70: TWORD
71: simpstr( d, s )
72: {
73: /* return STRTY if not, and CHAR, INT, SHORT, or LONG if simple */
74: register sz, al;
75:
76: sz = tsize( STRTY, d, s );
77: al = talign( STRTY, s );
78: if( sz == SZINT && !( al % ALINT) ) return( INT );
79: else if( sz == SZCHAR && !( al % ALCHAR) ) return( CHAR );
80: else if( sz == SZLONG && !( al % ALLONG) ) return( LONG );
81: else if( sz == SZSHORT && !( al % ALSHORT) ) return( SHORT );
82: return( STRTY );
83: }
84: # endif
85:
86: tydown( p )
87: NODE *p;
88: {
89: /* reflect the type of p downwards, as appropriate */
90: /* the type is typically getting smaller */
91: /* returns 1 if it makes a real change */
92:
93: TWORD t;
94: NODE *l, *r;
95: int flag;
96:
97: #ifndef NODBG
98: if( opdebug )
99: {
100: printf( "tydown(%d) called with:\n", p-node );
101: eprint( p );
102: }
103: #endif
104: t = p->tn.type;
105: if( ISPTR(t) || ISARY(t) ) return(0);
106:
107: /* work these types down into the tree */
108:
109: flag = 0;
110:
111: switch( p->tn.op )
112: {
113:
114: case AND:
115: case OR:
116: case PLUS:
117: case MINUS:
118: case ER:
119: case COMOP:
120: #ifndef NODBG
121: if( opdebug )
122: {
123: printf( "tydown:\n" );
124: eprint( p );
125: }
126: #endif
127: r = p->in.right;
128: if( bigsize(r->tn.type) > bigsize(p->tn.type ) )
129: {
130: r = makety( r, t, 0, (int) t );
131: tydown( r );
132: p->in.right = doptim( r );
133: #ifndef NODBG
134: if( opdebug )
135: {
136: printf( "tydown(%d), after doptim(R):\n",
137: p-node );
138: eprint(p);
139: }
140: #endif
141: flag = 1;
142: }
143: if( p->tn.op == COMOP ) return( flag );
144: /* FALLTHRU */
145:
146: case UNARY MINUS:
147: case COMPL:
148: l = p->in.left;
149: if( bigsize(l->tn.type) > bigsize(p->tn.type ) )
150: {
151: l = makety( l, t, 0, (int) t );
152: tydown( l );
153: p->in.left = doptim( l );
154: #ifndef NODBG
155: if( opdebug )
156: {
157: printf( "tydown(%d), after doptim(L):\n",
158: p-node );
159: eprint(p);
160: }
161: #endif
162: flag = 1;
163: }
164: return(flag);
165: }
166: return( 0 ); /* no change */
167: }
168:
169: # ifndef MYCONVERT
170: NODE *
171: sconvert( p )
172: register NODE *p;
173: {
174: register TWORD t, lt;
175: register NODE *l;
176: register o;
177:
178: /* optimize CONV nodes */
179: /* the unsigned-ness is ignored */
180: /* if the CONV involves floats or doubles, retain unless null */
181: /* if the CONV makes things bigger, retain */
182: /* if the CONV keeps things the same size, just paint the type */
183: /* if the CONV makes things smaller, adjust the addressing with
184: ** memory references, and paint the new type
185: */
186: /* if a pointer is being converted, convert as if it were PTRTYPE */
187: /* finally, if CONV converts a constant, do it in place */
188: again:
189: #ifndef NODBG
190: if( opdebug )
191: {
192: printf( "sconvert(%d) called:\n", p-node );
193: eprint( p );
194: }
195: #endif
196: if( p->tn.op != CONV ) cerror( "sconvert" );
197: l = p->in.left;
198: t = p->tn.type;
199: lt = l->tn.type;
200: o = l->tn.op;
201:
202: if( o == FCON )
203: {
204: /* for a floating point const, paint type,
205: ** round when it is output */
206: if( t == FLOAT || t == DOUBLE ) goto paint;
207: /* otherwise, convert it to long and treat as long conversion */
208: l->tn.op = ICON;
209: l->tn.lval = l->fpn.dval; /* MACHINE-DEPENDENT CONVERSION */
210: l->tn.rval = NONAME;
211: goto icon;
212: }
213:
214: if( o==CONV && cbigger( l ) )
215: {
216: merge:
217: p->in.left = l->in.left;
218: l->tn.op = FREE;
219: tydown( p );
220: goto again;
221: }
222: if( t == lt && (t == DOUBLE || t == FLOAT) )
223: goto paint; /* float over float, double over double */
224: if( t==FLOAT || t==DOUBLE || t == VOID
225: || lt==FLOAT || lt==DOUBLE ) return( p );
226:
227: if( ISUNSIGNED(t) ) t = DEUNSIGN(t);
228: if( ISUNSIGNED(lt) ) lt = DEUNSIGN(lt);
229: if( ISPTR(lt) )
230: # ifdef MEMONLY
231: if( o==STAR || o==NAME || o==VAUTO || o==VPARAM )
232: # endif
233: lt = PTRTYPE;
234: if( t == lt ) goto paint;
235: if( ISPTR(lt) || ISARY(lt) ) return(p);
236:
237: if( o == ICON )
238: {
239: icon:
240: l->tn.lval = ccast( l->tn.lval, p->tn.type );
241: paint:
242: l->tn.type = p->tn.type;
243: l->fn.csiz = p->fn.csiz;
244: l->fn.cdim = p->fn.cdim;
245: p->tn.op = FREE;
246: if( tydown(l) )
247: {
248: l = doptim(l);
249: }
250: return( l );
251: }
252: if( cbigger(p) ) return( p );
253: /* p makes things smaller */
254: if( o==CONV )
255: {
256: /* two conversions in a row: the second makes things smaller */
257: /* make them into one */
258: goto merge;
259: }
260: if( o==STAR || o==NAME || o==VAUTO || o==VPARAM )
261: {
262: /* memory reference: determine the adjustment */
263: # ifdef RTOLBYTES
264: # ifdef LOWINT
265: if( lt == LONG ) if( !adjust( l, LOWINT )) cerror( "adj" );
266: # endif
267: # else
268: register adj = 0;
269: if( lt == LONG ) adj = SZLONG;
270: else if( lt == INT ) adj = SZINT;
271: else if( lt == SHORT ) adj = SZSHORT;
272: else cerror( "sconv:lt 0%o", lt );
273: if( t == INT ) adj -= SZINT;
274: else if( t == SHORT ) adj -= SZSHORT;
275: else if( t == CHAR ) adj -= SZCHAR;
276: else cerror( "sconv:t 0%o", t );
277: # ifdef LOWINT
278: if( lt == LONG ) adj += LOWINT;
279: # endif
280: if( adj ) if( !adjust( l, adj ) ) cerror( "adj1" );
281: # endif
282: }
283:
284: /* other cases are where it is computed into a reg; */
285: /* simply paint the type */
286: /* must avoid clobbering the type for assignment nodes */
287: /* however, we must copy (e.g., apply the CONV) for register vars */
288: /* also, can't paint type over fields */
289: if( o == REG || asgop(o) || o == FLD ) return( p );
290: goto paint;
291: }
292: # endif
293:
294: NODE *
295: pvconvert( p )
296: register NODE *p;
297: {
298: /* p is a CONV node; convert */
299: /* this does something only when the descendent is not a ptr */
300: register NODE *l;
301: register int o;
302: l = p->in.left;
303: if( ISPTR( l->tn.type ) ) return( clocal(p) );
304: # ifdef MEMONLY
305: o = l->tn.op;
306: if( o==STAR || o==NAME || o==VAUTO || o==VPARAM || o==ICON )
307: # endif
308: {
309: /* optimize this reference */
310: /* sconvert and optimize to PTRTYPE */
311: l = makety( l, PTRTYPE, 0, PTRTYPE );
312: if( l->tn.op == CONV ) l = sconvert( l );
313: l->tn.type = p->tn.type;
314: l->fn.cdim = p->fn.cdim;
315: l->fn.csiz = p->fn.csiz;
316: p->tn.op = FREE;
317: p = l;
318: }
319: return( clocal(p) );
320: }
321:
322: NODE *
323: fortarg( p )
324: register NODE *p;
325: {
326: /* fortran function arguments */
327:
328: if( p->in.op == CM )
329: {
330: p->in.left = fortarg( p->in.left );
331: p->in.right = fortarg( p->in.right );
332: return(p);
333: }
334: while( ISPTR(p->in.type) )
335: {
336: p = buildtree( STAR, p, NIL );
337: }
338: return( optim(p) );
339: }
340:
341: /* mapping relationals when the sides are reversed */
342: short revrel[] =
343: {
344: EQ, NE, GE, GT, LE, LT, UGE, UGT, ULE, ULT
345: };
346:
347: #ifndef NODBG
348: # define REPORT(x) if(opdebug)printf( "optim turns %d into %d\n",p-node,x-node);
349: #else
350: # define REPORT(x)
351: #endif
352: NODE *
353: doptim(p)
354: register NODE *p;
355: {
356: /* local optimizations, most of which are machine independent */
357: /* doptim is called for each node by optim; it assumes that
358: ** the children of p are already optimized
359: */
360: /* p is not a leaf */
361: register NODE *l, *r, *sp;
362: register o, i;
363: register TWORD t;
364:
365: #ifndef NODBG
366: if( opdebug )
367: {
368: printf( "doptim called on:\n" );
369: eprint(p);
370: }
371: #endif
372:
373: if( (t=BTYPE(p->in.type))==ENUMTY || t==MOETY ) econvert(p);
374: switch( optype( o = p->tn.op ) )
375: {
376: case BITYPE:
377: r = p->in.right;
378: /* FALLTHRU */
379: case UTYPE:
380: l = p->in.left;
381: break;
382: case LTYPE:
383: /* nothing more to do (after doing the enum stuff) */
384: return( p );
385: }
386: sp = conval( p );
387: /* return only if conval did something */
388: if( sp != p ) return( doptim(sp) );
389: #ifndef NODBG
390: if( opdebug )
391: {
392: printf( "doptim works on:\n" );
393: eprint(p);
394: }
395: #endif
396: switch(o)
397: {
398: case CONV:
399: /* someday, make pvconvert and sconvert the same */
400: return( ISPTR(p->tn.type)?pvconvert(p):sconvert(p) );
401: case ASG PLUS:
402: case ASG MINUS:
403: case ASG AND:
404: case ASG OR:
405: case ASG ER:
406: case ASG LS:
407: case ASG RS:
408: case ASG MUL:
409: case ASG DIV:
410: case ASG MOD:
411: /* if conversion ops on the lhs, transfer them to the rhs */
412: t = l->in.type;
413:
414: /* (CONV A) op= B into A op= (CONV B)
415: ** this only holds if the result depends only on the
416: ** low order part of B (e.g., that part of B that
417: ** is the width of A
418: ** this is not true for /=, %=, or floats */
419:
420: if( l->tn.op == CONV && t!=FLOAT && t!=DOUBLE
421: && o!=ASG DIV && o != ASG MOD )
422: {
423: p->in.left = l->in.left;
424: l->tn.op = FREE;
425: l = l->in.left;
426: r = makety( r, p->in.type, p->fn.cdim, p->fn.csiz );
427: p->in.right = doptim( r );
428: }
429: if( !nncon(r) ) break; /* no more optimization */
430: /* get rid of 0 ops that don't change anything... */
431: if( !r->tn.lval && (o==ASG PLUS || o==ASG MINUS || o==ASG OR ||
432: o==ASG ER || o==ASG LS || o==ASG RS) )
433: {
434: /* the answer is the lhs */
435: goto bless;
436: }
437: if( r->tn.lval == 1 && (o==ASG MUL || o==ASG DIV) )
438: {
439: /* the answer is the lhs */
440: goto bless;
441: }
442: if( (i = ispow2( r->tn.lval ))>=0 && o==ASG MUL )
443: {
444: o = p->in.op = ASG LS;
445: r->tn.lval = i;
446: }
447: break;
448: case LS:
449: case RS:
450: if( !nncon(r) || r->tn.lval )
451: break; /* do nothing */
452: goto bless; /* shifts by 0 */
453: case FORTCALL:
454: p->in.right = fortarg( r );
455: break;
456: case UNARY AND:
457: switch( l->tn.op )
458: {
459: case STAR:
460: /* fake up to use setuleft */
461: l->tn.op = FREE;
462: l=l->in.left;
463: goto setuleft;
464: case VAUTO:
465: case VPARAM:
466: case TEMP:
467: /* the next two lines come from short structs */
468: case CALL:
469: case UNARY CALL:
470: break;
471:
472: case RNODE:
473: # ifdef ARGSRET
474: /* RNODE disappears if structure simple */
475: if( simpstr( p->fn.cdim, p->fn.csiz ) == STRTY )
476: {
477: /* complicated: make it look like first arg */
478: l->tn.op = VPARAM;
479: l->tn.lval = BITOOR(ARGINIT);
480: l->tn.rval = NONAME;
481: }
482: break;
483: #else
484: # ifdef STATSRET
485: /* simple structures will disappear */
486: if( simpstr( p->fn.cdim, p->fn.csiz ) != STRTY ) break;
487: /* otherwise, make & RNODE into ICON for static area */
488: l->tn.rval = -strftn;
489: # else
490: break; /* & of RNODE is just fine */
491: #endif
492: #endif
493: case NAME:
494: # ifdef ANDABLE
495: if( !ANDABLE(l) ) return(p);
496: # endif
497: l->tn.op = ICON;
498: setuleft:
499: /* set the type of lhs with the type of the top */
500: l->in.type = p->in.type;
501: l->fn.cdim = p->fn.cdim;
502: l->fn.csiz = p->fn.csiz;
503: p->in.op = FREE;
504: REPORT(l);
505: return( l );
506: default:
507: cerror( "& error" );
508: }
509: break;
510: case STCALL:
511: case UNARY STCALL:
512: /* use l in case return type overwritten */
513: t = simpstr( l->fn.cdim, l->fn.csiz );
514: if( t != STRTY )
515: {
516: /* take some care to keep the types OK */
517: /* the type of the return might well have been
518: ** overwritten by (say) a structure reference
519: */
520: /* MAY NOT BE QUITE RIGHT IF TWO FLAVORS OF PTR */
521: l = p;
522: p->tn.type = DECREF( p->tn.type );
523: p = buildtree( UNARY AND, l, NIL );
524: if( o == STCALL ) l->tn.op = CALL;
525: else l->tn.op = UNARY CALL;
526: l->fn.type = l->fn.csiz = t;
527: l->fn.cdim = 0;
528: }
529: break;
530: case STAR:
531: if( p->tn.type == STRTY || p->tn.type == UNIONTY )
532: {
533: p->tn.op = FREE;
534: REPORT(l);
535: return( l );
536: }
537: if( l->tn.op == UNARY AND && !callop(l->in.left->tn.op) )
538: {
539: /* & of call used in structure optimization */
540: /* fake up to use setuleft */
541: l->tn.op = FREE;
542: l = l->in.left;
543: goto setuleft;
544: }
545: if( l->tn.op != ICON ) break;
546: l->tn.op = NAME;
547: goto setuleft;
548: case MINUS:
549: if( !nncon(r) ) break;
550: r->tn.lval = - r->tn.lval;
551: o = p->in.op = PLUS;
552: case MUL:
553: case PLUS:
554: case AND:
555: case OR:
556: case ER:
557: /* commutative ops; for now, just collect constants */
558: /* someday, do it right */
559: if( o==r->tn.op || nncon(l) || ( ISCON(l) && !ISCON(r) ) )
560: {
561: /* make ops tower to the left, not the right */
562: /* also, put constants on the right */
563: sp = l;
564: l = p->in.left = r;
565: r = p->in.right = sp;
566: }
567: /* do (A + C1) + C2, etc. */
568: /* the number of special cases is horrifying */
569: /* many bugs have been found here; this code is very cautious */
570: /* (A + C1) + C2, where C2 can be a ptr, C1 not */
571: if( o==PLUS && l->tn.op==PLUS && ISCON(r) &&
572: nncon(l->in.right) )
573: {
574: p->in.left = l->in.left;
575: l->tn.op = FREE;
576: l = l->in.right;
577: r->tn.lval += l->tn.lval;
578: l->tn.op = FREE;
579: return( doptim( p ) );
580: }
581: /* (A + C1) + C2, where C1 can be a ptr, C2 not */
582: if( o==PLUS && l->tn.op==PLUS && nncon(r) &&
583: ISCON(l->in.right) )
584: {
585: l->in.right->tn.lval += r->tn.lval;
586: goto bless; /* return l as the result */
587: }
588: /* (A - C1) + C2, where C2 can be a ptr, C1 not */
589: if( o==PLUS && l->tn.op==MINUS && ISCON(r) &&
590: nncon(l->in.right) )
591: {
592: p->in.left = l->in.left;
593: l->tn.op = FREE;
594: l = l->in.right;
595: r->tn.lval -= l->tn.lval;
596: l->tn.op = FREE;
597: return( doptim( p ) );
598: }
599: /* (&A)+C */
600: if( o==PLUS && l->tn.op == UNARY AND && ISCON(r) )
601: {
602: switch( l->in.left->tn.op )
603: {
604: case NAME:
605: case VPARAM:
606: case VAUTO:
607: l->in.left->tn.lval += r->tn.lval;
608: goto bless;
609: }
610: }
611: /* change muls to shifts */
612: if( o==MUL && nncon(r) && (i=ispow2(r->tn.lval))>=0)
613: {
614: if( i == 0 )
615: {
616: /* multiplication by 1 */
617: bless:
618: /* return l, with the type of p */
619: l = makety( l, p->tn.type, p->fn.cdim,
620: p->fn.csiz );
621: r->tn.op = FREE;
622: p->tn.op = FREE;
623: /* if a conversion op was added, optimize */
624: l = doptim( l );
625: #ifndef NODBG
626: if( opdebug )
627: {
628: printf( "optim replaces op1 (%d) by:\n",
629: p-node );
630: eprint(l);
631: }
632: #endif
633: return( l );
634: }
635: o = p->in.op = LS;
636: r->tn.lval = i;
637: }
638: /* change +'s of negative consts back to - */
639: if( o==PLUS && nncon(r) && r->tn.lval<0 )
640: {
641: r->tn.lval = -r->tn.lval;
642: o = p->in.op = MINUS;
643: }
644: if( nncon(r) && !r->tn.lval && (o==PLUS||o==MINUS) )
645: {
646: /* get rid of add or subtract of 0 */
647: goto bless;
648: }
649: # ifdef PTRLEFT
650: if( o==PLUS && ISPTR(p->tn.type) && ISPTR(r->tn.type)
651: # ifdef CONSRIGHT
652: && r->tn.op != ICON
653: # endif
654: )
655: {
656: sp = l;
657: p->in.left = r;
658: p->in.right = sp;
659: }
660: # endif
661: # ifdef PTRRIGHT
662: if( o==PLUS && ISPTR(p->tn.type) && ISPTR(l->tn.type)
663: # ifdef CONSRIGHT
664: && r->tn.op != ICON
665: # endif
666: )
667: {
668: sp = l;
669: p->in.left = r;
670: p->in.right = sp;
671: }
672: # endif
673: break;
674: case DIV:
675: if( nncon( r ) && r->tn.lval == 1 ) goto bless;
676: break;
677: case EQ:
678: case NE:
679: case LT:
680: case LE:
681: case GT:
682: case GE:
683: case ULT:
684: case ULE:
685: case UGT:
686: case UGE:
687: if( ISCON(l) && !ISCON(r) )
688: {
689: /* exchange operands */
690: p->in.op = revrel[p->in.op - EQ ];
691: sp = l;
692: l = p->in.left = r;
693: r = p->in.right = sp;
694: }
695: break;
696: case STASG:
697: if( (t=simpstr( p->fn.cdim, p->fn.csiz ) ) != STRTY )
698: {
699: /* rewrite = as simpler */
700: if( ISPTR(r->tn.type) )
701: {
702: r = buildtree( STAR, r, NIL );
703: r->fn.type = r->fn.csiz = t;
704: r->fn.cdim = 0;
705: p->in.right = r = doptim( r );
706: }
707: l = buildtree( STAR, l, NIL );
708: l->fn.type = l->fn.csiz = t;
709: l->fn.cdim = 0;
710: p->in.left = l = doptim( l );
711: p->fn.type = p->fn.csiz = t;
712: p->fn.cdim = 0;
713: p->fn.op = ASSIGN;
714: return( p );
715: }
716: case STARG:
717: if( (t=simpstr( p->fn.cdim, p->fn.csiz ) ) != STRTY )
718: {
719: /* rewrite as simpler */
720: if( ISPTR(l->fn.type) )
721: {
722: l = buildtree( STAR, l, NIL );
723: l->fn.type = l->fn.csiz = t;
724: l->fn.cdim = 0;
725: p->in.left = l = doptim( l );
726: }
727: p->fn.type = p->fn.csiz = (t==LONG ? LONG : INT);
728: p->fn.cdim = 0;
729: p->fn.op = FUNARG;
730: return( p );
731: }
732: # ifdef ENDSTRUCT
733: p->in.left = aadjust( l, p->stn.stsize );
734: # endif
735: break;
736: }
737: return(p);
738: }
739:
740: NODE *
741: optim( p )
742: register NODE *p;
743: {
744: switch( optype( p->tn.op ) )
745: {
746: case BITYPE:
747: p->in.right = optim( p->in.right );
748: /* FALLTHRU */
749: case UTYPE:
750: p->in.left = optim( p->in.left );
751: }
752: return( doptim( p ) );
753: }
754:
755: ispow2( c )
756: register CONSZ c;
757: {
758: register i;
759: if( c <= 0 || (c&(c-1)) ) return(-1);
760: for( i=0; c>1; ++i) c >>= 1;
761: return(i);
762: }
763:
764: nncon( p )
765: register NODE *p;
766: {
767: /* is p a constant without a name */
768: return( p->tn.op == ICON && p->tn.rval == NONAME && !ISPTR(p->tn.type));
769: }
770:
771: /* some routines for debugging and tree transformation */
772: #ifndef MYOFFCON
773: NODE *
774: offcon( off, t, d, s )
775: OFFSZ off;
776: TWORD t;
777: {
778: /* return a node, for structure references, which is suitable for
779: ** being added to a pointer of type t, in order to be off bits offset
780: ** into a structure
781: */
782: register NODE *p;
783:
784: /* t, d, and s are the type, dimension offset, and size offset */
785: /* in general they may be necessary for offcon */
786: p = bcon(0);
787: p->tn.lval = BITOOR(off);
788: p->fn.type = p->fn.csiz = PTRTYPE;
789: return(p);
790: }
791: # endif
792:
793: bccode()
794: {
795: /* called just before executing code */
796: /* beware: called several times if there is auto. initialization */
797: /*# ifdef SDB
798: /* static bcclev;
799: /* if( blevel != bcclev )
800: /* {
801: /* bcclev = blevel;
802: /* pstab( S_LBRAC, blevel-1 );
803: /* }
804: /*# endif
805: /**/
806: # ifdef MYBCCODE
807: MYBCCODE;
808: # endif
809: p2bbeg( autooff, regvar );
810: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.