github.com/sbinet/go@v0.0.0-20160827155028-54d7de7dd62b/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  		ctxt.Logf("%5.2f noops\n", obj.Cputime())
   246  	}
   247  
   248  	var q *obj.Prog
   249  	var q1 *obj.Prog
   250  	for p := cursym.Text; p != nil; p = p.Link {
   251  		switch p.As {
   252  		/* too hard, just leave alone */
   253  		case obj.ATEXT:
   254  			q = p
   255  
   256  			p.Mark |= LABEL | LEAF | SYNC
   257  			if p.Link != nil {
   258  				p.Link.Mark |= LABEL
   259  			}
   260  
   261  		case ANOR:
   262  			q = p
   263  			if p.To.Type == obj.TYPE_REG {
   264  				if p.To.Reg == REGZERO {
   265  					p.Mark |= LABEL | SYNC
   266  				}
   267  			}
   268  
   269  		case ASYNC,
   270  			AWORD:
   271  			q = p
   272  			p.Mark |= LABEL | SYNC
   273  			continue
   274  
   275  		case AMOVW, AMOVWZ, AMOVD:
   276  			q = p
   277  			if p.From.Reg >= REG_RESERVED || p.To.Reg >= REG_RESERVED {
   278  				p.Mark |= LABEL | SYNC
   279  			}
   280  			continue
   281  
   282  		case AFABS,
   283  			AFADD,
   284  			AFDIV,
   285  			AFMADD,
   286  			AFMOVD,
   287  			AFMOVS,
   288  			AFMSUB,
   289  			AFMUL,
   290  			AFNABS,
   291  			AFNEG,
   292  			AFNMADD,
   293  			AFNMSUB,
   294  			ALEDBR,
   295  			ALDEBR,
   296  			AFSUB:
   297  			q = p
   298  
   299  			p.Mark |= FLOAT
   300  			continue
   301  
   302  		case ABL,
   303  			ABCL,
   304  			obj.ADUFFZERO,
   305  			obj.ADUFFCOPY:
   306  			cursym.Text.Mark &^= LEAF
   307  			fallthrough
   308  
   309  		case ABC,
   310  			ABEQ,
   311  			ABGE,
   312  			ABGT,
   313  			ABLE,
   314  			ABLT,
   315  			ABNE,
   316  			ABR,
   317  			ABVC,
   318  			ABVS,
   319  			ACMPBEQ,
   320  			ACMPBGE,
   321  			ACMPBGT,
   322  			ACMPBLE,
   323  			ACMPBLT,
   324  			ACMPBNE,
   325  			ACMPUBEQ,
   326  			ACMPUBGE,
   327  			ACMPUBGT,
   328  			ACMPUBLE,
   329  			ACMPUBLT,
   330  			ACMPUBNE:
   331  			p.Mark |= BRANCH
   332  			q = p
   333  			q1 = p.Pcond
   334  			if q1 != nil {
   335  				for q1.As == obj.ANOP {
   336  					q1 = q1.Link
   337  					p.Pcond = q1
   338  				}
   339  
   340  				if q1.Mark&LEAF == 0 {
   341  					q1.Mark |= LABEL
   342  				}
   343  			} else {
   344  				p.Mark |= LABEL
   345  			}
   346  			q1 = p.Link
   347  			if q1 != nil {
   348  				q1.Mark |= LABEL
   349  			}
   350  			continue
   351  
   352  		case AFCMPO, AFCMPU:
   353  			q = p
   354  			p.Mark |= FCMP | FLOAT
   355  			continue
   356  
   357  		case obj.ARET:
   358  			q = p
   359  			if p.Link != nil {
   360  				p.Link.Mark |= LABEL
   361  			}
   362  			continue
   363  
   364  		case obj.ANOP:
   365  			q1 = p.Link
   366  			q.Link = q1 /* q is non-nop */
   367  			q1.Mark |= p.Mark
   368  			continue
   369  
   370  		default:
   371  			q = p
   372  			continue
   373  		}
   374  	}
   375  
   376  	autosize := int32(0)
   377  	var p1 *obj.Prog
   378  	var p2 *obj.Prog
   379  	var pLast *obj.Prog
   380  	var pPre *obj.Prog
   381  	var pPreempt *obj.Prog
   382  	wasSplit := false
   383  	for p := cursym.Text; p != nil; p = p.Link {
   384  		pLast = p
   385  		switch p.As {
   386  		case obj.ATEXT:
   387  			autosize = int32(textstksiz)
   388  
   389  			if p.Mark&LEAF != 0 && autosize == 0 && p.From3.Offset&obj.NOFRAME == 0 {
   390  				// A leaf function with no locals has no frame.
   391  				p.From3.Offset |= obj.NOFRAME
   392  			}
   393  
   394  			if p.From3.Offset&obj.NOFRAME == 0 {
   395  				// If there is a stack frame at all, it includes
   396  				// space to save the LR.
   397  				autosize += int32(ctxt.FixedFrameSize())
   398  			}
   399  
   400  			p.To.Offset = int64(autosize)
   401  
   402  			q = p
   403  
   404  			if p.From3.Offset&obj.NOSPLIT == 0 {
   405  				p, pPreempt = stacksplitPre(ctxt, p, autosize) // emit pre part of split check
   406  				pPre = p
   407  				wasSplit = true //need post part of split
   408  			}
   409  
   410  			if autosize != 0 {
   411  				q = obj.Appendp(ctxt, p)
   412  				q.As = AMOVD
   413  				q.From.Type = obj.TYPE_ADDR
   414  				q.From.Offset = int64(-autosize)
   415  				q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
   416  				q.To.Type = obj.TYPE_REG
   417  				q.To.Reg = REGSP
   418  				q.Spadj = autosize
   419  			} else if cursym.Text.Mark&LEAF == 0 {
   420  				// A very few functions that do not return to their caller
   421  				// (e.g. gogo) are not identified as leaves but still have
   422  				// no frame.
   423  				cursym.Text.Mark |= LEAF
   424  			}
   425  
   426  			if cursym.Text.Mark&LEAF != 0 {
   427  				cursym.Leaf = true
   428  				break
   429  			}
   430  
   431  			q = obj.Appendp(ctxt, q)
   432  			q.As = AMOVD
   433  			q.From.Type = obj.TYPE_REG
   434  			q.From.Reg = REG_LR
   435  			q.To.Type = obj.TYPE_MEM
   436  			q.To.Reg = REGSP
   437  			q.To.Offset = 0
   438  
   439  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   440  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   441  				//
   442  				//	MOVD g_panic(g), R3
   443  				//	CMP R0, R3
   444  				//	BEQ end
   445  				//	MOVD panic_argp(R3), R4
   446  				//	ADD $(autosize+8), R1, R5
   447  				//	CMP R4, R5
   448  				//	BNE end
   449  				//	ADD $8, R1, R6
   450  				//	MOVD R6, panic_argp(R3)
   451  				// end:
   452  				//	NOP
   453  				//
   454  				// The NOP is needed to give the jumps somewhere to land.
   455  				// It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
   456  
   457  				q = obj.Appendp(ctxt, q)
   458  
   459  				q.As = AMOVD
   460  				q.From.Type = obj.TYPE_MEM
   461  				q.From.Reg = REGG
   462  				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   463  				q.To.Type = obj.TYPE_REG
   464  				q.To.Reg = REG_R3
   465  
   466  				q = obj.Appendp(ctxt, q)
   467  				q.As = ACMP
   468  				q.From.Type = obj.TYPE_REG
   469  				q.From.Reg = REG_R0
   470  				q.To.Type = obj.TYPE_REG
   471  				q.To.Reg = REG_R3
   472  
   473  				q = obj.Appendp(ctxt, q)
   474  				q.As = ABEQ
   475  				q.To.Type = obj.TYPE_BRANCH
   476  				p1 = q
   477  
   478  				q = obj.Appendp(ctxt, q)
   479  				q.As = AMOVD
   480  				q.From.Type = obj.TYPE_MEM
   481  				q.From.Reg = REG_R3
   482  				q.From.Offset = 0 // Panic.argp
   483  				q.To.Type = obj.TYPE_REG
   484  				q.To.Reg = REG_R4
   485  
   486  				q = obj.Appendp(ctxt, q)
   487  				q.As = AADD
   488  				q.From.Type = obj.TYPE_CONST
   489  				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
   490  				q.Reg = REGSP
   491  				q.To.Type = obj.TYPE_REG
   492  				q.To.Reg = REG_R5
   493  
   494  				q = obj.Appendp(ctxt, q)
   495  				q.As = ACMP
   496  				q.From.Type = obj.TYPE_REG
   497  				q.From.Reg = REG_R4
   498  				q.To.Type = obj.TYPE_REG
   499  				q.To.Reg = REG_R5
   500  
   501  				q = obj.Appendp(ctxt, q)
   502  				q.As = ABNE
   503  				q.To.Type = obj.TYPE_BRANCH
   504  				p2 = q
   505  
   506  				q = obj.Appendp(ctxt, q)
   507  				q.As = AADD
   508  				q.From.Type = obj.TYPE_CONST
   509  				q.From.Offset = ctxt.FixedFrameSize()
   510  				q.Reg = REGSP
   511  				q.To.Type = obj.TYPE_REG
   512  				q.To.Reg = REG_R6
   513  
   514  				q = obj.Appendp(ctxt, q)
   515  				q.As = AMOVD
   516  				q.From.Type = obj.TYPE_REG
   517  				q.From.Reg = REG_R6
   518  				q.To.Type = obj.TYPE_MEM
   519  				q.To.Reg = REG_R3
   520  				q.To.Offset = 0 // Panic.argp
   521  
   522  				q = obj.Appendp(ctxt, q)
   523  
   524  				q.As = obj.ANOP
   525  				p1.Pcond = q
   526  				p2.Pcond = q
   527  			}
   528  
   529  		case obj.ARET:
   530  			if p.From.Type == obj.TYPE_CONST {
   531  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   532  				break
   533  			}
   534  
   535  			retTarget := p.To.Sym
   536  
   537  			if cursym.Text.Mark&LEAF != 0 {
   538  				if autosize == 0 {
   539  					p.As = ABR
   540  					p.From = obj.Addr{}
   541  					if retTarget == nil {
   542  						p.To.Type = obj.TYPE_REG
   543  						p.To.Reg = REG_LR
   544  					} else {
   545  						p.To.Type = obj.TYPE_BRANCH
   546  						p.To.Sym = retTarget
   547  					}
   548  					p.Mark |= BRANCH
   549  					break
   550  				}
   551  
   552  				p.As = AADD
   553  				p.From.Type = obj.TYPE_CONST
   554  				p.From.Offset = int64(autosize)
   555  				p.To.Type = obj.TYPE_REG
   556  				p.To.Reg = REGSP
   557  				p.Spadj = -autosize
   558  
   559  				q = obj.Appendp(ctxt, p)
   560  				q.As = ABR
   561  				q.From = obj.Addr{}
   562  				q.To.Type = obj.TYPE_REG
   563  				q.To.Reg = REG_LR
   564  				q.Mark |= BRANCH
   565  				q.Spadj = autosize
   566  				break
   567  			}
   568  
   569  			p.As = AMOVD
   570  			p.From.Type = obj.TYPE_MEM
   571  			p.From.Reg = REGSP
   572  			p.From.Offset = 0
   573  			p.To.Type = obj.TYPE_REG
   574  			p.To.Reg = REG_LR
   575  
   576  			q = p
   577  
   578  			if autosize != 0 {
   579  				q = obj.Appendp(ctxt, q)
   580  				q.As = AADD
   581  				q.From.Type = obj.TYPE_CONST
   582  				q.From.Offset = int64(autosize)
   583  				q.To.Type = obj.TYPE_REG
   584  				q.To.Reg = REGSP
   585  				q.Spadj = -autosize
   586  			}
   587  
   588  			q = obj.Appendp(ctxt, q)
   589  			q.As = ABR
   590  			q.From = obj.Addr{}
   591  			if retTarget == nil {
   592  				q.To.Type = obj.TYPE_REG
   593  				q.To.Reg = REG_LR
   594  			} else {
   595  				q.To.Type = obj.TYPE_BRANCH
   596  				q.To.Sym = retTarget
   597  			}
   598  			q.Mark |= BRANCH
   599  			q.Spadj = autosize
   600  
   601  		case AADD:
   602  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   603  				p.Spadj = int32(-p.From.Offset)
   604  			}
   605  		}
   606  	}
   607  	if wasSplit {
   608  		pLast = stacksplitPost(ctxt, pLast, pPre, pPreempt) // emit post part of split check
   609  	}
   610  }
   611  
   612  /*
   613  // instruction scheduling
   614  	if(debug['Q'] == 0)
   615  		return;
   616  
   617  	curtext = nil;
   618  	q = nil;	// p - 1
   619  	q1 = firstp;	// top of block
   620  	o = 0;		// count of instructions
   621  	for(p = firstp; p != nil; p = p1) {
   622  		p1 = p->link;
   623  		o++;
   624  		if(p->mark & NOSCHED){
   625  			if(q1 != p){
   626  				sched(q1, q);
   627  			}
   628  			for(; p != nil; p = p->link){
   629  				if(!(p->mark & NOSCHED))
   630  					break;
   631  				q = p;
   632  			}
   633  			p1 = p;
   634  			q1 = p;
   635  			o = 0;
   636  			continue;
   637  		}
   638  		if(p->mark & (LABEL|SYNC)) {
   639  			if(q1 != p)
   640  				sched(q1, q);
   641  			q1 = p;
   642  			o = 1;
   643  		}
   644  		if(p->mark & (BRANCH|SYNC)) {
   645  			sched(q1, p);
   646  			q1 = p1;
   647  			o = 0;
   648  		}
   649  		if(o >= NSCHED) {
   650  			sched(q1, p);
   651  			q1 = p1;
   652  			o = 0;
   653  		}
   654  		q = p;
   655  	}
   656  */
   657  func stacksplitPre(ctxt *obj.Link, p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
   658  	var q *obj.Prog
   659  
   660  	// MOVD	g_stackguard(g), R3
   661  	p = obj.Appendp(ctxt, p)
   662  
   663  	p.As = AMOVD
   664  	p.From.Type = obj.TYPE_MEM
   665  	p.From.Reg = REGG
   666  	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
   667  	if ctxt.Cursym.Cfunc {
   668  		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
   669  	}
   670  	p.To.Type = obj.TYPE_REG
   671  	p.To.Reg = REG_R3
   672  
   673  	q = nil
   674  	if framesize <= obj.StackSmall {
   675  		// small stack: SP < stackguard
   676  		//	CMP	stackguard, SP
   677  
   678  		//p.To.Type = obj.TYPE_REG
   679  		//p.To.Reg = REGSP
   680  
   681  		// q1: BLT	done
   682  
   683  		p = obj.Appendp(ctxt, p)
   684  		//q1 = p
   685  		p.From.Type = obj.TYPE_REG
   686  		p.From.Reg = REG_R3
   687  		p.Reg = REGSP
   688  		p.As = ACMPUBGE
   689  		p.To.Type = obj.TYPE_BRANCH
   690  		//p = obj.Appendp(ctxt, p)
   691  
   692  		//p.As = ACMPU
   693  		//p.From.Type = obj.TYPE_REG
   694  		//p.From.Reg = REG_R3
   695  		//p.To.Type = obj.TYPE_REG
   696  		//p.To.Reg = REGSP
   697  
   698  		//p = obj.Appendp(ctxt, p)
   699  		//p.As = ABGE
   700  		//p.To.Type = obj.TYPE_BRANCH
   701  
   702  	} else if framesize <= obj.StackBig {
   703  		// large stack: SP-framesize < stackguard-StackSmall
   704  		//	ADD $-framesize, SP, R4
   705  		//	CMP stackguard, R4
   706  		p = obj.Appendp(ctxt, p)
   707  
   708  		p.As = AADD
   709  		p.From.Type = obj.TYPE_CONST
   710  		p.From.Offset = int64(-framesize)
   711  		p.Reg = REGSP
   712  		p.To.Type = obj.TYPE_REG
   713  		p.To.Reg = REG_R4
   714  
   715  		p = obj.Appendp(ctxt, p)
   716  		p.From.Type = obj.TYPE_REG
   717  		p.From.Reg = REG_R3
   718  		p.Reg = REG_R4
   719  		p.As = ACMPUBGE
   720  		p.To.Type = obj.TYPE_BRANCH
   721  
   722  	} else {
   723  		// Such a large stack we need to protect against wraparound.
   724  		// If SP is close to zero:
   725  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   726  		// The +StackGuard on both sides is required to keep the left side positive:
   727  		// SP is allowed to be slightly below stackguard. See stack.h.
   728  		//
   729  		// Preemption sets stackguard to StackPreempt, a very large value.
   730  		// That breaks the math above, so we have to check for that explicitly.
   731  		//	// stackguard is R3
   732  		//	CMP	R3, $StackPreempt
   733  		//	BEQ	label-of-call-to-morestack
   734  		//	ADD	$StackGuard, SP, R4
   735  		//	SUB	R3, R4
   736  		//	MOVD	$(framesize+(StackGuard-StackSmall)), TEMP
   737  		//	CMPUBGE	TEMP, R4
   738  		p = obj.Appendp(ctxt, p)
   739  
   740  		p.As = ACMP
   741  		p.From.Type = obj.TYPE_REG
   742  		p.From.Reg = REG_R3
   743  		p.To.Type = obj.TYPE_CONST
   744  		p.To.Offset = obj.StackPreempt
   745  
   746  		p = obj.Appendp(ctxt, p)
   747  		q = p
   748  		p.As = ABEQ
   749  		p.To.Type = obj.TYPE_BRANCH
   750  
   751  		p = obj.Appendp(ctxt, p)
   752  		p.As = AADD
   753  		p.From.Type = obj.TYPE_CONST
   754  		p.From.Offset = obj.StackGuard
   755  		p.Reg = REGSP
   756  		p.To.Type = obj.TYPE_REG
   757  		p.To.Reg = REG_R4
   758  
   759  		p = obj.Appendp(ctxt, p)
   760  		p.As = ASUB
   761  		p.From.Type = obj.TYPE_REG
   762  		p.From.Reg = REG_R3
   763  		p.To.Type = obj.TYPE_REG
   764  		p.To.Reg = REG_R4
   765  
   766  		p = obj.Appendp(ctxt, p)
   767  		p.As = AMOVD
   768  		p.From.Type = obj.TYPE_CONST
   769  		p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
   770  		p.To.Type = obj.TYPE_REG
   771  		p.To.Reg = REGTMP
   772  
   773  		p = obj.Appendp(ctxt, p)
   774  		p.From.Type = obj.TYPE_REG
   775  		p.From.Reg = REGTMP
   776  		p.Reg = REG_R4
   777  		p.As = ACMPUBGE
   778  		p.To.Type = obj.TYPE_BRANCH
   779  	}
   780  
   781  	return p, q
   782  }
   783  
   784  func stacksplitPost(ctxt *obj.Link, p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog) *obj.Prog {
   785  
   786  	// MOVD	LR, R5
   787  	p = obj.Appendp(ctxt, p)
   788  	pPre.Pcond = p
   789  	p.As = AMOVD
   790  	p.From.Type = obj.TYPE_REG
   791  	p.From.Reg = REG_LR
   792  	p.To.Type = obj.TYPE_REG
   793  	p.To.Reg = REG_R5
   794  	if pPreempt != nil {
   795  		pPreempt.Pcond = p
   796  	}
   797  
   798  	// BL	runtime.morestack(SB)
   799  	p = obj.Appendp(ctxt, p)
   800  
   801  	p.As = ABL
   802  	p.To.Type = obj.TYPE_BRANCH
   803  	if ctxt.Cursym.Cfunc {
   804  		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
   805  	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
   806  		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
   807  	} else {
   808  		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack", 0)
   809  	}
   810  
   811  	// BR	start
   812  	p = obj.Appendp(ctxt, p)
   813  
   814  	p.As = ABR
   815  	p.To.Type = obj.TYPE_BRANCH
   816  	p.Pcond = ctxt.Cursym.Text.Link
   817  	return p
   818  }
   819  
   820  var pc_cnt int64
   821  
   822  func follow(ctxt *obj.Link, s *obj.LSym) {
   823  	ctxt.Cursym = s
   824  
   825  	pc_cnt = 0
   826  	firstp := ctxt.NewProg()
   827  	lastp := firstp
   828  	xfol(ctxt, s.Text, &lastp)
   829  	lastp.Link = nil
   830  	s.Text = firstp.Link
   831  }
   832  
   833  func relinv(a obj.As) obj.As {
   834  	switch a {
   835  	case ABEQ:
   836  		return ABNE
   837  	case ABNE:
   838  		return ABEQ
   839  
   840  	case ABGE:
   841  		return ABLT
   842  	case ABLT:
   843  		return ABGE
   844  
   845  	case ABGT:
   846  		return ABLE
   847  	case ABLE:
   848  		return ABGT
   849  
   850  	case ABVC:
   851  		return ABVS
   852  	case ABVS:
   853  		return ABVC
   854  	}
   855  
   856  	return 0
   857  }
   858  
   859  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
   860  	var q *obj.Prog
   861  	var r *obj.Prog
   862  	var b obj.As
   863  
   864  	for p != nil {
   865  		a := p.As
   866  		if a == ABR {
   867  			q = p.Pcond
   868  			if (p.Mark&NOSCHED != 0) || q != nil && (q.Mark&NOSCHED != 0) {
   869  				p.Mark |= FOLL
   870  				(*last).Link = p
   871  				*last = p
   872  				(*last).Pc = pc_cnt
   873  				pc_cnt += 1
   874  				p = p.Link
   875  				xfol(ctxt, p, last)
   876  				p = q
   877  				if p != nil && p.Mark&FOLL == 0 {
   878  					continue
   879  				}
   880  				return
   881  			}
   882  
   883  			if q != nil {
   884  				p.Mark |= FOLL
   885  				p = q
   886  				if p.Mark&FOLL == 0 {
   887  					continue
   888  				}
   889  			}
   890  		}
   891  
   892  		if p.Mark&FOLL != 0 {
   893  			q = p
   894  			for i := 0; i < 4; i, q = i+1, q.Link {
   895  				if q == *last || (q.Mark&NOSCHED != 0) {
   896  					break
   897  				}
   898  				b = 0 /* set */
   899  				a = q.As
   900  				if a == obj.ANOP {
   901  					i--
   902  					continue
   903  				}
   904  				if a != ABR && a != obj.ARET {
   905  					if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
   906  						continue
   907  					}
   908  					b = relinv(a)
   909  					if b == 0 {
   910  						continue
   911  					}
   912  				}
   913  
   914  				for {
   915  					r = ctxt.NewProg()
   916  					*r = *p
   917  					if r.Mark&FOLL == 0 {
   918  						fmt.Printf("can't happen 1\n")
   919  					}
   920  					r.Mark |= FOLL
   921  					if p != q {
   922  						p = p.Link
   923  						(*last).Link = r
   924  						*last = r
   925  						(*last).Pc = pc_cnt
   926  						pc_cnt += 1
   927  						continue
   928  					}
   929  
   930  					(*last).Link = r
   931  					*last = r
   932  					(*last).Pc = pc_cnt
   933  					pc_cnt += 1
   934  					if a == ABR || a == obj.ARET {
   935  						return
   936  					}
   937  					r.As = b
   938  					r.Pcond = p.Link
   939  					r.Link = p.Pcond
   940  					if r.Link.Mark&FOLL == 0 {
   941  						xfol(ctxt, r.Link, last)
   942  					}
   943  					if r.Pcond.Mark&FOLL == 0 {
   944  						fmt.Printf("can't happen 2\n")
   945  					}
   946  					return
   947  				}
   948  			}
   949  
   950  			a = ABR
   951  			q = ctxt.NewProg()
   952  			q.As = a
   953  			q.Lineno = p.Lineno
   954  			q.To.Type = obj.TYPE_BRANCH
   955  			q.To.Offset = p.Pc
   956  			q.Pcond = p
   957  			p = q
   958  		}
   959  
   960  		p.Mark |= FOLL
   961  		(*last).Link = p
   962  		*last = p
   963  		(*last).Pc = pc_cnt
   964  		pc_cnt += 1
   965  
   966  		if a == ABR || a == obj.ARET {
   967  			if p.Mark&NOSCHED != 0 {
   968  				p = p.Link
   969  				continue
   970  			}
   971  
   972  			return
   973  		}
   974  
   975  		if p.Pcond != nil {
   976  			if a != ABL && p.Link != nil {
   977  				xfol(ctxt, p.Link, last)
   978  				p = p.Pcond
   979  				if p == nil || (p.Mark&FOLL != 0) {
   980  					return
   981  				}
   982  				continue
   983  			}
   984  		}
   985  
   986  		p = p.Link
   987  	}
   988  }
   989  
   990  var unaryDst = map[obj.As]bool{
   991  	ASTCK:  true,
   992  	ASTCKC: true,
   993  	ASTCKE: true,
   994  	ASTCKF: true,
   995  	ANEG:   true,
   996  	AVONE:  true,
   997  	AVZERO: true,
   998  }
   999  
  1000  var Links390x = obj.LinkArch{
  1001  	Arch:       sys.ArchS390X,
  1002  	Preprocess: preprocess,
  1003  	Assemble:   spanz,
  1004  	Follow:     follow,
  1005  	Progedit:   progedit,
  1006  	UnaryDst:   unaryDst,
  1007  }