github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/src/cmd/8g/peep.c (about)

     1  // Derived from Inferno utils/6c/peep.c
     2  // http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
     3  //
     4  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     5  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     6  //	Portions Copyright © 1997-1999 Vita Nuova Limited
     7  //	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
     8  //	Portions Copyright © 2004,2006 Bruce Ellis
     9  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
    10  //	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
    11  //	Portions Copyright © 2009 The Go Authors.  All rights reserved.
    12  //
    13  // Permission is hereby granted, free of charge, to any person obtaining a copy
    14  // of this software and associated documentation files (the "Software"), to deal
    15  // in the Software without restriction, including without limitation the rights
    16  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    17  // copies of the Software, and to permit persons to whom the Software is
    18  // furnished to do so, subject to the following conditions:
    19  //
    20  // The above copyright notice and this permission notice shall be included in
    21  // all copies or substantial portions of the Software.
    22  //
    23  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    26  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    29  // THE SOFTWARE.
    30  
    31  #include <u.h>
    32  #include <libc.h>
    33  #include "gg.h"
    34  #include "opt.h"
    35  
    36  enum {
    37  	REGEXT = 0,
    38  };
    39  
    40  static void	conprop(Flow *r);
    41  static void	elimshortmov(Graph*);
    42  static int	subprop(Flow*);
    43  static int	copyprop(Graph*, Flow*);
    44  static int	copy1(Adr*, Adr*, Flow*, int);
    45  static int	copyas(Adr*, Adr*);
    46  static int	copyau(Adr*, Adr*);
    47  static int	copysub(Adr*, Adr*, Adr*, int);
    48  
    49  static uint32	gactive;
    50  
    51  // do we need the carry bit
    52  static int
    53  needc(Prog *p)
    54  {
    55  	ProgInfo info;
    56  
    57  	while(p != P) {
    58  		proginfo(&info, p);
    59  		if(info.flags & UseCarry)
    60  			return 1;
    61  		if(info.flags & (SetCarry|KillCarry))
    62  			return 0;
    63  		p = p->link;
    64  	}
    65  	return 0;
    66  }
    67  
    68  static Flow*
    69  rnops(Flow *r)
    70  {
    71  	Prog *p;
    72  	Flow *r1;
    73  
    74  	if(r != nil)
    75  	for(;;) {
    76  		p = r->prog;
    77  		if(p->as != ANOP || p->from.type != D_NONE || p->to.type != D_NONE)
    78  			break;
    79  		r1 = uniqs(r);
    80  		if(r1 == nil)
    81  			break;
    82  		r = r1;
    83  	}
    84  	return r;
    85  }
    86  
    87  void
    88  peep(Prog *firstp)
    89  {
    90  	Flow *r, *r1;
    91  	Graph *g;
    92  	Prog *p, *p1;
    93  	int t;
    94  
    95  	g = flowstart(firstp, sizeof(Flow));
    96  	if(g == nil)
    97  		return;
    98  	gactive = 0;
    99  
   100  	// byte, word arithmetic elimination.
   101  	elimshortmov(g);
   102  
   103  	// constant propagation
   104  	// find MOV $con,R followed by
   105  	// another MOV $con,R without
   106  	// setting R in the interim
   107  	for(r=g->start; r!=nil; r=r->link) {
   108  		p = r->prog;
   109  		switch(p->as) {
   110  		case ALEAL:
   111  			if(regtyp(&p->to))
   112  			if(p->from.sym != nil)
   113  			if(p->from.index == D_NONE || p->from.index == D_CONST)
   114  				conprop(r);
   115  			break;
   116  
   117  		case AMOVB:
   118  		case AMOVW:
   119  		case AMOVL:
   120  		case AMOVSS:
   121  		case AMOVSD:
   122  			if(regtyp(&p->to))
   123  			if(p->from.type == D_CONST || p->from.type == D_FCONST)
   124  				conprop(r);
   125  			break;
   126  		}
   127  	}
   128  
   129  loop1:
   130  	if(debug['P'] && debug['v'])
   131  		dumpit("loop1", g->start, 0);
   132  
   133  	t = 0;
   134  	for(r=g->start; r!=nil; r=r->link) {
   135  		p = r->prog;
   136  		switch(p->as) {
   137  		case AMOVL:
   138  		case AMOVSS:
   139  		case AMOVSD:
   140  			if(regtyp(&p->to))
   141  			if(regtyp(&p->from)) {
   142  				if(copyprop(g, r)) {
   143  					excise(r);
   144  					t++;
   145  				} else
   146  				if(subprop(r) && copyprop(g, r)) {
   147  					excise(r);
   148  					t++;
   149  				}
   150  			}
   151  			break;
   152  
   153  		case AMOVBLZX:
   154  		case AMOVWLZX:
   155  		case AMOVBLSX:
   156  		case AMOVWLSX:
   157  			if(regtyp(&p->to)) {
   158  				r1 = rnops(uniqs(r));
   159  				if(r1 != nil) {
   160  					p1 = r1->prog;
   161  					if(p->as == p1->as && p->to.type == p1->from.type){
   162  						p1->as = AMOVL;
   163  						t++;
   164  					}
   165  				}
   166  			}
   167  			break;
   168  
   169  		case AADDL:
   170  		case AADDW:
   171  			if(p->from.type != D_CONST || needc(p->link))
   172  				break;
   173  			if(p->from.offset == -1){
   174  				if(p->as == AADDL)
   175  					p->as = ADECL;
   176  				else
   177  					p->as = ADECW;
   178  				p->from = zprog.from;
   179  				break;
   180  			}
   181  			if(p->from.offset == 1){
   182  				if(p->as == AADDL)
   183  					p->as = AINCL;
   184  				else
   185  					p->as = AINCW;
   186  				p->from = zprog.from;
   187  				break;
   188  			}
   189  			break;
   190  
   191  		case ASUBL:
   192  		case ASUBW:
   193  			if(p->from.type != D_CONST || needc(p->link))
   194  				break;
   195  			if(p->from.offset == -1) {
   196  				if(p->as == ASUBL)
   197  					p->as = AINCL;
   198  				else
   199  					p->as = AINCW;
   200  				p->from = zprog.from;
   201  				break;
   202  			}
   203  			if(p->from.offset == 1){
   204  				if(p->as == ASUBL)
   205  					p->as = ADECL;
   206  				else
   207  					p->as = ADECW;
   208  				p->from = zprog.from;
   209  				break;
   210  			}
   211  			break;
   212  		}
   213  	}
   214  	if(t)
   215  		goto loop1;
   216  
   217  	// MOVSD removal.
   218  	// We never use packed registers, so a MOVSD between registers
   219  	// can be replaced by MOVAPD, which moves the pair of float64s
   220  	// instead of just the lower one.  We only use the lower one, but
   221  	// the processor can do better if we do moves using both.
   222  	for(r=g->start; r!=nil; r=r->link) {
   223  		p = r->prog;
   224  		if(p->as == AMOVSD)
   225  		if(regtyp(&p->from))
   226  		if(regtyp(&p->to))
   227  			p->as = AMOVAPD;
   228  	}
   229  	
   230  	flowend(g);
   231  }
   232  
   233  void
   234  excise(Flow *r)
   235  {
   236  	Prog *p;
   237  
   238  	p = r->prog;
   239  	if(debug['P'] && debug['v'])
   240  		print("%P ===delete===\n", p);
   241  
   242  	p->as = ANOP;
   243  	p->from = zprog.from;
   244  	p->to = zprog.to;
   245  
   246  	ostats.ndelmov++;
   247  }
   248  
   249  int
   250  regtyp(Adr *a)
   251  {
   252  	int t;
   253  
   254  	t = a->type;
   255  	if(t >= D_AX && t <= D_DI)
   256  		return 1;
   257  	if(t >= D_X0 && t <= D_X7)
   258  		return 1;
   259  	return 0;
   260  }
   261  
   262  // movb elimination.
   263  // movb is simulated by the linker
   264  // when a register other than ax, bx, cx, dx
   265  // is used, so rewrite to other instructions
   266  // when possible.  a movb into a register
   267  // can smash the entire 64-bit register without
   268  // causing any trouble.
   269  static void
   270  elimshortmov(Graph *g)
   271  {
   272  	Prog *p;
   273  	Flow *r;
   274  
   275  	for(r=g->start; r!=nil; r=r->link) {
   276  		p = r->prog;
   277  		if(regtyp(&p->to)) {
   278  			switch(p->as) {
   279  			case AINCB:
   280  			case AINCW:
   281  				p->as = AINCL;
   282  				break;
   283  			case ADECB:
   284  			case ADECW:
   285  				p->as = ADECL;
   286  				break;
   287  			case ANEGB:
   288  			case ANEGW:
   289  				p->as = ANEGL;
   290  				break;
   291  			case ANOTB:
   292  			case ANOTW:
   293  				p->as = ANOTL;
   294  				break;
   295  			}
   296  			if(regtyp(&p->from) || p->from.type == D_CONST) {
   297  				// move or artihmetic into partial register.
   298  				// from another register or constant can be movl.
   299  				// we don't switch to 32-bit arithmetic if it can
   300  				// change how the carry bit is set (and the carry bit is needed).
   301  				switch(p->as) {
   302  				case AMOVB:
   303  				case AMOVW:
   304  					p->as = AMOVL;
   305  					break;
   306  				case AADDB:
   307  				case AADDW:
   308  					if(!needc(p->link))
   309  						p->as = AADDL;
   310  					break;
   311  				case ASUBB:
   312  				case ASUBW:
   313  					if(!needc(p->link))
   314  						p->as = ASUBL;
   315  					break;
   316  				case AMULB:
   317  				case AMULW:
   318  					p->as = AMULL;
   319  					break;
   320  				case AIMULB:
   321  				case AIMULW:
   322  					p->as = AIMULL;
   323  					break;
   324  				case AANDB:
   325  				case AANDW:
   326  					p->as = AANDL;
   327  					break;
   328  				case AORB:
   329  				case AORW:
   330  					p->as = AORL;
   331  					break;
   332  				case AXORB:
   333  				case AXORW:
   334  					p->as = AXORL;
   335  					break;
   336  				case ASHLB:
   337  				case ASHLW:
   338  					p->as = ASHLL;
   339  					break;
   340  				}
   341  			} else {
   342  				// explicit zero extension
   343  				switch(p->as) {
   344  				case AMOVB:
   345  					p->as = AMOVBLZX;
   346  					break;
   347  				case AMOVW:
   348  					p->as = AMOVWLZX;
   349  					break;
   350  				}
   351  			}
   352  		}
   353  	}
   354  }
   355  
   356  /*
   357   * the idea is to substitute
   358   * one register for another
   359   * from one MOV to another
   360   *	MOV	a, R0
   361   *	ADD	b, R0	/ no use of R1
   362   *	MOV	R0, R1
   363   * would be converted to
   364   *	MOV	a, R1
   365   *	ADD	b, R1
   366   *	MOV	R1, R0
   367   * hopefully, then the former or latter MOV
   368   * will be eliminated by copy propagation.
   369   */
   370  static int
   371  subprop(Flow *r0)
   372  {
   373  	Prog *p;
   374  	Adr *v1, *v2;
   375  	Flow *r;
   376  	int t;
   377  	ProgInfo info;
   378  
   379  	p = r0->prog;
   380  	v1 = &p->from;
   381  	if(!regtyp(v1))
   382  		return 0;
   383  	v2 = &p->to;
   384  	if(!regtyp(v2))
   385  		return 0;
   386  	for(r=uniqp(r0); r!=nil; r=uniqp(r)) {
   387  		if(debug['P'] && debug['v'])
   388  			print("\t? %P\n", r->prog);
   389  		if(uniqs(r) == nil)
   390  			break;
   391  		p = r->prog;
   392  		if(p->as == AVARDEF || p->as == AVARKILL)
   393  			continue;
   394  		proginfo(&info, p);
   395  		if(info.flags & Call)
   396  			return 0;
   397  
   398  		if(info.reguse | info.regset)
   399  			return 0;
   400  
   401  		if((info.flags & Move) && (info.flags & (SizeL|SizeQ|SizeF|SizeD)) && p->to.type == v1->type)
   402  			goto gotit;
   403  
   404  		if(copyau(&p->from, v2) || copyau(&p->to, v2))
   405  			break;
   406  		if(copysub(&p->from, v1, v2, 0) || copysub(&p->to, v1, v2, 0))
   407  			break;
   408  	}
   409  	return 0;
   410  
   411  gotit:
   412  	copysub(&p->to, v1, v2, 1);
   413  	if(debug['P']) {
   414  		print("gotit: %D->%D\n%P", v1, v2, r->prog);
   415  		if(p->from.type == v2->type)
   416  			print(" excise");
   417  		print("\n");
   418  	}
   419  	for(r=uniqs(r); r!=r0; r=uniqs(r)) {
   420  		p = r->prog;
   421  		copysub(&p->from, v1, v2, 1);
   422  		copysub(&p->to, v1, v2, 1);
   423  		if(debug['P'])
   424  			print("%P\n", r->prog);
   425  	}
   426  	t = v1->type;
   427  	v1->type = v2->type;
   428  	v2->type = t;
   429  	if(debug['P'])
   430  		print("%P last\n", r->prog);
   431  	return 1;
   432  }
   433  
   434  /*
   435   * The idea is to remove redundant copies.
   436   *	v1->v2	F=0
   437   *	(use v2	s/v2/v1/)*
   438   *	set v1	F=1
   439   *	use v2	return fail
   440   *	-----------------
   441   *	v1->v2	F=0
   442   *	(use v2	s/v2/v1/)*
   443   *	set v1	F=1
   444   *	set v2	return success
   445   */
   446  static int
   447  copyprop(Graph *g, Flow *r0)
   448  {
   449  	Prog *p;
   450  	Adr *v1, *v2;
   451  
   452  	USED(g);
   453  	p = r0->prog;
   454  	v1 = &p->from;
   455  	v2 = &p->to;
   456  	if(copyas(v1, v2))
   457  		return 1;
   458  	gactive++;
   459  	return copy1(v1, v2, r0->s1, 0);
   460  }
   461  
   462  static int
   463  copy1(Adr *v1, Adr *v2, Flow *r, int f)
   464  {
   465  	int t;
   466  	Prog *p;
   467  
   468  	if(r->active == gactive) {
   469  		if(debug['P'])
   470  			print("act set; return 1\n");
   471  		return 1;
   472  	}
   473  	r->active = gactive;
   474  	if(debug['P'])
   475  		print("copy %D->%D f=%d\n", v1, v2, f);
   476  	for(; r != nil; r = r->s1) {
   477  		p = r->prog;
   478  		if(debug['P'])
   479  			print("%P", p);
   480  		if(!f && uniqp(r) == nil) {
   481  			f = 1;
   482  			if(debug['P'])
   483  				print("; merge; f=%d", f);
   484  		}
   485  		t = copyu(p, v2, nil);
   486  		switch(t) {
   487  		case 2:	/* rar, can't split */
   488  			if(debug['P'])
   489  				print("; %D rar; return 0\n", v2);
   490  			return 0;
   491  
   492  		case 3:	/* set */
   493  			if(debug['P'])
   494  				print("; %D set; return 1\n", v2);
   495  			return 1;
   496  
   497  		case 1:	/* used, substitute */
   498  		case 4:	/* use and set */
   499  			if(f) {
   500  				if(!debug['P'])
   501  					return 0;
   502  				if(t == 4)
   503  					print("; %D used+set and f=%d; return 0\n", v2, f);
   504  				else
   505  					print("; %D used and f=%d; return 0\n", v2, f);
   506  				return 0;
   507  			}
   508  			if(copyu(p, v2, v1)) {
   509  				if(debug['P'])
   510  					print("; sub fail; return 0\n");
   511  				return 0;
   512  			}
   513  			if(debug['P'])
   514  				print("; sub %D/%D", v2, v1);
   515  			if(t == 4) {
   516  				if(debug['P'])
   517  					print("; %D used+set; return 1\n", v2);
   518  				return 1;
   519  			}
   520  			break;
   521  		}
   522  		if(!f) {
   523  			t = copyu(p, v1, nil);
   524  			if(!f && (t == 2 || t == 3 || t == 4)) {
   525  				f = 1;
   526  				if(debug['P'])
   527  					print("; %D set and !f; f=%d", v1, f);
   528  			}
   529  		}
   530  		if(debug['P'])
   531  			print("\n");
   532  		if(r->s2)
   533  			if(!copy1(v1, v2, r->s2, f))
   534  				return 0;
   535  	}
   536  	return 1;
   537  }
   538  
   539  /*
   540   * return
   541   * 1 if v only used (and substitute),
   542   * 2 if read-alter-rewrite
   543   * 3 if set
   544   * 4 if set and used
   545   * 0 otherwise (not touched)
   546   */
   547  int
   548  copyu(Prog *p, Adr *v, Adr *s)
   549  {
   550  	ProgInfo info;
   551  
   552  	switch(p->as) {
   553  	case AJMP:
   554  		if(s != nil) {
   555  			if(copysub(&p->to, v, s, 1))
   556  				return 1;
   557  			return 0;
   558  		}
   559  		if(copyau(&p->to, v))
   560  			return 1;
   561  		return 0;
   562  
   563  	case ARET:
   564  		if(s != nil)
   565  			return 1;
   566  		return 3;
   567  
   568  	case ACALL:
   569  		if(REGEXT && v->type <= REGEXT && v->type > exregoffset)
   570  			return 2;
   571  		if(REGARG >= 0 && v->type == (uchar)REGARG)
   572  			return 2;
   573  		if(v->type == p->from.type)
   574  			return 2;
   575  
   576  		if(s != nil) {
   577  			if(copysub(&p->to, v, s, 1))
   578  				return 1;
   579  			return 0;
   580  		}
   581  		if(copyau(&p->to, v))
   582  			return 4;
   583  		return 3;
   584  
   585  	case ATEXT:
   586  		if(REGARG >= 0 && v->type == (uchar)REGARG)
   587  			return 3;
   588  		return 0;
   589  	}
   590  
   591  	if(p->as == AVARDEF || p->as == AVARKILL)
   592  		return 0;
   593  	proginfo(&info, p);
   594  
   595  	if((info.reguse|info.regset) & RtoB(v->type))
   596  		return 2;
   597  		
   598  	if(info.flags & LeftAddr)
   599  		if(copyas(&p->from, v))
   600  			return 2;
   601  
   602  	if((info.flags & (RightRead|RightWrite)) == (RightRead|RightWrite))
   603  		if(copyas(&p->to, v))
   604  			return 2;
   605  	
   606  	if(info.flags & RightWrite) {
   607  		if(copyas(&p->to, v)) {
   608  			if(s != nil)
   609  				return copysub(&p->from, v, s, 1);
   610  			if(copyau(&p->from, v))
   611  				return 4;
   612  			return 3;
   613  		}
   614  	}
   615  	
   616  	if(info.flags & (LeftAddr|LeftRead|LeftWrite|RightAddr|RightRead|RightWrite)) {
   617  		if(s != nil) {
   618  			if(copysub(&p->from, v, s, 1))
   619  				return 1;
   620  			return copysub(&p->to, v, s, 1);
   621  		}
   622  		if(copyau(&p->from, v))
   623  			return 1;
   624  		if(copyau(&p->to, v))
   625  			return 1;
   626  	}
   627  
   628  	return 0;
   629  }
   630  
   631  /*
   632   * direct reference,
   633   * could be set/use depending on
   634   * semantics
   635   */
   636  static int
   637  copyas(Adr *a, Adr *v)
   638  {
   639  	if(D_AL <= a->type && a->type <= D_BL)
   640  		fatal("use of byte register");
   641  	if(D_AL <= v->type && v->type <= D_BL)
   642  		fatal("use of byte register");
   643  
   644  	if(a->type != v->type)
   645  		return 0;
   646  	if(regtyp(v))
   647  		return 1;
   648  	if(v->type == D_AUTO || v->type == D_PARAM)
   649  		if(v->offset == a->offset)
   650  			return 1;
   651  	return 0;
   652  }
   653  
   654  int
   655  sameaddr(Addr *a, Addr *v)
   656  {
   657  	if(a->type != v->type)
   658  		return 0;
   659  	if(regtyp(v))
   660  		return 1;
   661  	if(v->type == D_AUTO || v->type == D_PARAM)
   662  		if(v->offset == a->offset)
   663  			return 1;
   664  	return 0;
   665  }
   666  
   667  /*
   668   * either direct or indirect
   669   */
   670  static int
   671  copyau(Adr *a, Adr *v)
   672  {
   673  
   674  	if(copyas(a, v))
   675  		return 1;
   676  	if(regtyp(v)) {
   677  		if(a->type-D_INDIR == v->type)
   678  			return 1;
   679  		if(a->index == v->type)
   680  			return 1;
   681  	}
   682  	return 0;
   683  }
   684  
   685  /*
   686   * substitute s for v in a
   687   * return failure to substitute
   688   */
   689  static int
   690  copysub(Adr *a, Adr *v, Adr *s, int f)
   691  {
   692  	int t;
   693  
   694  	if(copyas(a, v)) {
   695  		t = s->type;
   696  		if(t >= D_AX && t <= D_DI || t >= D_X0 && t <= D_X7) {
   697  			if(f)
   698  				a->type = t;
   699  		}
   700  		return 0;
   701  	}
   702  	if(regtyp(v)) {
   703  		t = v->type;
   704  		if(a->type == t+D_INDIR) {
   705  			if((s->type == D_BP) && a->index != D_NONE)
   706  				return 1;	/* can't use BP-base with index */
   707  			if(f)
   708  				a->type = s->type+D_INDIR;
   709  //			return 0;
   710  		}
   711  		if(a->index == t) {
   712  			if(f)
   713  				a->index = s->type;
   714  			return 0;
   715  		}
   716  		return 0;
   717  	}
   718  	return 0;
   719  }
   720  
   721  static void
   722  conprop(Flow *r0)
   723  {
   724  	Flow *r;
   725  	Prog *p, *p0;
   726  	int t;
   727  	Adr *v0;
   728  
   729  	p0 = r0->prog;
   730  	v0 = &p0->to;
   731  	r = r0;
   732  
   733  loop:
   734  	r = uniqs(r);
   735  	if(r == nil || r == r0)
   736  		return;
   737  	if(uniqp(r) == nil)
   738  		return;
   739  
   740  	p = r->prog;
   741  	t = copyu(p, v0, nil);
   742  	switch(t) {
   743  	case 0:	// miss
   744  	case 1:	// use
   745  		goto loop;
   746  
   747  	case 2:	// rar
   748  	case 4:	// use and set
   749  		break;
   750  
   751  	case 3:	// set
   752  		if(p->as == p0->as)
   753  		if(p->from.type == p0->from.type)
   754  		if(p->from.node == p0->from.node)
   755  		if(p->from.offset == p0->from.offset)
   756  		if(p->from.scale == p0->from.scale)
   757  		if(p->from.type == D_FCONST && p->from.u.dval == p0->from.u.dval)
   758  		if(p->from.index == p0->from.index) {
   759  			excise(r);
   760  			goto loop;
   761  		}
   762  		break;
   763  	}
   764  }
   765  
   766  int
   767  smallindir(Addr *a, Addr *reg)
   768  {
   769  	return regtyp(reg) &&
   770  		a->type == D_INDIR + reg->type &&
   771  		a->index == D_NONE &&
   772  		0 <= a->offset && a->offset < 4096;
   773  }
   774  
   775  int
   776  stackaddr(Addr *a)
   777  {
   778  	return regtyp(a) && a->type == D_SP;
   779  }