github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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.Set(obj.AttrLocal, 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.Set(obj.AttrLocal, 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.Set(obj.AttrLocal, 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 && isint32(-p.From.Offset) {
   113  			p.From.Offset = -p.From.Offset
   114  			p.As = AADDC
   115  		}
   116  
   117  	case ASUB:
   118  		if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
   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 ASYNC,
   262  			AWORD:
   263  			q = p
   264  			p.Mark |= LABEL | SYNC
   265  			continue
   266  
   267  		case AMOVW, AMOVWZ, AMOVD:
   268  			q = p
   269  			if p.From.Reg >= REG_RESERVED || p.To.Reg >= REG_RESERVED {
   270  				p.Mark |= LABEL | SYNC
   271  			}
   272  			continue
   273  
   274  		case AFABS,
   275  			AFADD,
   276  			AFDIV,
   277  			AFMADD,
   278  			AFMOVD,
   279  			AFMOVS,
   280  			AFMSUB,
   281  			AFMUL,
   282  			AFNABS,
   283  			AFNEG,
   284  			ALEDBR,
   285  			ALDEBR,
   286  			AFSUB:
   287  			q = p
   288  
   289  			p.Mark |= FLOAT
   290  			continue
   291  
   292  		case ABL,
   293  			ABCL,
   294  			obj.ADUFFZERO,
   295  			obj.ADUFFCOPY:
   296  			cursym.Text.Mark &^= LEAF
   297  			fallthrough
   298  
   299  		case ABC,
   300  			ABEQ,
   301  			ABGE,
   302  			ABGT,
   303  			ABLE,
   304  			ABLT,
   305  			ABLEU,
   306  			ABLTU,
   307  			ABNE,
   308  			ABR,
   309  			ABVC,
   310  			ABVS,
   311  			ACMPBEQ,
   312  			ACMPBGE,
   313  			ACMPBGT,
   314  			ACMPBLE,
   315  			ACMPBLT,
   316  			ACMPBNE,
   317  			ACMPUBEQ,
   318  			ACMPUBGE,
   319  			ACMPUBGT,
   320  			ACMPUBLE,
   321  			ACMPUBLT,
   322  			ACMPUBNE:
   323  			p.Mark |= BRANCH
   324  			q = p
   325  			q1 = p.Pcond
   326  			if q1 != nil {
   327  				for q1.As == obj.ANOP {
   328  					q1 = q1.Link
   329  					p.Pcond = q1
   330  				}
   331  
   332  				if q1.Mark&LEAF == 0 {
   333  					q1.Mark |= LABEL
   334  				}
   335  			} else {
   336  				p.Mark |= LABEL
   337  			}
   338  			q1 = p.Link
   339  			if q1 != nil {
   340  				q1.Mark |= LABEL
   341  			}
   342  			continue
   343  
   344  		case AFCMPO, AFCMPU:
   345  			q = p
   346  			p.Mark |= FCMP | FLOAT
   347  			continue
   348  
   349  		case obj.ARET:
   350  			q = p
   351  			if p.Link != nil {
   352  				p.Link.Mark |= LABEL
   353  			}
   354  			continue
   355  
   356  		case obj.ANOP:
   357  			q1 = p.Link
   358  			q.Link = q1 /* q is non-nop */
   359  			q1.Mark |= p.Mark
   360  			continue
   361  
   362  		default:
   363  			q = p
   364  			continue
   365  		}
   366  	}
   367  
   368  	autosize := int32(0)
   369  	var p1 *obj.Prog
   370  	var p2 *obj.Prog
   371  	var pLast *obj.Prog
   372  	var pPre *obj.Prog
   373  	var pPreempt *obj.Prog
   374  	wasSplit := false
   375  	for p := cursym.Text; p != nil; p = p.Link {
   376  		pLast = p
   377  		switch p.As {
   378  		case obj.ATEXT:
   379  			autosize = int32(textstksiz)
   380  
   381  			if p.Mark&LEAF != 0 && autosize == 0 {
   382  				// A leaf function with no locals has no frame.
   383  				p.From3.Offset |= obj.NOFRAME
   384  			}
   385  
   386  			if p.From3.Offset&obj.NOFRAME == 0 {
   387  				// If there is a stack frame at all, it includes
   388  				// space to save the LR.
   389  				autosize += int32(ctxt.FixedFrameSize())
   390  			}
   391  
   392  			if p.Mark&LEAF != 0 && autosize < obj.StackSmall {
   393  				// A leaf function with a small stack can be marked
   394  				// NOSPLIT, avoiding a stack check.
   395  				p.From3.Offset |= obj.NOSPLIT
   396  			}
   397  
   398  			p.To.Offset = int64(autosize)
   399  
   400  			q = p
   401  
   402  			if p.From3.Offset&obj.NOSPLIT == 0 {
   403  				p, pPreempt = stacksplitPre(ctxt, p, autosize) // emit pre part of split check
   404  				pPre = p
   405  				wasSplit = true //need post part of split
   406  			}
   407  
   408  			if autosize != 0 {
   409  				// Make sure to save link register for non-empty frame, even if
   410  				// it is a leaf function, so that traceback works.
   411  				// Store link register before decrementing SP, so if a signal comes
   412  				// during the execution of the function prologue, the traceback
   413  				// code will not see a half-updated stack frame.
   414  				q = obj.Appendp(ctxt, p)
   415  				q.As = AMOVD
   416  				q.From.Type = obj.TYPE_REG
   417  				q.From.Reg = REG_LR
   418  				q.To.Type = obj.TYPE_MEM
   419  				q.To.Reg = REGSP
   420  				q.To.Offset = int64(-autosize)
   421  
   422  				q = obj.Appendp(ctxt, q)
   423  				q.As = AMOVD
   424  				q.From.Type = obj.TYPE_ADDR
   425  				q.From.Offset = int64(-autosize)
   426  				q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
   427  				q.To.Type = obj.TYPE_REG
   428  				q.To.Reg = REGSP
   429  				q.Spadj = autosize
   430  			} else if cursym.Text.Mark&LEAF == 0 {
   431  				// A very few functions that do not return to their caller
   432  				// (e.g. gogo) are not identified as leaves but still have
   433  				// no frame.
   434  				cursym.Text.Mark |= LEAF
   435  			}
   436  
   437  			if cursym.Text.Mark&LEAF != 0 {
   438  				cursym.Set(obj.AttrLeaf, true)
   439  				break
   440  			}
   441  
   442  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   443  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   444  				//
   445  				//	MOVD g_panic(g), R3
   446  				//	CMP R3, $0
   447  				//	BEQ end
   448  				//	MOVD panic_argp(R3), R4
   449  				//	ADD $(autosize+8), R1, R5
   450  				//	CMP R4, R5
   451  				//	BNE end
   452  				//	ADD $8, R1, R6
   453  				//	MOVD R6, panic_argp(R3)
   454  				// end:
   455  				//	NOP
   456  				//
   457  				// The NOP is needed to give the jumps somewhere to land.
   458  				// It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
   459  
   460  				q = obj.Appendp(ctxt, q)
   461  
   462  				q.As = AMOVD
   463  				q.From.Type = obj.TYPE_MEM
   464  				q.From.Reg = REGG
   465  				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   466  				q.To.Type = obj.TYPE_REG
   467  				q.To.Reg = REG_R3
   468  
   469  				q = obj.Appendp(ctxt, q)
   470  				q.As = ACMP
   471  				q.From.Type = obj.TYPE_REG
   472  				q.From.Reg = REG_R3
   473  				q.To.Type = obj.TYPE_CONST
   474  				q.To.Offset = 0
   475  
   476  				q = obj.Appendp(ctxt, q)
   477  				q.As = ABEQ
   478  				q.To.Type = obj.TYPE_BRANCH
   479  				p1 = q
   480  
   481  				q = obj.Appendp(ctxt, q)
   482  				q.As = AMOVD
   483  				q.From.Type = obj.TYPE_MEM
   484  				q.From.Reg = REG_R3
   485  				q.From.Offset = 0 // Panic.argp
   486  				q.To.Type = obj.TYPE_REG
   487  				q.To.Reg = REG_R4
   488  
   489  				q = obj.Appendp(ctxt, q)
   490  				q.As = AADD
   491  				q.From.Type = obj.TYPE_CONST
   492  				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
   493  				q.Reg = REGSP
   494  				q.To.Type = obj.TYPE_REG
   495  				q.To.Reg = REG_R5
   496  
   497  				q = obj.Appendp(ctxt, q)
   498  				q.As = ACMP
   499  				q.From.Type = obj.TYPE_REG
   500  				q.From.Reg = REG_R4
   501  				q.To.Type = obj.TYPE_REG
   502  				q.To.Reg = REG_R5
   503  
   504  				q = obj.Appendp(ctxt, q)
   505  				q.As = ABNE
   506  				q.To.Type = obj.TYPE_BRANCH
   507  				p2 = q
   508  
   509  				q = obj.Appendp(ctxt, q)
   510  				q.As = AADD
   511  				q.From.Type = obj.TYPE_CONST
   512  				q.From.Offset = ctxt.FixedFrameSize()
   513  				q.Reg = REGSP
   514  				q.To.Type = obj.TYPE_REG
   515  				q.To.Reg = REG_R6
   516  
   517  				q = obj.Appendp(ctxt, q)
   518  				q.As = AMOVD
   519  				q.From.Type = obj.TYPE_REG
   520  				q.From.Reg = REG_R6
   521  				q.To.Type = obj.TYPE_MEM
   522  				q.To.Reg = REG_R3
   523  				q.To.Offset = 0 // Panic.argp
   524  
   525  				q = obj.Appendp(ctxt, q)
   526  
   527  				q.As = obj.ANOP
   528  				p1.Pcond = q
   529  				p2.Pcond = q
   530  			}
   531  
   532  		case obj.ARET:
   533  			if p.From.Type == obj.TYPE_CONST {
   534  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   535  				break
   536  			}
   537  
   538  			retTarget := p.To.Sym
   539  
   540  			if cursym.Text.Mark&LEAF != 0 {
   541  				if autosize == 0 {
   542  					p.As = ABR
   543  					p.From = obj.Addr{}
   544  					if retTarget == nil {
   545  						p.To.Type = obj.TYPE_REG
   546  						p.To.Reg = REG_LR
   547  					} else {
   548  						p.To.Type = obj.TYPE_BRANCH
   549  						p.To.Sym = retTarget
   550  					}
   551  					p.Mark |= BRANCH
   552  					break
   553  				}
   554  
   555  				p.As = AADD
   556  				p.From.Type = obj.TYPE_CONST
   557  				p.From.Offset = int64(autosize)
   558  				p.To.Type = obj.TYPE_REG
   559  				p.To.Reg = REGSP
   560  				p.Spadj = -autosize
   561  
   562  				q = obj.Appendp(ctxt, p)
   563  				q.As = ABR
   564  				q.From = obj.Addr{}
   565  				q.To.Type = obj.TYPE_REG
   566  				q.To.Reg = REG_LR
   567  				q.Mark |= BRANCH
   568  				q.Spadj = autosize
   569  				break
   570  			}
   571  
   572  			p.As = AMOVD
   573  			p.From.Type = obj.TYPE_MEM
   574  			p.From.Reg = REGSP
   575  			p.From.Offset = 0
   576  			p.To.Type = obj.TYPE_REG
   577  			p.To.Reg = REG_LR
   578  
   579  			q = p
   580  
   581  			if autosize != 0 {
   582  				q = obj.Appendp(ctxt, q)
   583  				q.As = AADD
   584  				q.From.Type = obj.TYPE_CONST
   585  				q.From.Offset = int64(autosize)
   586  				q.To.Type = obj.TYPE_REG
   587  				q.To.Reg = REGSP
   588  				q.Spadj = -autosize
   589  			}
   590  
   591  			q = obj.Appendp(ctxt, q)
   592  			q.As = ABR
   593  			q.From = obj.Addr{}
   594  			if retTarget == nil {
   595  				q.To.Type = obj.TYPE_REG
   596  				q.To.Reg = REG_LR
   597  			} else {
   598  				q.To.Type = obj.TYPE_BRANCH
   599  				q.To.Sym = retTarget
   600  			}
   601  			q.Mark |= BRANCH
   602  			q.Spadj = autosize
   603  
   604  		case AADD:
   605  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   606  				p.Spadj = int32(-p.From.Offset)
   607  			}
   608  		}
   609  	}
   610  	if wasSplit {
   611  		pLast = stacksplitPost(ctxt, pLast, pPre, pPreempt, autosize) // emit post part of split check
   612  	}
   613  }
   614  
   615  /*
   616  // instruction scheduling
   617  	if(debug['Q'] == 0)
   618  		return;
   619  
   620  	curtext = nil;
   621  	q = nil;	// p - 1
   622  	q1 = firstp;	// top of block
   623  	o = 0;		// count of instructions
   624  	for(p = firstp; p != nil; p = p1) {
   625  		p1 = p->link;
   626  		o++;
   627  		if(p->mark & NOSCHED){
   628  			if(q1 != p){
   629  				sched(q1, q);
   630  			}
   631  			for(; p != nil; p = p->link){
   632  				if(!(p->mark & NOSCHED))
   633  					break;
   634  				q = p;
   635  			}
   636  			p1 = p;
   637  			q1 = p;
   638  			o = 0;
   639  			continue;
   640  		}
   641  		if(p->mark & (LABEL|SYNC)) {
   642  			if(q1 != p)
   643  				sched(q1, q);
   644  			q1 = p;
   645  			o = 1;
   646  		}
   647  		if(p->mark & (BRANCH|SYNC)) {
   648  			sched(q1, p);
   649  			q1 = p1;
   650  			o = 0;
   651  		}
   652  		if(o >= NSCHED) {
   653  			sched(q1, p);
   654  			q1 = p1;
   655  			o = 0;
   656  		}
   657  		q = p;
   658  	}
   659  */
   660  func stacksplitPre(ctxt *obj.Link, p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
   661  	var q *obj.Prog
   662  
   663  	// MOVD	g_stackguard(g), R3
   664  	p = obj.Appendp(ctxt, p)
   665  
   666  	p.As = AMOVD
   667  	p.From.Type = obj.TYPE_MEM
   668  	p.From.Reg = REGG
   669  	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
   670  	if ctxt.Cursym.CFunc() {
   671  		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
   672  	}
   673  	p.To.Type = obj.TYPE_REG
   674  	p.To.Reg = REG_R3
   675  
   676  	q = nil
   677  	if framesize <= obj.StackSmall {
   678  		// small stack: SP < stackguard
   679  		//	CMP	stackguard, SP
   680  
   681  		//p.To.Type = obj.TYPE_REG
   682  		//p.To.Reg = REGSP
   683  
   684  		// q1: BLT	done
   685  
   686  		p = obj.Appendp(ctxt, p)
   687  		//q1 = p
   688  		p.From.Type = obj.TYPE_REG
   689  		p.From.Reg = REG_R3
   690  		p.Reg = REGSP
   691  		p.As = ACMPUBGE
   692  		p.To.Type = obj.TYPE_BRANCH
   693  		//p = obj.Appendp(ctxt, p)
   694  
   695  		//p.As = ACMPU
   696  		//p.From.Type = obj.TYPE_REG
   697  		//p.From.Reg = REG_R3
   698  		//p.To.Type = obj.TYPE_REG
   699  		//p.To.Reg = REGSP
   700  
   701  		//p = obj.Appendp(ctxt, p)
   702  		//p.As = ABGE
   703  		//p.To.Type = obj.TYPE_BRANCH
   704  
   705  	} else if framesize <= obj.StackBig {
   706  		// large stack: SP-framesize < stackguard-StackSmall
   707  		//	ADD $-framesize, SP, R4
   708  		//	CMP stackguard, R4
   709  		p = obj.Appendp(ctxt, p)
   710  
   711  		p.As = AADD
   712  		p.From.Type = obj.TYPE_CONST
   713  		p.From.Offset = int64(-framesize)
   714  		p.Reg = REGSP
   715  		p.To.Type = obj.TYPE_REG
   716  		p.To.Reg = REG_R4
   717  
   718  		p = obj.Appendp(ctxt, p)
   719  		p.From.Type = obj.TYPE_REG
   720  		p.From.Reg = REG_R3
   721  		p.Reg = REG_R4
   722  		p.As = ACMPUBGE
   723  		p.To.Type = obj.TYPE_BRANCH
   724  
   725  	} else {
   726  		// Such a large stack we need to protect against wraparound.
   727  		// If SP is close to zero:
   728  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   729  		// The +StackGuard on both sides is required to keep the left side positive:
   730  		// SP is allowed to be slightly below stackguard. See stack.h.
   731  		//
   732  		// Preemption sets stackguard to StackPreempt, a very large value.
   733  		// That breaks the math above, so we have to check for that explicitly.
   734  		//	// stackguard is R3
   735  		//	CMP	R3, $StackPreempt
   736  		//	BEQ	label-of-call-to-morestack
   737  		//	ADD	$StackGuard, SP, R4
   738  		//	SUB	R3, R4
   739  		//	MOVD	$(framesize+(StackGuard-StackSmall)), TEMP
   740  		//	CMPUBGE	TEMP, R4
   741  		p = obj.Appendp(ctxt, p)
   742  
   743  		p.As = ACMP
   744  		p.From.Type = obj.TYPE_REG
   745  		p.From.Reg = REG_R3
   746  		p.To.Type = obj.TYPE_CONST
   747  		p.To.Offset = obj.StackPreempt
   748  
   749  		p = obj.Appendp(ctxt, p)
   750  		q = p
   751  		p.As = ABEQ
   752  		p.To.Type = obj.TYPE_BRANCH
   753  
   754  		p = obj.Appendp(ctxt, p)
   755  		p.As = AADD
   756  		p.From.Type = obj.TYPE_CONST
   757  		p.From.Offset = obj.StackGuard
   758  		p.Reg = REGSP
   759  		p.To.Type = obj.TYPE_REG
   760  		p.To.Reg = REG_R4
   761  
   762  		p = obj.Appendp(ctxt, p)
   763  		p.As = ASUB
   764  		p.From.Type = obj.TYPE_REG
   765  		p.From.Reg = REG_R3
   766  		p.To.Type = obj.TYPE_REG
   767  		p.To.Reg = REG_R4
   768  
   769  		p = obj.Appendp(ctxt, p)
   770  		p.As = AMOVD
   771  		p.From.Type = obj.TYPE_CONST
   772  		p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
   773  		p.To.Type = obj.TYPE_REG
   774  		p.To.Reg = REGTMP
   775  
   776  		p = obj.Appendp(ctxt, p)
   777  		p.From.Type = obj.TYPE_REG
   778  		p.From.Reg = REGTMP
   779  		p.Reg = REG_R4
   780  		p.As = ACMPUBGE
   781  		p.To.Type = obj.TYPE_BRANCH
   782  	}
   783  
   784  	return p, q
   785  }
   786  
   787  func stacksplitPost(ctxt *obj.Link, p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
   788  	// Now we are at the end of the function, but logically
   789  	// we are still in function prologue. We need to fix the
   790  	// SP data and PCDATA.
   791  	spfix := obj.Appendp(ctxt, p)
   792  	spfix.As = obj.ANOP
   793  	spfix.Spadj = -framesize
   794  
   795  	pcdata := obj.Appendp(ctxt, spfix)
   796  	pcdata.Pos = ctxt.Cursym.Text.Pos
   797  	pcdata.Mode = ctxt.Cursym.Text.Mode
   798  	pcdata.As = obj.APCDATA
   799  	pcdata.From.Type = obj.TYPE_CONST
   800  	pcdata.From.Offset = obj.PCDATA_StackMapIndex
   801  	pcdata.To.Type = obj.TYPE_CONST
   802  	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
   803  
   804  	// MOVD	LR, R5
   805  	p = obj.Appendp(ctxt, pcdata)
   806  	pPre.Pcond = p
   807  	p.As = AMOVD
   808  	p.From.Type = obj.TYPE_REG
   809  	p.From.Reg = REG_LR
   810  	p.To.Type = obj.TYPE_REG
   811  	p.To.Reg = REG_R5
   812  	if pPreempt != nil {
   813  		pPreempt.Pcond = p
   814  	}
   815  
   816  	// BL	runtime.morestack(SB)
   817  	p = obj.Appendp(ctxt, p)
   818  
   819  	p.As = ABL
   820  	p.To.Type = obj.TYPE_BRANCH
   821  	if ctxt.Cursym.CFunc() {
   822  		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
   823  	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
   824  		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
   825  	} else {
   826  		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack", 0)
   827  	}
   828  
   829  	// BR	start
   830  	p = obj.Appendp(ctxt, p)
   831  
   832  	p.As = ABR
   833  	p.To.Type = obj.TYPE_BRANCH
   834  	p.Pcond = ctxt.Cursym.Text.Link
   835  	return p
   836  }
   837  
   838  var unaryDst = map[obj.As]bool{
   839  	ASTCK:  true,
   840  	ASTCKC: true,
   841  	ASTCKE: true,
   842  	ASTCKF: true,
   843  	ANEG:   true,
   844  	ANEGW:  true,
   845  	AVONE:  true,
   846  	AVZERO: true,
   847  }
   848  
   849  var Links390x = obj.LinkArch{
   850  	Arch:       sys.ArchS390X,
   851  	Preprocess: preprocess,
   852  	Assemble:   spanz,
   853  	Progedit:   progedit,
   854  	UnaryDst:   unaryDst,
   855  }