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