github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/src/liblink/obj5.c (about)

     1  // Derived from Inferno utils/5c/swt.c
     2  // http://code.google.com/p/inferno-os/source/browse/utils/5c/swt.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 <bio.h>
    34  #include <link.h>
    35  #include "../cmd/5l/5.out.h"
    36  #include "../pkg/runtime/stack.h"
    37  
    38  static Prog zprg = {
    39  	.as = AGOK,
    40  	.scond = C_SCOND_NONE,
    41  	.reg = NREG,
    42  	.from = {
    43  		.name = D_NONE,
    44  		.type = D_NONE,
    45  		.reg = NREG,
    46  	},
    47  	.to = {
    48  		.name = D_NONE,
    49  		.type = D_NONE,
    50  		.reg = NREG,
    51  	},
    52  };
    53  
    54  static int
    55  symtype(Addr *a)
    56  {
    57  	return a->name;
    58  }
    59  
    60  static int
    61  isdata(Prog *p)
    62  {
    63  	return p->as == ADATA || p->as == AGLOBL;
    64  }
    65  
    66  static int
    67  iscall(Prog *p)
    68  {
    69  	return p->as == ABL;
    70  }
    71  
    72  static int
    73  datasize(Prog *p)
    74  {
    75  	return p->reg;
    76  }
    77  
    78  static int
    79  textflag(Prog *p)
    80  {
    81  	return p->reg;
    82  }
    83  
    84  static void
    85  settextflag(Prog *p, int f)
    86  {
    87  	p->reg = f;
    88  }
    89  
    90  static void
    91  progedit(Link *ctxt, Prog *p)
    92  {
    93  	char literal[64];
    94  	LSym *s;
    95  	LSym *tlsfallback;
    96  
    97  	p->from.class = 0;
    98  	p->to.class = 0;
    99  
   100  	// Rewrite B/BL to symbol as D_BRANCH.
   101  	switch(p->as) {
   102  	case AB:
   103  	case ABL:
   104  	case ADUFFZERO:
   105  	case ADUFFCOPY:
   106  		if(p->to.type == D_OREG && (p->to.name == D_EXTERN || p->to.name == D_STATIC) && p->to.sym != nil)
   107  			p->to.type = D_BRANCH;
   108  		break;
   109  	}
   110  
   111  	// Replace TLS register fetches on older ARM procesors.
   112  	switch(p->as) {
   113  	case AMRC:
   114  		// If the instruction matches MRC 15, 0, <reg>, C13, C0, 3, replace it.
   115  		if(ctxt->goarm < 7 && (p->to.offset & 0xffff0fff) == 0xee1d0f70) {
   116  			tlsfallback = linklookup(ctxt, "runtime.read_tls_fallback", 0);
   117  
   118  			// BL runtime.read_tls_fallback(SB)
   119  			p->as = ABL;
   120  			p->to.type = D_BRANCH;
   121  			p->to.sym = tlsfallback;
   122  			p->to.offset = 0;
   123  		} else {
   124  			// Otherwise, MRC/MCR instructions need no further treatment.
   125  			p->as = AWORD;
   126  		}
   127  		break;
   128  	}
   129  
   130  	// Rewrite float constants to values stored in memory.
   131  	switch(p->as) {
   132  	case AMOVF:
   133  		if(p->from.type == D_FCONST && chipfloat5(ctxt, p->from.u.dval) < 0 &&
   134  		   (chipzero5(ctxt, p->from.u.dval) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) {
   135  			int32 i32;
   136  			float32 f32;
   137  			f32 = p->from.u.dval;
   138  			memmove(&i32, &f32, 4);
   139  			sprint(literal, "$f32.%08ux", (uint32)i32);
   140  			s = linklookup(ctxt, literal, 0);
   141  			if(s->type == 0) {
   142  				s->type = SRODATA;
   143  				adduint32(ctxt, s, i32);
   144  				s->reachable = 0;
   145  			}
   146  			p->from.type = D_OREG;
   147  			p->from.sym = s;
   148  			p->from.name = D_EXTERN;
   149  			p->from.offset = 0;
   150  		}
   151  		break;
   152  
   153  	case AMOVD:
   154  		if(p->from.type == D_FCONST && chipfloat5(ctxt, p->from.u.dval) < 0 &&
   155  		   (chipzero5(ctxt, p->from.u.dval) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) {
   156  			int64 i64;
   157  			memmove(&i64, &p->from.u.dval, 8);
   158  			sprint(literal, "$f64.%016llux", (uvlong)i64);
   159  			s = linklookup(ctxt, literal, 0);
   160  			if(s->type == 0) {
   161  				s->type = SRODATA;
   162  				adduint64(ctxt, s, i64);
   163  				s->reachable = 0;
   164  			}
   165  			p->from.type = D_OREG;
   166  			p->from.sym = s;
   167  			p->from.name = D_EXTERN;
   168  			p->from.offset = 0;
   169  		}
   170  		break;
   171  	}
   172  
   173  	if(ctxt->flag_shared) {
   174  		// Shared libraries use R_ARM_TLS_IE32 instead of 
   175  		// R_ARM_TLS_LE32, replacing the link time constant TLS offset in
   176  		// runtime.tlsgm with an address to a GOT entry containing the 
   177  		// offset. Rewrite $runtime.tlsgm(SB) to runtime.tlsgm(SB) to
   178  		// compensate.
   179  		if(ctxt->gmsym == nil)
   180  			ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0);
   181  
   182  		if(p->from.type == D_CONST && p->from.name == D_EXTERN && p->from.sym == ctxt->gmsym)
   183  			p->from.type = D_OREG;
   184  		if(p->to.type == D_CONST && p->to.name == D_EXTERN && p->to.sym == ctxt->gmsym)
   185  			p->to.type = D_OREG;
   186  	}
   187  }
   188  
   189  static Prog*
   190  prg(void)
   191  {
   192  	Prog *p;
   193  
   194  	p = emallocz(sizeof(*p));
   195  	*p = zprg;
   196  	return p;
   197  }
   198  
   199  static	Prog*	stacksplit(Link*, Prog*, int32, int);
   200  static	void		initdiv(Link*);
   201  static	void	softfloat(Link*, LSym*);
   202  
   203  // Prog.mark
   204  enum
   205  {
   206  	FOLL = 1<<0,
   207  	LABEL = 1<<1,
   208  	LEAF = 1<<2,
   209  };
   210  
   211  static void
   212  linkcase(Prog *casep)
   213  {
   214  	Prog *p;
   215  
   216  	for(p = casep; p != nil; p = p->link){
   217  		if(p->as == ABCASE) {
   218  			for(; p != nil && p->as == ABCASE; p = p->link)
   219  				p->pcrel = casep;
   220  			break;
   221  		}
   222  	}
   223  }
   224  
   225  static void
   226  nocache(Prog *p)
   227  {
   228  	p->optab = 0;
   229  	p->from.class = 0;
   230  	p->to.class = 0;
   231  }
   232  
   233  static void
   234  addstacksplit(Link *ctxt, LSym *cursym)
   235  {
   236  	Prog *p, *pl, *q, *q1, *q2;
   237  	int o;
   238  	int32 autosize, autoffset;
   239  	
   240  	autosize = 0;
   241  
   242  	if(ctxt->symmorestack[0] == nil) {
   243  		ctxt->symmorestack[0] = linklookup(ctxt, "runtime.morestack", 0);
   244  		ctxt->symmorestack[1] = linklookup(ctxt, "runtime.morestack_noctxt", 0);
   245  	}
   246  
   247  	q = nil;
   248  	
   249  	ctxt->cursym = cursym;
   250  
   251  	if(cursym->text == nil || cursym->text->link == nil)
   252  		return;				
   253  
   254  	softfloat(ctxt, cursym);
   255  
   256  	p = cursym->text;
   257  	autoffset = p->to.offset;
   258  	if(autoffset < 0)
   259  		autoffset = 0;
   260  	cursym->locals = autoffset;
   261  	cursym->args = p->to.offset2;
   262  
   263  	if(ctxt->debugzerostack) {
   264  		if(autoffset && !(p->reg&NOSPLIT)) {
   265  			// MOVW $4(R13), R1
   266  			p = appendp(ctxt, p);
   267  			p->as = AMOVW;
   268  			p->from.type = D_CONST;
   269  			p->from.reg = 13;
   270  			p->from.offset = 4;
   271  			p->to.type = D_REG;
   272  			p->to.reg = 1;
   273  	
   274  			// MOVW $n(R13), R2
   275  			p = appendp(ctxt, p);
   276  			p->as = AMOVW;
   277  			p->from.type = D_CONST;
   278  			p->from.reg = 13;
   279  			p->from.offset = 4 + autoffset;
   280  			p->to.type = D_REG;
   281  			p->to.reg = 2;
   282  	
   283  			// MOVW $0, R3
   284  			p = appendp(ctxt, p);
   285  			p->as = AMOVW;
   286  			p->from.type = D_CONST;
   287  			p->from.offset = 0;
   288  			p->to.type = D_REG;
   289  			p->to.reg = 3;
   290  	
   291  			// L:
   292  			//	MOVW.nil R3, 0(R1) +4
   293  			//	CMP R1, R2
   294  			//	BNE L
   295  			p = pl = appendp(ctxt, p);
   296  			p->as = AMOVW;
   297  			p->from.type = D_REG;
   298  			p->from.reg = 3;
   299  			p->to.type = D_OREG;
   300  			p->to.reg = 1;
   301  			p->to.offset = 4;
   302  			p->scond |= C_PBIT;
   303  	
   304  			p = appendp(ctxt, p);
   305  			p->as = ACMP;
   306  			p->from.type = D_REG;
   307  			p->from.reg = 1;
   308  			p->reg = 2;
   309  	
   310  			p = appendp(ctxt, p);
   311  			p->as = ABNE;
   312  			p->to.type = D_BRANCH;
   313  			p->pcond = pl;
   314  		}
   315  	}
   316  
   317  	/*
   318  	 * find leaf subroutines
   319  	 * strip NOPs
   320  	 * expand RET
   321  	 * expand BECOME pseudo
   322  	 */
   323  
   324  	for(p = cursym->text; p != nil; p = p->link) {
   325  		switch(p->as) {
   326  		case ACASE:
   327  			if(ctxt->flag_shared)
   328  				linkcase(p);
   329  			break;
   330  
   331  		case ATEXT:
   332  			p->mark |= LEAF;
   333  			break;
   334  
   335  		case ARET:
   336  			break;
   337  
   338  		case ADIV:
   339  		case ADIVU:
   340  		case AMOD:
   341  		case AMODU:
   342  			q = p;
   343  			if(ctxt->sym_div == nil)
   344  				initdiv(ctxt);
   345  			cursym->text->mark &= ~LEAF;
   346  			continue;
   347  
   348  		case ANOP:
   349  			q1 = p->link;
   350  			q->link = q1;		/* q is non-nop */
   351  			if(q1 != nil)
   352  				q1->mark |= p->mark;
   353  			continue;
   354  
   355  		case ABL:
   356  		case ABX:
   357  		case ADUFFZERO:
   358  		case ADUFFCOPY:
   359  			cursym->text->mark &= ~LEAF;
   360  
   361  		case ABCASE:
   362  		case AB:
   363  
   364  		case ABEQ:
   365  		case ABNE:
   366  		case ABCS:
   367  		case ABHS:
   368  		case ABCC:
   369  		case ABLO:
   370  		case ABMI:
   371  		case ABPL:
   372  		case ABVS:
   373  		case ABVC:
   374  		case ABHI:
   375  		case ABLS:
   376  		case ABGE:
   377  		case ABLT:
   378  		case ABGT:
   379  		case ABLE:
   380  			q1 = p->pcond;
   381  			if(q1 != nil) {
   382  				while(q1->as == ANOP) {
   383  					q1 = q1->link;
   384  					p->pcond = q1;
   385  				}
   386  			}
   387  			break;
   388  		}
   389  		q = p;
   390  	}
   391  
   392  	for(p = cursym->text; p != nil; p = p->link) {
   393  		o = p->as;
   394  		switch(o) {
   395  		case ATEXT:
   396  			autosize = p->to.offset + 4;
   397  			if(autosize <= 4)
   398  			if(cursym->text->mark & LEAF) {
   399  				p->to.offset = -4;
   400  				autosize = 0;
   401  			}
   402  
   403  			if(!autosize && !(cursym->text->mark & LEAF)) {
   404  				if(ctxt->debugvlog) {
   405  					Bprint(ctxt->bso, "save suppressed in: %s\n",
   406  						cursym->name);
   407  					Bflush(ctxt->bso);
   408  				}
   409  				cursym->text->mark |= LEAF;
   410  			}
   411  			if(cursym->text->mark & LEAF) {
   412  				cursym->leaf = 1;
   413  				if(!autosize)
   414  					break;
   415  			}
   416  
   417  			if(!(p->reg & NOSPLIT))
   418  				p = stacksplit(ctxt, p, autosize, !(cursym->text->reg&NEEDCTXT)); // emit split check
   419  			
   420  			// MOVW.W		R14,$-autosize(SP)
   421  			p = appendp(ctxt, p);
   422  			p->as = AMOVW;
   423  			p->scond |= C_WBIT;
   424  			p->from.type = D_REG;
   425  			p->from.reg = REGLINK;
   426  			p->to.type = D_OREG;
   427  			p->to.offset = -autosize;
   428  			p->to.reg = REGSP;
   429  			p->spadj = autosize;
   430  			
   431  			if(cursym->text->reg & WRAPPER) {
   432  				// g->panicwrap += autosize;
   433  				// MOVW panicwrap_offset(g), R3
   434  				// ADD $autosize, R3
   435  				// MOVW R3 panicwrap_offset(g)
   436  				p = appendp(ctxt, p);
   437  				p->as = AMOVW;
   438  				p->from.type = D_OREG;
   439  				p->from.reg = REGG;
   440  				p->from.offset = 2*ctxt->arch->ptrsize;
   441  				p->to.type = D_REG;
   442  				p->to.reg = 3;
   443  			
   444  				p = appendp(ctxt, p);
   445  				p->as = AADD;
   446  				p->from.type = D_CONST;
   447  				p->from.offset = autosize;
   448  				p->to.type = D_REG;
   449  				p->to.reg = 3;
   450  				
   451  				p = appendp(ctxt, p);
   452  				p->as = AMOVW;
   453  				p->from.type = D_REG;
   454  				p->from.reg = 3;
   455  				p->to.type = D_OREG;
   456  				p->to.reg = REGG;
   457  				p->to.offset = 2*ctxt->arch->ptrsize;
   458  			}
   459  			break;
   460  
   461  		case ARET:
   462  			nocache(p);
   463  			if(cursym->text->mark & LEAF) {
   464  				if(!autosize) {
   465  					p->as = AB;
   466  					p->from = zprg.from;
   467  					if(p->to.sym) { // retjmp
   468  						p->to.type = D_BRANCH;
   469  					} else {
   470  						p->to.type = D_OREG;
   471  						p->to.offset = 0;
   472  						p->to.reg = REGLINK;
   473  					}
   474  					break;
   475  				}
   476  			}
   477  
   478  			if(cursym->text->reg & WRAPPER) {
   479  				int scond;
   480  				
   481  				// Preserve original RET's cond, to allow RET.EQ
   482  				// in the implementation of reflect.call.
   483  				scond = p->scond;
   484  				p->scond = C_SCOND_NONE;
   485  
   486  				// g->panicwrap -= autosize;
   487  				// MOVW panicwrap_offset(g), R3
   488  				// SUB $autosize, R3
   489  				// MOVW R3 panicwrap_offset(g)
   490  				p->as = AMOVW;
   491  				p->from.type = D_OREG;
   492  				p->from.reg = REGG;
   493  				p->from.offset = 2*ctxt->arch->ptrsize;
   494  				p->to.type = D_REG;
   495  				p->to.reg = 3;
   496  				p = appendp(ctxt, p);
   497  			
   498  				p->as = ASUB;
   499  				p->from.type = D_CONST;
   500  				p->from.offset = autosize;
   501  				p->to.type = D_REG;
   502  				p->to.reg = 3;
   503  				p = appendp(ctxt, p);
   504  
   505  				p->as = AMOVW;
   506  				p->from.type = D_REG;
   507  				p->from.reg = 3;
   508  				p->to.type = D_OREG;
   509  				p->to.reg = REGG;
   510  				p->to.offset = 2*ctxt->arch->ptrsize;
   511  				p = appendp(ctxt, p);
   512  
   513  				p->scond = scond;
   514  			}
   515  
   516  			p->as = AMOVW;
   517  			p->scond |= C_PBIT;
   518  			p->from.type = D_OREG;
   519  			p->from.offset = autosize;
   520  			p->from.reg = REGSP;
   521  			p->to.type = D_REG;
   522  			p->to.reg = REGPC;
   523  			// If there are instructions following
   524  			// this ARET, they come from a branch
   525  			// with the same stackframe, so no spadj.
   526  			
   527  			if(p->to.sym) { // retjmp
   528  				p->to.reg = REGLINK;
   529  				q2 = appendp(ctxt, p);
   530  				q2->as = AB;
   531  				q2->to.type = D_BRANCH;
   532  				q2->to.sym = p->to.sym;
   533  				p->to.sym = nil;
   534  				p = q2;
   535  			}
   536  			break;
   537  
   538  		case AADD:
   539  			if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP)
   540  				p->spadj = -p->from.offset;
   541  			break;
   542  
   543  		case ASUB:
   544  			if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP)
   545  				p->spadj = p->from.offset;
   546  			break;
   547  
   548  		case ADIV:
   549  		case ADIVU:
   550  		case AMOD:
   551  		case AMODU:
   552  			if(ctxt->debugdivmod)
   553  				break;
   554  			if(p->from.type != D_REG)
   555  				break;
   556  			if(p->to.type != D_REG)
   557  				break;
   558  			q1 = p;
   559  
   560  			/* MOV a,4(SP) */
   561  			p = appendp(ctxt, p);
   562  			p->as = AMOVW;
   563  			p->lineno = q1->lineno;
   564  			p->from.type = D_REG;
   565  			p->from.reg = q1->from.reg;
   566  			p->to.type = D_OREG;
   567  			p->to.reg = REGSP;
   568  			p->to.offset = 4;
   569  
   570  			/* MOV b,REGTMP */
   571  			p = appendp(ctxt, p);
   572  			p->as = AMOVW;
   573  			p->lineno = q1->lineno;
   574  			p->from.type = D_REG;
   575  			p->from.reg = q1->reg;
   576  			if(q1->reg == NREG)
   577  				p->from.reg = q1->to.reg;
   578  			p->to.type = D_REG;
   579  			p->to.reg = REGTMP;
   580  			p->to.offset = 0;
   581  
   582  			/* CALL appropriate */
   583  			p = appendp(ctxt, p);
   584  			p->as = ABL;
   585  			p->lineno = q1->lineno;
   586  			p->to.type = D_BRANCH;
   587  			switch(o) {
   588  			case ADIV:
   589  				p->to.sym = ctxt->sym_div;
   590  				break;
   591  			case ADIVU:
   592  				p->to.sym = ctxt->sym_divu;
   593  				break;
   594  			case AMOD:
   595  				p->to.sym = ctxt->sym_mod;
   596  				break;
   597  			case AMODU:
   598  				p->to.sym = ctxt->sym_modu;
   599  				break;
   600  			}
   601  
   602  			/* MOV REGTMP, b */
   603  			p = appendp(ctxt, p);
   604  			p->as = AMOVW;
   605  			p->lineno = q1->lineno;
   606  			p->from.type = D_REG;
   607  			p->from.reg = REGTMP;
   608  			p->from.offset = 0;
   609  			p->to.type = D_REG;
   610  			p->to.reg = q1->to.reg;
   611  
   612  			/* ADD $8,SP */
   613  			p = appendp(ctxt, p);
   614  			p->as = AADD;
   615  			p->lineno = q1->lineno;
   616  			p->from.type = D_CONST;
   617  			p->from.reg = NREG;
   618  			p->from.offset = 8;
   619  			p->reg = NREG;
   620  			p->to.type = D_REG;
   621  			p->to.reg = REGSP;
   622  			p->spadj = -8;
   623  
   624  			/* Keep saved LR at 0(SP) after SP change. */
   625  			/* MOVW 0(SP), REGTMP; MOVW REGTMP, -8!(SP) */
   626  			/* TODO: Remove SP adjustments; see issue 6699. */
   627  			q1->as = AMOVW;
   628  			q1->from.type = D_OREG;
   629  			q1->from.reg = REGSP;
   630  			q1->from.offset = 0;
   631  			q1->reg = NREG;
   632  			q1->to.type = D_REG;
   633  			q1->to.reg = REGTMP;
   634  
   635  			/* SUB $8,SP */
   636  			q1 = appendp(ctxt, q1);
   637  			q1->as = AMOVW;
   638  			q1->from.type = D_REG;
   639  			q1->from.reg = REGTMP;
   640  			q1->reg = NREG;
   641  			q1->to.type = D_OREG;
   642  			q1->to.reg = REGSP;
   643  			q1->to.offset = -8;
   644  			q1->scond |= C_WBIT;
   645  			q1->spadj = 8;
   646  
   647  			break;
   648  		case AMOVW:
   649  			if((p->scond & C_WBIT) && p->to.type == D_OREG && p->to.reg == REGSP)
   650  				p->spadj = -p->to.offset;
   651  			if((p->scond & C_PBIT) && p->from.type == D_OREG && p->from.reg == REGSP && p->to.reg != REGPC)
   652  				p->spadj = -p->from.offset;
   653  			if(p->from.type == D_CONST && p->from.reg == REGSP && p->to.type == D_REG && p->to.reg == REGSP)
   654  				p->spadj = -p->from.offset;
   655  			break;
   656  		}
   657  	}
   658  }
   659  
   660  static void
   661  softfloat(Link *ctxt, LSym *cursym)
   662  {
   663  	Prog *p, *next;
   664  	LSym *symsfloat;
   665  	int wasfloat;
   666  
   667  	if(ctxt->goarm > 5)
   668  		return;
   669  
   670  	symsfloat = linklookup(ctxt, "_sfloat", 0);
   671  
   672  	wasfloat = 0;
   673  	for(p = cursym->text; p != nil; p = p->link)
   674  		if(p->pcond != nil)
   675  			p->pcond->mark |= LABEL;
   676  	for(p = cursym->text; p != nil; p = p->link) {
   677  		switch(p->as) {
   678  		case AMOVW:
   679  			if(p->to.type == D_FREG || p->from.type == D_FREG)
   680  				goto soft;
   681  			goto notsoft;
   682  
   683  		case AMOVWD:
   684  		case AMOVWF:
   685  		case AMOVDW:
   686  		case AMOVFW:
   687  		case AMOVFD:
   688  		case AMOVDF:
   689  		case AMOVF:
   690  		case AMOVD:
   691  
   692  		case ACMPF:
   693  		case ACMPD:
   694  		case AADDF:
   695  		case AADDD:
   696  		case ASUBF:
   697  		case ASUBD:
   698  		case AMULF:
   699  		case AMULD:
   700  		case ADIVF:
   701  		case ADIVD:
   702  		case ASQRTF:
   703  		case ASQRTD:
   704  		case AABSF:
   705  		case AABSD:
   706  			goto soft;
   707  
   708  		default:
   709  			goto notsoft;
   710  
   711  		soft:
   712  			if (!wasfloat || (p->mark&LABEL)) {
   713  				next = ctxt->arch->prg();
   714  				*next = *p;
   715  
   716  				// BL _sfloat(SB)
   717  				*p = zprg;
   718  				p->link = next;
   719  				p->as = ABL;
   720   				p->to.type = D_BRANCH;
   721  				p->to.sym = symsfloat;
   722  				p->lineno = next->lineno;
   723  
   724  				p = next;
   725  				wasfloat = 1;
   726  			}
   727  			break;
   728  
   729  		notsoft:
   730  			wasfloat = 0;
   731  		}
   732  	}
   733  }
   734  
   735  static Prog*
   736  stacksplit(Link *ctxt, Prog *p, int32 framesize, int noctxt)
   737  {
   738  	int32 arg;
   739  
   740  	// MOVW			g_stackguard(g), R1
   741  	p = appendp(ctxt, p);
   742  	p->as = AMOVW;
   743  	p->from.type = D_OREG;
   744  	p->from.reg = REGG;
   745  	p->to.type = D_REG;
   746  	p->to.reg = 1;
   747  	
   748  	if(framesize <= StackSmall) {
   749  		// small stack: SP < stackguard
   750  		//	CMP	stackguard, SP
   751  		p = appendp(ctxt, p);
   752  		p->as = ACMP;
   753  		p->from.type = D_REG;
   754  		p->from.reg = 1;
   755  		p->reg = REGSP;
   756  	} else if(framesize <= StackBig) {
   757  		// large stack: SP-framesize < stackguard-StackSmall
   758  		//	MOVW $-framesize(SP), R2
   759  		//	CMP stackguard, R2
   760  		p = appendp(ctxt, p);
   761  		p->as = AMOVW;
   762  		p->from.type = D_CONST;
   763  		p->from.reg = REGSP;
   764  		p->from.offset = -framesize;
   765  		p->to.type = D_REG;
   766  		p->to.reg = 2;
   767  		
   768  		p = appendp(ctxt, p);
   769  		p->as = ACMP;
   770  		p->from.type = D_REG;
   771  		p->from.reg = 1;
   772  		p->reg = 2;
   773  	} else {
   774  		// Such a large stack we need to protect against wraparound
   775  		// if SP is close to zero.
   776  		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
   777  		// The +StackGuard on both sides is required to keep the left side positive:
   778  		// SP is allowed to be slightly below stackguard. See stack.h.
   779  		//	CMP $StackPreempt, R1
   780  		//	MOVW.NE $StackGuard(SP), R2
   781  		//	SUB.NE R1, R2
   782  		//	MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
   783  		//	CMP.NE R3, R2
   784  		p = appendp(ctxt, p);
   785  		p->as = ACMP;
   786  		p->from.type = D_CONST;
   787  		p->from.offset = (uint32)StackPreempt;
   788  		p->reg = 1;
   789  
   790  		p = appendp(ctxt, p);
   791  		p->as = AMOVW;
   792  		p->from.type = D_CONST;
   793  		p->from.reg = REGSP;
   794  		p->from.offset = StackGuard;
   795  		p->to.type = D_REG;
   796  		p->to.reg = 2;
   797  		p->scond = C_SCOND_NE;
   798  		
   799  		p = appendp(ctxt, p);
   800  		p->as = ASUB;
   801  		p->from.type = D_REG;
   802  		p->from.reg = 1;
   803  		p->to.type = D_REG;
   804  		p->to.reg = 2;
   805  		p->scond = C_SCOND_NE;
   806  		
   807  		p = appendp(ctxt, p);
   808  		p->as = AMOVW;
   809  		p->from.type = D_CONST;
   810  		p->from.offset = framesize + (StackGuard - StackSmall);
   811  		p->to.type = D_REG;
   812  		p->to.reg = 3;
   813  		p->scond = C_SCOND_NE;
   814  		
   815  		p = appendp(ctxt, p);
   816  		p->as = ACMP;
   817  		p->from.type = D_REG;
   818  		p->from.reg = 3;
   819  		p->reg = 2;
   820  		p->scond = C_SCOND_NE;
   821  	}
   822  	
   823  	// MOVW.LS		$framesize, R1
   824  	p = appendp(ctxt, p);
   825  	p->as = AMOVW;
   826  	p->scond = C_SCOND_LS;
   827  	p->from.type = D_CONST;
   828  	p->from.offset = framesize;
   829  	p->to.type = D_REG;
   830  	p->to.reg = 1;
   831  
   832  	// MOVW.LS		$args, R2
   833  	p = appendp(ctxt, p);
   834  	p->as = AMOVW;
   835  	p->scond = C_SCOND_LS;
   836  	p->from.type = D_CONST;
   837  	arg = ctxt->cursym->text->to.offset2;
   838  	if(arg == 1) // special marker for known 0
   839  		arg = 0;
   840  	if(arg&3)
   841  		ctxt->diag("misaligned argument size in stack split");
   842  	p->from.offset = arg;
   843  	p->to.type = D_REG;
   844  	p->to.reg = 2;
   845  
   846  	// MOVW.LS	R14, R3
   847  	p = appendp(ctxt, p);
   848  	p->as = AMOVW;
   849  	p->scond = C_SCOND_LS;
   850  	p->from.type = D_REG;
   851  	p->from.reg = REGLINK;
   852  	p->to.type = D_REG;
   853  	p->to.reg = 3;
   854  
   855  	// BL.LS		runtime.morestack(SB) // modifies LR, returns with LO still asserted
   856  	p = appendp(ctxt, p);
   857  	p->as = ABL;
   858  	p->scond = C_SCOND_LS;
   859  	p->to.type = D_BRANCH;
   860  	p->to.sym = ctxt->symmorestack[noctxt];
   861  	
   862  	// BLS	start
   863  	p = appendp(ctxt, p);
   864  	p->as = ABLS;
   865  	p->to.type = D_BRANCH;
   866  	p->pcond = ctxt->cursym->text->link;
   867  	
   868  	return p;
   869  }
   870  
   871  static void
   872  initdiv(Link *ctxt)
   873  {
   874  	if(ctxt->sym_div != nil)
   875  		return;
   876  	ctxt->sym_div = linklookup(ctxt, "_div", 0);
   877  	ctxt->sym_divu = linklookup(ctxt, "_divu", 0);
   878  	ctxt->sym_mod = linklookup(ctxt, "_mod", 0);
   879  	ctxt->sym_modu = linklookup(ctxt, "_modu", 0);
   880  }
   881  
   882  static void xfol(Link*, Prog*, Prog**);
   883  
   884  static void
   885  follow(Link *ctxt, LSym *s)
   886  {
   887  	Prog *firstp, *lastp;
   888  
   889  	ctxt->cursym = s;
   890  
   891  	firstp = ctxt->arch->prg();
   892  	lastp = firstp;
   893  	xfol(ctxt, s->text, &lastp);
   894  	lastp->link = nil;
   895  	s->text = firstp->link;
   896  }
   897  
   898  static int
   899  relinv(int a)
   900  {
   901  	switch(a) {
   902  	case ABEQ:	return ABNE;
   903  	case ABNE:	return ABEQ;
   904  	case ABCS:	return ABCC;
   905  	case ABHS:	return ABLO;
   906  	case ABCC:	return ABCS;
   907  	case ABLO:	return ABHS;
   908  	case ABMI:	return ABPL;
   909  	case ABPL:	return ABMI;
   910  	case ABVS:	return ABVC;
   911  	case ABVC:	return ABVS;
   912  	case ABHI:	return ABLS;
   913  	case ABLS:	return ABHI;
   914  	case ABGE:	return ABLT;
   915  	case ABLT:	return ABGE;
   916  	case ABGT:	return ABLE;
   917  	case ABLE:	return ABGT;
   918  	}
   919  	sysfatal("unknown relation: %s", anames5[a]);
   920  	return 0;
   921  }
   922  
   923  static void
   924  xfol(Link *ctxt, Prog *p, Prog **last)
   925  {
   926  	Prog *q, *r;
   927  	int a, i;
   928  
   929  loop:
   930  	if(p == nil)
   931  		return;
   932  	a = p->as;
   933  	if(a == AB) {
   934  		q = p->pcond;
   935  		if(q != nil && q->as != ATEXT) {
   936  			p->mark |= FOLL;
   937  			p = q;
   938  			if(!(p->mark & FOLL))
   939  				goto loop;
   940  		}
   941  	}
   942  	if(p->mark & FOLL) {
   943  		for(i=0,q=p; i<4; i++,q=q->link) {
   944  			if(q == *last || q == nil)
   945  				break;
   946  			a = q->as;
   947  			if(a == ANOP) {
   948  				i--;
   949  				continue;
   950  			}
   951  			if(a == AB || (a == ARET && q->scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF)
   952  				goto copy;
   953  			if(q->pcond == nil || (q->pcond->mark&FOLL))
   954  				continue;
   955  			if(a != ABEQ && a != ABNE)
   956  				continue;
   957  		copy:
   958  			for(;;) {
   959  				r = ctxt->arch->prg();
   960  				*r = *p;
   961  				if(!(r->mark&FOLL))
   962  					print("can't happen 1\n");
   963  				r->mark |= FOLL;
   964  				if(p != q) {
   965  					p = p->link;
   966  					(*last)->link = r;
   967  					*last = r;
   968  					continue;
   969  				}
   970  				(*last)->link = r;
   971  				*last = r;
   972  				if(a == AB || (a == ARET && q->scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF)
   973  					return;
   974  				r->as = ABNE;
   975  				if(a == ABNE)
   976  					r->as = ABEQ;
   977  				r->pcond = p->link;
   978  				r->link = p->pcond;
   979  				if(!(r->link->mark&FOLL))
   980  					xfol(ctxt, r->link, last);
   981  				if(!(r->pcond->mark&FOLL))
   982  					print("can't happen 2\n");
   983  				return;
   984  			}
   985  		}
   986  		a = AB;
   987  		q = ctxt->arch->prg();
   988  		q->as = a;
   989  		q->lineno = p->lineno;
   990  		q->to.type = D_BRANCH;
   991  		q->to.offset = p->pc;
   992  		q->pcond = p;
   993  		p = q;
   994  	}
   995  	p->mark |= FOLL;
   996  	(*last)->link = p;
   997  	*last = p;
   998  	if(a == AB || (a == ARET && p->scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF){
   999  		return;
  1000  	}
  1001  	if(p->pcond != nil)
  1002  	if(a != ABL && a != ABX && p->link != nil) {
  1003  		q = brchain(ctxt, p->link);
  1004  		if(a != ATEXT && a != ABCASE)
  1005  		if(q != nil && (q->mark&FOLL)) {
  1006  			p->as = relinv(a);
  1007  			p->link = p->pcond;
  1008  			p->pcond = q;
  1009  		}
  1010  		xfol(ctxt, p->link, last);
  1011  		q = brchain(ctxt, p->pcond);
  1012  		if(q == nil)
  1013  			q = p->pcond;
  1014  		if(q->mark&FOLL) {
  1015  			p->pcond = q;
  1016  			return;
  1017  		}
  1018  		p = q;
  1019  		goto loop;
  1020  	}
  1021  	p = p->link;
  1022  	goto loop;
  1023  }
  1024  
  1025  LinkArch linkarm = {
  1026  	.name = "arm",
  1027  	.thechar = '5',
  1028  
  1029  	.addstacksplit = addstacksplit,
  1030  	.assemble = span5,
  1031  	.datasize = datasize,
  1032  	.follow = follow,
  1033  	.iscall = iscall,
  1034  	.isdata = isdata,
  1035  	.prg = prg,
  1036  	.progedit = progedit,
  1037  	.settextflag = settextflag,
  1038  	.symtype = symtype,
  1039  	.textflag = textflag,
  1040  
  1041  	.minlc = 4,
  1042  	.ptrsize = 4,
  1043  	.regsize = 4,
  1044  
  1045  	.D_ADDR = D_ADDR,
  1046  	.D_AUTO = D_AUTO,
  1047  	.D_BRANCH = D_BRANCH,
  1048  	.D_CONST = D_CONST,
  1049  	.D_EXTERN = D_EXTERN,
  1050  	.D_FCONST = D_FCONST,
  1051  	.D_NONE = D_NONE,
  1052  	.D_PARAM = D_PARAM,
  1053  	.D_SCONST = D_SCONST,
  1054  	.D_STATIC = D_STATIC,
  1055  
  1056  	.ACALL = ABL,
  1057  	.ADATA = ADATA,
  1058  	.AEND = AEND,
  1059  	.AFUNCDATA = AFUNCDATA,
  1060  	.AGLOBL = AGLOBL,
  1061  	.AJMP = AB,
  1062  	.ANOP = ANOP,
  1063  	.APCDATA = APCDATA,
  1064  	.ARET = ARET,
  1065  	.ATEXT = ATEXT,
  1066  	.ATYPE = ATYPE,
  1067  	.AUSEFIELD = AUSEFIELD,
  1068  };