github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/cmd/internal/obj/s390x/objz.go (about)

     1  // Based on cmd/internal/obj/ppc64/obj9.go.
     2  //
     3  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     4  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     5  //	Portions Copyright © 1997-1999 Vita Nuova Limited
     6  //	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
     7  //	Portions Copyright © 2004,2006 Bruce Ellis
     8  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
     9  //	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
    10  //	Portions Copyright © 2009 The Go Authors. All rights reserved.
    11  //
    12  // Permission is hereby granted, free of charge, to any person obtaining a copy
    13  // of this software and associated documentation files (the "Software"), to deal
    14  // in the Software without restriction, including without limitation the rights
    15  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    16  // copies of the Software, and to permit persons to whom the Software is
    17  // furnished to do so, subject to the following conditions:
    18  //
    19  // The above copyright notice and this permission notice shall be included in
    20  // all copies or substantial portions of the Software.
    21  //
    22  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    23  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    24  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    25  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    26  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    27  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    28  // THE SOFTWARE.
    29  
    30  package s390x
    31  
    32  import (
    33  	"cmd/internal/obj"
    34  	"cmd/internal/sys"
    35  	"fmt"
    36  	"math"
    37  )
    38  
    39  func progedit(ctxt *obj.Link, p *obj.Prog) {
    40  	p.From.Class = 0
    41  	p.To.Class = 0
    42  
    43  	// Rewrite BR/BL to symbol as TYPE_BRANCH.
    44  	switch p.As {
    45  	case ABR,
    46  		ABL,
    47  		obj.ARET,
    48  		obj.ADUFFZERO,
    49  		obj.ADUFFCOPY:
    50  		if p.To.Sym != nil {
    51  			p.To.Type = obj.TYPE_BRANCH
    52  		}
    53  	}
    54  
    55  	// Rewrite float constants to values stored in memory unless they are +0.
    56  	switch p.As {
    57  	case AFMOVS:
    58  		if p.From.Type == obj.TYPE_FCONST {
    59  			f32 := float32(p.From.Val.(float64))
    60  			i32 := math.Float32bits(f32)
    61  			if i32 == 0 { // +0
    62  				break
    63  			}
    64  			literal := fmt.Sprintf("$f32.%08x", i32)
    65  			s := obj.Linklookup(ctxt, literal, 0)
    66  			s.Size = 4
    67  			p.From.Type = obj.TYPE_MEM
    68  			p.From.Sym = s
    69  			p.From.Sym.Local = true
    70  			p.From.Name = obj.NAME_EXTERN
    71  			p.From.Offset = 0
    72  		}
    73  
    74  	case AFMOVD:
    75  		if p.From.Type == obj.TYPE_FCONST {
    76  			i64 := math.Float64bits(p.From.Val.(float64))
    77  			if i64 == 0 { // +0
    78  				break
    79  			}
    80  			literal := fmt.Sprintf("$f64.%016x", i64)
    81  			s := obj.Linklookup(ctxt, literal, 0)
    82  			s.Size = 8
    83  			p.From.Type = obj.TYPE_MEM
    84  			p.From.Sym = s
    85  			p.From.Sym.Local = true
    86  			p.From.Name = obj.NAME_EXTERN
    87  			p.From.Offset = 0
    88  		}
    89  
    90  		// put constants not loadable by LOAD IMMEDIATE into memory
    91  	case AMOVD:
    92  		if p.From.Type == obj.TYPE_CONST {
    93  			val := p.From.Offset
    94  			if int64(int32(val)) != val &&
    95  				int64(uint32(val)) != val &&
    96  				int64(uint64(val)&(0xffffffff<<32)) != val {
    97  				literal := fmt.Sprintf("$i64.%016x", uint64(p.From.Offset))
    98  				s := obj.Linklookup(ctxt, literal, 0)
    99  				s.Size = 8
   100  				p.From.Type = obj.TYPE_MEM
   101  				p.From.Sym = s
   102  				p.From.Sym.Local = true
   103  				p.From.Name = obj.NAME_EXTERN
   104  				p.From.Offset = 0
   105  			}
   106  		}
   107  	}
   108  
   109  	// Rewrite SUB constants into ADD.
   110  	switch p.As {
   111  	case ASUBC:
   112  		if p.From.Type == obj.TYPE_CONST {
   113  			p.From.Offset = -p.From.Offset
   114  			p.As = AADDC
   115  		}
   116  
   117  	case ASUB:
   118  		if p.From.Type == obj.TYPE_CONST {
   119  			p.From.Offset = -p.From.Offset
   120  			p.As = AADD
   121  		}
   122  	}
   123  
   124  	if ctxt.Flag_dynlink {
   125  		rewriteToUseGot(ctxt, p)
   126  	}
   127  }
   128  
   129  // Rewrite p, if necessary, to access global data via the global offset table.
   130  func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
   131  	// At the moment EXRL instructions are not emitted by the compiler and only reference local symbols in
   132  	// assembly code.
   133  	if p.As == AEXRL {
   134  		return
   135  	}
   136  
   137  	// We only care about global data: NAME_EXTERN means a global
   138  	// symbol in the Go sense, and p.Sym.Local is true for a few
   139  	// internally defined symbols.
   140  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   141  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   142  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   143  		if p.To.Type != obj.TYPE_REG || p.As != AMOVD {
   144  			ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
   145  		}
   146  		p.From.Type = obj.TYPE_MEM
   147  		p.From.Name = obj.NAME_GOTREF
   148  		q := p
   149  		if p.From.Offset != 0 {
   150  			q = obj.Appendp(ctxt, p)
   151  			q.As = AADD
   152  			q.From.Type = obj.TYPE_CONST
   153  			q.From.Offset = p.From.Offset
   154  			q.To = p.To
   155  			p.From.Offset = 0
   156  		}
   157  	}
   158  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   159  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   160  	}
   161  	var source *obj.Addr
   162  	// MOVD sym, Ry becomes MOVD sym@GOT, REGTMP; MOVD (REGTMP), Ry
   163  	// MOVD Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
   164  	// An addition may be inserted between the two MOVs if there is an offset.
   165  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   166  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   167  			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   168  		}
   169  		source = &p.From
   170  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   171  		source = &p.To
   172  	} else {
   173  		return
   174  	}
   175  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   176  		return
   177  	}
   178  	if source.Sym.Type == obj.STLSBSS {
   179  		return
   180  	}
   181  	if source.Type != obj.TYPE_MEM {
   182  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   183  	}
   184  	p1 := obj.Appendp(ctxt, p)
   185  	p2 := obj.Appendp(ctxt, p1)
   186  
   187  	p1.As = AMOVD
   188  	p1.From.Type = obj.TYPE_MEM
   189  	p1.From.Sym = source.Sym
   190  	p1.From.Name = obj.NAME_GOTREF
   191  	p1.To.Type = obj.TYPE_REG
   192  	p1.To.Reg = REGTMP
   193  
   194  	p2.As = p.As
   195  	p2.From = p.From
   196  	p2.To = p.To
   197  	if p.From.Name == obj.NAME_EXTERN {
   198  		p2.From.Reg = REGTMP
   199  		p2.From.Name = obj.NAME_NONE
   200  		p2.From.Sym = nil
   201  	} else if p.To.Name == obj.NAME_EXTERN {
   202  		p2.To.Reg = REGTMP
   203  		p2.To.Name = obj.NAME_NONE
   204  		p2.To.Sym = nil
   205  	} else {
   206  		return
   207  	}
   208  	obj.Nopout(p)
   209  }
   210  
   211  func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
   212  	// TODO(minux): add morestack short-cuts with small fixed frame-size.
   213  	ctxt.Cursym = cursym
   214  
   215  	if cursym.Text == nil || cursym.Text.Link == nil {
   216  		return
   217  	}
   218  
   219  	p := cursym.Text
   220  	textstksiz := p.To.Offset
   221  	if textstksiz == -8 {
   222  		// Compatibility hack.
   223  		p.From3.Offset |= obj.NOFRAME
   224  		textstksiz = 0
   225  	}
   226  	if textstksiz%8 != 0 {
   227  		ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
   228  	}
   229  	if p.From3.Offset&obj.NOFRAME != 0 {
   230  		if textstksiz != 0 {
   231  			ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   232  		}
   233  	}
   234  
   235  	cursym.Args = p.To.Val.(int32)
   236  	cursym.Locals = int32(textstksiz)
   237  
   238  	/*
   239  	 * find leaf subroutines
   240  	 * strip NOPs
   241  	 * expand RET
   242  	 * expand BECOME pseudo
   243  	 */
   244  	if ctxt.Debugvlog != 0 {
   245  		fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", obj.Cputime())
   246  	}
   247  	ctxt.Bso.Flush()
   248  
   249  	var q *obj.Prog
   250  	var q1 *obj.Prog
   251  	for p := cursym.Text; p != nil; p = p.Link {
   252  		switch p.As {
   253  		/* too hard, just leave alone */
   254  		case obj.ATEXT:
   255  			q = p
   256  
   257  			p.Mark |= LABEL | LEAF | SYNC
   258  			if p.Link != nil {
   259  				p.Link.Mark |= LABEL
   260  			}
   261  
   262  		case ANOR:
   263  			q = p
   264  			if p.To.Type == obj.TYPE_REG {
   265  				if p.To.Reg == REGZERO {
   266  					p.Mark |= LABEL | SYNC
   267  				}
   268  			}
   269  
   270  		case ASYNC,
   271  			AWORD:
   272  			q = p
   273  			p.Mark |= LABEL | SYNC
   274  			continue
   275  
   276  		case AMOVW, AMOVWZ, AMOVD:
   277  			q = p
   278  			if p.From.Reg >= REG_RESERVED || p.To.Reg >= REG_RESERVED {
   279  				p.Mark |= LABEL | SYNC
   280  			}
   281  			continue
   282  
   283  		case AFABS,
   284  			AFADD,
   285  			AFDIV,
   286  			AFMADD,
   287  			AFMOVD,
   288  			AFMOVS,
   289  			AFMSUB,
   290  			AFMUL,
   291  			AFNABS,
   292  			AFNEG,
   293  			AFNMADD,
   294  			AFNMSUB,
   295  			ALEDBR,
   296  			ALDEBR,
   297  			AFSUB:
   298  			q = p
   299  
   300  			p.Mark |= FLOAT
   301  			continue
   302  
   303  		case ABL,
   304  			ABCL,
   305  			obj.ADUFFZERO,
   306  			obj.ADUFFCOPY:
   307  			cursym.Text.Mark &^= LEAF
   308  			fallthrough
   309  
   310  		case ABC,
   311  			ABEQ,
   312  			ABGE,
   313  			ABGT,
   314  			ABLE,
   315  			ABLT,
   316  			ABNE,
   317  			ABR,
   318  			ABVC,
   319  			ABVS,
   320  			ACMPBEQ,
   321  			ACMPBGE,
   322  			ACMPBGT,
   323  			ACMPBLE,
   324  			ACMPBLT,
   325  			ACMPBNE,
   326  			ACMPUBEQ,
   327  			ACMPUBGE,
   328  			ACMPUBGT,
   329  			ACMPUBLE,
   330  			ACMPUBLT,
   331  			ACMPUBNE:
   332  			p.Mark |= BRANCH
   333  			q = p
   334  			q1 = p.Pcond
   335  			if q1 != nil {
   336  				for q1.As == obj.ANOP {
   337  					q1 = q1.Link
   338  					p.Pcond = q1
   339  				}
   340  
   341  				if q1.Mark&LEAF == 0 {
   342  					q1.Mark |= LABEL
   343  				}
   344  			} else {
   345  				p.Mark |= LABEL
   346  			}
   347  			q1 = p.Link
   348  			if q1 != nil {
   349  				q1.Mark |= LABEL
   350  			}
   351  			continue
   352  
   353  		case AFCMPO, AFCMPU:
   354  			q = p
   355  			p.Mark |= FCMP | FLOAT
   356  			continue
   357  
   358  		case obj.ARET:
   359  			q = p
   360  			if p.Link != nil {
   361  				p.Link.Mark |= LABEL
   362  			}
   363  			continue
   364  
   365  		case obj.ANOP:
   366  			q1 = p.Link
   367  			q.Link = q1 /* q is non-nop */
   368  			q1.Mark |= p.Mark
   369  			continue
   370  
   371  		default:
   372  			q = p
   373  			continue
   374  		}
   375  	}
   376  
   377  	autosize := int32(0)
   378  	var p1 *obj.Prog
   379  	var p2 *obj.Prog
   380  	var pLast *obj.Prog
   381  	var pPre *obj.Prog
   382  	var pPreempt *obj.Prog
   383  	wasSplit := false
   384  	for p := cursym.Text; p != nil; p = p.Link {
   385  		pLast = p
   386  		switch p.As {
   387  		case obj.ATEXT:
   388  			autosize = int32(textstksiz)
   389  
   390  			if p.Mark&LEAF != 0 && autosize == 0 && p.From3.Offset&obj.NOFRAME == 0 {
   391  				// A leaf function with no locals has no frame.
   392  				p.From3.Offset |= obj.NOFRAME
   393  			}
   394  
   395  			if p.From3.Offset&obj.NOFRAME == 0 {
   396  				// If there is a stack frame at all, it includes
   397  				// space to save the LR.
   398  				autosize += int32(ctxt.FixedFrameSize())
   399  			}
   400  
   401  			p.To.Offset = int64(autosize)
   402  
   403  			q = p
   404  
   405  			if p.From3.Offset&obj.NOSPLIT == 0 {
   406  				p, pPreempt = stacksplitPre(ctxt, p, autosize) // emit pre part of split check
   407  				pPre = p
   408  				wasSplit = true //need post part of split
   409  			}
   410  
   411  			if autosize != 0 {
   412  				q = obj.Appendp(ctxt, p)
   413  				q.As = AMOVD
   414  				q.From.Type = obj.TYPE_ADDR
   415  				q.From.Offset = int64(-autosize)
   416  				q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
   417  				q.To.Type = obj.TYPE_REG
   418  				q.To.Reg = REGSP
   419  				q.Spadj = autosize
   420  			} else if cursym.Text.Mark&LEAF == 0 {
   421  				// A very few functions that do not return to their caller
   422  				// (e.g. gogo) are not identified as leaves but still have
   423  				// no frame.
   424  				cursym.Text.Mark |= LEAF
   425  			}
   426  
   427  			if cursym.Text.Mark&LEAF != 0 {
   428  				cursym.Leaf = true
   429  				break
   430  			}
   431  
   432  			q = obj.Appendp(ctxt, q)
   433  			q.As = AMOVD
   434  			q.From.Type = obj.TYPE_REG
   435  			q.From.Reg = REG_LR
   436  			q.To.Type = obj.TYPE_MEM
   437  			q.To.Reg = REGSP
   438  			q.To.Offset = 0
   439  
   440  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   441  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   442  				//
   443  				//	MOVD g_panic(g), R3
   444  				//	CMP R0, R3
   445  				//	BEQ end
   446  				//	MOVD panic_argp(R3), R4
   447  				//	ADD $(autosize+8), R1, R5
   448  				//	CMP R4, R5
   449  				//	BNE end
   450  				//	ADD $8, R1, R6
   451  				//	MOVD R6, panic_argp(R3)
   452  				// end:
   453  				//	NOP
   454  				//
   455  				// The NOP is needed to give the jumps somewhere to land.
   456  				// It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
   457  
   458  				q = obj.Appendp(ctxt, q)
   459  
   460  				q.As = AMOVD
   461  				q.From.Type = obj.TYPE_MEM
   462  				q.From.Reg = REGG
   463  				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   464  				q.To.Type = obj.TYPE_REG
   465  				q.To.Reg = REG_R3
   466  
   467  				q = obj.Appendp(ctxt, q)
   468  				q.As = ACMP
   469  				q.From.Type = obj.TYPE_REG
   470  				q.From.Reg = REG_R0
   471  				q.To.Type = obj.TYPE_REG
   472  				q.To.Reg = REG_R3
   473  
   474  				q = obj.Appendp(ctxt, q)
   475  				q.As = ABEQ
   476  				q.To.Type = obj.TYPE_BRANCH
   477  				p1 = q
   478  
   479  				q = obj.Appendp(ctxt, q)
   480  				q.As = AMOVD
   481  				q.From.Type = obj.TYPE_MEM
   482  				q.From.Reg = REG_R3
   483  				q.From.Offset = 0 // Panic.argp
   484  				q.To.Type = obj.TYPE_REG
   485  				q.To.Reg = REG_R4
   486  
   487  				q = obj.Appendp(ctxt, q)
   488  				q.As = AADD
   489  				q.From.Type = obj.TYPE_CONST
   490  				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
   491  				q.Reg = REGSP
   492  				q.To.Type = obj.TYPE_REG
   493  				q.To.Reg = REG_R5
   494  
   495  				q = obj.Appendp(ctxt, q)
   496  				q.As = ACMP
   497  				q.From.Type = obj.TYPE_REG
   498  				q.From.Reg = REG_R4
   499  				q.To.Type = obj.TYPE_REG
   500  				q.To.Reg = REG_R5
   501  
   502  				q = obj.Appendp(ctxt, q)
   503  				q.As = ABNE
   504  				q.To.Type = obj.TYPE_BRANCH
   505  				p2 = q
   506  
   507  				q = obj.Appendp(ctxt, q)
   508  				q.As = AADD
   509  				q.From.Type = obj.TYPE_CONST
   510  				q.From.Offset = ctxt.FixedFrameSize()
   511  				q.Reg = REGSP
   512  				q.To.Type = obj.TYPE_REG
   513  				q.To.Reg = REG_R6
   514  
   515  				q = obj.Appendp(ctxt, q)
   516  				q.As = AMOVD
   517  				q.From.Type = obj.TYPE_REG
   518  				q.From.Reg = REG_R6
   519  				q.To.Type = obj.TYPE_MEM
   520  				q.To.Reg = REG_R3
   521  				q.To.Offset = 0 // Panic.argp
   522  
   523  				q = obj.Appendp(ctxt, q)
   524  
   525  				q.As = obj.ANOP
   526  				p1.Pcond = q
   527  				p2.Pcond = q
   528  			}
   529  
   530  		case obj.ARET:
   531  			if p.From.Type == obj.TYPE_CONST {
   532  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   533  				break
   534  			}
   535  
   536  			retTarget := p.To.Sym
   537  
   538  			if cursym.Text.Mark&LEAF != 0 {
   539  				if autosize == 0 {
   540  					p.As = ABR
   541  					p.From = obj.Addr{}
   542  					if retTarget == nil {
   543  						p.To.Type = obj.TYPE_REG
   544  						p.To.Reg = REG_LR
   545  					} else {
   546  						p.To.Type = obj.TYPE_BRANCH
   547  						p.To.Sym = retTarget
   548  					}
   549  					p.Mark |= BRANCH
   550  					break
   551  				}
   552  
   553  				p.As = AADD
   554  				p.From.Type = obj.TYPE_CONST
   555  				p.From.Offset = int64(autosize)
   556  				p.To.Type = obj.TYPE_REG
   557  				p.To.Reg = REGSP
   558  				p.Spadj = -autosize
   559  
   560  				q = obj.Appendp(ctxt, p)
   561  				q.As = ABR
   562  				q.From = obj.Addr{}
   563  				q.To.Type = obj.TYPE_REG
   564  				q.To.Reg = REG_LR
   565  				q.Mark |= BRANCH
   566  				q.Spadj = autosize
   567  				break
   568  			}
   569  
   570  			p.As = AMOVD
   571  			p.From.Type = obj.TYPE_MEM
   572  			p.From.Reg = REGSP
   573  			p.From.Offset = 0
   574  			p.To.Type = obj.TYPE_REG
   575  			p.To.Reg = REG_LR
   576  
   577  			q = p
   578  
   579  			if autosize != 0 {
   580  				q = obj.Appendp(ctxt, q)
   581  				q.As = AADD
   582  				q.From.Type = obj.TYPE_CONST
   583  				q.From.Offset = int64(autosize)
   584  				q.To.Type = obj.TYPE_REG
   585  				q.To.Reg = REGSP
   586  				q.Spadj = -autosize
   587  			}
   588  
   589  			q = obj.Appendp(ctxt, q)
   590  			q.As = ABR
   591  			q.From = obj.Addr{}
   592  			if retTarget == nil {
   593  				q.To.Type = obj.TYPE_REG
   594  				q.To.Reg = REG_LR
   595  			} else {
   596  				q.To.Type = obj.TYPE_BRANCH
   597  				q.To.Sym = retTarget
   598  			}
   599  			q.Mark |= BRANCH
   600  			q.Spadj = autosize
   601  
   602  		case AADD:
   603  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   604  				p.Spadj = int32(-p.From.Offset)
   605  			}
   606  		}
   607  	}
   608  	if wasSplit {
   609  		pLast = stacksplitPost(ctxt, pLast, pPre, pPreempt) // emit post part of split check
   610  	}
   611  }
   612  
   613  /*
   614  // instruction scheduling
   615  	if(debug['Q'] == 0)
   616  		return;
   617  
   618  	curtext = nil;
   619  	q = nil;	// p - 1
   620  	q1 = firstp;	// top of block
   621  	o = 0;		// count of instructions
   622  	for(p = firstp; p != nil; p = p1) {
   623  		p1 = p->link;
   624  		o++;
   625  		if(p->mark & NOSCHED){
   626  			if(q1 != p){
   627  				sched(q1, q);
   628  			}
   629  			for(; p != nil; p = p->link){
   630  				if(!(p->mark & NOSCHED))
   631  					break;
   632  				q = p;
   633  			}
   634  			p1 = p;
   635  			q1 = p;
   636  			o = 0;
   637  			continue;
   638  		}
   639  		if(p->mark & (LABEL|SYNC)) {
   640  			if(q1 != p)
   641  				sched(q1, q);
   642  			q1 = p;
   643  			o = 1;
   644  		}
   645  		if(p->mark & (BRANCH|SYNC)) {
   646  			sched(q1, p);
   647  			q1 = p1;
   648  			o = 0;
   649  		}
   650  		if(o >= NSCHED) {
   651  			sched(q1, p);
   652  			q1 = p1;
   653  			o = 0;
   654  		}
   655  		q = p;
   656  	}
   657  */
   658  func stacksplitPre(ctxt *obj.Link, p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
   659  	var q *obj.Prog
   660  
   661  	// MOVD	g_stackguard(g), R3
   662  	p = obj.Appendp(ctxt, p)
   663  
   664  	p.As = AMOVD
   665  	p.From.Type = obj.TYPE_MEM
   666  	p.From.Reg = REGG
   667  	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
   668  	if ctxt.Cursym.Cfunc {
   669  		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
   670  	}
   671  	p.To.Type = obj.TYPE_REG
   672  	p.To.Reg = REG_R3
   673  
   674  	q = nil
   675  	if framesize <= obj.StackSmall {
   676  		// small stack: SP < stackguard
   677  		//	CMP	stackguard, SP
   678  
   679  		//p.To.Type = obj.TYPE_REG
   680  		//p.To.Reg = REGSP
   681  
   682  		// q1: BLT	done
   683  
   684  		p = obj.Appendp(ctxt, p)
   685  		//q1 = p
   686  		p.From.Type = obj.TYPE_REG
   687  		p.From.Reg = REG_R3
   688  		p.Reg = REGSP
   689  		p.As = ACMPUBGE
   690  		p.To.Type = obj.TYPE_BRANCH
   691  		//p = obj.Appendp(ctxt, p)
   692  
   693  		//p.As = ACMPU
   694  		//p.From.Type = obj.TYPE_REG
   695  		//p.From.Reg = REG_R3
   696  		//p.To.Type = obj.TYPE_REG
   697  		//p.To.Reg = REGSP
   698  
   699  		//p = obj.Appendp(ctxt, p)
   700  		//p.As = ABGE
   701  		//p.To.Type = obj.TYPE_BRANCH
   702  
   703  	} else if framesize <= obj.StackBig {
   704  		// large stack: SP-framesize < stackguard-StackSmall
   705  		//	ADD $-framesize, SP, R4
   706  		//	CMP stackguard, R4
   707  		p = obj.Appendp(ctxt, p)
   708  
   709  		p.As = AADD
   710  		p.From.Type = obj.TYPE_CONST
   711  		p.From.Offset = int64(-framesize)
   712  		p.Reg = REGSP
   713  		p.To.Type = obj.TYPE_REG
   714  		p.To.Reg = REG_R4
   715  
   716  		p = obj.Appendp(ctxt, p)
   717  		p.From.Type = obj.TYPE_REG
   718  		p.From.Reg = REG_R3
   719  		p.Reg = REG_R4
   720  		p.As = ACMPUBGE
   721  		p.To.Type = obj.TYPE_BRANCH
   722  
   723  	} else {
   724  		// Such a large stack we need to protect against wraparound.
   725  		// If SP is close to zero:
   726  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   727  		// The +StackGuard on both sides is required to keep the left side positive:
   728  		// SP is allowed to be slightly below stackguard. See stack.h.
   729  		//
   730  		// Preemption sets stackguard to StackPreempt, a very large value.
   731  		// That breaks the math above, so we have to check for that explicitly.
   732  		//	// stackguard is R3
   733  		//	CMP	R3, $StackPreempt
   734  		//	BEQ	label-of-call-to-morestack
   735  		//	ADD	$StackGuard, SP, R4
   736  		//	SUB	R3, R4
   737  		//	MOVD	$(framesize+(StackGuard-StackSmall)), TEMP
   738  		//	CMPUBGE	TEMP, R4
   739  		p = obj.Appendp(ctxt, p)
   740  
   741  		p.As = ACMP
   742  		p.From.Type = obj.TYPE_REG
   743  		p.From.Reg = REG_R3
   744  		p.To.Type = obj.TYPE_CONST
   745  		p.To.Offset = obj.StackPreempt
   746  
   747  		p = obj.Appendp(ctxt, p)
   748  		q = p
   749  		p.As = ABEQ
   750  		p.To.Type = obj.TYPE_BRANCH
   751  
   752  		p = obj.Appendp(ctxt, p)
   753  		p.As = AADD
   754  		p.From.Type = obj.TYPE_CONST
   755  		p.From.Offset = obj.StackGuard
   756  		p.Reg = REGSP
   757  		p.To.Type = obj.TYPE_REG
   758  		p.To.Reg = REG_R4
   759  
   760  		p = obj.Appendp(ctxt, p)
   761  		p.As = ASUB
   762  		p.From.Type = obj.TYPE_REG
   763  		p.From.Reg = REG_R3
   764  		p.To.Type = obj.TYPE_REG
   765  		p.To.Reg = REG_R4
   766  
   767  		p = obj.Appendp(ctxt, p)
   768  		p.As = AMOVD
   769  		p.From.Type = obj.TYPE_CONST
   770  		p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
   771  		p.To.Type = obj.TYPE_REG
   772  		p.To.Reg = REGTMP
   773  
   774  		p = obj.Appendp(ctxt, p)
   775  		p.From.Type = obj.TYPE_REG
   776  		p.From.Reg = REGTMP
   777  		p.Reg = REG_R4
   778  		p.As = ACMPUBGE
   779  		p.To.Type = obj.TYPE_BRANCH
   780  	}
   781  
   782  	return p, q
   783  }
   784  
   785  func stacksplitPost(ctxt *obj.Link, p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog) *obj.Prog {
   786  
   787  	// MOVD	LR, R5
   788  	p = obj.Appendp(ctxt, p)
   789  	pPre.Pcond = p
   790  	p.As = AMOVD
   791  	p.From.Type = obj.TYPE_REG
   792  	p.From.Reg = REG_LR
   793  	p.To.Type = obj.TYPE_REG
   794  	p.To.Reg = REG_R5
   795  	if pPreempt != nil {
   796  		pPreempt.Pcond = p
   797  	}
   798  
   799  	// BL	runtime.morestack(SB)
   800  	p = obj.Appendp(ctxt, p)
   801  
   802  	p.As = ABL
   803  	p.To.Type = obj.TYPE_BRANCH
   804  	if ctxt.Cursym.Cfunc {
   805  		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
   806  	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
   807  		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
   808  	} else {
   809  		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack", 0)
   810  	}
   811  
   812  	// BR	start
   813  	p = obj.Appendp(ctxt, p)
   814  
   815  	p.As = ABR
   816  	p.To.Type = obj.TYPE_BRANCH
   817  	p.Pcond = ctxt.Cursym.Text.Link
   818  	return p
   819  }
   820  
   821  var pc_cnt int64
   822  
   823  func follow(ctxt *obj.Link, s *obj.LSym) {
   824  	ctxt.Cursym = s
   825  
   826  	pc_cnt = 0
   827  	firstp := ctxt.NewProg()
   828  	lastp := firstp
   829  	xfol(ctxt, s.Text, &lastp)
   830  	lastp.Link = nil
   831  	s.Text = firstp.Link
   832  }
   833  
   834  func relinv(a obj.As) obj.As {
   835  	switch a {
   836  	case ABEQ:
   837  		return ABNE
   838  	case ABNE:
   839  		return ABEQ
   840  
   841  	case ABGE:
   842  		return ABLT
   843  	case ABLT:
   844  		return ABGE
   845  
   846  	case ABGT:
   847  		return ABLE
   848  	case ABLE:
   849  		return ABGT
   850  
   851  	case ABVC:
   852  		return ABVS
   853  	case ABVS:
   854  		return ABVC
   855  	}
   856  
   857  	return 0
   858  }
   859  
   860  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
   861  	var q *obj.Prog
   862  	var r *obj.Prog
   863  	var b obj.As
   864  
   865  	for p != nil {
   866  		a := p.As
   867  		if a == ABR {
   868  			q = p.Pcond
   869  			if (p.Mark&NOSCHED != 0) || q != nil && (q.Mark&NOSCHED != 0) {
   870  				p.Mark |= FOLL
   871  				(*last).Link = p
   872  				*last = p
   873  				(*last).Pc = pc_cnt
   874  				pc_cnt += 1
   875  				p = p.Link
   876  				xfol(ctxt, p, last)
   877  				p = q
   878  				if p != nil && p.Mark&FOLL == 0 {
   879  					continue
   880  				}
   881  				return
   882  			}
   883  
   884  			if q != nil {
   885  				p.Mark |= FOLL
   886  				p = q
   887  				if p.Mark&FOLL == 0 {
   888  					continue
   889  				}
   890  			}
   891  		}
   892  
   893  		if p.Mark&FOLL != 0 {
   894  			q = p
   895  			for i := 0; i < 4; i, q = i+1, q.Link {
   896  				if q == *last || (q.Mark&NOSCHED != 0) {
   897  					break
   898  				}
   899  				b = 0 /* set */
   900  				a = q.As
   901  				if a == obj.ANOP {
   902  					i--
   903  					continue
   904  				}
   905  				if a != ABR && a != obj.ARET {
   906  					if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
   907  						continue
   908  					}
   909  					b = relinv(a)
   910  					if b == 0 {
   911  						continue
   912  					}
   913  				}
   914  
   915  				for {
   916  					r = ctxt.NewProg()
   917  					*r = *p
   918  					if r.Mark&FOLL == 0 {
   919  						fmt.Printf("can't happen 1\n")
   920  					}
   921  					r.Mark |= FOLL
   922  					if p != q {
   923  						p = p.Link
   924  						(*last).Link = r
   925  						*last = r
   926  						(*last).Pc = pc_cnt
   927  						pc_cnt += 1
   928  						continue
   929  					}
   930  
   931  					(*last).Link = r
   932  					*last = r
   933  					(*last).Pc = pc_cnt
   934  					pc_cnt += 1
   935  					if a == ABR || a == obj.ARET {
   936  						return
   937  					}
   938  					r.As = b
   939  					r.Pcond = p.Link
   940  					r.Link = p.Pcond
   941  					if r.Link.Mark&FOLL == 0 {
   942  						xfol(ctxt, r.Link, last)
   943  					}
   944  					if r.Pcond.Mark&FOLL == 0 {
   945  						fmt.Printf("can't happen 2\n")
   946  					}
   947  					return
   948  				}
   949  			}
   950  
   951  			a = ABR
   952  			q = ctxt.NewProg()
   953  			q.As = a
   954  			q.Lineno = p.Lineno
   955  			q.To.Type = obj.TYPE_BRANCH
   956  			q.To.Offset = p.Pc
   957  			q.Pcond = p
   958  			p = q
   959  		}
   960  
   961  		p.Mark |= FOLL
   962  		(*last).Link = p
   963  		*last = p
   964  		(*last).Pc = pc_cnt
   965  		pc_cnt += 1
   966  
   967  		if a == ABR || a == obj.ARET {
   968  			if p.Mark&NOSCHED != 0 {
   969  				p = p.Link
   970  				continue
   971  			}
   972  
   973  			return
   974  		}
   975  
   976  		if p.Pcond != nil {
   977  			if a != ABL && p.Link != nil {
   978  				xfol(ctxt, p.Link, last)
   979  				p = p.Pcond
   980  				if p == nil || (p.Mark&FOLL != 0) {
   981  					return
   982  				}
   983  				continue
   984  			}
   985  		}
   986  
   987  		p = p.Link
   988  	}
   989  }
   990  
   991  var unaryDst = map[obj.As]bool{
   992  	ASTCK:  true,
   993  	ASTCKC: true,
   994  	ASTCKE: true,
   995  	ASTCKF: true,
   996  	ANEG:   true,
   997  	AVONE:  true,
   998  	AVZERO: true,
   999  }
  1000  
  1001  var Links390x = obj.LinkArch{
  1002  	Arch:       sys.ArchS390X,
  1003  	Preprocess: preprocess,
  1004  	Assemble:   spanz,
  1005  	Follow:     follow,
  1006  	Progedit:   progedit,
  1007  	UnaryDst:   unaryDst,
  1008  }