Source file src/cmd/internal/obj/loong64/obj.go

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

View as plain text