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