github.com/bir3/gocompiler@v0.9.2202/src/cmd/internal/obj/loong64/obj.go (about)

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package loong64
     6  
     7  import (
     8  	"github.com/bir3/gocompiler/src/cmd/internal/obj"
     9  	"github.com/bir3/gocompiler/src/cmd/internal/objabi"
    10  	"github.com/bir3/gocompiler/src/cmd/internal/sys"
    11  	"github.com/bir3/gocompiler/src/internal/abi"
    12  	"log"
    13  	"math"
    14  )
    15  
    16  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
    17  	// Rewrite JMP/JAL to symbol as TYPE_BRANCH.
    18  	switch p.As {
    19  	case AJMP,
    20  		AJAL,
    21  		ARET,
    22  		obj.ADUFFZERO,
    23  		obj.ADUFFCOPY:
    24  		if p.To.Sym != nil {
    25  			p.To.Type = obj.TYPE_BRANCH
    26  		}
    27  	}
    28  
    29  	// Rewrite float constants to values stored in memory.
    30  	switch p.As {
    31  	case AMOVF:
    32  		if p.From.Type == obj.TYPE_FCONST {
    33  			f32 := float32(p.From.Val.(float64))
    34  			if math.Float32bits(f32) == 0 {
    35  				p.As = AMOVW
    36  				p.From.Type = obj.TYPE_REG
    37  				p.From.Reg = REGZERO
    38  				break
    39  			}
    40  			p.From.Type = obj.TYPE_MEM
    41  			p.From.Sym = ctxt.Float32Sym(f32)
    42  			p.From.Name = obj.NAME_EXTERN
    43  			p.From.Offset = 0
    44  		}
    45  
    46  	case AMOVD:
    47  		if p.From.Type == obj.TYPE_FCONST {
    48  			f64 := p.From.Val.(float64)
    49  			if math.Float64bits(f64) == 0 {
    50  				p.As = AMOVV
    51  				p.From.Type = obj.TYPE_REG
    52  				p.From.Reg = REGZERO
    53  				break
    54  			}
    55  			p.From.Type = obj.TYPE_MEM
    56  			p.From.Sym = ctxt.Float64Sym(f64)
    57  			p.From.Name = obj.NAME_EXTERN
    58  			p.From.Offset = 0
    59  		}
    60  	}
    61  
    62  	// Rewrite SUB constants into ADD.
    63  	switch p.As {
    64  	case ASUB:
    65  		if p.From.Type == obj.TYPE_CONST {
    66  			p.From.Offset = -p.From.Offset
    67  			p.As = AADD
    68  		}
    69  
    70  	case ASUBU:
    71  		if p.From.Type == obj.TYPE_CONST {
    72  			p.From.Offset = -p.From.Offset
    73  			p.As = AADDU
    74  		}
    75  
    76  	case ASUBV:
    77  		if p.From.Type == obj.TYPE_CONST {
    78  			p.From.Offset = -p.From.Offset
    79  			p.As = AADDV
    80  		}
    81  
    82  	case ASUBVU:
    83  		if p.From.Type == obj.TYPE_CONST {
    84  			p.From.Offset = -p.From.Offset
    85  			p.As = AADDVU
    86  		}
    87  	}
    88  
    89  	if ctxt.Flag_dynlink {
    90  		rewriteToUseGot(ctxt, p, newprog)
    91  	}
    92  }
    93  
    94  func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
    95  	//     ADUFFxxx $offset
    96  	// becomes
    97  	//     MOVV runtime.duffxxx@GOT, REGTMP
    98  	//     ADD $offset, REGTMP
    99  	//     JAL REGTMP
   100  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   101  		var sym *obj.LSym
   102  		if p.As == obj.ADUFFZERO {
   103  			sym = ctxt.Lookup("runtime.duffzero")
   104  		} else {
   105  			sym = ctxt.Lookup("runtime.duffcopy")
   106  		}
   107  		offset := p.To.Offset
   108  		p.As = AMOVV
   109  		p.From.Type = obj.TYPE_MEM
   110  		p.From.Sym = sym
   111  		p.From.Name = obj.NAME_GOTREF
   112  		p.To.Type = obj.TYPE_REG
   113  		p.To.Reg = REGTMP
   114  		p.To.Name = obj.NAME_NONE
   115  		p.To.Offset = 0
   116  		p.To.Sym = nil
   117  		p1 := obj.Appendp(p, newprog)
   118  		p1.As = AADDV
   119  		p1.From.Type = obj.TYPE_CONST
   120  		p1.From.Offset = offset
   121  		p1.To.Type = obj.TYPE_REG
   122  		p1.To.Reg = REGTMP
   123  		p2 := obj.Appendp(p1, newprog)
   124  		p2.As = AJAL
   125  		p2.To.Type = obj.TYPE_MEM
   126  		p2.To.Reg = REGTMP
   127  	}
   128  
   129  	// We only care about global data: NAME_EXTERN means a global
   130  	// symbol in the Go sense, and p.Sym.Local is true for a few
   131  	// internally defined symbols.
   132  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   133  		// MOVV $sym, Rx becomes MOVV sym@GOT, Rx
   134  		// MOVV $sym+<off>, Rx becomes MOVV sym@GOT, Rx; ADD <off>, Rx
   135  		if p.As != AMOVV {
   136  			ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -shared", p)
   137  		}
   138  		if p.To.Type != obj.TYPE_REG {
   139  			ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -shared", p)
   140  		}
   141  		p.From.Type = obj.TYPE_MEM
   142  		p.From.Name = obj.NAME_GOTREF
   143  		if p.From.Offset != 0 {
   144  			q := obj.Appendp(p, newprog)
   145  			q.As = AADDV
   146  			q.From.Type = obj.TYPE_CONST
   147  			q.From.Offset = p.From.Offset
   148  			q.To = p.To
   149  			p.From.Offset = 0
   150  		}
   151  	}
   152  	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
   153  		ctxt.Diag("don't know how to handle %v with -shared", p)
   154  	}
   155  
   156  	var source *obj.Addr
   157  	// MOVx sym, Ry becomes MOVV sym@GOT, REGTMP; MOVx (REGTMP), Ry
   158  	// MOVx Ry, sym becomes MOVV sym@GOT, REGTMP; MOVx Ry, (REGTMP)
   159  	// An addition may be inserted between the two MOVs if there is an offset.
   160  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   161  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   162  			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -shared", p)
   163  		}
   164  		source = &p.From
   165  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   166  		source = &p.To
   167  	} else {
   168  		return
   169  	}
   170  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   171  		return
   172  	}
   173  	if source.Sym.Type == objabi.STLSBSS {
   174  		return
   175  	}
   176  	if source.Type != obj.TYPE_MEM {
   177  		ctxt.Diag("don't know how to handle %v with -shared", p)
   178  	}
   179  	p1 := obj.Appendp(p, newprog)
   180  	p2 := obj.Appendp(p1, newprog)
   181  	p1.As = AMOVV
   182  	p1.From.Type = obj.TYPE_MEM
   183  	p1.From.Sym = source.Sym
   184  	p1.From.Name = obj.NAME_GOTREF
   185  	p1.To.Type = obj.TYPE_REG
   186  	p1.To.Reg = REGTMP
   187  
   188  	p2.As = p.As
   189  	p2.From = p.From
   190  	p2.To = p.To
   191  	if p.From.Name == obj.NAME_EXTERN {
   192  		p2.From.Reg = REGTMP
   193  		p2.From.Name = obj.NAME_NONE
   194  		p2.From.Sym = nil
   195  	} else if p.To.Name == obj.NAME_EXTERN {
   196  		p2.To.Reg = REGTMP
   197  		p2.To.Name = obj.NAME_NONE
   198  		p2.To.Sym = nil
   199  	} else {
   200  		return
   201  	}
   202  
   203  	obj.Nopout(p)
   204  }
   205  
   206  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   207  	c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym}
   208  
   209  	p := c.cursym.Func().Text
   210  	textstksiz := p.To.Offset
   211  
   212  	if textstksiz < 0 {
   213  		c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
   214  	}
   215  	if p.From.Sym.NoFrame() {
   216  		if textstksiz != 0 {
   217  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   218  		}
   219  	}
   220  
   221  	c.cursym.Func().Args = p.To.Val.(int32)
   222  	c.cursym.Func().Locals = int32(textstksiz)
   223  
   224  	/*
   225  	 * find leaf subroutines
   226  	 * expand RET
   227  	 */
   228  
   229  	for p := c.cursym.Func().Text; p != nil; p = p.Link {
   230  		switch p.As {
   231  		case obj.ATEXT:
   232  			p.Mark |= LABEL | LEAF | SYNC
   233  			if p.Link != nil {
   234  				p.Link.Mark |= LABEL
   235  			}
   236  
   237  		case AMOVW,
   238  			AMOVV:
   239  			if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL {
   240  				p.Mark |= LABEL | SYNC
   241  				break
   242  			}
   243  			if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL {
   244  				p.Mark |= LABEL | SYNC
   245  			}
   246  
   247  		case ASYSCALL,
   248  			AWORD:
   249  			p.Mark |= LABEL | SYNC
   250  
   251  		case ANOR:
   252  			if p.To.Type == obj.TYPE_REG {
   253  				if p.To.Reg == REGZERO {
   254  					p.Mark |= LABEL | SYNC
   255  				}
   256  			}
   257  
   258  		case AJAL,
   259  			obj.ADUFFZERO,
   260  			obj.ADUFFCOPY:
   261  			c.cursym.Func().Text.Mark &^= LEAF
   262  			fallthrough
   263  
   264  		case AJMP,
   265  			ABEQ,
   266  			ABGEU,
   267  			ABLTU,
   268  			ABLTZ,
   269  			ABNE,
   270  			ABFPT, ABFPF:
   271  			p.Mark |= BRANCH
   272  			q1 := p.To.Target()
   273  			if q1 != nil {
   274  				for q1.As == obj.ANOP {
   275  					q1 = q1.Link
   276  					p.To.SetTarget(q1)
   277  				}
   278  
   279  				if q1.Mark&LEAF == 0 {
   280  					q1.Mark |= LABEL
   281  				}
   282  			}
   283  			q1 = p.Link
   284  			if q1 != nil {
   285  				q1.Mark |= LABEL
   286  			}
   287  
   288  		case ARET:
   289  			if p.Link != nil {
   290  				p.Link.Mark |= LABEL
   291  			}
   292  		}
   293  	}
   294  
   295  	var mov, add obj.As
   296  
   297  	add = AADDV
   298  	mov = AMOVV
   299  
   300  	var q *obj.Prog
   301  	var q1 *obj.Prog
   302  	autosize := int32(0)
   303  	var p1 *obj.Prog
   304  	var p2 *obj.Prog
   305  	for p := c.cursym.Func().Text; p != nil; p = p.Link {
   306  		o := p.As
   307  		switch o {
   308  		case obj.ATEXT:
   309  			autosize = int32(textstksiz)
   310  
   311  			if p.Mark&LEAF != 0 && autosize == 0 {
   312  				// A leaf function with no locals has no frame.
   313  				p.From.Sym.Set(obj.AttrNoFrame, true)
   314  			}
   315  
   316  			if !p.From.Sym.NoFrame() {
   317  				// If there is a stack frame at all, it includes
   318  				// space to save the LR.
   319  				autosize += int32(c.ctxt.Arch.FixedFrameSize)
   320  			}
   321  
   322  			if autosize&4 != 0 {
   323  				autosize += 4
   324  			}
   325  
   326  			if autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
   327  				if c.cursym.Func().Text.From.Sym.NoSplit() {
   328  					if ctxt.Debugvlog {
   329  						ctxt.Logf("save suppressed in: %s\n", c.cursym.Name)
   330  					}
   331  
   332  					c.cursym.Func().Text.Mark |= LEAF
   333  				}
   334  			}
   335  
   336  			p.To.Offset = int64(autosize) - ctxt.Arch.FixedFrameSize
   337  
   338  			if c.cursym.Func().Text.Mark&LEAF != 0 {
   339  				c.cursym.Set(obj.AttrLeaf, true)
   340  				if p.From.Sym.NoFrame() {
   341  					break
   342  				}
   343  			}
   344  
   345  			if !p.From.Sym.NoSplit() {
   346  				p = c.stacksplit(p, autosize)	// emit split check
   347  			}
   348  
   349  			q = p
   350  
   351  			if autosize != 0 {
   352  				// Make sure to save link register for non-empty frame, even if
   353  				// it is a leaf function, so that traceback works.
   354  				// Store link register before decrement SP, so if a signal comes
   355  				// during the execution of the function prologue, the traceback
   356  				// code will not see a half-updated stack frame.
   357  				// This sequence is not async preemptible, as if we open a frame
   358  				// at the current SP, it will clobber the saved LR.
   359  				q = c.ctxt.StartUnsafePoint(q, c.newprog)
   360  
   361  				q = obj.Appendp(q, newprog)
   362  				q.As = mov
   363  				q.Pos = p.Pos
   364  				q.From.Type = obj.TYPE_REG
   365  				q.From.Reg = REGLINK
   366  				q.To.Type = obj.TYPE_MEM
   367  				q.To.Offset = int64(-autosize)
   368  				q.To.Reg = REGSP
   369  
   370  				q = obj.Appendp(q, newprog)
   371  				q.As = add
   372  				q.Pos = p.Pos
   373  				q.From.Type = obj.TYPE_CONST
   374  				q.From.Offset = int64(-autosize)
   375  				q.To.Type = obj.TYPE_REG
   376  				q.To.Reg = REGSP
   377  				q.Spadj = +autosize
   378  
   379  				q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
   380  
   381  				// On Linux, in a cgo binary we may get a SIGSETXID signal early on
   382  				// before the signal stack is set, as glibc doesn't allow us to block
   383  				// SIGSETXID. So a signal may land on the current stack and clobber
   384  				// the content below the SP. We store the LR again after the SP is
   385  				// decremented.
   386  				q = obj.Appendp(q, newprog)
   387  				q.As = mov
   388  				q.Pos = p.Pos
   389  				q.From.Type = obj.TYPE_REG
   390  				q.From.Reg = REGLINK
   391  				q.To.Type = obj.TYPE_MEM
   392  				q.To.Offset = 0
   393  				q.To.Reg = REGSP
   394  			}
   395  
   396  			if c.cursym.Func().Text.From.Sym.Wrapper() && c.cursym.Func().Text.Mark&LEAF == 0 {
   397  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   398  				//
   399  				//	MOV	g_panic(g), R20
   400  				//	BEQ	R20, end
   401  				//	MOV	panic_argp(R20), R24
   402  				//	ADD	$(autosize+FIXED_FRAME), R3, R30
   403  				//	BNE	R24, R30, end
   404  				//	ADD	$FIXED_FRAME, R3, R24
   405  				//	MOV	R24, panic_argp(R20)
   406  				// end:
   407  				//	NOP
   408  				//
   409  				// The NOP is needed to give the jumps somewhere to land.
   410  				// It is a liblink NOP, not a hardware NOP: it encodes to 0 instruction bytes.
   411  				//
   412  				// We don't generate this for leafs because that means the wrapped
   413  				// function was inlined into the wrapper.
   414  
   415  				q = obj.Appendp(q, newprog)
   416  
   417  				q.As = mov
   418  				q.From.Type = obj.TYPE_MEM
   419  				q.From.Reg = REGG
   420  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize)	// G.panic
   421  				q.To.Type = obj.TYPE_REG
   422  				q.To.Reg = REG_R20
   423  
   424  				q = obj.Appendp(q, newprog)
   425  				q.As = ABEQ
   426  				q.From.Type = obj.TYPE_REG
   427  				q.From.Reg = REG_R20
   428  				q.To.Type = obj.TYPE_BRANCH
   429  				q.Mark |= BRANCH
   430  				p1 = q
   431  
   432  				q = obj.Appendp(q, newprog)
   433  				q.As = mov
   434  				q.From.Type = obj.TYPE_MEM
   435  				q.From.Reg = REG_R20
   436  				q.From.Offset = 0	// Panic.argp
   437  				q.To.Type = obj.TYPE_REG
   438  				q.To.Reg = REG_R24
   439  
   440  				q = obj.Appendp(q, newprog)
   441  				q.As = add
   442  				q.From.Type = obj.TYPE_CONST
   443  				q.From.Offset = int64(autosize) + ctxt.Arch.FixedFrameSize
   444  				q.Reg = REGSP
   445  				q.To.Type = obj.TYPE_REG
   446  				q.To.Reg = REG_R30
   447  
   448  				q = obj.Appendp(q, newprog)
   449  				q.As = ABNE
   450  				q.From.Type = obj.TYPE_REG
   451  				q.From.Reg = REG_R24
   452  				q.Reg = REG_R30
   453  				q.To.Type = obj.TYPE_BRANCH
   454  				q.Mark |= BRANCH
   455  				p2 = q
   456  
   457  				q = obj.Appendp(q, newprog)
   458  				q.As = add
   459  				q.From.Type = obj.TYPE_CONST
   460  				q.From.Offset = ctxt.Arch.FixedFrameSize
   461  				q.Reg = REGSP
   462  				q.To.Type = obj.TYPE_REG
   463  				q.To.Reg = REG_R24
   464  
   465  				q = obj.Appendp(q, newprog)
   466  				q.As = mov
   467  				q.From.Type = obj.TYPE_REG
   468  				q.From.Reg = REG_R24
   469  				q.To.Type = obj.TYPE_MEM
   470  				q.To.Reg = REG_R20
   471  				q.To.Offset = 0	// Panic.argp
   472  
   473  				q = obj.Appendp(q, newprog)
   474  
   475  				q.As = obj.ANOP
   476  				p1.To.SetTarget(q)
   477  				p2.To.SetTarget(q)
   478  			}
   479  
   480  		case ARET:
   481  			if p.From.Type == obj.TYPE_CONST {
   482  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   483  				break
   484  			}
   485  
   486  			retSym := p.To.Sym
   487  			p.To.Name = obj.NAME_NONE	// clear fields as we may modify p to other instruction
   488  			p.To.Sym = nil
   489  
   490  			if c.cursym.Func().Text.Mark&LEAF != 0 {
   491  				if autosize == 0 {
   492  					p.As = AJMP
   493  					p.From = obj.Addr{}
   494  					if retSym != nil {	// retjmp
   495  						p.To.Type = obj.TYPE_BRANCH
   496  						p.To.Name = obj.NAME_EXTERN
   497  						p.To.Sym = retSym
   498  					} else {
   499  						p.To.Type = obj.TYPE_MEM
   500  						p.To.Reg = REGLINK
   501  						p.To.Offset = 0
   502  					}
   503  					p.Mark |= BRANCH
   504  					break
   505  				}
   506  
   507  				p.As = add
   508  				p.From.Type = obj.TYPE_CONST
   509  				p.From.Offset = int64(autosize)
   510  				p.To.Type = obj.TYPE_REG
   511  				p.To.Reg = REGSP
   512  				p.Spadj = -autosize
   513  
   514  				q = c.newprog()
   515  				q.As = AJMP
   516  				q.Pos = p.Pos
   517  				if retSym != nil {	// retjmp
   518  					q.To.Type = obj.TYPE_BRANCH
   519  					q.To.Name = obj.NAME_EXTERN
   520  					q.To.Sym = retSym
   521  				} else {
   522  					q.To.Type = obj.TYPE_MEM
   523  					q.To.Offset = 0
   524  					q.To.Reg = REGLINK
   525  				}
   526  				q.Mark |= BRANCH
   527  				q.Spadj = +autosize
   528  
   529  				q.Link = p.Link
   530  				p.Link = q
   531  				break
   532  			}
   533  
   534  			p.As = mov
   535  			p.From.Type = obj.TYPE_MEM
   536  			p.From.Offset = 0
   537  			p.From.Reg = REGSP
   538  			p.To.Type = obj.TYPE_REG
   539  			p.To.Reg = REGLINK
   540  
   541  			if autosize != 0 {
   542  				q = c.newprog()
   543  				q.As = add
   544  				q.Pos = p.Pos
   545  				q.From.Type = obj.TYPE_CONST
   546  				q.From.Offset = int64(autosize)
   547  				q.To.Type = obj.TYPE_REG
   548  				q.To.Reg = REGSP
   549  				q.Spadj = -autosize
   550  
   551  				q.Link = p.Link
   552  				p.Link = q
   553  			}
   554  
   555  			q1 = c.newprog()
   556  			q1.As = AJMP
   557  			q1.Pos = p.Pos
   558  			if retSym != nil {	// retjmp
   559  				q1.To.Type = obj.TYPE_BRANCH
   560  				q1.To.Name = obj.NAME_EXTERN
   561  				q1.To.Sym = retSym
   562  			} else {
   563  				q1.To.Type = obj.TYPE_MEM
   564  				q1.To.Offset = 0
   565  				q1.To.Reg = REGLINK
   566  			}
   567  			q1.Mark |= BRANCH
   568  			q1.Spadj = +autosize
   569  
   570  			q1.Link = q.Link
   571  			q.Link = q1
   572  
   573  		case AADD,
   574  			AADDU,
   575  			AADDV,
   576  			AADDVU:
   577  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   578  				p.Spadj = int32(-p.From.Offset)
   579  			}
   580  
   581  		case obj.AGETCALLERPC:
   582  			if cursym.Leaf() {
   583  				// MOV LR, Rd
   584  				p.As = mov
   585  				p.From.Type = obj.TYPE_REG
   586  				p.From.Reg = REGLINK
   587  			} else {
   588  				// MOV (RSP), Rd
   589  				p.As = mov
   590  				p.From.Type = obj.TYPE_MEM
   591  				p.From.Reg = REGSP
   592  			}
   593  		}
   594  
   595  		if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
   596  			f := c.cursym.Func()
   597  			if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
   598  				c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
   599  				if ctxt.Debugvlog || !ctxt.IsAsm {
   600  					ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
   601  					if !ctxt.IsAsm {
   602  						ctxt.Diag("invalid auto-SPWRITE in non-assembly")
   603  						ctxt.DiagFlush()
   604  						log.Fatalf("bad SPWRITE")
   605  					}
   606  				}
   607  			}
   608  		}
   609  	}
   610  }
   611  
   612  func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
   613  	var mov, add obj.As
   614  
   615  	add = AADDV
   616  	mov = AMOVV
   617  	if c.ctxt.Flag_maymorestack != "" {
   618  		// Save LR and REGCTXT.
   619  		frameSize := 2 * c.ctxt.Arch.PtrSize
   620  
   621  		p = c.ctxt.StartUnsafePoint(p, c.newprog)
   622  
   623  		// Spill Arguments. This has to happen before we open
   624  		// any more frame space.
   625  		p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
   626  
   627  		// MOV	REGLINK, -8/-16(SP)
   628  		p = obj.Appendp(p, c.newprog)
   629  		p.As = mov
   630  		p.From.Type = obj.TYPE_REG
   631  		p.From.Reg = REGLINK
   632  		p.To.Type = obj.TYPE_MEM
   633  		p.To.Offset = int64(-frameSize)
   634  		p.To.Reg = REGSP
   635  
   636  		// MOV	REGCTXT, -4/-8(SP)
   637  		p = obj.Appendp(p, c.newprog)
   638  		p.As = mov
   639  		p.From.Type = obj.TYPE_REG
   640  		p.From.Reg = REGCTXT
   641  		p.To.Type = obj.TYPE_MEM
   642  		p.To.Offset = -int64(c.ctxt.Arch.PtrSize)
   643  		p.To.Reg = REGSP
   644  
   645  		// ADD	$-8/$-16, SP
   646  		p = obj.Appendp(p, c.newprog)
   647  		p.As = add
   648  		p.From.Type = obj.TYPE_CONST
   649  		p.From.Offset = int64(-frameSize)
   650  		p.To.Type = obj.TYPE_REG
   651  		p.To.Reg = REGSP
   652  		p.Spadj = int32(frameSize)
   653  
   654  		// JAL	maymorestack
   655  		p = obj.Appendp(p, c.newprog)
   656  		p.As = AJAL
   657  		p.To.Type = obj.TYPE_BRANCH
   658  		// See ../x86/obj6.go
   659  		p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
   660  		p.Mark |= BRANCH
   661  
   662  		// Restore LR and REGCTXT.
   663  
   664  		// MOV	0(SP), REGLINK
   665  		p = obj.Appendp(p, c.newprog)
   666  		p.As = mov
   667  		p.From.Type = obj.TYPE_MEM
   668  		p.From.Offset = 0
   669  		p.From.Reg = REGSP
   670  		p.To.Type = obj.TYPE_REG
   671  		p.To.Reg = REGLINK
   672  
   673  		// MOV	4/8(SP), REGCTXT
   674  		p = obj.Appendp(p, c.newprog)
   675  		p.As = mov
   676  		p.From.Type = obj.TYPE_MEM
   677  		p.From.Offset = int64(c.ctxt.Arch.PtrSize)
   678  		p.From.Reg = REGSP
   679  		p.To.Type = obj.TYPE_REG
   680  		p.To.Reg = REGCTXT
   681  
   682  		// ADD	$8/$16, SP
   683  		p = obj.Appendp(p, c.newprog)
   684  		p.As = add
   685  		p.From.Type = obj.TYPE_CONST
   686  		p.From.Offset = int64(frameSize)
   687  		p.To.Type = obj.TYPE_REG
   688  		p.To.Reg = REGSP
   689  		p.Spadj = int32(-frameSize)
   690  
   691  		// Unspill arguments
   692  		p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
   693  		p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
   694  	}
   695  
   696  	// Jump back to here after morestack returns.
   697  	startPred := p
   698  
   699  	// MOV	g_stackguard(g), R20
   700  	p = obj.Appendp(p, c.newprog)
   701  
   702  	p.As = mov
   703  	p.From.Type = obj.TYPE_MEM
   704  	p.From.Reg = REGG
   705  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize)	// G.stackguard0
   706  	if c.cursym.CFunc() {
   707  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize)	// G.stackguard1
   708  	}
   709  	p.To.Type = obj.TYPE_REG
   710  	p.To.Reg = REG_R20
   711  
   712  	// Mark the stack bound check and morestack call async nonpreemptible.
   713  	// If we get preempted here, when resumed the preemption request is
   714  	// cleared, but we'll still call morestack, which will double the stack
   715  	// unnecessarily. See issue #35470.
   716  	p = c.ctxt.StartUnsafePoint(p, c.newprog)
   717  
   718  	var q *obj.Prog
   719  	if framesize <= abi.StackSmall {
   720  		// small stack: SP < stackguard
   721  		//	AGTU	SP, stackguard, R20
   722  		p = obj.Appendp(p, c.newprog)
   723  
   724  		p.As = ASGTU
   725  		p.From.Type = obj.TYPE_REG
   726  		p.From.Reg = REGSP
   727  		p.Reg = REG_R20
   728  		p.To.Type = obj.TYPE_REG
   729  		p.To.Reg = REG_R20
   730  	} else {
   731  		// large stack: SP-framesize < stackguard-StackSmall
   732  		offset := int64(framesize) - abi.StackSmall
   733  		if framesize > abi.StackBig {
   734  			// Such a large stack we need to protect against underflow.
   735  			// The runtime guarantees SP > objabi.StackBig, but
   736  			// framesize is large enough that SP-framesize may
   737  			// underflow, causing a direct comparison with the
   738  			// stack guard to incorrectly succeed. We explicitly
   739  			// guard against underflow.
   740  			//
   741  			//      SGTU    $(framesize-StackSmall), SP, R24
   742  			//      BNE     R24, label-of-call-to-morestack
   743  
   744  			p = obj.Appendp(p, c.newprog)
   745  			p.As = ASGTU
   746  			p.From.Type = obj.TYPE_CONST
   747  			p.From.Offset = offset
   748  			p.Reg = REGSP
   749  			p.To.Type = obj.TYPE_REG
   750  			p.To.Reg = REG_R24
   751  
   752  			p = obj.Appendp(p, c.newprog)
   753  			q = p
   754  			p.As = ABNE
   755  			p.From.Type = obj.TYPE_REG
   756  			p.From.Reg = REG_R24
   757  			p.To.Type = obj.TYPE_BRANCH
   758  			p.Mark |= BRANCH
   759  		}
   760  
   761  		p = obj.Appendp(p, c.newprog)
   762  
   763  		p.As = add
   764  		p.From.Type = obj.TYPE_CONST
   765  		p.From.Offset = -offset
   766  		p.Reg = REGSP
   767  		p.To.Type = obj.TYPE_REG
   768  		p.To.Reg = REG_R24
   769  
   770  		p = obj.Appendp(p, c.newprog)
   771  		p.As = ASGTU
   772  		p.From.Type = obj.TYPE_REG
   773  		p.From.Reg = REG_R24
   774  		p.Reg = REG_R20
   775  		p.To.Type = obj.TYPE_REG
   776  		p.To.Reg = REG_R20
   777  	}
   778  
   779  	// q1: BNE	R20, done
   780  	p = obj.Appendp(p, c.newprog)
   781  	q1 := p
   782  
   783  	p.As = ABNE
   784  	p.From.Type = obj.TYPE_REG
   785  	p.From.Reg = REG_R20
   786  	p.To.Type = obj.TYPE_BRANCH
   787  	p.Mark |= BRANCH
   788  
   789  	// MOV	LINK, R31
   790  	p = obj.Appendp(p, c.newprog)
   791  
   792  	p.As = mov
   793  	p.From.Type = obj.TYPE_REG
   794  	p.From.Reg = REGLINK
   795  	p.To.Type = obj.TYPE_REG
   796  	p.To.Reg = REG_R31
   797  	if q != nil {
   798  		q.To.SetTarget(p)
   799  		p.Mark |= LABEL
   800  	}
   801  
   802  	p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
   803  
   804  	// Spill the register args that could be clobbered by the
   805  	// morestack code
   806  	p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
   807  
   808  	// JAL	runtime.morestack(SB)
   809  	p = obj.Appendp(p, c.newprog)
   810  
   811  	p.As = AJAL
   812  	p.To.Type = obj.TYPE_BRANCH
   813  	if c.cursym.CFunc() {
   814  		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
   815  	} else if !c.cursym.Func().Text.From.Sym.NeedCtxt() {
   816  		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
   817  	} else {
   818  		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
   819  	}
   820  	p.Mark |= BRANCH
   821  
   822  	p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
   823  	p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
   824  
   825  	// JMP	start
   826  	p = obj.Appendp(p, c.newprog)
   827  
   828  	p.As = AJMP
   829  	p.To.Type = obj.TYPE_BRANCH
   830  	p.To.SetTarget(startPred.Link)
   831  	startPred.Link.Mark |= LABEL
   832  	p.Mark |= BRANCH
   833  
   834  	// placeholder for q1's jump target
   835  	p = obj.Appendp(p, c.newprog)
   836  
   837  	p.As = obj.ANOP	// zero-width place holder
   838  	q1.To.SetTarget(p)
   839  
   840  	return p
   841  }
   842  
   843  func (c *ctxt0) addnop(p *obj.Prog) {
   844  	q := c.newprog()
   845  	q.As = ANOOP
   846  	q.Pos = p.Pos
   847  	q.Link = p.Link
   848  	p.Link = q
   849  }
   850  
   851  var Linkloong64 = obj.LinkArch{
   852  	Arch:		sys.ArchLoong64,
   853  	Init:		buildop,
   854  	Preprocess:	preprocess,
   855  	Assemble:	span0,
   856  	Progedit:	progedit,
   857  	DWARFRegisters:	LOONG64DWARFRegisters,
   858  }