github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/cmd/5l/noop.c (about)

     1  // Inferno utils/5l/noop.c
     2  // http://code.google.com/p/inferno-os/source/browse/utils/5l/noop.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  // Code transformations.
    32  
    33  #include	"l.h"
    34  #include	"../ld/lib.h"
    35  #include	"../../pkg/runtime/stack.h"
    36  
    37  static	Sym*	sym_div;
    38  static	Sym*	sym_divu;
    39  static	Sym*	sym_mod;
    40  static	Sym*	sym_modu;
    41  
    42  static void
    43  linkcase(Prog *casep)
    44  {
    45  	Prog *p;
    46  
    47  	for(p = casep; p != P; p = p->link){
    48  		if(p->as == ABCASE) {
    49  			for(; p != P && p->as == ABCASE; p = p->link)
    50  				p->pcrel = casep;
    51  			break;
    52  		}
    53  	}
    54  }
    55  
    56  void
    57  noops(void)
    58  {
    59  	Prog *p, *q, *q1, *q2;
    60  	int o;
    61  	int32 arg;
    62  	Prog *pmorestack;
    63  	Sym *symmorestack, *tlsfallback, *gmsym;
    64  
    65  	/*
    66  	 * find leaf subroutines
    67  	 * strip NOPs
    68  	 * expand RET
    69  	 * expand BECOME pseudo
    70  	 * fixup TLS
    71  	 */
    72  
    73  	if(debug['v'])
    74  		Bprint(&bso, "%5.2f noops\n", cputime());
    75  	Bflush(&bso);
    76  
    77  	symmorestack = lookup("runtime.morestack", 0);
    78  	if(symmorestack->type != STEXT) {
    79  		diag("runtime·morestack not defined");
    80  		errorexit();
    81  	}
    82  	pmorestack = symmorestack->text;
    83  	pmorestack->reg |= NOSPLIT;
    84  
    85  	tlsfallback = lookup("runtime.read_tls_fallback", 0);
    86  	gmsym = S;
    87  	if(linkmode == LinkExternal)
    88  		gmsym = lookup("runtime.tlsgm", 0);
    89  	q = P;
    90  	for(cursym = textp; cursym != nil; cursym = cursym->next) {
    91  		for(p = cursym->text; p != P; p = p->link) {
    92  			switch(p->as) {
    93  			case ACASE:
    94  				if(flag_shared)
    95  					linkcase(p);
    96  				break;
    97  
    98  			case ATEXT:
    99  				p->mark |= LEAF;
   100  				break;
   101  	
   102  			case ARET:
   103  				break;
   104  	
   105  			case ADIV:
   106  			case ADIVU:
   107  			case AMOD:
   108  			case AMODU:
   109  				q = p;
   110  				if(prog_div == P)
   111  					initdiv();
   112  				cursym->text->mark &= ~LEAF;
   113  				continue;
   114  	
   115  			case ANOP:
   116  				q1 = p->link;
   117  				q->link = q1;		/* q is non-nop */
   118  				if(q1 != P)
   119  					q1->mark |= p->mark;
   120  				continue;
   121  	
   122  			case ABL:
   123  			case ABX:
   124  				cursym->text->mark &= ~LEAF;
   125  	
   126  			case ABCASE:
   127  			case AB:
   128  	
   129  			case ABEQ:
   130  			case ABNE:
   131  			case ABCS:
   132  			case ABHS:
   133  			case ABCC:
   134  			case ABLO:
   135  			case ABMI:
   136  			case ABPL:
   137  			case ABVS:
   138  			case ABVC:
   139  			case ABHI:
   140  			case ABLS:
   141  			case ABGE:
   142  			case ABLT:
   143  			case ABGT:
   144  			case ABLE:
   145  				q1 = p->cond;
   146  				if(q1 != P) {
   147  					while(q1->as == ANOP) {
   148  						q1 = q1->link;
   149  						p->cond = q1;
   150  					}
   151  				}
   152  				break;
   153  			case AWORD:
   154  				// Rewrite TLS register fetch: MRC 15, 0, <reg>, C13, C0, 3
   155  				if((p->to.offset & 0xffff0fff) == 0xee1d0f70) {
   156  					if(HEADTYPE == Hopenbsd) {
   157  						p->as = ARET;
   158  					} else if(goarm < 7) {
   159  						if(tlsfallback->type != STEXT) {
   160  							diag("runtime·read_tls_fallback not defined");
   161  							errorexit();
   162  						}
   163  						// BL runtime.read_tls_fallback(SB)
   164  						p->as = ABL;
   165  						p->to.type = D_BRANCH;
   166  						p->to.sym = tlsfallback;
   167  						p->cond = tlsfallback->text;
   168  						p->to.offset = 0;
   169  						cursym->text->mark &= ~LEAF;
   170  					}
   171  					if(linkmode == LinkExternal) {
   172  						// runtime.tlsgm is relocated with R_ARM_TLS_LE32
   173  						// and $runtime.tlsgm will contain the TLS offset.
   174  						//
   175  						// MOV $runtime.tlsgm+tlsoffset(SB), REGTMP
   176  						// ADD REGTMP, <reg>
   177  						//
   178  						// In shared mode, runtime.tlsgm is relocated with
   179  						// R_ARM_TLS_IE32 and runtime.tlsgm(SB) will point
   180  						// to the GOT entry containing the TLS offset.
   181  						//
   182  						// MOV runtime.tlsgm(SB), REGTMP
   183  						// ADD REGTMP, <reg>
   184  						// SUB -tlsoffset, <reg>
   185  						//
   186  						// The SUB compensates for tlsoffset
   187  						// used in runtime.save_gm and runtime.load_gm.
   188  						q = p;
   189  						p = appendp(p);
   190  						p->as = AMOVW;
   191  						p->scond = 14;
   192  						p->reg = NREG;
   193  						if(flag_shared) {
   194  							p->from.type = D_OREG;
   195  							p->from.offset = 0;
   196  						} else {
   197  							p->from.type = D_CONST;
   198  							p->from.offset = tlsoffset;
   199  						}
   200  						p->from.sym = gmsym;
   201  						p->from.name = D_EXTERN;
   202  						p->to.type = D_REG;
   203  						p->to.reg = REGTMP;
   204  						p->to.offset = 0;
   205  
   206  						p = appendp(p);
   207  						p->as = AADD;
   208  						p->scond = 14;
   209  						p->reg = NREG;
   210  						p->from.type = D_REG;
   211  						p->from.reg = REGTMP;
   212  						p->to.type = D_REG;
   213  						p->to.reg = (q->to.offset & 0xf000) >> 12;
   214  						p->to.offset = 0;
   215  
   216  						if(flag_shared) {
   217  							p = appendp(p);
   218  							p->as = ASUB;
   219  							p->scond = 14;
   220  							p->reg = NREG;
   221  							p->from.type = D_CONST;
   222  							p->from.offset = -tlsoffset;
   223  							p->to.type = D_REG;
   224  							p->to.reg = (q->to.offset & 0xf000) >> 12;
   225  							p->to.offset = 0;
   226  						}
   227  					}
   228  				}
   229  			}
   230  			q = p;
   231  		}
   232  	}
   233  
   234  	for(cursym = textp; cursym != nil; cursym = cursym->next) {
   235  		for(p = cursym->text; p != P; p = p->link) {
   236  			o = p->as;
   237  			switch(o) {
   238  			case ATEXT:
   239  				autosize = p->to.offset + 4;
   240  				if(autosize <= 4)
   241  				if(cursym->text->mark & LEAF) {
   242  					p->to.offset = -4;
   243  					autosize = 0;
   244  				}
   245  	
   246  				if(!autosize && !(cursym->text->mark & LEAF)) {
   247  					if(debug['v'])
   248  						Bprint(&bso, "save suppressed in: %s\n",
   249  							cursym->name);
   250  					Bflush(&bso);
   251  					cursym->text->mark |= LEAF;
   252  				}
   253  				if(cursym->text->mark & LEAF) {
   254  					cursym->leaf = 1;
   255  					if(!autosize)
   256  						break;
   257  				}
   258  	
   259  				if(!(p->reg & NOSPLIT)) {
   260  					// MOVW			g_stackguard(g), R1
   261  					p = appendp(p);
   262  					p->as = AMOVW;
   263  					p->from.type = D_OREG;
   264  					p->from.reg = REGG;
   265  					p->to.type = D_REG;
   266  					p->to.reg = 1;
   267  					
   268  					if(autosize <= StackSmall) {
   269  						// small stack: SP < stackguard
   270  						//	CMP	stackguard, SP
   271  						p = appendp(p);
   272  						p->as = ACMP;
   273  						p->from.type = D_REG;
   274  						p->from.reg = 1;
   275  						p->reg = REGSP;
   276  					} else if(autosize <= StackBig) {
   277  						// large stack: SP-framesize < stackguard-StackSmall
   278  						//	MOVW $-autosize(SP), R2
   279  						//	CMP stackguard, R2
   280  						p = appendp(p);
   281  						p->as = AMOVW;
   282  						p->from.type = D_CONST;
   283  						p->from.reg = REGSP;
   284  						p->from.offset = -autosize;
   285  						p->to.type = D_REG;
   286  						p->to.reg = 2;
   287  						
   288  						p = appendp(p);
   289  						p->as = ACMP;
   290  						p->from.type = D_REG;
   291  						p->from.reg = 1;
   292  						p->reg = 2;
   293  					} else {
   294  						// Such a large stack we need to protect against wraparound
   295  						// if SP is close to zero.
   296  						//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
   297  						// The +StackGuard on both sides is required to keep the left side positive:
   298  						// SP is allowed to be slightly below stackguard. See stack.h.
   299  						//	CMP $StackPreempt, R1
   300  						//	MOVW.NE $StackGuard(SP), R2
   301  						//	SUB.NE R1, R2
   302  						//	MOVW.NE $(autosize+(StackGuard-StackSmall)), R3
   303  						//	CMP.NE R3, R2
   304  						p = appendp(p);
   305  						p->as = ACMP;
   306  						p->from.type = D_CONST;
   307  						p->from.offset = (uint32)StackPreempt;
   308  						p->reg = 1;
   309  
   310  						p = appendp(p);
   311  						p->as = AMOVW;
   312  						p->from.type = D_CONST;
   313  						p->from.reg = REGSP;
   314  						p->from.offset = StackGuard;
   315  						p->to.type = D_REG;
   316  						p->to.reg = 2;
   317  						p->scond = C_SCOND_NE;
   318  						
   319  						p = appendp(p);
   320  						p->as = ASUB;
   321  						p->from.type = D_REG;
   322  						p->from.reg = 1;
   323  						p->to.type = D_REG;
   324  						p->to.reg = 2;
   325  						p->scond = C_SCOND_NE;
   326  						
   327  						p = appendp(p);
   328  						p->as = AMOVW;
   329  						p->from.type = D_CONST;
   330  						p->from.offset = autosize + (StackGuard - StackSmall);
   331  						p->to.type = D_REG;
   332  						p->to.reg = 3;
   333  						p->scond = C_SCOND_NE;
   334  						
   335  						p = appendp(p);
   336  						p->as = ACMP;
   337  						p->from.type = D_REG;
   338  						p->from.reg = 3;
   339  						p->reg = 2;
   340  						p->scond = C_SCOND_NE;
   341  					}
   342  					
   343  					// MOVW.LS		$autosize, R1
   344  					p = appendp(p);
   345  					p->as = AMOVW;
   346  					p->scond = C_SCOND_LS;
   347  					p->from.type = D_CONST;
   348  					p->from.offset = autosize;
   349  					p->to.type = D_REG;
   350  					p->to.reg = 1;
   351  	
   352  					// MOVW.LS		$args, R2
   353  					p = appendp(p);
   354  					p->as = AMOVW;
   355  					p->scond = C_SCOND_LS;
   356  					p->from.type = D_CONST;
   357  					arg = cursym->text->to.offset2;
   358  					if(arg == 1) // special marker for known 0
   359  						arg = 0;
   360  					if(arg&3)
   361  						diag("misaligned argument size in stack split");
   362  					p->from.offset = arg;
   363  					p->to.type = D_REG;
   364  					p->to.reg = 2;
   365  	
   366  					// MOVW.LS	R14, R3
   367  					p = appendp(p);
   368  					p->as = AMOVW;
   369  					p->scond = C_SCOND_LS;
   370  					p->from.type = D_REG;
   371  					p->from.reg = REGLINK;
   372  					p->to.type = D_REG;
   373  					p->to.reg = 3;
   374  	
   375  					// BL.LS		runtime.morestack(SB) // modifies LR, returns with LO still asserted
   376  					p = appendp(p);
   377  					p->as = ABL;
   378  					p->scond = C_SCOND_LS;
   379  					p->to.type = D_BRANCH;
   380  					p->to.sym = symmorestack;
   381  					p->cond = pmorestack;
   382  					
   383  					// BLS	start
   384  					p = appendp(p);
   385  					p->as = ABLS;
   386  					p->to.type = D_BRANCH;
   387  					p->cond = cursym->text->link;
   388  				}
   389  				
   390  				// MOVW.W		R14,$-autosize(SP)
   391  				p = appendp(p);
   392  				p->as = AMOVW;
   393  				p->scond |= C_WBIT;
   394  				p->from.type = D_REG;
   395  				p->from.reg = REGLINK;
   396  				p->to.type = D_OREG;
   397  				p->to.offset = -autosize;
   398  				p->to.reg = REGSP;
   399  				p->spadj = autosize;
   400  				break;
   401  	
   402  			case ARET:
   403  				nocache(p);
   404  				if(cursym->text->mark & LEAF) {
   405  					if(!autosize) {
   406  						p->as = AB;
   407  						p->from = zprg.from;
   408  						if(p->to.sym) { // retjmp
   409  							p->to.type = D_BRANCH;
   410  							p->cond = p->to.sym->text;
   411  						} else {
   412  							p->to.type = D_OREG;
   413  							p->to.offset = 0;
   414  							p->to.reg = REGLINK;
   415  						}
   416  						break;
   417  					}
   418  				}
   419  				p->as = AMOVW;
   420  				p->scond |= C_PBIT;
   421  				p->from.type = D_OREG;
   422  				p->from.offset = autosize;
   423  				p->from.reg = REGSP;
   424  				p->to.type = D_REG;
   425  				p->to.reg = REGPC;
   426  				// If there are instructions following
   427  				// this ARET, they come from a branch
   428  				// with the same stackframe, so no spadj.
   429  				
   430  				if(p->to.sym) { // retjmp
   431  					p->to.reg = REGLINK;
   432  					q2 = appendp(p);
   433  					q2->as = AB;
   434  					q2->to.type = D_BRANCH;
   435  					q2->to.sym = p->to.sym;
   436  					q2->cond = p->to.sym->text;
   437  					p->to.sym = nil;
   438  					p = q2;
   439  				}
   440  				break;
   441  	
   442  			case AADD:
   443  				if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP)
   444  					p->spadj = -p->from.offset;
   445  				break;
   446  
   447  			case ASUB:
   448  				if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP)
   449  					p->spadj = p->from.offset;
   450  				break;
   451  
   452  			case ADIV:
   453  			case ADIVU:
   454  			case AMOD:
   455  			case AMODU:
   456  				if(debug['M'])
   457  					break;
   458  				if(p->from.type != D_REG)
   459  					break;
   460  				if(p->to.type != D_REG)
   461  					break;
   462  				q1 = p;
   463  	
   464  				/* MOV a,4(SP) */
   465  				p = appendp(p);
   466  				p->as = AMOVW;
   467  				p->line = q1->line;
   468  				p->from.type = D_REG;
   469  				p->from.reg = q1->from.reg;
   470  				p->to.type = D_OREG;
   471  				p->to.reg = REGSP;
   472  				p->to.offset = 4;
   473  	
   474  				/* MOV b,REGTMP */
   475  				p = appendp(p);
   476  				p->as = AMOVW;
   477  				p->line = q1->line;
   478  				p->from.type = D_REG;
   479  				p->from.reg = q1->reg;
   480  				if(q1->reg == NREG)
   481  					p->from.reg = q1->to.reg;
   482  				p->to.type = D_REG;
   483  				p->to.reg = REGTMP;
   484  				p->to.offset = 0;
   485  	
   486  				/* CALL appropriate */
   487  				p = appendp(p);
   488  				p->as = ABL;
   489  				p->line = q1->line;
   490  				p->to.type = D_BRANCH;
   491  				p->cond = p;
   492  				switch(o) {
   493  				case ADIV:
   494  					p->cond = prog_div;
   495  					p->to.sym = sym_div;
   496  					break;
   497  				case ADIVU:
   498  					p->cond = prog_divu;
   499  					p->to.sym = sym_divu;
   500  					break;
   501  				case AMOD:
   502  					p->cond = prog_mod;
   503  					p->to.sym = sym_mod;
   504  					break;
   505  				case AMODU:
   506  					p->cond = prog_modu;
   507  					p->to.sym = sym_modu;
   508  					break;
   509  				}
   510  	
   511  				/* MOV REGTMP, b */
   512  				p = appendp(p);
   513  				p->as = AMOVW;
   514  				p->line = q1->line;
   515  				p->from.type = D_REG;
   516  				p->from.reg = REGTMP;
   517  				p->from.offset = 0;
   518  				p->to.type = D_REG;
   519  				p->to.reg = q1->to.reg;
   520  	
   521  				/* ADD $8,SP */
   522  				p = appendp(p);
   523  				p->as = AADD;
   524  				p->line = q1->line;
   525  				p->from.type = D_CONST;
   526  				p->from.reg = NREG;
   527  				p->from.offset = 8;
   528  				p->reg = NREG;
   529  				p->to.type = D_REG;
   530  				p->to.reg = REGSP;
   531  				p->spadj = -8;
   532  	
   533  				/* SUB $8,SP */
   534  				q1->as = ASUB;
   535  				q1->from.type = D_CONST;
   536  				q1->from.offset = 8;
   537  				q1->from.reg = NREG;
   538  				q1->reg = NREG;
   539  				q1->to.type = D_REG;
   540  				q1->to.reg = REGSP;
   541  				q1->spadj = 8;
   542  	
   543  				break;
   544  			case AMOVW:
   545  				if((p->scond & C_WBIT) && p->to.type == D_OREG && p->to.reg == REGSP)
   546  					p->spadj = -p->to.offset;
   547  				if((p->scond & C_PBIT) && p->from.type == D_OREG && p->from.reg == REGSP && p->to.reg != REGPC)
   548  					p->spadj = -p->from.offset;
   549  				if(p->from.type == D_CONST && p->from.reg == REGSP && p->to.type == D_REG && p->to.reg == REGSP)
   550  					p->spadj = -p->from.offset;
   551  				break;
   552  			}
   553  		}
   554  	}
   555  }
   556  
   557  static void
   558  sigdiv(char *n)
   559  {
   560  	Sym *s;
   561  
   562  	s = lookup(n, 0);
   563  	if(s->type == STEXT)
   564  		if(s->sig == 0)
   565  			s->sig = SIGNINTERN;
   566  }
   567  
   568  void
   569  divsig(void)
   570  {
   571  	sigdiv("_div");
   572  	sigdiv("_divu");
   573  	sigdiv("_mod");
   574  	sigdiv("_modu");
   575  }
   576  
   577  void
   578  initdiv(void)
   579  {
   580  	Sym *s2, *s3, *s4, *s5;
   581  
   582  	if(prog_div != P)
   583  		return;
   584  	sym_div = s2 = lookup("_div", 0);
   585  	sym_divu = s3 = lookup("_divu", 0);
   586  	sym_mod = s4 = lookup("_mod", 0);
   587  	sym_modu = s5 = lookup("_modu", 0);
   588  	prog_div = s2->text;
   589  	prog_divu = s3->text;
   590  	prog_mod = s4->text;
   591  	prog_modu = s5->text;
   592  	if(prog_div == P) {
   593  		diag("undefined: %s", s2->name);
   594  		prog_div = cursym->text;
   595  	}
   596  	if(prog_divu == P) {
   597  		diag("undefined: %s", s3->name);
   598  		prog_divu = cursym->text;
   599  	}
   600  	if(prog_mod == P) {
   601  		diag("undefined: %s", s4->name);
   602  		prog_mod = cursym->text;
   603  	}
   604  	if(prog_modu == P) {
   605  		diag("undefined: %s", s5->name);
   606  		prog_modu = cursym->text;
   607  	}
   608  }
   609  
   610  void
   611  nocache(Prog *p)
   612  {
   613  	p->optab = 0;
   614  	p->from.class = 0;
   615  	p->to.class = 0;
   616  }